-
Notifications
You must be signed in to change notification settings - Fork 120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Posets in OSCAR #3610
base: master
Are you sure you want to change the base?
Posets in OSCAR #3610
Changes from all commits
8d470c7
02adfb2
9e19fd0
ef8ca15
d0b4b98
2d78e9f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
# Poset | ||
|
||
struct Poset | ||
cov::Matrix{Int} # covering relations of the poset | ||
rel::BitMatrix # general relations | ||
set::BitMatrix # indicates whether a general relation was computed | ||
elems::Vector{Symbol} # symbols to use for the elements | ||
end | ||
|
||
function Base.length(P::Poset) | ||
return length(P.elems) | ||
end | ||
|
||
function Base.show(io::IO, P::Poset) | ||
print(io, "Poset with $(ItemQuantity(length(P), "element"))") | ||
end | ||
|
||
# PosetElem | ||
|
||
struct PosetElem | ||
parent::Poset | ||
i::Int | ||
end | ||
|
||
function index(x::PosetElem) | ||
return x.i | ||
end | ||
|
||
function parent(x::PosetElem) | ||
return x.parent | ||
end | ||
|
||
function Base.show(io::IO, elem::PosetElem) | ||
return parent(elem).elems[index(elem)] | ||
end | ||
|
||
# MaximalChainsIterator | ||
|
||
struct MaximalChainsIterator | ||
poset::Poset | ||
inplace::Bool | ||
|
||
function MaximalChainsIterator(P::Poset, inplace::Bool=false) | ||
return new(P, inplace) | ||
end | ||
end | ||
|
||
# Poset constructors | ||
|
||
@doc raw""" | ||
poset(cov::Matrix{Int}, elems::Vector{<:VarName}) -> Poset | ||
|
||
Construct a poset from covering relations `cov`, given in the form of the adjacency matrix | ||
of a Hasse diagram. The covering relations must be given in topological order, i.e. `cov` | ||
must be strictly upper triangular. By default the elements of the poset are named `x_i`. | ||
""" | ||
function poset(cov::Matrix{Int}, elems::Vector{<:VarName}=["x_$i" for i in 1:ncols(cov)]) | ||
@req is_upper_triangular(cov, 1) "matrix must be strictly upper triangular" | ||
@req nrows(cov) == ncols(cov) "must be a square matrix" | ||
@req ncols(cov) == length(elems) "size of matrix must match number of elements" | ||
@req allunique(elems) "element names must be unique" | ||
|
||
d = nrows(cov) | ||
rel = BitMatrix(!iszero(cov[i, j]) for i in 1:d, j in 1:d) | ||
return Poset(cov, rel, copy(rel), Symbol.(elems)) | ||
end | ||
|
||
# PosetElem constructors | ||
|
||
function (P::Poset)(i::Int) | ||
@req 1 <= i <= length(P.elems) "index out of range" | ||
return PosetElem(P, i) | ||
end | ||
|
||
function (P::Poset)(elem::VarName) | ||
i = findfirst(==(Symbol(elem)), P.elems) | ||
if isnothing(i) | ||
error("unknown element") | ||
end | ||
|
||
return PosetElem(P, i) | ||
end | ||
|
||
# MaximalChainsIterator constructors | ||
|
||
@doc raw""" | ||
maximal_chains(P::Poset) -> MaximalChainsIterator | ||
|
||
Returns an iterator over the maximal chains of `P`. | ||
""" | ||
function maximal_chains(P::Poset) | ||
return MaximalChainsIterator(P) | ||
end | ||
|
||
# PosetElem functions | ||
|
||
function Base.:(<)(x::PosetElem, y::PosetElem) | ||
@req parent(x) === parent(y) "elements must belong to the same poset" | ||
ps = parent(x) | ||
|
||
# upper triangularity | ||
if y.i <= x.i | ||
return false | ||
end | ||
|
||
# linearised index | ||
len = ncols(ps.cov) | ||
|
||
# fast path | ||
if ps.set[x.i, y.i] | ||
return ps.rel[x.i, y.i] | ||
end | ||
|
||
# slow path using covering relations | ||
q = Int[x.i] | ||
|
||
while !isempty(q) | ||
@label outer | ||
n = last(q) | ||
|
||
# because of upper triangularity we only need to go to y.i | ||
for k in (n + 1):(y.i) | ||
if !iszero(ps.cov[n, k]) | ||
# set the relation for all previous elements | ||
for m in q | ||
ps.set[m, k] = true | ||
ps.rel[m, k] = true | ||
end | ||
|
||
# we are done | ||
if k == y.i | ||
return ps.rel[x.i, y.i] | ||
end | ||
|
||
if ps.set[k, y.i] | ||
# check if can take the fast path | ||
if ps.rel[k, y.i] | ||
# set relation for elements in the stack | ||
for m in q | ||
ps.set[m, y.i] = true | ||
ps.rel[m, y.i] = true | ||
end | ||
return ps.rel[x.i, y.i] | ||
else | ||
# k is not comparable to y | ||
continue | ||
end | ||
end | ||
|
||
# add k to the stack and continue from k | ||
push!(q, k) | ||
@goto outer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can just replace this goto by a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I replace this with break, I will have to introduce a variable and an if statement after for loop. In the end I felt that goto is easier and the flow is clear too. |
||
end | ||
end | ||
|
||
# we now know that n is not comparable y | ||
ps.set[n, y.i] = true | ||
ps.rel[n, y.i] = false | ||
|
||
# continue with previous element | ||
pop!(q) | ||
end | ||
|
||
return false | ||
end | ||
|
||
# MaximalChainsIterator implementation | ||
|
||
function Base.IteratorSize(::Type{MaximalChainsIterator}) | ||
return Base.SizeUnknown() | ||
end | ||
|
||
function Base.eltype(::Type{MaximalChainsIterator}) | ||
return Vector{Int} | ||
end | ||
|
||
function Base.iterate(iter::MaximalChainsIterator, chain::Vector{Int}=[1, 1]) | ||
j = 0 | ||
while true | ||
s = pop!(chain) + 1 | ||
if isempty(chain) | ||
return nothing | ||
end | ||
|
||
j = findnext(!=(0), iter.poset.cov[last(chain), :], s) | ||
if !isnothing(j) | ||
break | ||
end | ||
end | ||
|
||
while !isnothing(j) | ||
push!(chain, j) | ||
j = findnext(!=(0), iter.poset.cov[last(chain), :], j) | ||
end | ||
|
||
if iter.inplace | ||
return chain, chain | ||
end | ||
return deepcopy(chain), chain | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
module Posets | ||
|
||
using ..Oscar | ||
|
||
import Base: length, parent | ||
import Oscar: index | ||
|
||
export MaximalChainsIterator | ||
export Poset, PosetElem | ||
|
||
export maximal_chains | ||
export poset | ||
|
||
include("Poset.jl") | ||
|
||
end | ||
|
||
using .Posets | ||
|
||
export MaximalChainsIterator | ||
export Poset, PosetElem | ||
|
||
export maximal_chains | ||
export poset |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
@testset "Posets" begin | ||
# covering relations, A2 adjoint rep | ||
a2_adj_cov = [ | ||
0 1 1 0 0 0 | ||
0 0 0 2 1 0 | ||
0 0 0 1 2 0 | ||
0 0 0 0 0 1 | ||
0 0 0 0 0 1 | ||
0 0 0 0 0 0 | ||
] | ||
|
||
# general relations, A2 adjoint rep | ||
a2_adj_rel = BitMatrix( | ||
[ | ||
0 1 1 1 1 1 | ||
0 0 0 1 1 1 | ||
0 0 0 1 1 1 | ||
0 0 0 0 0 1 | ||
0 0 0 0 0 1 | ||
0 0 0 0 0 0 | ||
] | ||
) | ||
|
||
# covering relations, B2 adjoint rep | ||
b2_adj_cov = [ | ||
0 1 1 0 0 0 0 0 | ||
0 0 0 3 1 0 0 0 | ||
0 0 0 1 2 0 0 0 | ||
0 0 0 0 0 2 1 0 | ||
0 0 0 0 0 1 3 0 | ||
0 0 0 0 0 0 0 1 | ||
0 0 0 0 0 0 0 1 | ||
0 0 0 0 0 0 0 0 | ||
] | ||
|
||
# general relations, B2 adjoint rep | ||
b2_adj_rel = BitMatrix( | ||
[ | ||
0 1 1 1 1 1 1 1 | ||
0 0 0 1 1 1 1 1 | ||
0 0 0 1 1 1 1 1 | ||
0 0 0 0 0 1 1 1 | ||
0 0 0 0 0 1 1 1 | ||
0 0 0 0 0 0 0 1 | ||
0 0 0 0 0 0 0 1 | ||
0 0 0 0 0 0 0 0 | ||
], | ||
) | ||
|
||
@testset "<(x::PosetElem, y::PosetElem)" begin | ||
# rel: expected relations | ||
function test_poset(cov::Matrix{Int}, rel::BitMatrix) | ||
sz = ncols(cov) | ||
for _ in 1:10 | ||
ps = poset(cov) | ||
for _ in 1:5 | ||
x = rand(1:sz) | ||
y = rand(1:sz) | ||
|
||
@test rel[x, y] == (ps(x) < ps(y)) | ||
@test all( | ||
ps.set[i, j] == false || ps.rel[i, j] == rel[i, j] for i in 1:sz for | ||
j in (i + 1):sz | ||
) | ||
end | ||
end | ||
end | ||
|
||
test_poset(a2_adj_cov, a2_adj_rel) | ||
test_poset(b2_adj_cov, b2_adj_rel) | ||
end | ||
|
||
@testset "iterate(::MaximalChainsIterator, ::Vector{Int})" begin | ||
ps = poset(a2_adj_cov) | ||
@test collect(maximal_chains(ps)) == | ||
[[1, 2, 4, 6], [1, 2, 5, 6], [1, 3, 4, 6], [1, 3, 5, 6]] | ||
|
||
ps = poset(b2_adj_cov) | ||
@test collect(maximal_chains(ps)) == [ | ||
[1, 2, 4, 6, 8], | ||
[1, 2, 4, 7, 8], | ||
[1, 2, 5, 6, 8], | ||
[1, 2, 5, 7, 8], | ||
[1, 3, 4, 6, 8], | ||
[1, 3, 4, 7, 8], | ||
[1, 3, 5, 6, 8], | ||
[1, 3, 5, 7, 8], | ||
] | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The already existing
index
methods do something completely different. Does somebody know of other occurrences in the Oscar world of something like this and how we call it there?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its mostly called
data
to access the defining datum...