diff --git a/experimental/AlgebraicShifting/docs/src/exterior_shifting.md b/experimental/AlgebraicShifting/docs/src/exterior_shifting.md index 1f042a510f4..95038fc36f5 100644 --- a/experimental/AlgebraicShifting/docs/src/exterior_shifting.md +++ b/experimental/AlgebraicShifting/docs/src/exterior_shifting.md @@ -5,9 +5,20 @@ DocTestSetup = Oscar.doctestsetup() # Exterior Shifting +## Uniform Hypergraphs +```@docs +uniform_hypergraph +``` + ## Helpful Matrix constructions ```@docs generic_unipotent_matrix rothe_matrix +compound_matrix +``` + +## Exterior (Partial) Shifting +```@docs +exterior_shift ``` diff --git a/experimental/AlgebraicShifting/docs/src/partial_shift_graph.md b/experimental/AlgebraicShifting/docs/src/partial_shift_graph.md index 26ed10b84da..c1de71984bd 100644 --- a/experimental/AlgebraicShifting/docs/src/partial_shift_graph.md +++ b/experimental/AlgebraicShifting/docs/src/partial_shift_graph.md @@ -1 +1,10 @@ +```@meta +CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() +``` + # Partial Shift Graph + +```@docs +partial_shift_graph_vertices +``` diff --git a/experimental/AlgebraicShifting/src/AlgebraicShifting.jl b/experimental/AlgebraicShifting/src/AlgebraicShifting.jl index cd6dbe0dd91..b1a0a4f7efb 100644 --- a/experimental/AlgebraicShifting/src/AlgebraicShifting.jl +++ b/experimental/AlgebraicShifting/src/AlgebraicShifting.jl @@ -6,6 +6,7 @@ export UniformHypergraph export compound_matrix export exterior_shift +export face_size export generic_unipotent_matrix export partial_shift_graph_vertices export partial_shift_graph diff --git a/experimental/AlgebraicShifting/src/PartialShift.jl b/experimental/AlgebraicShifting/src/PartialShift.jl index b82bc70d403..8d8242ce522 100644 --- a/experimental/AlgebraicShifting/src/PartialShift.jl +++ b/experimental/AlgebraicShifting/src/PartialShift.jl @@ -26,11 +26,6 @@ function generic_unipotent_matrix(R::MPolyRing) return u end -function generic_unipotent_matrix(F::Field, n::Int) - Fx, x = polynomial_ring(F, :x => (1:n, 1:n)) - return generic_unipotent_matrix(Fx) -end - @doc raw""" generic_unipotent_matrix(R::MPolyRing) generic_unipotent_matrix(F::Field, n::Int) @@ -55,6 +50,10 @@ julia> generic_unipotent_matrix(GF(2), 2) [0 1] ``` """ +function generic_unipotent_matrix(F::Field, n::Int) + Fx, x = polynomial_ring(F, :x => (1:n, 1:n)) + return generic_unipotent_matrix(Fx) +end @doc raw""" rothe_matrix(F::Field, w::WeylGroupElem; K::Union{SimplicialComplex, Nothing} = nothing) @@ -62,16 +61,34 @@ julia> generic_unipotent_matrix(GF(2), 2) For a base field `F` and a weyl group element `w` return the matrix with entries in the multivariate polynomial ring `R` with `n^2` many indeterminants where `n - 1` is the rank of the root system of the weyl group. -We know that since `general_linear_group(n^2, R)` has a Bruhat decomposition, and element lies in some double coset $BwB$. -The \emph{Rothe matrix} is a normal form for a the matrix on the left of a representative for the double coset corresponding to `w`. -We use the name \emph{Rothe matrix} because of its resemblance with a \emph{Rothe diagram} (add ref?) +We know that since `general_linear_group(n^2, R)` has a Bruhat decomposition, any element lies in some double coset $BwB$. +The Rothe matrix is a normal form for the matrix on the left of a representative for the double coset corresponding to `w`. +(this might need to be explained further and reference the preprint) +We use the name Rothe matrix because of its resemblance with a Rothe diagram. (add ref?) # Examples ```jldoctest + julia> W = weyl_group(:A, 4) +Weyl group for root system defined by Cartan matrix [2 -1 0 0; -1 2 -1 0; 0 -1 2 -1; 0 0 -1 2] + +julia> s = gens(W) +4-element Vector{WeylGroupElem}: + s1 + s2 + s3 + s4 +julia> w = s[2] * s[3] * s[4] +s2 * s3 * s4 + +julia> rothe_matrix(GF(2), w) +[1 0 0 0 0] +[0 x[2, 3] x[2, 4] x[2, 5] 1] +[0 1 0 0 0] +[0 0 1 0 0] +[0 0 0 1 0] ``` """ - function rothe_matrix(F::Field, w::WeylGroupElem) n = rank(root_system(parent(w)))+1 Fx, x = polynomial_ring(F, :x => (1:n, 1:n)) @@ -82,22 +99,66 @@ function rothe_matrix(F::Field, w::WeylGroupElem) return u * permutation_matrix(F, perm(w)) end -""" The `K`-indexed rows of the `k`th compound matrix of a square matrix `X`, where `K` is `k`-homogeneous. """ -function compound_matrix(m::MatElem, K::Vector{Vector{Int}}) +@doc raw""" + compound_matrix(m::MatElem, k::Int) + compound_matrix(p::PermGroupElem, k::Int) + compound_matrix(w::WeylGroupElem, k::Int) + compound_matrix(m::MatElem, K::Vector{Vector{Int}}) + +Given a matrix `m` return the matrix where each entry is a `k` minor of `m`. +The entries of the compound matrix are ordered with respect to the lexicographic order on sets. +When passed a `PermGroupElem` or `WeylGroupElem`, return the copound matrix for their +permutation matrix representation. + +Alternatively, passing a `UniformHypergraph` `K` will return the compound matrix with entries the `face_size(K)` minors, and restrict the rows to the rows corresponding to `K` + +# Examples +```jldoctest + +julia> M = generic_unipotent_matrix(QQ, 3) +[1 x[1, 2] x[1, 3]] +[0 1 x[2, 3]] +[0 0 1] + +julia> compound_matrix(M, 2) +[1 x[2, 3] x[1, 2]*x[2, 3] - x[1, 3]] +[0 1 x[1, 2]] +[0 0 1] + +julia> compound_matrix(perm([1, 3, 2]), 2) +[0 1 0] +[1 0 0] +[0 0 -1] + +julia> W = weyl_group(:A, 2) +Weyl group for root system defined by Cartan matrix [2 -1; -1 2] + +julia> compound_matrix(longest_element(W), 2) +[ 0 0 -1] +[ 0 -1 0] +[-1 0 0] + +julia> K = uniform_hypergraph([[1, 2], [2, 3]]) +UniformHypergraph(3, 2, [[1, 2], [2, 3]]) + +julia> compound_matrix(M, K) +[1 x[2, 3] x[1, 2]*x[2, 3] - x[1, 3]] +[0 0 1] +``` +""" +function compound_matrix(m::MatElem, K::UniformHypergraph) @req size(m,1) == size(m,2) "Only valid for square matrices" - @req length(Set(map(length, K))) == 1 "All entries in K must have the same size." n = size(m, 1) - k = collect(Set(map(length, K)))[1] - @req all(1 <= i <= n for s in K for i in s) "All entries in K must represent $k-element subsets of [n]." + k = face_size(K) nCk = sort(subsets(n, k)) - return matrix(base_ring(m), [det(m[row, col]) for row in K, col in nCk]) + return matrix(base_ring(m), [det(m[row, col]) for row in faces(K), col in nCk]) end -compound_matrix(m::MatElem, k::Int) = compound_matrix(m, sort(subsets(size(m, 1), k))) +compound_matrix(m::MatElem, k::Int) = compound_matrix(m, uniform_hypergraph(sort(subsets(size(m, 1), k)))) compound_matrix(p::PermGroupElem, k::Int) = compound_matrix(permutation_matrix(ZZ, p), k) compound_matrix(w::WeylGroupElem, k::Int) = compound_matrix(perm(w), k) -""" Given `K` checks if matrix entry can be set to zero zero """ +# this might be removed (currently is not used) function _set_to_zero(K::SimplicialComplex, indices::Tuple{Int, Int}) row, col = indices row == col && return false @@ -128,7 +189,7 @@ function exterior_shift(K::UniformHypergraph, g::MatElem) @req size(g, 1) == n_vertices(K) "Matrix size does not match K." matrix_base = base_ring(g) nCk = sort!(subsets(n_vertices(K), face_size(K))) - c = compound_matrix(g, faces(K)) + c = compound_matrix(g, K) if matrix_base isa MPolyRing Oscar.ModStdQt.ref_ff_rc!(c) elseif matrix_base isa MPolyQuoRing @@ -149,30 +210,60 @@ function exterior_shift(K::SimplicialComplex, g::MatElem) end @doc raw""" - exterior_shift(F::Field, K::ComplexOrHypergraph, w::WeylGroupElem) - exterior_shift(K::ComplexOrHypergraph, w::WeylGroupElem) - exterior_shift(K::ComplexOrHypergraph) + exterior_shift(F::Field, K::SimplicialComplex, w::WeylGroupElem) + exterior_shift(F::Field, K::UniformHypergraph, w::WeylGroupElem) + exterior_shift(K::SimplicialComplex, w::WeylGroupElem) + exterior_shift(K::UniformHypergraph, w::WeylGroupElem) + exterior_shift(K::SimplicialComplex) + exterior_shift(K::UniformHypergraph) Computes the (partial) exterior shift of a simplical complex or uniform hypergraph `K` with respect to the Weyl group element `w` and the field `F`. If the field is not given then `QQ` is used during the computation. If `w` is not given then `longest_element(weyl_group(:A, n_vertices(K) - 1))` is used - -# Example -Compute the exterior generic shift of the real projective plane: -``` -julia> K = real_projective_plane() +# Examples +```jldoctest julia> is_shifted(K) +false + julia> L = exterior_shift(K) +Abstract simplicial complex of dimension 2 on 6 vertices + +julia> facets(L) +10-element Vector{Set{Int64}}: + Set([2, 3, 1]) + Set([4, 2, 1]) + Set([5, 2, 1]) + Set([6, 2, 1]) + Set([4, 3, 1]) + Set([5, 3, 1]) + Set([6, 3, 1]) + Set([5, 4, 1]) + Set([4, 6, 1]) + Set([5, 6, 1]) + julia> is_shifted(L) -julia> betti_numbers(K) == betti_numbers(L) -``` -Apply the partial generic shift w.r.t. a permutation ``w``: -``` -julia> W = weyl_group(:A, 5) +true + +julia> betti_numbers(L) == betti_numbers(K) +true + +julia> W = weyl_group(:A, n_vertices(K) - 1) +Weyl group for root system defined by Cartan matrix [2 -1 0 0 0; -1 2 -1 0 0; 0 -1 2 -1 0; 0 0 -1 2 -1; 0 0 0 -1 2] + julia> s = gens(W) -julia> w = s[1] * s[2] * s[1] -julia> L = exterior_shift(K, w) +5-element Vector{WeylGroupElem}: + s1 + s2 + s3 + s4 + s5 + +julia> w = s[2] * s[3] * s[4] +s2 * s3 * s4 + +julia> L = exterior_shift(GF(2), K, w) +Abstract simplicial complex of dimension 2 on 6 vertices ``` """ function exterior_shift(F::Field, K::ComplexOrHypergraph, w::WeylGroupElem) diff --git a/experimental/AlgebraicShifting/src/PartialShiftGraph.jl b/experimental/AlgebraicShifting/src/PartialShiftGraph.jl index 60a3f19bb6e..543c0b5348c 100644 --- a/experimental/AlgebraicShifting/src/PartialShiftGraph.jl +++ b/experimental/AlgebraicShifting/src/PartialShiftGraph.jl @@ -14,8 +14,36 @@ end """ isless_lex(K1::SimplicialComplex, K2::SimplicialComplex) = isless_lex(Set(facets(K1)), Set(facets(K2))) -""" - Discovers the nodes of partial shift graph starting from `K`. +@doc raw""" + partial_shift_graph_vertices(F::Field,::SimplicialComplex, W::Union{WeylGroup, Vector{WeylGroupElem}};) + +Given a field `F` discover the vertices of the partial shift graph starting from `K` +using exterior partial shifts corresponding to elements in `W`. +Returns a `Vector{SimplicialCompplex}` ordered lexicographically. + +#Example +```jldoctest +julia> K = simplicial_complex([[1, 2], [2, 3], [3, 4]]) +Abstract simplicial complex of dimension 1 on 4 vertices + +julia> shifts = partial_shift_graph_vertices(QQ, K, weyl_group(:A, 3)) +6-element Vector{SimplicialComplex}: + Abstract simplicial complex of dimension 1 on 4 vertices + Abstract simplicial complex of dimension 1 on 4 vertices + Abstract simplicial complex of dimension 1 on 4 vertices + Abstract simplicial complex of dimension 1 on 4 vertices + Abstract simplicial complex of dimension 1 on 4 vertices + Abstract simplicial complex of dimension 1 on 4 vertices + +julia> facets.(shifts) +6-element Vector{Vector{Set{Int64}}}: + [Set([3, 1]), Set([4, 1]), Set([2, 1])] + [Set([3, 1]), Set([2, 3]), Set([2, 1]), Set([4])] + [Set([3, 1]), Set([4, 2]), Set([2, 1])] + [Set([4, 3]), Set([3, 1]), Set([2, 1])] + [Set([2, 1]), Set([2, 3]), Set([4, 2])] + [Set([2, 1]), Set([2, 3]), Set([4, 3])] +``` """ function partial_shift_graph_vertices(F::Field, K::SimplicialComplex, @@ -23,7 +51,7 @@ 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 list since it is sorted + # we know that K we be the last in this sorted list unvisited = unique( x -> Set(facets(x)), sort([exterior_shift(F, K, w) for w in W]; lt=isless_lex))[1:end - 1] @@ -66,31 +94,36 @@ function multi_edges(F::Field, end @doc raw""" - partial_shift_graph(complexes::Vector{Simplicialcomplex}) - partial_shift_graph(complexes::Vector{Uniformhypergraph}) + partial_shift_graph(F::Field, complexes::Vector{Simplicialcomplex}; parallel=false) + partial_shift_graph(F::Field, complexes::Vector{Uniformhypergraph}; parallel=false) + partial_shift_graph(F::Field, complexes::Vector{Simplicialcomplex}, W::Union{WeylGroup, Vector{WeylGroupElem}}; parallel=false) + partial_shift_graph(F::Field, complexes::Vector{Uniformhypergraph}, W::Union{WeylGroup, Vector{WeylGroupElem}}; parallel=false) + 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 `K` are the `i`th and `j`th entry of `complexes`, resp., +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`. # Arguments - `complexes`: A vector of simplicial complexes of uniform hypergraphs (all belonging to the same ``Γ(n, k, l)``). -- `parallel :: Bool` (default: `false`) run the process in parrallel using the `Distributed` package; the setup is left to the user. +- `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. # Examples ``` -julia> Γ(n,k,l) = uniform_hypergraph.(subsets(subsets(n, k), l), n) -julia> Ks = Γ(4,2,5) -julia> G, D = construct_full_graph(Ks) +julia> gamma(n,k,l) = uniform_hypergraph.(subsets(subsets(n, k), l), n) + +julia> Ks = gamma(4,2,5) + +julia> G, D = partial_shift_graph(QQ, Ks) ``` """ -function partial_shift_graph(F::Field, complexes::Vector{T}, W::Union{Nothing, WeylGroup, Vector{WeylGroupElem}} = nothing; +function partial_shift_graph(F::Field, complexes::Vector{T}, W::Union{WeylGroup, Vector{WeylGroupElem}}; parallel::Bool = false) :: Tuple{Graph{Directed}, EdgeLabels} where T <: ComplexOrHypergraph; # Deal with trivial case if length(complexes) <= 1 @@ -103,15 +136,10 @@ function partial_shift_graph(F::Field, complexes::Vector{T}, W::Union{Nothing, W # inverse lookup K → index of K in complexes complex_labels = Dict(Set(facets(K)) => index for (index, K) in enumerate(complexes)) - - # set the weyl group to be used to construct the shifts - if isnothing(W) - W = weyl_group(:A, n - 1); - else - W2 = only(unique(parent.(W))) - rs_type = root_system_type(root_system(W2)) - @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])." - end + + W2 = only(unique(parent.(W))) + rs_type = root_system_type(root_system(W2)) + @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 @@ -141,3 +169,13 @@ function partial_shift_graph(F::Field, complexes::Vector{T}, W::Union{Nothing, W return (graph, edge_labels) end +function partial_shift_graph(F::Field, complexes::Vector{T}; parallel=false) where T <: ComplexOrHypergraph + # Deal with trivial case + if length(complexes) <= 1 + return (graph_from_adjacency_matrix(Directed, zeros(length(complexes),length(complexes))), EdgeLabels()) + end + + n = n_vertices(complexes[1]) + W = weyl_group(:A, n - 1) + return partial_shift_graph(F, complexes, W;parallel=parallel) +end diff --git a/experimental/AlgebraicShifting/src/UniformHypergraph.jl b/experimental/AlgebraicShifting/src/UniformHypergraph.jl index e79e8a455b7..8b77727fb67 100644 --- a/experimental/AlgebraicShifting/src/UniformHypergraph.jl +++ b/experimental/AlgebraicShifting/src/UniformHypergraph.jl @@ -5,38 +5,66 @@ function complex_faces(K :: SimplicialComplex, d :: Int) :: Vector{Vector{Int}} end struct UniformHypergraph - n_vertices :: Int - k :: Int - faces :: Vector{Vector{Int}} + n_vertices::Int + k::Int + faces::Vector{Vector{Int}} end -function uniform_hypergraph(faces :: Vector{Vector{Int}}, n :: Int, k :: Int) +function uniform_hypergraph(faces::Vector{Vector{Int}}, n::Int, k::Int) faces = sort(collect(Set(sort.(collect.(Set.(faces)))))) @req all(length(f) == k && 1 <= minimum(f) && maximum(f) <= n for f in faces) "Parameters don't define a uniform hypergraph." return UniformHypergraph(n, k, faces) end -function uniform_hypergraph(faces :: Vector{Vector{Int}}, n :: Int) +function uniform_hypergraph(faces::Vector{Vector{Int}}, n::Int) return uniform_hypergraph(faces, n, only(Set(length.(faces)))) end -function uniform_hypergraph(faces :: Vector{Vector{Int}}) +function uniform_hypergraph(faces::Vector{Vector{Int}}) return uniform_hypergraph(faces, maximum(maximum.(faces))) end -function uniform_hypergraph(K :: SimplicialComplex, k :: Int) +@doc raw""" + uniform_hypergraph(faces::Vector{Vector{Int}}, n::Int, k::Int) + uniform_hypergraph(faces::Vector{Vector{Int}}, n::Int) + uniform_hypergraph(faces::Vector{Vector{Int}}) + uniform_hypergraph(K::SimplicialComplex, k::Int) + +Create a uniform hypergraph using `faces`, the size of each face should be `k` and all faces should be subsets of $[n]$. +One can also create a `UniformHypergraph` for the `k` faces of a `SimplicialComplex` `K`. + +#Examples +```jldoctext +julia> U = uniform_hypergraph([[1, 2], [2, 3]], 4) +UniformHypergraph(4, 2, [[1, 2], [2, 3]]) + +julia> U = uniform_hypergraph([[1, 2], [2, 3]]) +UniformHypergraph(3, 2, [[1, 2], [2, 3]]) + +julia> U = uniform_hypergraph(simplicial_complex([[1 ,2, 3], [1, 3, 4]]), 2) +UniformHypergraph(4, 2, [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4]]) + +julia> face_size(U) +2 + +julia> faces(U) +5-element Vector{Vector{Int64}}: + [1, 2] + [1, 3] + [1, 4] + [2, 3] + [3, 4] +``` +""" +function uniform_hypergraph(K::SimplicialComplex, k::Int) return uniform_hypergraph(complex_faces(K, k-1), n_vertices(K), k) end -import Oscar: n_vertices, simplicial_complex - -function simplicial_complex(K :: UniformHypergraph) +function simplicial_complex(K::UniformHypergraph) return simplicial_complex([[[i] for i in 1:n_vertices(K)]; faces(K)]) end -function n_vertices(K :: UniformHypergraph) - return K.n_vertices -end +n_vertices(K::UniformHypergraph) = K.n_vertices faces(K::UniformHypergraph) = K.faces # added for covenience when writting functions for Simplicial complex and Uniform Hypergraph