diff --git a/src/Combinatorics/PartiallyOrderedSet/functions.jl b/src/Combinatorics/PartiallyOrderedSet/functions.jl new file mode 100644 index 000000000000..f9ba5aa76d80 --- /dev/null +++ b/src/Combinatorics/PartiallyOrderedSet/functions.jl @@ -0,0 +1,169 @@ +function pm_object(P::PartiallyOrderedSet) + return P.pm_poset +end +# TODO: move to Polymake.jl soon +Polymake.Meta.translate_type_to_pm_string(::Type{<:Polymake.BasicDecoration}) = "graph::BasicDecoration" + +function partially_ordered_set(pp::Polymake.BigObject) + @req startswith(Polymake.type_name(pp), "PartiallyOrderedSet") "Not a polymake PartiallyOrderedSet: $(Polymake.type_name(pp))" + return PartiallyOrderedSet(pp) +end + +@doc raw""" + partially_ordered_set(covrels::Matrix{Int}) + +Construct a partially ordered set from covering relations `covrels`, given in +the form of the adjacency matrix of a Hasse diagram. The covering relations +must be given in topological order, i.e. `covrels` must be strictly upper +triangular. +""" +function partially_ordered_set(covrels::Matrix{Int}) + pg = Polymake.Graph{Directed}(IncidenceMatrix(covrels)) + nelem = nrows(covrels) + return partially_ordered_set(Graph{Directed}(pg), 1, nelem) +end + + +@doc raw""" + partially_ordered_set_from_inclusions(i::IncidenceMatrix) + +Construct an inclusion based partially ordered with rows of `i` as co-atoms. +""" +function partially_ordered_set_from_inclusions(i::IncidenceMatrix) + # we want to make sure it is always built from the bottom up + # and just need to specify some arbitrary upper bound for the rank + pos = Polymake.call_function(:polytope, :lower_hasse_diagram, i, ncols(i)) + return partially_ordered_set(pos) +end + + +_pmdec(elem::Int, r::Int) = Polymake.BasicDecoration(Polymake.Set{Int}(elem), r) +_pmdec(r::Int) = Polymake.BasicDecoration(Polymake.Set{Int}(), r) + +@doc raw""" + partially_ordered_set(g::Graph{Directed}, minimal_element::Int, maximal_element::Int) + +Construct a partially ordered set from a directed graph describing the Hasse diagram. +The graph must be acyclic and have unique minimal and maximal elements. +""" +function partially_ordered_set(g::Graph{Directed}, minimal_element::Int, maximal_element::Int) + stack = [minimal_element] + gc = Polymake.Graph{Directed}(pm_object(g)) + dec = Polymake.NodeMap{Directed, Polymake.BasicDecoration}(gc) + # we need to generate some valid rank function + es = Polymake.Set{Int}() + Polymake._set_entry(dec, minimal_element-1, _pmdec(minimal_element, 0)) + while !isempty(stack) + src = pop!(stack) + r = Polymake.decoration_rank(Polymake._get_entry(dec, src-1)) + for t in neighbors(g, src) + rr = Polymake.decoration_rank(Polymake._get_entry(dec, t-1)) + Polymake._set_entry(dec, t-1, _pmdec(t, max(r+1, rr))) + push!(stack, t) + end + end + pos = Polymake.graph.PartiallyOrderedSet{Polymake.BasicDecoration}( + ADJACENCY=gc, + DECORATION=dec, + TOP_NODE=maximal_element-1, + BOTTOM_NODE=minimal_element-1 + ) + return PartiallyOrderedSet(pos) +end + +@doc raw""" + partially_ordered_set(g::Graph{Directed}, node_ranks::Dict{Int,Int}) + +Construct a partially ordered set from a directed graph describing the Hasse diagram. +The graph must be acyclic and have unique minimal and maximal elements. +A The dictionary `node_ranks` must give a valid rank for each node in the graph, strictly +increasing from the unique minimal element to the unique maximal element. +""" +function partially_ordered_set(g::Graph{Directed}, node_ranks::Dict{Int,Int}) + gc = Polymake.Graph{Directed}(pm_object(g)) + dec = Polymake.NodeMap{Directed, Polymake.BasicDecoration}(gc) + for n in 1:n_vertices(g) + Polymake._set_entry(dec, n-1, _pmdec(n, node_ranks[n])) + end + bottom = findmin(node_ranks)[2] + top = findmax(node_ranks)[2] + pos = Polymake.graph.PartiallyOrderedSet{Polymake.BasicDecoration}( + ADJACENCY=gc, + DECORATION=dec, + TOP_NODE=top-1, + BOTTOM_NODE=bottom-1 + ) + return PartiallyOrderedSet(pos) +end + +comparability_graph(pos::PartiallyOrderedSet) = Graph{Undirected}(pm_object(pos).COMPARABILITY_GRAPH::Polymake.Graph{Undirected}) + +# this will compute the comparability graph on first use +# the graph will be kept in the polymake object +function compare(pos::PartiallyOrderedSet, a::Int, b::Int) + cg = comparability_graph(pos) + has_edge(cg, a, b) || return false + return rank(pos, a) < rank(pos, b) +end + +function compare(a::PartiallyOrderedSetElement, b::PartiallyOrderedSetElement) + @req parent(a) === parent(b) "cannot compare elements in different posets" + return compare(parent(a), data(a), data(b)) +end + +parent(pose::PartiallyOrderedSetElement) = pose.parent +data(pose::PartiallyOrderedSetElement) = pose.node_id + +Base.length(p::PartiallyOrderedSet) = pm_object(p).N_NODES::Int + +function rank(p::PartiallyOrderedSet, i::Int64) + dec = pm_object(p).DECORATION::Polymake.NodeMap{Directed,Polymake.BasicDecoration} + elemdec = Polymake._get_entry(dec, i-1)::Polymake.BasicDecoration + return Polymake.decoration_rank(elemdec)::Int +end + +function maximal_chains(p::PartiallyOrderedSet) + mc = Polymake.graph.maximal_chains_of_lattice(pm_object(p))::IncidenceMatrix + return row.(Ref(mc), 1:nrows(mc)) +end + +function Base.show(io::IO, p::PartiallyOrderedSet) + if is_terse(io) + print(io, "Partially ordered set") + else + print(io, "Partially ordered set on $(length(p)) elements") + end +end + +function visualize(p::PartiallyOrderedSet; kwargs...) + Polymake.visual(pm_object(p); kwargs...) +end + +function face_lattice(p::Union{Polyhedron,Cone,PolyhedralFan,PolyhedralComplex}) + return partially_ordered_set(pm_object(p).HASSE_DIAGRAM) +end + +function order_polytope(p::PartiallyOrderedSet) + return polyhedron(Polymake.polytope.order_polytope(pm_object(p))) +end + +function chain_polytope(p::PartiallyOrderedSet) + return polyhedron(Polymake.polytope.chain_polytope(pm_object(p))) +end + +@doc raw""" + maximal_ranked_poset(v::AbstractVector{Int}) + +Maximal ranked partially ordered set with number of nodes per rank given in +`v`, from bottom to top and excluding the minimal and maximal elements. + +See Ahmad, Fourier, Joswig, arXiv:2309.01626 +TODO: ref +""" +function maximal_ranked_poset(v::AbstractVector{Int}) + return partially_ordered_set(Polymake.graph.maximal_ranked_poset(Polymake.Array{Int}(v))) +end + +graph(p::PartiallyOrderedSet) = Graph{Directed}(pm_object(p).ADJACENCY) + +node_ranks(p::PartiallyOrderedSet) = Dict((i => rank(p, i)) for i in 1:length(p)) diff --git a/src/Combinatorics/PartiallyOrderedSet/structs.jl b/src/Combinatorics/PartiallyOrderedSet/structs.jl new file mode 100644 index 000000000000..d58a9cfbbe95 --- /dev/null +++ b/src/Combinatorics/PartiallyOrderedSet/structs.jl @@ -0,0 +1,11 @@ +abstract type AbstractPartiallyOrderedSet end + +struct PartiallyOrderedSet <: AbstractPartiallyOrderedSet + pm_poset::Polymake.BigObject +end + +struct PartiallyOrderedSetElement{T} + parent::PartiallyOrderedSet + node_id::Int + elem::T +end diff --git a/src/Oscar.jl b/src/Oscar.jl index 282a1d516578..652a70d2d73f 100644 --- a/src/Oscar.jl +++ b/src/Oscar.jl @@ -274,6 +274,8 @@ include("Combinatorics/SimplicialComplexes.jl") include("Combinatorics/OrderedMultiIndex.jl") include("Combinatorics/Matroids/JMatroids.jl") include("Combinatorics/EnumerativeCombinatorics/EnumerativeCombinatorics.jl") +include("Combinatorics/PartiallyOrderedSet/structs.jl") +include("Combinatorics/PartiallyOrderedSet/functions.jl") include("PolyhedralGeometry/visualization.jl") # needs SimplicialComplex diff --git a/src/exports.jl b/src/exports.jl index 4add8831ea1f..d7aa719f3b02 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -308,6 +308,7 @@ export cellular_primary_decomposition export center, has_center, set_center export centralizer export chain_complex +export chain_polytope export chain_range export chamber export character_field @@ -355,6 +356,7 @@ export collector export coloops export column export combinatorial_symmetries +export comparability_graph export composition export compositions export comm @@ -992,11 +994,13 @@ export maxes export maximal_abelian_quotient, has_maximal_abelian_quotient, set_maximal_abelian_quotient export maximal_blocks export maximal_cells +export maximal_chains export maximal_cones export maximal_extension export maximal_groebner_cone export maximal_normal_subgroups, has_maximal_normal_subgroups, set_maximal_normal_subgroups export maximal_polyhedra, maximal_polyhedra_and_multiplicities +export maximal_ranked_poset export maximal_subgroup_classes, has_maximal_subgroup_classes, set_maximal_subgroup_classes export maximal_subgroups export metadata @@ -1134,6 +1138,7 @@ export orbit_representatives_and_stabilizers export orbits export order, has_order, set_order export order_field_of_definition +export order_polytope export ordering export orders_centralizers export orders_class_representatives @@ -1149,6 +1154,7 @@ export parallel_extension export parametrization export parametrization_conic export parent +export partially_ordered_set export partition export partitions export patches