Skip to content

Commit

Permalink
added progress meter + finished docs
Browse files Browse the repository at this point in the history
  • Loading branch information
antonydellavecchia committed Oct 30, 2024
1 parent dbf2651 commit 043f979
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 33 deletions.
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Polymake = "d720cf60-89b5-51f5-aff5-213f193123e7"
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RandomExtensions = "fb686558-2515-59ef-acaa-46db3789a887"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
Expand All @@ -37,6 +38,7 @@ Markdown = "1.6"
Nemo = "0.47.1"
Pkg = "1.6"
Polymake = "0.11.20"
ProgressMeter = "1.10.2"
Random = "1.6"
RandomExtensions = "0.4.3"
Serialization = "1.6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ DocTestSetup = Oscar.doctestsetup()

```@docs
partial_shift_graph_vertices
partial_shift_graph
contracted_partial_shift_graph
```
3 changes: 2 additions & 1 deletion experimental/AlgebraicShifting/src/AlgebraicShifting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ include("PartialShiftGraph.jl")
export UniformHypergraph

export compound_matrix
export contracted_partial_shift_graph
export exterior_shift
export face_size
export generic_unipotent_matrix
export partial_shift_graph_vertices
export partial_shift_graph
export partial_shift_graph_vertices
export rothe_matrix
export uniform_hypergraph

Expand Down
160 changes: 131 additions & 29 deletions experimental/AlgebraicShifting/src/PartialShiftGraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ end
Given two simplicial complexes `K1`, `K2` return true if
`K1` is lexicographically less than `K2`
"""
isless_lex(K1::SimplicialComplex, K2::SimplicialComplex) = isless_lex(Set(facets(K1)), Set(facets(K2)))
isless_lex(K1::ComplexOrHypergraph, K2::ComplexOrHypergraph) = isless_lex(Set(facets(K1)), Set(facets(K2)))

@doc raw"""
partial_shift_graph_vertices(F::Field,::SimplicialComplex, W::Union{WeylGroup, Vector{WeylGroupElem}};)
Expand Down Expand Up @@ -51,7 +51,8 @@ function partial_shift_graph_vertices(F::Field,
current = K
visited = [current]
# by properties of algebraic shifting
# we know that K we be the last in this sorted list
# we know that K will be the last in this sorted list
# sorting here should also speed up unique according to julia docs
unvisited = unique(
x -> Set(facets(x)),
sort([exterior_shift(F, K, w) for w in W]; lt=isless_lex))[1:end - 1]
Expand Down Expand Up @@ -79,7 +80,7 @@ function multi_edges(F::Field,
complexes::Vector{Tuple{Int,T}},
complex_labels::Dict{Set{Set{Int}}, Int}
) :: Dict{Tuple{Int, Int}, Vector{WeylGroupElem}} where T <: ComplexOrHypergraph;
# For each complex K with index i, compute the shifted complex delta of K by w for each w W.
# For each complex K with index i, compute the shifted complex delta of K by w for each w in W.
# For nontrivial delta, place (i, delta) → w in a singleton dictionary, and eventually merge all dictionaries
# to obtain a dictionary (i, delta) → [w that yield the shift K → delta]
reduce(
Expand All @@ -102,38 +103,82 @@ end
Constructs the partial shift graph on `complexes`.
Returns a tuple `(G, D)`, where `G` is a directed graph whose vertices correspond to the `complexes`,
such that there is an edge `K → L` if there exists a shift matrix `w` such that `L` is the partial generic shift of `K` by `w`.
If `K` and `L` are the `i`th and `j`th entry of `complexes`, resp.,
`D[i,j]` contains all `w ∈ W` such that `L` is the partial generic shift of `K` by `w`.
Returns a tuple `(G, EL, VL)`, where `G` is a `Graph{Directed}`, `EL` is a `Dict{Tuple{Int Int}, Vector{Weylgroupelem}` and
`VL` is a lexicographically sorted `complexes`, hence is either a `Vector{SimplicialComplex}` or `Vector{Uniformhypergraph}`.
`EL` are the edges labels and `VL` are the vertex labels.
There is an edge from the vertex labelled `K` to the vertex labelled `L` if `L` is the partial shift of `K` by some `w` in `W`.
If `K` and `L` are the `i`th and `j`th entry of `VL`, resp.,
`EL[i,j]` contains all `w` in `W` such that `L` is the partial generic shift of `K` by `w`.
# Arguments
- `complexes`: A vector of simplicial complexes of uniform hypergraphs (all belonging to the same ``Γ(n, k, l)``).
- `complexes`: A vector of simplicial complexes or uniform hypergraphs (all should have the same number of vertices).
- `parallel :: Bool` (default: `false`) run the process in parrallel using the `Distributed` package; make sure to do `@everywhere using Oscar`.
- `W`: The user may provide a list `W` of Weyl group elements to be used to construct the shifts.
`W` must be a subset the (same instance of the) symmetric group of the same order as the complexes.
If `W` is not provided, the function will use the symmetric group of the same order as the complexes.
All elements of `W` should have the same parent.
`W` must be a subset the (same instance of the) symmetric group of order equal to the number of vertices of a complex in complexes (they should all be equal).
If `W` is not provided, the function will use the symmetric group of the same order as vertices in each complex complexes.
# Examples
```
julia> gamma(n,k,l) = uniform_hypergraph.(subsets(subsets(n, k), l), n)
gamma (generic function with 1 method)
julia> Ks = gamma(4,2,5)
6-element Vector{UniformHypergraph}:
UniformHypergraph(4, 2, [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 3], [2, 3], [2, 4], [3, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 4], [2, 3], [2, 4], [3, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 3], [1, 4], [2, 4], [3, 4]])
UniformHypergraph(4, 2, [[1, 3], [1, 4], [2, 3], [2, 4], [3, 4]])
julia> G, D = partial_shift_graph(QQ, Ks)
julia> G, EL, VL = partial_shift_graph(QQ, Ks);
Progress: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| Time: 0:00:00
julia> collect(edges(G))
14-element Vector{Edge}:
Edge(2, 1)
Edge(3, 1)
Edge(3, 2)
Edge(4, 1)
Edge(4, 2)
Edge(5, 1)
Edge(5, 2)
Edge(5, 3)
Edge(5, 4)
Edge(6, 1)
Edge(6, 2)
Edge(6, 3)
Edge(6, 4)
Edge(6, 5)
julia> EL[6, 5]
4-element Vector{WeylGroupElem}:
s1 * s2
s2
s3 * s1 * s2
s3 * s2
julia> facets.(VL[[6, 5]])
2-element Vector{Vector{Set{Int64}}}:
[Set([3, 1]), Set([4, 1]), Set([2, 3]), Set([4, 2]), Set([4, 3])]
[Set([2, 1]), Set([4, 1]), Set([2, 3]), Set([4, 2]), Set([4, 3])]
```
"""
function partial_shift_graph(F::Field, complexes::Vector{T}, W::Union{WeylGroup, Vector{WeylGroupElem}};
parallel::Bool = false) :: Tuple{Graph{Directed}, EdgeLabels} where T <: ComplexOrHypergraph;
parallel::Bool = false) :: Tuple{Graph{Directed}, EdgeLabels, Vector{ComplexOrHypergraph}} where T <: ComplexOrHypergraph;
# Deal with trivial case
if length(complexes) <= 1
return (graph_from_adjacency_matrix(Directed, zeros(length(complexes),length(complexes))), EdgeLabels())
end

# maybe we provide a flag to skip if the complexes are already sorted?
complexes = sort(complexes;lt=Oscar.isless_lex)

ns_vertices = Set(n_vertices.(complexes))
@req length(ns_vertices) == 1 "All complexes are required to have the same number of vertices."
n = collect(ns_vertices)[1]

# inverse lookup K → index of K in complexes
complex_labels = Dict(Set(facets(K)) => index for (index, K) in enumerate(complexes))

Expand All @@ -142,31 +187,22 @@ function partial_shift_graph(F::Field, complexes::Vector{T}, W::Union{WeylGroup,
@req rs_type[1][1] == :A && rs_type[1][2] == n - 1 "Only Weyl groups type A_$(n-1) are currently support and received type $(T[1])."

task_size = 1
map_function = map
if parallel
# setup parallel parameters
channels = Oscar.params_channels(Union{RootSystem, WeylGroup, Vector{SimplicialComplex}, Vector{UniformHypergraph}})
# setup parents needed to be sent to each process
Oscar.put_params(channels, root_system(W))
Oscar.put_params(channels, W)
map_function = pmap
end

# Basically, the following does the same as the following, but with a progress indicator:
# edge_tuples = multi_edges(W, collect(enumerate(complexes)), complex_labels)
edge_labels = multi_edges(F, W, collect(enumerate(complexes)), complex_labels)
#edge_labels = reduce(
# (d1, d2) -> mergewith!(vcat, d1, d2),
# # a progress bar here would be nice for the users, but this requires adding a dependency to Oscar
# # @showprogress map_function(
# map_function(
# Ks -> multi_edges(F, W, Ks, complex_labels),
# Iterators.partition(enumerate(complexes), task_size)
# )
#)

# edge_tuples = multi_edges(F, W, collect(enumerate(complexes)), complex_labels)
# Basically, the following does the same as the preceeding, but with a progress indicator:
edge_labels = reduce((d1, d2) -> mergewith!(vcat, d1, d2),
@showprogress pmap(
Ks -> multi_edges(F, W, Ks, complex_labels),
Iterators.partition(enumerate(complexes), task_size)))
graph = graph_from_edges(Directed, [[i,j] for (i,j) in keys(edge_labels)])
return (graph, edge_labels)
return (graph, edge_labels, complexes)
end

function partial_shift_graph(F::Field, complexes::Vector{T}; parallel=false) where T <: ComplexOrHypergraph
Expand All @@ -179,3 +215,69 @@ function partial_shift_graph(F::Field, complexes::Vector{T}; parallel=false) whe
W = weyl_group(:A, n - 1)
return partial_shift_graph(F, complexes, W;parallel=parallel)
end

@doc raw"""
contracted_partial_shift_graph(G::Graph{Directed}, edge_labels::Dict{Tuple{Int, Int}, Vector{WeylGroupElem}})
Returns a triple `(CG, S, P)`, where `CG` is a graph that contains a vertex `v` for every vertex `S[v]` in `G`.
`S` is a list of indices for the sinks in the original graph `G`.
A vertex `i` is in `P[s]` if there exists an edge from `i` to `s` in `G` with `w0` in its edge label,
in this way `P` is a partition of the vertices of the orignal graph `G`.
There is an edge from `s` to `t` in `CG` whenever there is an edge from `i` to `j` in `G` and `i` in `P[s]` and `j` in `P[t]`.
#Examples
```jldoctest
julia> gamma(n,k,l) = uniform_hypergraph.(subsets(subsets(n, k), l), n)
gamma (generic function with 1 method)
julia> Ks = gamma(4,2,5)
6-element Vector{UniformHypergraph}:
UniformHypergraph(4, 2, [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 3], [2, 3], [2, 4], [3, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 4], [2, 3], [2, 4], [3, 4]])
UniformHypergraph(4, 2, [[1, 2], [1, 3], [1, 4], [2, 4], [3, 4]])
UniformHypergraph(4, 2, [[1, 3], [1, 4], [2, 3], [2, 4], [3, 4]])
julia> G, EL, VL = partial_shift_graph(QQ, Ks);
Progress: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| Time: 0:00:00
julia> contracted_partial_shift_graph(G, EL)
(Directed graph with 1 nodes and 0 edges, [1], [[5, 4, 6, 2, 3, 1]])
```
"""
function contracted_partial_shift_graph(G::Graph{Directed}, edge_labels::Dict{Tuple{Int, Int}, Vector{WeylGroupElem}})
n = n_vertices(G)
W = parent(first(first(values(edge_labels))))
w0 = longest_element(W)

# all arrows corresponding to edges that contain w0 in their edge labels
w0_action = Dict(i => j for ((i,j), ws) in edge_labels if w0 in ws)

sinks = findall(iszero, degree(G))
sinks_indices = Dict(s => i for (i,s) in enumerate(sinks))
for i in 1:n
if !haskey(w0_action, i)
@req i in sinks "Vertex $i is not a sink, but has no outbound edge with w0 in its edge label."
w0_action[i] = i
end
end

p = [Int[] for _ in sinks]
for (i, s) in w0_action
push!(p[sinks_indices[s]], i)
end

return (
graph_from_edges(Directed, [
[sinks_indices[s],sinks_indices[t]]
for (s,t) in (
(w0_action[i], (haskey(w0_action, j) ? w0_action[j] : j))
for (i, j) in keys(edge_labels)
)
if s != t
], length(sinks)),
sinks,
p
)
end
2 changes: 1 addition & 1 deletion experimental/AlgebraicShifting/src/UniformHypergraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ n_vertices(K::UniformHypergraph) = K.n_vertices

faces(K::UniformHypergraph) = K.faces
# added for covenience when writting functions for Simplicial complex and Uniform Hypergraph
facets(K::UniformHypergraph) = K.faces
facets(K::UniformHypergraph) = Set.(K.faces)
face_size(K::UniformHypergraph) = K.k

function Base.hash(K :: UniformHypergraph, u :: UInt)
Expand Down
5 changes: 3 additions & 2 deletions src/Combinatorics/Graphs/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1172,8 +1172,9 @@ end
function graph_from_edges(::Type{T},
edges::Vector{Edge},
n_vertices::Int=-1) where {T <: Union{Directed, Undirected}}

n_needed = maximum(reduce(append!,[[src(e),dst(e)] for e in edges]))
isempty(edges) && return Graph{T}(n_vertices)

n_needed = maximum(reduce(append!,[[src(e),dst(e)] for e in edges];))
@req (n_vertices >= n_needed || n_vertices < 0) "n_vertices must be at least the maximum vertex in the edges"

g = Graph{T}(max(n_needed, n_vertices))
Expand Down
1 change: 1 addition & 0 deletions src/imports.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# standard packages
using Pkg
using ProgressMeter
using Random
using RandomExtensions
using UUIDs
Expand Down

0 comments on commit 043f979

Please sign in to comment.