Skip to content

Commit

Permalink
Merge pull request #52 from kalmarek/enh/WeightedLex
Browse files Browse the repository at this point in the history
add weighted lex ordering
  • Loading branch information
Marek Kaluba authored Dec 18, 2021
2 parents 74f6bdb + e275a07 commit 5419264
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 64 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "KnuthBendix"
uuid = "c2604015-7b3d-4a30-8a26-9074551ec60a"
authors = ["Marek Kaluba <[email protected]>", "Mikołaj Pabiszczak <[email protected]>"]
version = "0.3.0"
version = "0.3.1"

[deps]
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
Expand Down
121 changes: 81 additions & 40 deletions src/orderings.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Base.Order: lt, Ordering
export LenLex, WreathOrder, RecursivePathOrder
export LenLex, WreathOrder, RecursivePathOrder, WeightedLex

"""
WordOrdering <: Ordering
Abstract type representing word orderings.
Abstract type representing well-orderings of words which are translation invariant.
The subtypes of `WordOrdering` should contain a field `A` storing the `Alphabet`
over which a particular order is defined. Moreover, an `Base.lt` method should be
defined to compare whether one word is less than the other (in the ordering
defined).
The subtypes of `WordOrdering` should implement:
* `alphabet` - function (not necessary if they contain a field `A` storing the `Alphabet`), over which a particular order is defined;
* `Base.Order.lt(o::WordOrdering, a, b)` - method to test whether `a` is less than `b` according to the ordering `o`.
* `Base.hash` - a simple hashing function.
"""
abstract type WordOrdering <: Ordering end

Expand All @@ -17,25 +17,19 @@ Base.:(==)(o1::T, o2::T) where {T<:WordOrdering} = alphabet(o1) == alphabet(o2)

"""
struct LenLex{T} <: WordOrdering
LenLex(A::Alphabet)
Basic structure representing Length+Lexicographic (left-to-right) ordering of
the words over given Alphabet. Lexicographic ordering of an Alphabet is
implicitly specified inside Alphabet struct.
`LenLex` order compares words first by length and then by lexicographic (left-to-right) order determined by the order of letters `A`.
"""
struct LenLex{T} <: WordOrdering
A::Alphabet{T}
end

Base.hash(o::LenLex, h::UInt) = hash(o.A, hash(h, hash(LenLex)))

"""
lt(o::LenLex, p::AbstractWord, q::AbstractWord)
Return whether the first word is less then the other one in a given LenLex ordering.
"""
function lt(o::LenLex, p::AbstractWord, q::AbstractWord)
if length(p) == length(q)
for (a, b) in zip(p,q)
for (a, b) in zip(p, q)
a == b || return isless(a, b) # comparing only on positive pointer values
end
return false
Expand All @@ -44,25 +38,26 @@ function lt(o::LenLex, p::AbstractWord, q::AbstractWord)
end
end


"""
struct WreathOrder{T} <: WordOrdering
WreathOrder(A::Alphabet)
Structure representing Basic Wreath-Product ordering (determined by the Lexicographic
ordering of the Alphabet) of the words over given Alphabet. This Lexicographic
ordering of an Alphabet is implicitly specified inside Alphabet struct.
`WreathOrder` compares words wreath-product ordering of words over the given `Alphabet`. Internal lexicographic ordering is determined by the order of letters `A`.
Here wreath product refers to
> `⟨A[1]⟩ ≀ ⟨A[2]⟩ ≀ … ≀ ⟨A[n]⟩`,
where `⟨A[i]⟩` denotes the monoid generated by `i`-th letter.
For the precise definition see e.g.
> Charles C. Sims, _Computation with finitely presented groups_
> Cambridge University Press 1994,
> pages 46-47.
"""
struct WreathOrder{T} <: WordOrdering
A::Alphabet{T}
end

Base.hash(o::WreathOrder, h::UInt) = hash(o.A, hash(h, hash(WreathOrder)))

"""
lt(o::WreathOrder, p::AbstractWord, q::AbstractWord)
Return whether the first word is less then the other one in a given WreathOrder ordering.
"""
function lt(o::WreathOrder, p::AbstractWord, q::AbstractWord)
iprefix = lcp(p, q) + 1

Expand All @@ -89,33 +84,28 @@ function lt(o::WreathOrder, p::AbstractWord, q::AbstractWord)
# i.e. head_p_len == head_q_len
first_pp = findfirst(isequal(max_p), pp)
first_qq = findfirst(isequal(max_p), qq)
return @views lt(o, pp[1:first_pp - 1], qq[1:first_qq - 1])
return @views lt(o, pp[1:first_pp-1], qq[1:first_qq-1])
end


"""
struct RecursivePathOrder{T} <: WordOrdering
RecursivePathOrder(A::Alphabet)
`RecursivePathOrder` represents a rewriting ordering of words over the given `Alphabet`.
Internal lexicographic ordering is determined by the order of letters `A`.
Structure representing Recursive Path Ordering (determined by the Lexicographic
ordering of the Alphabet) of the words over given Alphabet. This Lexicographic
ordering of an Alphabet is implicitly specified inside Alphabet struct.
For the definition see
> Susan M. Hermiller, Rewriting systems for Coxeter groups
> _Journal of Pure and Applied Algebra_
> Volume 92, Issue 2, 7 March 1994, Pages 137-148.
For the precise definition see
> Susan M. Hermiller, Rewriting systems for Coxeter groups,
> _Journal of Pure and Applied Algebra_,
> Volume 92, Issue 2, 7 March 1994, pages 137-148.
"""
struct RecursivePathOrder{T} <: WordOrdering
A::Alphabet{T}
end

Base.hash(o::RecursivePathOrder, h::UInt) = hash(o.A, hash(h, hash(RecursivePathOrder)))

"""
lt(o::RecursivePathOrder, p::AbstractWord, q::AbstractWord)
Base.hash(o::RecursivePathOrder, h::UInt) =
hash(o.A, hash(h, hash(RecursivePathOrder)))

Return whether the first word is less then the other one in a given
RecursivePathOrder ordering.
"""
function lt(o::RecursivePathOrder, p::AbstractWord, q::AbstractWord)
isone(p) && return !isone(q)
isone(q) && return false
Expand All @@ -132,3 +122,54 @@ function lt(o::RecursivePathOrder, p::AbstractWord, q::AbstractWord)
end
return false
end

"""
struct WeightedLex{T} <: WordOrdering
WeightedLex(A::Alphabet, weights::AbstractVector)
`WeightedLex` order compares words first according to their weight and then
by the lexicographic order determined by the order of letters `A`.
The `weight` array assigns weights to each letter and the weight of a word is
simply the sum of weights of all letters.
The `LenLex` ordering is a special case of `WeightedLex` when all weights are equal to `1`.
!!! note:
Since empty word is assigned a value of `zero(eltype(weights))` a vector of
positive weights is strongly recommended.
"""
struct WeightedLex{T,S} <: WordOrdering
A::Alphabet{T}
weights::Vector{S}

function WeightedLex(A::Alphabet{T}, weights::AbstractVector{S}) where {T,S}
@assert length(weights) == length(A)
@assert all(w-> w >=(zero(S)), weights)
return new{T,S}(A, weights)
end
end

Base.hash(wl::WeightedLex, h::UInt) = hash(wl.weights, hash(wl.lenlex, h))

Base.@propagate_inbounds weight(o::WeightedLex, l::Integer) = o.weights[l]

function weight(o::WeightedLex, p::AbstractWord)
isone(p) && return zero(eltype(o.weights))
return @inbounds sum(weight(o, l) for l in p)
end

function Base.Order.lt(o::WeightedLex, p::AbstractWord, q::AbstractWord)
S = eltype(o.weights)

weight_p = weight(o, p)
weight_q = weight(o, q)

if weight_p == weight_q
for (a, b) in zip(p, q)
# comparing only on positive pointer values
a == b || return isless(a, b)
end
return false
else
return isless(weight_p, weight_q)
end
end
11 changes: 11 additions & 0 deletions test/orderings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,15 @@
@test sort(a, order = rpo) == [ε, w1, w14, w214, w1224]
@test sort(b, order = rpo) == [ε, w1, w2, w214, w41, w241, w32141]

@test_throws AssertionError WeightedLex(A, [1,2,3])
@test_throws AssertionError WeightedLex(A, [1,2, 3,-4])

wtlex = WeightedLex(A, [1,2,3,4])

@test lt(wtlex, one(Word{Int}), Word([1]))
@test !lt(wtlex, Word([1]), Word([1]))
@test lt(wtlex, Word([1,1]), Word([2]))
@test lt(wtlex, Word([2]), Word([1,1,1]))
@test lt(wtlex, Word([2,4]), Word([4,2]))
@test lt(wtlex, Word([2,4]), Word([4,1,1]))
end
Loading

2 comments on commit 5419264

@kalmarek
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/50790

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.3.1 -m "<description of version>" 5419264ec0c79ddd04dbc4f99062c83c26d401cf
git push origin v0.3.1

Please sign in to comment.