From 3ed0c9a6e6c544e4ce7917d6b0bb9cbbbd7f6479 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Wed, 31 Jul 2024 14:49:50 +0200 Subject: [PATCH 01/27] Extend exterior algebra functionality. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 173 ++++++++++++++++++ experimental/ExteriorAlgebra/src/Types.jl | 31 ++++ 2 files changed, 204 insertions(+) create mode 100644 experimental/ExteriorAlgebra/src/Types.jl diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index b259ba961f09..797da1059e5c 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -193,3 +193,176 @@ end # # (1) Computations with elements DO NOT AUTOMATICALLY REDUCE # # modulo the squares of the generators. # # (2) Do we want/need a special printing function? (show/display) + + +### Manual realization of exterior algebras + +include("Types.jl") +function ExtAlgElem(E::ExteriorAlgebra{T}, a::Vector{Tuple{Int, SRow{T}}}; check::Bool=true) where {T} + return ExtAlgElem(E, Dict{Int, SRow{T}}(i => v for (i, v) in a if !iszero(v))) +end + +function ExtAlgElem(E::ExteriorAlgebra{T}, p::Int, v::SRow{T}; check::Bool=true) where {T} + return ExtAlgElem(E, Dict{Int, SRow{T}}(i => v for (i, v) in a if !iszero(v))) +end + +function mul!(a::T, w::ExtAlgElem{T}) where {T} + @assert parent(a) === base_ring(parent(w)) + for i in keys(components(w)) + components(w)[i]*=a + iszero(components(w)[i]) && delete!(components(w), i) + end + return w +end + +function *(a::T, w::ExtAlgElem{T}) where {T} + @assert parent(a) === base_ring(parent(w)) + nc = [(i, a*v) for (i, v) in components(w)] + return ExtAlgElem(parent(w), + Dict{Int, SRow{T}}(i => v for (i, v) in nc if !iszero(v)); + check=false + ) +end + +function *(a, w::ExtAlgElem) + return base_ring(w)(a)*w +end + +function add!(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} + for (i, a) in components(w) + if i in keys(components(v)) + components(v)[i] += components(w)[i] + iszero(components(v)[i]) && delete!(components(v), i) + else + components(v)[i] = components(w)[i] + end + end + return v +end + +function copy(w::ExtAlgElem) + return ExtAlgElem(parent(w), copy(components(w)); check=false) +end + +function -(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} + return v + (-w) +end + +function +(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} + return add!(copy(v), w) +end + +function -(w::ExtAlgElem) + return -one(base_ring(w))*w +end + +base_ring(w::ExtAlgElem) = base_ring(parent(w)) + +parent(w::ExtAlgElem) = w.parent + +rank(E::ExteriorAlgebra) = E.rank +base_ring(E::ExteriorAlgebra) = E.base_ring + +zero(E::ExteriorAlgebra{T}) where {T} = ExtAlgElem(E) +zero(w::ExtAlgElem) = zero(parent(w)) + +function components(w::ExtAlgElem{T}) where {T} + if !isdefined(w, :components) + w.components = Dict{Int, SRow{T}}() + end + return w.components +end + +function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where T + E = parent(v) + @assert E === parent(w) + R = base_ring(E) + n = rank(E) + result = zero(E) + for (q, b) in components(w) + for (p, a) in components(v) + r = p + q + r > n && continue + new_elem = sizehint!(Tuple{Int, T}[], length(a)*length(b)) + ht = multiplication_hash_table(E, p, q) + for (i, c) in a + for (j, d) in b + s, k = ht[i, j] + is_zero(s) && continue + res = s * c * d + !iszero(res) && push!(new_elem, (k, res)) + end + end + if !isempty(new_elem) + if haskey(components(result), r) + components(result)[r] += sparse_row(R, new_elem) + else + components(result)[r] = sparse_row(R, new_elem) + end + end + end + end + for (i, v) in components(result) + iszero(v) && delete!(components(result), i) + end + return result +end + +function print_symbols(E::ExteriorAlgebra, p::Int) + if !isdefined(E, :print_symbols) + E.print_symbols = Dict{Int, Vector{Symbol}}() + end + roof = is_unicode_allowed() ? "∧" : "^" + if !haskey(E.print_symbols, p) + E.print_symbols[p] = [Symbol(join([string(E.symbols[i]) for i in indices(I)], roof)) for I in OrderedMultiIndexSet(p, rank(E))] + end + return E.print_symbols[p] +end + +function gens(E::ExteriorAlgebra{T}) where {T} + return [ExtAlgElem(E, Dict{Int, SRow{T}}(1 => sparse_row(base_ring(E), + [(i, one(base_ring(E)))]))) + for i in 1:rank(E)] +end + +one(E::ExteriorAlgebra{T}) where {T} = ExtAlgElem(E, Dict{Int, SRow{T}}(0 => sparse_row(base_ring(E), [(1, one(base_ring(E)))])); check=false) + +function Base.show(io::IO, w::ExtAlgElem) + E = parent(w) + if is_empty(components(w)) + println(io, "0 : 0") + return + end + + for p in 0:rank(E) + !haskey(components(w), p) && continue + v = components(w)[p] + if iszero(p) + println(io, "0 : $(first(v.values))") + continue + end + print(io, "$p : ") + p_symb = print_symbols(E, p) + println(io, join(["$(c)*"*string(p_symb[i]) for (i, c) in v], " + ")) + end +end + +function ==(v::ExtAlgElem, w::ExtAlgElem) + E = parent(v) + @assert E === parent(w) + return components(v) == components(w) +end + +function multiplication_hash_table(E::ExteriorAlgebra, p::Int, q::Int) + if !isdefined(E, :multiplication_hash_tables) + E.multiplication_hash_tables = Dict{Tuple{Int, Int}, Matrix{Tuple{Int, Int}}}() + end + n = rank(E) + if !haskey(E.multiplication_hash_tables, (p, q)) + A = [_wedge(ordered_multi_index(i, p, n), ordered_multi_index(j, q, n)) for i in 1:binomial(n, p), j in 1:binomial(n, q)] + B = [(s, linear_index(k)) for (s, k) in A] + E.multiplication_hash_tables[(p, q)] = B + end + return E.multiplication_hash_tables[(p, q)] +end + diff --git a/experimental/ExteriorAlgebra/src/Types.jl b/experimental/ExteriorAlgebra/src/Types.jl new file mode 100644 index 000000000000..dba32ece5921 --- /dev/null +++ b/experimental/ExteriorAlgebra/src/Types.jl @@ -0,0 +1,31 @@ +mutable struct ExteriorAlgebra{T} + base_ring::NCRing + symbols::Vector{Symbol} + rank::Int + + print_symbols::Dict{Int, Vector{Symbol}} + multiplication_hash_tables::Dict{Tuple{Int, Int}, Matrix{Tuple{Int,Int}}} + + function ExteriorAlgebra(R::NCRing, a::Vector{Symbol}) + return new{elem_type(R)}(R, a, length(a)) + end +end + +mutable struct ExtAlgElem{T} + parent::ExteriorAlgebra{T} + components::Dict{Int, SRow{T}} + + function ExtAlgElem(E::ExteriorAlgebra{T}) where {T} + return new{T}(E) + end + + function ExtAlgElem(E::ExteriorAlgebra{T}, comp::Dict{Int, SRow{T}}; + check::Bool=true + ) where {T} + @assert all(base_ring(x) === base_ring(E) for (_, x) in comp) + @check all(!iszero(x) for (_, x) in comp) + return new{T}(E, comp) + end +end + + From 77c00e320cd0e43d38d83cc369cf6a30981e0c58 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Wed, 31 Jul 2024 16:04:10 +0200 Subject: [PATCH 02/27] Make ExteriorAlgebra an NCRing. --- experimental/ExteriorAlgebra/src/Types.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/Types.jl b/experimental/ExteriorAlgebra/src/Types.jl index dba32ece5921..899ef95bd051 100644 --- a/experimental/ExteriorAlgebra/src/Types.jl +++ b/experimental/ExteriorAlgebra/src/Types.jl @@ -1,4 +1,4 @@ -mutable struct ExteriorAlgebra{T} +mutable struct ExteriorAlgebra{T} <: NCRing base_ring::NCRing symbols::Vector{Symbol} rank::Int @@ -11,7 +11,7 @@ mutable struct ExteriorAlgebra{T} end end -mutable struct ExtAlgElem{T} +mutable struct ExtAlgElem{T} <: NCRingElem parent::ExteriorAlgebra{T} components::Dict{Int, SRow{T}} From e74c6435b9de48cbd8e590dbf4ff22fe8d4389b7 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Wed, 31 Jul 2024 16:04:20 +0200 Subject: [PATCH 03/27] Some more tweaks. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 797da1059e5c..52388b3de93e 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -279,25 +279,37 @@ function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where T R = base_ring(E) n = rank(E) result = zero(E) + new_elem = Dict{Int, T}() for (q, b) in components(w) for (p, a) in components(v) r = p + q r > n && continue - new_elem = sizehint!(Tuple{Int, T}[], length(a)*length(b)) + #new_elem = sizehint!(Tuple{Int, T}[], length(a)*length(b)) + empty!(new_elem) ht = multiplication_hash_table(E, p, q) for (i, c) in a for (j, d) in b s, k = ht[i, j] is_zero(s) && continue res = s * c * d - !iszero(res) && push!(new_elem, (k, res)) + if !iszero(res) + if haskey(new_elem, k) + new_elem[k] += res + else + new_elem[k] = res + end + end + #!iszero(res) && push!(new_elem, (k, res)) end end + for (i, v) in new_elem + iszero(v) && delete!(new_elem, i) + end if !isempty(new_elem) if haskey(components(result), r) - components(result)[r] += sparse_row(R, new_elem) + components(result)[r] += sparse_row(R, [(i, v) for (i, v) in new_elem]) else - components(result)[r] = sparse_row(R, new_elem) + components(result)[r] = sparse_row(R, [(i, v) for (i, v) in new_elem]) end end end @@ -366,3 +378,5 @@ function multiplication_hash_table(E::ExteriorAlgebra, p::Int, q::Int) return E.multiplication_hash_tables[(p, q)] end +ExteriorAlgebra(R::NCRing, n::Int) = ExteriorAlgebra(R, [Symbol("e$i") for i in 1:n]) + From e2d137b31a5854083a6645c51290f46c2069a292 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 01:30:42 +0200 Subject: [PATCH 04/27] Implement homogeneous parts of morphisms and modules. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 177 ++++++++++++++++-- experimental/ExteriorAlgebra/src/Types.jl | 2 + 2 files changed, 168 insertions(+), 11 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 52388b3de93e..0f7cfc57151f 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -215,7 +215,7 @@ function mul!(a::T, w::ExtAlgElem{T}) where {T} return w end -function *(a::T, w::ExtAlgElem{T}) where {T} +function *(a::T, w::ExtAlgElem{T}) where {T <:NCRingElem} @assert parent(a) === base_ring(parent(w)) nc = [(i, a*v) for (i, v) in components(w)] return ExtAlgElem(parent(w), @@ -224,11 +224,15 @@ function *(a::T, w::ExtAlgElem{T}) where {T} ) end -function *(a, w::ExtAlgElem) +function *(a, w::ExtAlgElem{T}) where {T<:NCRingElem} return base_ring(w)(a)*w end -function add!(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} +function *(a::NCRingElement, w::ExtAlgElem{T}) where {T<:NCRingElem} + return base_ring(w)(a)*w +end + +function add!(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T <: NCRingElem} for (i, a) in components(w) if i in keys(components(v)) components(v)[i] += components(w)[i] @@ -240,8 +244,8 @@ function add!(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} return v end -function copy(w::ExtAlgElem) - return ExtAlgElem(parent(w), copy(components(w)); check=false) +function Base.deepcopy_internal(w::ExtAlgElem, id::IdDict) + return ExtAlgElem(parent(w), deepcopy_internal(components(w), id); check=false) end function -(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} @@ -273,9 +277,8 @@ function components(w::ExtAlgElem{T}) where {T} return w.components end -function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where T +function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T<:NCRingElem} E = parent(v) - @assert E === parent(w) R = base_ring(E) n = rank(E) result = zero(E) @@ -342,21 +345,26 @@ one(E::ExteriorAlgebra{T}) where {T} = ExtAlgElem(E, Dict{Int, SRow{T}}(0 => spa function Base.show(io::IO, w::ExtAlgElem) E = parent(w) if is_empty(components(w)) - println(io, "0 : 0") + println(io, "0") + #println(io, "0 : 0") return end + parts = String[] for p in 0:rank(E) !haskey(components(w), p) && continue v = components(w)[p] if iszero(p) - println(io, "0 : $(first(v.values))") + push!(parts, "$(first(v.values))") + #print(io, "0 : $(first(v.values))") continue end - print(io, "$p : ") + #print(io, "$p : ") p_symb = print_symbols(E, p) - println(io, join(["$(c)*"*string(p_symb[i]) for (i, c) in v], " + ")) + push!(parts, join(["$(c)*"*string(p_symb[i]) for (i, c) in v], " + ")) + #println(io, join(["$(c)*"*string(p_symb[i]) for (i, c) in v], " + ")) end + print(io, join(parts, " + ")) end function ==(v::ExtAlgElem, w::ExtAlgElem) @@ -380,3 +388,150 @@ end ExteriorAlgebra(R::NCRing, n::Int) = ExteriorAlgebra(R, [Symbol("e$i") for i in 1:n]) +is_graded(E::ExteriorAlgebra) = true +function grading_group(E::ExteriorAlgebra) + if !isdefined(E, :grading_group) + E.grading_group = free_abelian_group(1) + end + return E.grading_group +end + +elem_type(::Type{ExteriorAlgebra{T}}) where {T} = ExtAlgElem{T} +elem_type(E::ExteriorAlgebra) = elem_type(typeof(E)) +parent_type(::Type{ExtAlgElem{T}}) where {T} = ExteriorAlgebra{T} +parent_type(w::ExtAlgElem) = parent_type(typeof(w)) + +function Base.show(io::IO, E::ExteriorAlgebra) + print(io, "exterior algebra over $(base_ring(E)) in "*join(string.(E.symbols), ", ")) +end + +function degree(w::ExtAlgElem) + isempty(components(w)) && return nothing + return maximum(collect(keys(components(w))))*grading_group(parent(w))[1] +end + +function is_homogeneous(w::ExtAlgElem) + return length(components(w)) < 2 +end + +function (E::ExteriorAlgebra)(a::NCRingElem) + return E(base_ring(E)(a)) +end + +function (E::ExteriorAlgebra{T})(a::ExtAlgElem{T}) where {T} + @assert parent(a) === E + return a +end + +function (E::ExteriorAlgebra{T})(a::T) where {T} + return a*one(E) +end + +function (E::ExteriorAlgebra)(a::Int) + return a*one(E) +end + +function (E::ExteriorAlgebra)() + return zero(E) +end + +function _degree_fast(w::ExtAlgElem) + return degree(w) +end + +function getindex(E::ExteriorAlgebra, i::Int) + if !isdefined(E, :graded_parts) + E.graded_parts = Dict{Int, FreeMod}() + end + parts = E.graded_parts + R = base_ring(E) + n = rank(E) + if !haskey(parts, i) + if i == 1 + F = FreeMod(R, E.symbols) + F.S = E.symbols + parts[i] = F + elseif (i >= 0 && i <= n) + parts[i] = exterior_power(E[1], i)[1] + else + parts[i] = FreeMod(R, 0) + end + end + return parts[i]::FreeMod{elem_type(R)} +end + +@attr Dict{Int, FreeMod{T}} function _graded_parts(F::FreeMod{ExtAlgElem{T}}) where T + return Dict{Int, FreeMod{T}}() +end + +function _graded_part(F::FreeMod{ExtAlgElem{T}}, p::Int) where T + graded_parts = _graded_parts(F) + if !haskey(graded_parts, p) + E = base_ring(F) + R = base_ring(E) + parts = [E[p - Int(degree(F[k])[1])] for k in 1:ngens(F)] + result = direct_sum(parts...)[1] + graded_parts[p] = result + end + return graded_parts[p] +end + + +@attr Dict{Int, FreeModuleHom} function _graded_parts(phi::FreeModuleHom{ModuleType, ModuleType, Nothing}) where {ModuleType <: FreeMod{<:ExtAlgElem}} + return Dict{Int, FreeModuleHom}() +end + +function getindex(phi::FreeModuleHom{ModuleType, ModuleType, Nothing}, p::Int) where {ModuleType <: FreeMod{<:ExtAlgElem}} + @assert is_homogeneous(phi) && iszero(degree(phi)) "morphisms must be homogeneous of degree zero" + parts = _graded_parts(phi) + if !haskey(parts, p) + dom = domain(phi) + cod = codomain(phi) + + dom_p = _graded_part(dom, p) + cod_p = _graded_part(cod, p) + #= + img_gens = gens(dom_p) + img_gens = [dom(g, p) for g in img_gens] + img_gens = phi.(img_gens) + =# + img_gens = elem_type(cod_p)[cod_p(phi(dom(g, p))) for g in gens(dom_p)] + phi_p = hom(dom_p, cod_p, img_gens) + parts[p] = phi_p + end + return parts[p]::FreeModuleHom +end + +function (F::FreeMod{ExtAlgElem{T}})(v::FreeModElem{T}, p::Int) where {T} + E = base_ring(F) + F_p = parent(v) + R = base_ring(F_p) + @assert F_p === _graded_part(F, p) + g_E = gens(F) + d = degree.(gens(F)) + prs = canonical_projections(F_p) + v_comps = [p(v) for p in prs] + d = [Int(a[1]) for a in degree.(gens(F))] + v_comp_coords = [iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, SRow{T}}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] + return sum(a*g for (a, g) in zip(v_comp_coords, g_E); init=zero(F)) +end + +function (F_p::FreeMod{T})(v::FreeModElem{ExtAlgElem{T}}; check::Bool=true) where {T} + @check is_homogeneous(v) "elements must be homogeneous for conversion" + iszero(v) && return zero(F_p) + p = Int(degree(v)[1]) + F = parent(v) + @assert F_p === _graded_part(F, p) + c_v = coordinates(v)::SRow + inj = canonical_injections(F_p) + result = zero(F_p) + d = [Int(a[1]) for a in degree.(gens(F))] + G = grading_group(F) + for (i, c) in c_v + inc = inj[i] + v_i = domain(inc)(c.components[p - d[i]]) + result += inc(v_i) + end + return result +end + diff --git a/experimental/ExteriorAlgebra/src/Types.jl b/experimental/ExteriorAlgebra/src/Types.jl index 899ef95bd051..db9c3d52f64a 100644 --- a/experimental/ExteriorAlgebra/src/Types.jl +++ b/experimental/ExteriorAlgebra/src/Types.jl @@ -5,6 +5,8 @@ mutable struct ExteriorAlgebra{T} <: NCRing print_symbols::Dict{Int, Vector{Symbol}} multiplication_hash_tables::Dict{Tuple{Int, Int}, Matrix{Tuple{Int,Int}}} + grading_group::FinGenAbGroup + graded_parts::Dict{Int, FreeMod{T}} function ExteriorAlgebra(R::NCRing, a::Vector{Symbol}) return new{elem_type(R)}(R, a, length(a)) From eba968903172e63174c8814e2dcf62927fe09fc7 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 02:25:36 +0200 Subject: [PATCH 05/27] Implement graded parts of SubModuleOfFreeModule. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 0f7cfc57151f..563c1c26c576 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -508,7 +508,6 @@ function (F::FreeMod{ExtAlgElem{T}})(v::FreeModElem{T}, p::Int) where {T} R = base_ring(F_p) @assert F_p === _graded_part(F, p) g_E = gens(F) - d = degree.(gens(F)) prs = canonical_projections(F_p) v_comps = [p(v) for p in prs] d = [Int(a[1]) for a in degree.(gens(F))] @@ -535,3 +534,70 @@ function (F_p::FreeMod{T})(v::FreeModElem{ExtAlgElem{T}}; check::Bool=true) wher return result end +@attr Dict{Int, SubModuleOfFreeModule{T}} function _graded_parts(I::SubModuleOfFreeModule{ExtAlgElem{T}}) where {T} + return Dict{Int, SubModuleOfFreeModule{T}}() +end + +function _graded_part(I::SubModuleOfFreeModule{ExtAlgElem{T}}, p::Int) where {T} + graded_parts = _graded_parts(I) + if !haskey(graded_parts, p) + F = ambient_free_module(I) + pp = grading_group(F)([p]) + E = base_ring(F) + R = base_ring(E) + iszero(F) && return SubModuleOfFreeModule(F, elem_type(F)[]) + d = [Int(a[1]) for a in degree.(gens(F))] + d0 = minimum(d) + d_max = maximum(d) + F_p = _graded_part(F, p) + if p < d0 || p > d_max + rank(E) + result = SubModuleOfFreeModule(F_p, elem_type(F_p)[]) + graded_parts[p] = result + return result + elseif p == d0 + g_0 = [g for g in gens(I) if degree(g) == pp] + g_0_p = [F_p(g) for g in g_0] + result = SubModuleOfFreeModule(F_p, g_0) + set_attribute!(result, :_mapping_dict=>IdDict{elem_type(F_p), Tuple{elem_type(F), elem_type(E)}}(g_0_p[i] => (g_0[i], one(E)) for i in 1:length(g_0))) + graded_parts[p] = result + return result + end + + # we can assume that d0 < p <= d_max + rank(E) and by induction + # that I_{p-1} has already been computed + F_p = _graded_part(F, p) + gens_p = elem_type(F_p)[] + I_q = _graded_part(I, p-1) + f = gens(I_q) + # mapping the generators of F_p to ω ⋅ g for generators g of I and monomials ω ∈ E + map_dict_p = IdDict{elem_type(F_p), Tuple{elem_type(F), elem_type(E)}}() + map_dict_q = _mapping_dict(I_q) + mult_gens = elem_type(F)[] + for f_q in gens(I_q) + f, w = map_dict_q[f_q] + for (i, a) in enumerate(gens(E)) + g = a*f + (iszero(g) || g in mult_gens || -g in mult_gens) && continue + push!(mult_gens, g) + aw = a*w + g_p = F_p(g) + push!(gens_p, g_p) + map_dict_p[g_p] = (g, aw) + end + end + gens_ext = [g for g in gens(I) if degree(g) == pp] + gens_p_ext = elem_type(F_p)[F_p(g) for g in gens_ext] + result = SubModuleOfFreeModule(F_p, vcat(gens_p, gens_p_ext)) + for i in 1:length(gens_ext) + map_dict_p[gens_p_ext[i]] = (gens_ext[i], one(E)) + end + set_attribute!(result, :_mapping_dict=>map_dict_p) + graded_parts[p] = result + end + return graded_parts[p] +end + +@attr IdDict function _mapping_dict(I::SubModuleOfFreeModule) + error("this attribute needs to be set manually") +end + From 53be68dd022f1119eb6ef255755c518946a6e30e Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 03:02:37 +0200 Subject: [PATCH 06/27] Implement coordinates for homogeneous elements. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 563c1c26c576..0db245c52a19 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -423,7 +423,7 @@ function (E::ExteriorAlgebra{T})(a::ExtAlgElem{T}) where {T} return a end -function (E::ExteriorAlgebra{T})(a::T) where {T} +function (E::ExteriorAlgebra{T})(a::T) where {T <: NCRingElem} return a*one(E) end @@ -575,14 +575,15 @@ function _graded_part(I::SubModuleOfFreeModule{ExtAlgElem{T}}, p::Int) where {T} mult_gens = elem_type(F)[] for f_q in gens(I_q) f, w = map_dict_q[f_q] + wf = w*f for (i, a) in enumerate(gens(E)) - g = a*f + g = a*wf (iszero(g) || g in mult_gens || -g in mult_gens) && continue push!(mult_gens, g) aw = a*w g_p = F_p(g) push!(gens_p, g_p) - map_dict_p[g_p] = (g, aw) + map_dict_p[g_p] = (f, aw) end end gens_ext = [g for g in gens(I) if degree(g) == pp] @@ -601,3 +602,35 @@ end error("this attribute needs to be set manually") end +function in(v::FreeModElem{ExtAlgElem{T}}, I::SubModuleOfFreeModule{ExtAlgElem{T}}) where {T} + @assert is_homogeneous(v) + iszero(v) && return true + p = Int(degree(v)[1]) + I_p = _graded_part(I, p) + F = ambient_free_module(I) + F_p = _graded_part(F, p) + return F_p(v) in I_p +end + +function coordinates(v::FreeModElem{ExtAlgElem{T}}, I::SubModuleOfFreeModule{ExtAlgElem{T}}) where {T} + @assert is_homogeneous(v) + F = ambient_free_module(I) + @assert parent(v) === F + E = base_ring(F) + R = base_ring(E) + iszero(v) && return sparse_row(E) + p = Int(degree(v)[1]) + I_p = _graded_part(I, p) + F_p = _graded_part(F, p) + c = coordinates(F_p(v), I_p) + map_dict = _mapping_dict(I_p) + result_list = Tuple{Int, elem_type(E)}[] + for (i, a) in c + g_p = I_p[i] + g, w = map_dict[g_p] + j = findfirst(j->g===I[j], 1:ngens(I)) + j === nothing && error("generator not found") + push!(result_list, (j, a*w)) + end + return sparse_row(E, result_list) +end From 7fa96c35af415a6fe100bd9014805a4b47d5ad60 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 04:32:15 +0200 Subject: [PATCH 07/27] Implement kernels. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 73 ++++++++++++++++++- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 0db245c52a19..983af3edb9d8 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -253,7 +253,7 @@ function -(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} end function +(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} - return add!(copy(v), w) + return add!(deepcopy(v), w) end function -(w::ExtAlgElem) @@ -345,7 +345,7 @@ one(E::ExteriorAlgebra{T}) where {T} = ExtAlgElem(E, Dict{Int, SRow{T}}(0 => spa function Base.show(io::IO, w::ExtAlgElem) E = parent(w) if is_empty(components(w)) - println(io, "0") + print(io, "0") #println(io, "0 : 0") return end @@ -405,7 +405,7 @@ function Base.show(io::IO, E::ExteriorAlgebra) print(io, "exterior algebra over $(base_ring(E)) in "*join(string.(E.symbols), ", ")) end -function degree(w::ExtAlgElem) +function degree(w::ExtAlgElem; check::Bool=true) isempty(components(w)) && return nothing return maximum(collect(keys(components(w))))*grading_group(parent(w))[1] end @@ -634,3 +634,70 @@ function coordinates(v::FreeModElem{ExtAlgElem{T}}, I::SubModuleOfFreeModule{Ext end return sparse_row(E, result_list) end + +@attr Dict{Int, SubModuleOfFreeModule{ExtAlgElem{T}}} function _kernel_parts( + phi::FreeModuleHom{ModuleType, ModuleType, Nothing} + ) where {T, ModuleType <: FreeMod{ExtAlgElem{T}}} + return Dict{Int, SubModuleOfFreeModule{ExtAlgElem{T}}}() +end + +function _kernel_part( + phi::FreeModuleHom{ModuleType, ModuleType, Nothing}, p::Int + ) where {T, ModuleType <: FreeMod{ExtAlgElem{T}}} + kernel_parts = _kernel_parts(phi) + if !haskey(kernel_parts, p) + F = domain(phi) + F_p = _graded_part(F, p) + iszero(F) && return SubModuleOfFreeModule(F, elem_type(F)[]) + gens_deg = [Int(degree(g)[1]) for g in gens(F)] + d0 = minimum(gens_deg) + E = base_ring(F) + n = rank(E) + dmax = maximum(gens_deg) + n + + if p < d0 || p > dmax + result = SubModuleOfFreeModule(F, elem_type(F)[]) + kernel_parts[d0-1] = result + return result + elseif p == d0 + phi_p = phi[p] + K_p, _ = kernel(phi_p) + gens_p = filter!(!iszero, ambient_representatives_generators(K_p)) + g = elem_type(F)[F(v, p) for v in gens_p] + Z = SubModuleOfFreeModule(F, g) + kernel_parts[p] = Z + return Z + end + + phi_p = phi[p] + K_p, _ = kernel(phi_p) + B = _kernel_part(phi, p-1) + B_p = _graded_part(B, p) + if all(g in B_p for g in ambient_representatives_generators(K_p)) + kernel_parts[p] = B + return B + end + M = SubquoModule(K_p.sub, B_p) + M2, to_M = simplify_light(M) + new_gens_p = ambient_representative.(to_M.(gens(M2))) + new_gens = elem_type(F)[F(v, p) for v in new_gens_p] + Z = B + SubModuleOfFreeModule(F, new_gens) + kernel_parts[p] = Z + end + return kernel_parts[p] +end + + + + +function kernel(phi::FreeModuleHom{ModuleType, ModuleType, Nothing}) where {ModuleType <: FreeMod{<:ExtAlgElem}} + F = domain(phi) + iszero(F) && return sub(F, elem_type(F)[]) + d0 = minimum([Int(degree(g)[1]) for g in gens(F)]) + E = base_ring(F) + n = rank(E) + dmax = maximum([Int(degree(g)[1]) for g in gens(F)]) + n + + Z = _kernel_part(phi, dmax) + return sub(F, gens(Z)) +end From af8db311abdcd88cb3c6ea6d9165d43107b8bcca Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 15:34:06 +0200 Subject: [PATCH 08/27] Allow zero dimensional complexes. --- experimental/DoubleAndHyperComplexes/src/Objects/Types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl b/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl index 3a5486084c1e..f8f5a0982332 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl @@ -93,7 +93,7 @@ end upper_bounds::Vector=[nothing for i in 1:d], lower_bounds::Vector=[nothing for i in 1:d] ) where {ChainType, MorphismType} - @assert d > 0 "can not create zero or negative dimensional hypercomplex" + @assert d >= 0 "can not create negative dimensional hypercomplex" chains = Dict{Tuple, ChainType}() morphisms = Dict{Tuple, Dict{Int, <:MorphismType}}() return new{ChainType, MorphismType}(d, chains, morphisms, From 6f642a5f62c5db76dc152e67970dc33a2e3c09ba Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 15:34:20 +0200 Subject: [PATCH 09/27] WIP. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 983af3edb9d8..45fc1ad87782 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -277,6 +277,11 @@ function components(w::ExtAlgElem{T}) where {T} return w.components end +function getindex(w::ExtAlgElem, p::Int) + !haskey(w.components, p) && return sparse_row(base_ring(parent(w))) + return w.components[p] +end + function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T<:NCRingElem} E = parent(v) R = base_ring(E) @@ -697,7 +702,74 @@ function kernel(phi::FreeModuleHom{ModuleType, ModuleType, Nothing}) where {Modu E = base_ring(F) n = rank(E) dmax = maximum([Int(degree(g)[1]) for g in gens(F)]) + n + dmax > 0 && (dmax = 0) Z = _kernel_part(phi, dmax) return sub(F, gens(Z)) end + +function change_base_ring(R::NCRing, E::ExteriorAlgebra{T}) where {T<:NCRingElem} + @assert R === base_ring(E) "the ring needs to be the base ring of the exterior algebra" + return R, x->x[0][1] +end + +function change_base_ring(R::Ring, F::FreeMod{ExtAlgElem{T}}) where {T<:RingElem} + E = base_ring(F) + @assert R === base_ring(E) "the ring needs to be the base ring of the exterior algebra" + + indices = [i for i in 1:ngens(F) if iszero(degree(F[i]))] + FoR = FreeMod(R, length(indices)) + #set_attribute!(FoR, :degrees_of_generators=>degrees_of_generators(F)) + _, red0 = change_base_ring(R, E) + img_gens = [i in indices ? FoR[indices[i]] : zero(FoR) for i in 1:ngens(F)] + red = hom(F, FoR, img_gens, red0) + return FoR, red +end + +function _strand(F::FreeMod{T}, d) where {T<:MPolyDecRingElem} + @assert is_z_graded(F) + C = ZeroDimensionalComplex(F) + C0, map_to_orig = strand(C, d) + return C0[()], map_to_orig[()] +end + +function _ext_module_map(F::FreeMod{T}, d::Int) where {T<:MPolyDecRingElem} + Fd, Fd_to_F = _strand(F, d) + e = d+1 + Fe, Fe_to_F = _strand(F, e) + #F_to_Fe = + S = base_ring(F) + R = base_ring(S) + E = _exterior_algebra(S) + G = grading_group(E) + Fd_E = graded_free_module(E, [d*G[1] for i in 1:ngens(Fd)]) + Fe_E = graded_free_module(E, [e*G[1] for i in 1:ngens(Fe)]) + Fe_map = hom(Fe, Fe_E, gens(Fe_E), E) + img_gens = elem_type(E)[] + for (i, g) in enumerate(gens(Fd)) + img = zero(Fe_E) + for j in 1:nvars(S) + h = g + h = Fd_to_F(h) + h = S[j]*g + h = F_to_Fe(h) + h = Fe_map(h) + h = E[j]*h + img += h + end + push!(img_gens, img) + end + return hom(Fd_E, Fe_E, img_gens) +end + + + + + +@attr ExteriorAlgebra{T} function _exterior_algebra(S::MPolyDecRing{T}) where {T} + @assert is_z_graded + R = base_ring(S) + new_symb = symbol.([string(s)*" ̌" for s in symbols(S)]) + return ExteriorAlgebra(R, new_symb) +end + From ad298cf4bc719c93833e9e503eec558fc7b586da Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 22:00:29 +0200 Subject: [PATCH 10/27] Add verbosity scope for exterior algebras. --- src/Oscar.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Oscar.jl b/src/Oscar.jl index 282a1d516578..ae6d60bc0f64 100644 --- a/src/Oscar.jl +++ b/src/Oscar.jl @@ -177,6 +177,8 @@ function __init__() add_verbosity_scope(:ZZLatWithIsom) add_assertion_scope(:IdealSheaves) + + add_verbosity_scope(:ExteriorAlgebras) # Pkg.is_manifest_current() returns false if the manifest might be out of date # (but might return nothing when there is no project_hash) From 9853b991af101707a85d3739bbd642620c0d3e04 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 22:03:57 +0200 Subject: [PATCH 11/27] Improvements for cohomology computations. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 239 +++++++++++++++--- 1 file changed, 203 insertions(+), 36 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 45fc1ad87782..b02dc9620be5 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -375,7 +375,17 @@ end function ==(v::ExtAlgElem, w::ExtAlgElem) E = parent(v) @assert E === parent(w) - return components(v) == components(w) + (!isdefined(v, :components) && !isdefined(w, :components)) && (return !isdefined(v, :components) && !isdefined(w, :components)) + #return components(v) == components(w) + for (p, c) in components(v) + p in keys(components(w)) || return false + c == components(w)[p] || return false + end + return all(q in keys(components(v)) for q in keys(components(w))) + for (q, cc) in components(w) + q in keys(components(v)) || return false + end + return true end function multiplication_hash_table(E::ExteriorAlgebra, p::Int, q::Int) @@ -411,7 +421,7 @@ function Base.show(io::IO, E::ExteriorAlgebra) end function degree(w::ExtAlgElem; check::Bool=true) - isempty(components(w)) && return nothing + (!isdefined(w, :components) || isempty(components(w))) && return zero(grading_group(parent(w))) return maximum(collect(keys(components(w))))*grading_group(parent(w))[1] end @@ -474,7 +484,7 @@ function _graded_part(F::FreeMod{ExtAlgElem{T}}, p::Int) where T if !haskey(graded_parts, p) E = base_ring(F) R = base_ring(E) - parts = [E[p - Int(degree(F[k])[1])] for k in 1:ngens(F)] + parts = [E[p - Int(degree(g)[1])] for g in gens(F)] result = direct_sum(parts...)[1] graded_parts[p] = result end @@ -500,13 +510,29 @@ function getindex(phi::FreeModuleHom{ModuleType, ModuleType, Nothing}, p::Int) w img_gens = [dom(g, p) for g in img_gens] img_gens = phi.(img_gens) =# - img_gens = elem_type(cod_p)[cod_p(phi(dom(g, p))) for g in gens(dom_p)] + #img_gens = elem_type(cod_p)[cod_p(phi(dom(g, p)); check=false) for g in gens(dom_p)] + img_gens = cod_p(phi.(dom(gens(dom_p), p)); check=false) phi_p = hom(dom_p, cod_p, img_gens) parts[p] = phi_p end return parts[p]::FreeModuleHom end +function (F::FreeMod{ExtAlgElem{T}})(v::Vector{FreeModElem{T}}, p::Int) where {T} + E = base_ring(F) + isempty(v) && return elem_type(F)[] + F_p = parent(first(v)) + R = base_ring(F_p) + @assert F_p === _graded_part(F, p) + g_E = gens(F) + prs = canonical_projections(F_p) + v_comp_list = [[p(w) for p in prs] for w in v] + #d = [Int(degree(g; check=false)[1]) for g in gens(F)] + d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] + v_comp_coords_list = [[iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, SRow{T}}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] for v_comps in v_comp_list] + return elem_type(F)[sum(a*g for (a, g) in zip(v_comp_coords, g_E); init=zero(F)) for v_comp_coords in v_comp_coords_list] +end + function (F::FreeMod{ExtAlgElem{T}})(v::FreeModElem{T}, p::Int) where {T} E = base_ring(F) F_p = parent(v) @@ -515,7 +541,8 @@ function (F::FreeMod{ExtAlgElem{T}})(v::FreeModElem{T}, p::Int) where {T} g_E = gens(F) prs = canonical_projections(F_p) v_comps = [p(v) for p in prs] - d = [Int(a[1]) for a in degree.(gens(F))] + #d = [Int(degree(g; check=false)[1]) for g in gens(F)] + d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] v_comp_coords = [iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, SRow{T}}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] return sum(a*g for (a, g) in zip(v_comp_coords, g_E); init=zero(F)) end @@ -523,13 +550,14 @@ end function (F_p::FreeMod{T})(v::FreeModElem{ExtAlgElem{T}}; check::Bool=true) where {T} @check is_homogeneous(v) "elements must be homogeneous for conversion" iszero(v) && return zero(F_p) - p = Int(degree(v)[1]) + p = Int(degree(v; check=false)[1]) F = parent(v) @assert F_p === _graded_part(F, p) c_v = coordinates(v)::SRow inj = canonical_injections(F_p) result = zero(F_p) - d = [Int(a[1]) for a in degree.(gens(F))] + #d = [Int(degree(g; check=false)[1]) for g in gens(F)] + d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] G = grading_group(F) for (i, c) in c_v inc = inj[i] @@ -539,6 +567,32 @@ function (F_p::FreeMod{T})(v::FreeModElem{ExtAlgElem{T}}; check::Bool=true) wher return result end +function (F_p::FreeMod{T})(a::Vector{FreeModElem{ExtAlgElem{T}}}; check::Bool=true) where {T} + @check is_homogeneous(v) "elements must be homogeneous for conversion" + isempty(a) && return elem_type(F_p)[] + j = findfirst(!iszero(v) for v in a) + j === nothing && return [zero(F_p) for i in 1:length(a)] + p = Int(degree(a[j]; check=false)[1]) + F = parent(first(a)) + @assert F_p === _graded_part(F, p) + c_v_list = [coordinates(v)::SRow for v in a] + inj = canonical_injections(F_p) + result = elem_type(F_p)[] + #d = [Int(degree(g; check=false)[1]) for g in gens(F)] + d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] + G = grading_group(F) + for c_v in c_v_list + res = zero(F_p) + for (i, c) in c_v + inc = inj[i] + v_i = domain(inc)(c.components[p - d[i]]) + res += inc(v_i) + end + push!(result, res) + end + return result +end + @attr Dict{Int, SubModuleOfFreeModule{T}} function _graded_parts(I::SubModuleOfFreeModule{ExtAlgElem{T}}) where {T} return Dict{Int, SubModuleOfFreeModule{T}}() end @@ -551,7 +605,8 @@ function _graded_part(I::SubModuleOfFreeModule{ExtAlgElem{T}}, p::Int) where {T} E = base_ring(F) R = base_ring(E) iszero(F) && return SubModuleOfFreeModule(F, elem_type(F)[]) - d = [Int(a[1]) for a in degree.(gens(F))] + #d = [Int(degree(g; check=false)[1]) for g in gens(F)] + d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] d0 = minimum(d) d_max = maximum(d) F_p = _graded_part(F, p) @@ -560,9 +615,9 @@ function _graded_part(I::SubModuleOfFreeModule{ExtAlgElem{T}}, p::Int) where {T} graded_parts[p] = result return result elseif p == d0 - g_0 = [g for g in gens(I) if degree(g) == pp] - g_0_p = [F_p(g) for g in g_0] - result = SubModuleOfFreeModule(F_p, g_0) + g_0 = elem_type(F)[g for g in gens(I) if degree(g; check=false) == pp] + g_0_p = elem_type(F_p)[F_p(g) for g in g_0] + result = SubModuleOfFreeModule(F_p, g_0_p) set_attribute!(result, :_mapping_dict=>IdDict{elem_type(F_p), Tuple{elem_type(F), elem_type(E)}}(g_0_p[i] => (g_0[i], one(E)) for i in 1:length(g_0))) graded_parts[p] = result return result @@ -591,7 +646,7 @@ function _graded_part(I::SubModuleOfFreeModule{ExtAlgElem{T}}, p::Int) where {T} map_dict_p[g_p] = (f, aw) end end - gens_ext = [g for g in gens(I) if degree(g) == pp] + gens_ext = [g for g in gens(I) if degree(g; check=false) == pp] gens_p_ext = elem_type(F_p)[F_p(g) for g in gens_ext] result = SubModuleOfFreeModule(F_p, vcat(gens_p, gens_p_ext)) for i in 1:length(gens_ext) @@ -610,7 +665,7 @@ end function in(v::FreeModElem{ExtAlgElem{T}}, I::SubModuleOfFreeModule{ExtAlgElem{T}}) where {T} @assert is_homogeneous(v) iszero(v) && return true - p = Int(degree(v)[1]) + p = Int(degree(v; check=false)[1]) I_p = _graded_part(I, p) F = ambient_free_module(I) F_p = _graded_part(F, p) @@ -624,7 +679,7 @@ function coordinates(v::FreeModElem{ExtAlgElem{T}}, I::SubModuleOfFreeModule{Ext E = base_ring(F) R = base_ring(E) iszero(v) && return sparse_row(E) - p = Int(degree(v)[1]) + p = Int(degree(v; check=false)[1]) I_p = _graded_part(I, p) F_p = _graded_part(F, p) c = coordinates(F_p(v), I_p) @@ -650,11 +705,12 @@ function _kernel_part( phi::FreeModuleHom{ModuleType, ModuleType, Nothing}, p::Int ) where {T, ModuleType <: FreeMod{ExtAlgElem{T}}} kernel_parts = _kernel_parts(phi) + @vprint :ExteriorAlgebras 2 "computing kernel up to degree $p\n" if !haskey(kernel_parts, p) F = domain(phi) F_p = _graded_part(F, p) iszero(F) && return SubModuleOfFreeModule(F, elem_type(F)[]) - gens_deg = [Int(degree(g)[1]) for g in gens(F)] + gens_deg = [Int(degree(g; check=false)[1]) for g in gens(F)] d0 = minimum(gens_deg) E = base_ring(F) n = rank(E) @@ -663,47 +719,67 @@ function _kernel_part( if p < d0 || p > dmax result = SubModuleOfFreeModule(F, elem_type(F)[]) kernel_parts[d0-1] = result + @vprint :ExteriorAlgebras 2 "done with kernel computation up to degree $p\n" return result elseif p == d0 + @vprint :ExteriorAlgebras 3 "extracting the graded part of degree $p\n" phi_p = phi[p] + @vprint :ExteriorAlgebras 3 "restricted map has $(ngens(domain(phi_p))) generators in the domain and $(ngens(codomain(phi_p))) in the codomain\n" K_p, _ = kernel(phi_p) gens_p = filter!(!iszero, ambient_representatives_generators(K_p)) g = elem_type(F)[F(v, p) for v in gens_p] Z = SubModuleOfFreeModule(F, g) kernel_parts[p] = Z + @vprint :ExteriorAlgebras 2 "done with kernel computation up to degree $p\n" return Z end + @vprint :ExteriorAlgebras 3 "extracting the graded part of degree $p\n" phi_p = phi[p] K_p, _ = kernel(phi_p) + @vprint :ExteriorAlgebras 3 "restricted map has $(ngens(domain(phi_p))) generators in the domain and $(ngens(codomain(phi_p))) in the codomain\n" B = _kernel_part(phi, p-1) B_p = _graded_part(B, p) if all(g in B_p for g in ambient_representatives_generators(K_p)) kernel_parts[p] = B + @vprint :ExteriorAlgebras 3 "no new generators\n" + @vprint :ExteriorAlgebras 2 "done with kernel computation up to degree $p\n" return B end M = SubquoModule(K_p.sub, B_p) - M2, to_M = simplify_light(M) + #M2, to_M = simplify_light(M) + M2, to_M = prune_with_map(M) new_gens_p = ambient_representative.(to_M.(gens(M2))) + @vprint :ExteriorAlgebras 3 "$(length(new_gens_p)) new generators\n" new_gens = elem_type(F)[F(v, p) for v in new_gens_p] Z = B + SubModuleOfFreeModule(F, new_gens) kernel_parts[p] = Z end + @vprint :ExteriorAlgebras 2 "done with kernel computation up to degree $p\n" return kernel_parts[p] end -function kernel(phi::FreeModuleHom{ModuleType, ModuleType, Nothing}) where {ModuleType <: FreeMod{<:ExtAlgElem}} +function kernel( + phi::FreeModuleHom{ModuleType, ModuleType, Nothing}; + degree_bound::Union{Int, Nothing}=0 + ) where {ModuleType <: FreeMod{<:ExtAlgElem}} + @vprint :ExteriorAlgebras 1 "computing kernel of map with degrees\n" + @vprint :ExteriorAlgebras 1 "$([degree(g; check=false) for g in gens(domain(phi))])\n" + @vprint :ExteriorAlgebras 1 "in the domain and degrees\n" + @vprint :ExteriorAlgebras 1 "$([degree(g; check=false) for g in gens(codomain(phi))])\n" + @vprint :ExteriorAlgebras 1 "in the codomain up to degree $(degree_bound)\n" F = domain(phi) iszero(F) && return sub(F, elem_type(F)[]) - d0 = minimum([Int(degree(g)[1]) for g in gens(F)]) + d0 = minimum([Int(degree(g; check=false)[1]) for g in gens(F)]) E = base_ring(F) n = rank(E) - dmax = maximum([Int(degree(g)[1]) for g in gens(F)]) + n - dmax > 0 && (dmax = 0) - + dmax = maximum([Int(degree(g; check=false)[1]) for g in gens(F)]) + n + if degree_bound !== nothing + dmax > degree_bound && (dmax = degree_bound) + end Z = _kernel_part(phi, dmax) return sub(F, gens(Z)) end @@ -717,15 +793,41 @@ function change_base_ring(R::Ring, F::FreeMod{ExtAlgElem{T}}) where {T<:RingElem E = base_ring(F) @assert R === base_ring(E) "the ring needs to be the base ring of the exterior algebra" - indices = [i for i in 1:ngens(F) if iszero(degree(F[i]))] + indices = [i for i in 1:ngens(F) if iszero(degree(F[i]; check=false))] FoR = FreeMod(R, length(indices)) #set_attribute!(FoR, :degrees_of_generators=>degrees_of_generators(F)) _, red0 = change_base_ring(R, E) - img_gens = [i in indices ? FoR[indices[i]] : zero(FoR) for i in 1:ngens(F)] + img_gens = elem_type(FoR)[] + j = 1 + for (i, g) in enumerate(gens(F)) + if !iszero(degree(g; check=false)) + push!(img_gens, zero(FoR)) + else + push!(img_gens, gen(FoR, j)) + j += 1 + end + end + #img_gens = [i in indices ? FoR[indices[i]] : zero(FoR) for i in 1:ngens(F)] red = hom(F, FoR, img_gens, red0) return FoR, red end +function change_base_ring(R::Ring, f::ModuleFPHom{MT, MT, Nothing}; + domain_base_change::Map = change_base_ring(R, domain(f))[2], + codomain_base_change::Map = change_base_ring(R, codomain(f))[2] + ) where {MT <: ModuleFP{<:ExtAlgElem}} + bc_dom = codomain(domain_base_change) + bc_cod = codomain(codomain_base_change) + @assert domain(f) === domain(domain_base_change) + @assert codomain(f) === domain(codomain_base_change) + indices = [i for i in 1:ngens(domain(f)) if iszero(degree(domain(f)[i]; check=false))] + img_gens = gens(domain(f))[indices] + img_gens = f.(img_gens) + img_gens = codomain_base_change.(img_gens) + img_gens = [codomain_base_change(f(domain(f)[i])) for i in indices] + return hom(bc_dom, bc_cod, img_gens) +end + function _strand(F::FreeMod{T}, d) where {T<:MPolyDecRingElem} @assert is_z_graded(F) C = ZeroDimensionalComplex(F) @@ -733,43 +835,108 @@ function _strand(F::FreeMod{T}, d) where {T<:MPolyDecRingElem} return C0[()], map_to_orig[()] end -function _ext_module_map(F::FreeMod{T}, d::Int) where {T<:MPolyDecRingElem} - Fd, Fd_to_F = _strand(F, d) +### Partially copied from the StrandComplexes. TODO: Clean up to avoid duplication! +function strand(F::FreeMod{<:MPolyDecRingElem}, d::Int) + @assert is_z_graded(F) + S = base_ring(F) + R = base_ring(S) + Fd = FreeMod(R, length(all_exponents(F, d))) + + # Use a dictionary for fast mapping of the monomials to the + # generators of `Fd`. + inv_exp_dict = Dict{Tuple{Vector{Int}, Int}, elem_type(Fd)}(m=>Fd[k] for (k, m) in enumerate(all_exponents(F, d))) + # Hashing of FreeModElem's can not be assumed to be non-trivial. Hence we use the exponents directly. + img_gens = elem_type(F)[] + x = gens(S) + for (e, i) in all_exponents(F, d) # iterate through the generators of `F` + m = prod(x^k for (x, k) in zip(x, e); init=one(S))*F[i] + push!(img_gens, m) + end + Fd_to_F = hom(Fd, F, img_gens, S) + function my_map(v::FreeModElem) + @assert Int(degree(v; check=false)[1]) == d "input must be homogeneous of degree $d" + w = zero(Fd) + for (i, b) in coordinates(v) + w += sum(c*inv_exp_dict[(n, i)] for (c, n) in zip(coefficients(b), exponents(b)); init=zero(Fd)) + end + return w + end + F_to_Fd = MapFromFunc(F, Fd, my_map) + return Fd, Fd_to_F, F_to_Fd +end + +function _ext_module_map( + F::FreeMod{T}, d::Int; + domain_strand::Tuple{<:FreeMod, <:Map, <:Map}=strand(F, d), + codomain_strand::Tuple{<:FreeMod, <:Map, <:Map}=strand(F, d+1), + ) where {T<:MPolyDecRingElem} + @assert is_z_graded(F) + Fd, Fd_to_F, F_to_Fd = domain_strand e = d+1 - Fe, Fe_to_F = _strand(F, e) - #F_to_Fe = + Fe, Fe_to_F, F_to_Fe = codomain_strand S = base_ring(F) R = base_ring(S) E = _exterior_algebra(S) G = grading_group(E) - Fd_E = graded_free_module(E, [d*G[1] for i in 1:ngens(Fd)]) - Fe_E = graded_free_module(E, [e*G[1] for i in 1:ngens(Fe)]) + Fd_E = graded_free_module(E, [-d*G[1] for i in 1:ngens(Fd)]) + Fe_E = graded_free_module(E, [-e*G[1] for i in 1:ngens(Fe)]) Fe_map = hom(Fe, Fe_E, gens(Fe_E), E) - img_gens = elem_type(E)[] + Fd_map = hom(Fd, Fd_E, gens(Fd_E), E) + img_gens = elem_type(Fe_E)[] for (i, g) in enumerate(gens(Fd)) img = zero(Fe_E) for j in 1:nvars(S) h = g h = Fd_to_F(h) - h = S[j]*g + h = gen(S, j)*h h = F_to_Fe(h) h = Fe_map(h) - h = E[j]*h + h = gen(E, j)*h img += h end push!(img_gens, img) end - return hom(Fd_E, Fe_E, img_gens) + return hom(Fd_E, Fe_E, img_gens), Fd_map, Fe_map +end + +function _ext_module_map(M::SubquoModule{T}, d::Int) where {T<:MPolyDecRingElem} + pres = presentation(M) + F0 = pres[0] + F1 = pres[1] + b = map(pres, 1) # F1 -> F0 + F0_d, to_F0, to_F0_d = strand(F0, d) + F1_d, to_F1, to_F1_d = strand(F1, d) + e = d + 1 + F0_e, to_F0_2, to_F0_e = strand(F0, e) + F1_e, to_F1_2, to_F1_e = strand(F1, e) + + dom_rels, inc_dom = sub(F0_d, to_F0_d.(b.(to_F1.(gens(F1_d))))) + cod_rels, inc_cod = sub(F0_e, to_F0_e.(b.(to_F1_2.(gens(F1_e))))) + phi, F0_d_map, F0_e_map = _ext_module_map(F0, d; domain_strand = (F0_d, to_F0, to_F0_d), + codomain_strand = (F0_e, to_F0_2, to_F0_e)) + @assert domain(F0_d_map) === F0_d + @assert domain(F0_e_map) === F0_e + @assert domain(phi) === codomain(F0_d_map) + @assert codomain(phi) === codomain(F0_e_map) + + M_dom, pr_dom = quo(domain(phi), F0_d_map.(ambient_representatives_generators(dom_rels))) + M_cod, pr_cod = quo(codomain(phi), F0_e_map.(ambient_representatives_generators(cod_rels))) + @assert domain(pr_cod) === codomain(phi) + + psi = hom(M_dom, M_cod, pr_cod.(phi.(F0_d_map.(gens(F0_d))))) + return psi end - +function gen(E::ExteriorAlgebra{T}, i::Int) where {T} + return ExtAlgElem(E, Dict{Int, SRow{T}}(1 => sparse_row(base_ring(E), [(i, one(base_ring(E)))]))) +end @attr ExteriorAlgebra{T} function _exterior_algebra(S::MPolyDecRing{T}) where {T} - @assert is_z_graded + @assert is_z_graded(S) R = base_ring(S) - new_symb = symbol.([string(s)*" ̌" for s in symbols(S)]) + new_symb = Symbol.([string(s)*" ̌" for s in symbols(S)]) return ExteriorAlgebra(R, new_symb) end From 3ac15d93bd1291c6bf46ab9ed76231bbfb97ea41 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 01:06:36 +0200 Subject: [PATCH 12/27] Some tweaks. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index b02dc9620be5..54f4853018fa 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -525,8 +525,14 @@ function (F::FreeMod{ExtAlgElem{T}})(v::Vector{FreeModElem{T}}, p::Int) where {T R = base_ring(F_p) @assert F_p === _graded_part(F, p) g_E = gens(F) + #= + # old code which is correct, but slow prs = canonical_projections(F_p) v_comp_list = [[p(w) for p in prs] for w in v] + =# + ranges = get_attribute(F_p, :ranges)::Vector{UnitRange{Int}} + F_p_parts = get_attribute(F_p, :direct_product)::NTuple + v_comp_list = [[G(coordinates(w)[r]) for (G, r) in zip(F_p_parts, ranges)] for w in v] #d = [Int(degree(g; check=false)[1]) for g in gens(F)] d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] v_comp_coords_list = [[iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, SRow{T}}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] for v_comps in v_comp_list] @@ -539,8 +545,13 @@ function (F::FreeMod{ExtAlgElem{T}})(v::FreeModElem{T}, p::Int) where {T} R = base_ring(F_p) @assert F_p === _graded_part(F, p) g_E = gens(F) + #= prs = canonical_projections(F_p) v_comps = [p(v) for p in prs] + =# + ranges = get_attribute(F_p, :ranges)::Vector{UnitRange{Int}} + F_p_parts = get_attribute(F_p, :direct_product)::NTuple + v_comps = [G(coordinates(v)[r]) for (G, r) in zip(F_p_parts, ranges)] #d = [Int(degree(g; check=false)[1]) for g in gens(F)] d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] v_comp_coords = [iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, SRow{T}}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] @@ -554,11 +565,22 @@ function (F_p::FreeMod{T})(v::FreeModElem{ExtAlgElem{T}}; check::Bool=true) wher F = parent(v) @assert F_p === _graded_part(F, p) c_v = coordinates(v)::SRow - inj = canonical_injections(F_p) + # Slow code: result = zero(F_p) #d = [Int(degree(g; check=false)[1]) for g in gens(F)] d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] G = grading_group(F) + ranges = get_attribute(F_p, :ranges)::Vector{UnitRange{Int}} + for (i, c) in c_v + w = deepcopy(c.components[p-d[i]]) + offset = first(ranges[i])-1 + w.pos.+=offset + result += F_p(w) + end + return result + # old code left here to show what the intention was. + # the above is a hack to speed stuff up, but it messes with unstable internals + inj = canonical_injections(F_p) for (i, c) in c_v inc = inj[i] v_i = domain(inc)(c.components[p - d[i]]) @@ -854,6 +876,7 @@ function strand(F::FreeMod{<:MPolyDecRingElem}, d::Int) end Fd_to_F = hom(Fd, F, img_gens, S) function my_map(v::FreeModElem) + iszero(v) && return zero(Fd) @assert Int(degree(v; check=false)[1]) == d "input must be homogeneous of degree $d" w = zero(Fd) for (i, b) in coordinates(v) From ce7169246de9a75acf9dbb9143ca81f826efc1f3 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 12:38:17 +0200 Subject: [PATCH 13/27] Implement generic method for derived pushforward with BGG. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 54f4853018fa..08ac74388e5b 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -947,7 +947,8 @@ function _ext_module_map(M::SubquoModule{T}, d::Int) where {T<:MPolyDecRingElem} @assert domain(pr_cod) === codomain(phi) psi = hom(M_dom, M_cod, pr_cod.(phi.(F0_d_map.(gens(F0_d))))) - return psi + + return psi, MapFromFunc(M_dom, M, v->map(pres, 0)(to_F0(repres(v)))), MapFromFunc(M_cod, M, v->map(pres, 0)(to_F0_2(repres(v)))) end function gen(E::ExteriorAlgebra{T}, i::Int) where {T} @@ -963,3 +964,14 @@ end return ExteriorAlgebra(R, new_symb) end +function _derived_pushforward_BGG(M::ModuleFP{T}; regularity::Int=_regularity_bound(M)) where {T <: MPolyDecRingElem{<:RingElem}} + phi, _, _ = _ext_module_map(M, regularity+1) + S = base_ring(M) + R = base_ring(S) + I, inc = kernel(phi) + res, aug = free_resolution(SimpleFreeResolution, I) + res0, red = change_base_ring(R, res) + return res0 +end + + From 0afcc67e57d22e72d0c4c06c8bc576684886924b Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 14:28:52 +0200 Subject: [PATCH 14/27] Raw implementation of CA resolutions. --- .../src/DoubleAndHyperComplexes.jl | 2 + .../src/Morphisms/Types.jl | 20 ++ .../Morphisms/cartan_eilenberg_resolutions.jl | 23 +- .../Objects/cartan_eilenberg_resolution.jl | 196 ++++++++++++++++++ 4 files changed, 222 insertions(+), 19 deletions(-) create mode 100644 experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl diff --git a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl index 709dd1973a20..b554a991c419 100644 --- a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl @@ -9,6 +9,7 @@ include("Objects/koszul_complexes.jl") include("Objects/degree_zero_complexes.jl") include("Objects/new_complex_template.jl") include("Objects/linear_strands.jl") +include("Objects/cartan_eilenberg_resolutions.jl" include("Morphisms/Types.jl") include("Morphisms/cartan_eilenberg_resolutions.jl") @@ -25,3 +26,4 @@ include("Exports.jl") include("base_change_types.jl") include("base_change.jl") + diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl index 9e5a5c1ac05b..0551379623d6 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl @@ -142,3 +142,23 @@ end underlying_complex(c::SimpleFreeResolution) = c.underlying_complex original_module(c::SimpleFreeResolution) = c.M +### Lifting morphisms through projective resolutions +mutable struct MapLifter{MorphismType} <: HyperComplexMorphismFactory{MorphismType} + dom::AbsHyperComplex + cod::AbsHyperComplex + orig_map::Map + start_index::Int + offset::Int + check::Bool + + function MapLifter(::Type{MorphismType}, dom::AbsHyperComplex, cod::AbsHyperComplex, phi::Map; + start_index::Int=0, offset::Int=0, check::Bool=true + ) where {MorphismType} + @assert dim(dom) == 1 && dim(cod) == 1 "lifting of maps is only implemented in dimension one" + @assert (is_chain_complex(dom) && is_chain_complex(cod)) || (is_cochain_complex(dom) && is_cochain_complex(cod)) + @assert domain(phi) === dom[start_index] + @assert codomain(phi) === cod[start_index + offset] + return new{MorphismType}(dom, cod, phi, start_index, offset) + end +end + diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl index 9dbf5a1078dd..1823bdf37c54 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl @@ -1,22 +1,4 @@ -mutable struct MapLifter{MorphismType} <: HyperComplexMorphismFactory{MorphismType} - dom::AbsHyperComplex - cod::AbsHyperComplex - orig_map::Map - start_index::Int - offset::Int - check::Bool - - function MapLifter(::Type{MorphismType}, dom::AbsHyperComplex, cod::AbsHyperComplex, phi::Map; - start_index::Int=0, offset::Int=0, check::Bool=true - ) where {MorphismType} - @assert dim(dom) == 1 && dim(cod) == 1 "lifting of maps is only implemented in dimension one" - @assert (is_chain_complex(dom) && is_chain_complex(cod)) || (is_cochain_complex(dom) && is_cochain_complex(cod)) - @assert domain(phi) === dom[start_index] - @assert codomain(phi) === cod[start_index + offset] - return new{MorphismType}(dom, cod, phi, start_index, offset) - end -end - +### Lifting maps through projective resolutions is_chain_complex(c::AbsHyperComplex) = (dim(c) == 1 ? direction(c, 1) == (:chain) : error("complex is not one-dimensional")) is_cochain_complex(c::AbsHyperComplex) = (dim(c) == 1 ? direction(c, 1) == (:cochain) : error("complex is not one-dimensional")) @@ -57,3 +39,6 @@ function lift_map(dom::AbsHyperComplex{DomChainType}, cod::AbsHyperComplex{CodCh end morphism_type(::Type{T1}, ::Type{T2}) where {T1<:ModuleFP, T2<:ModuleFP} = ModuleFPHom{<:T1, <:T2} + +### Cartan Eilenberg resolutions + diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl new file mode 100644 index 000000000000..3570bd53a3d8 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -0,0 +1,196 @@ +#= Cartan Eilenberg resolutions of 1-dimensional complexes +# +# Suppose +# +# 0 ← C₀ ← C₁ ← C₂ ← … +# +# is a bounded below complex. We compute a double complex +# +# 0 ← P₀₀ ← P₀₁ ← P₀₂ ← … +# ↑ ↑ ↑ +# 0 ← P₁₀ ← P₁₁ ← P₁₂ ← … +# ↑ ↑ ↑ +# 0 ← P₂₀ ← P₂₁ ← P₂₂ ← … +# ↑ ↑ ↑ +# ⋮ ⋮ ⋮ +# +# which is quasi-isomorphic to C via some augmentation map ε. +# +# The challenge is that if we were only computing resolutions of the Cᵢ's +# and lifting the maps, then the rows of the resulting diagrams would +# not necessarily form complexes. To accomplish that, we split the original +# complex into short exact sequences +# +# 0 ← Bᵢ ← Cᵢ ← Zᵢ ← 0 +# +# and apply the Horse shoe lemma to these. Together with the induced maps +# from Bᵢ ↪ Zᵢ₋₁ we get the desired double complex. +# +# If the original complex C is known to be exact, then there is no need +# to compute the resolutions of both Bᵢ and Zᵢ and we can shorten the procedure. +=# +### Production of the chains +struct CEChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} + c::AbsHyperComplex + is_exact::Bool + kernel_resolutions::Dict{Int, <:AbsHyperComplex} # the kernels of Cᵢ → Cᵢ₋₁ + boundary_resolutions::Dict{Int, <:AbsHyperComplex} # the boundaries of Cᵢ₊₁ → Cᵢ + induced_maps::Dict{Int, <:AbsHyperComplexMorphism} # the induced maps from the free + # resolutions of the boundary and kernel + + function CEChainFactory(c::AbsHyperComplex; is_exact::Bool=false) + @assert dim(c) == 1 "complex must be 1-dimensional" + #@assert has_lower_bound(c, 1) "complex must be bounded from below" + return new{chain_type(c)}(c, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}()) + end +end + +function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) + (i, j) = I + + if iszero(j) + return fac.c[i] + end + + res_Z = getindex!(c.kernel_resolutions, i) do + Z, _ = kernel(fac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + + if can_compute_map(fac.c, 1, (i,)) + if fac.isexact + res_B = getindex!(c.kernel_resolutions, i-1) do + B, _ = kernel(fac.c, i-1) + return free_resolution(SimpleFreeResolution, B) + end + return direct_sum(res_B[j], res_Z[j]) + else + res_B = getindex!(c.boundary_resolutions, i-1) do + B, _ = boundary(fac.c, i-1) + return free_resolution(SimpleFreeResolution, B) + end + return direct_sum(res_B[j], res_Z[j]) + end + end + return res_Z[j] +end + +function can_compute(fac::CEChainFactory, self::AbsHyperComplex, I::Tuple) + (i, j) = I + can_compute_index(fac.c, (i,)) || return false + return j >= 0 +end + +### Production of the morphisms +struct CEMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} +end + +function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) + (i, j) = I + cfac = chain_factory(self) + if p == 1 + j == 0 && return map(cfac.c, 1, (i,)) + elseif p == 2 + if can_compute_map(cfac.c, 1, (i,)) + B_plus_Z = self[I] + C = cfac.c[(i,)] + B, pr_B = boundary(cfac.c, 1, (i-1,)) + if cfac.is_exact + # use the kernel instead; we have to adjust the map + Z, inc_Z = kernel(cfac.c, 1, (i-1,)) + B = Z + phi = map(cfac.c, 1, (i,)) + img_gens = [preimage(inc_Z, phi(g)) for g in gens(C)] + pr_B = hom(C, B, img_gens; check=true) # TODO: Set to false + end + Z, inc_Z = kernel(cfac.c, 1, (i,)) + @assert domain(pr_B) === C + @assert codomain(inc_Z) === C + if j == 1 + img_gens = elem_type(C)[preimage(pr_B, g) for g in gens(B)] + img_gens = vcat(img_gens, elem_type(C)[inc_Z(g) for g in gens(Z)]) + return hom(B_plus_Z, C, img_gens; check=true) # TODO: Set to false + else + # may assume j > 1 + cod = self[(i, j-1)] + res_Z = getindex!(cfac.kernel_resolutions, i) do + Z, _ = kernel(fac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + b = map(res_Z, 1, (j,)) + inc2 = canonical_injection(cod, 2) + @assert domain(inc2) === res_Z[j-1] === codomain(b) + @assert codomain(inc2) === cod + + if cfac.isexact + res_Z1 = getindex!(cfac.kernel_resolutions, i-1) do + Z, _ = kernel(cfac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + a = map(res_Z1, 1, (j,)) + inc1 = canonical_injection(cod, 1) + @assert domain(inc1) === res_Z1[j-1] === codomain(a) + @assert codomain(inc1) === cod + img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] + img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) + return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + else + res_B = getindex!(cfac.boundary_resolutions, i-1) do + B, _ = boundary(cfac.c, i) + return free_resolution(SimpleFreeResolution, B) + end + a = map(res_B, 1, (j,)) + inc1 = canonical_injection(cod, 1) + @assert domain(inc1) === res_B[j-1] === codomain(a) + @assert codomain(inc1) === cod + img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] + img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) + return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + end + error("execution should never reach this point") + end + else + res = getindex!(cfac.kernel_resolutions, i) do + Z, _ = kernel(fac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + return map(res, 1, (j,)) + end + end + error("direction $p out of bounds") +end + +function can_compute(fac::CEMapFactory, self::AbsHyperComplex, p::Int, I::Tuple) + (i, j) = I + if p == 1 + return can_compute_map(chain_factory(self).c, 1, (i-1,)) && j >= 0 + elseif p == 2 + return can_compute_index(self, I) && j > 0 + end + return false +end + +### The concrete struct +@attributes mutable struct CartanEilenbergResolution{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} + internal_complex::HyperComplex{ChainType, MorphismType} + + function CartanEilenbergResolution( + c::AbsHyperComplex{ChainType, MorphismType}; + is_exact::Bool=false + ) where {ChainType, MorphismType} + @assert dim(c) == 1 "complexes must be 1-dimensional" + @assert has_lower_bound(c, 1) "complexes must be bounded from below" + @assert direction(c, 1) == :chain "resolutions are only implemented for chain complexes" + chain_fac = CEChainFactory(c; is_exact) + map_fac = CEMapFactory{MorphismType}() # TODO: Do proper type inference here! + + # Assuming d is the dimension of the new complex + internal_complex = HyperComplex(2, chain_fac, map_fac, [:chain, :chain]) + # Assuming that ChainType and MorphismType are provided by the input + return new{ChainType, MorphismType}(internal_complex) + end +end + +### Implementing the AbsHyperComplex interface via `underlying_complex` +underlying_complex(c::CartanEilenbergResolution) = c.internal_complex + From 4e1d049bc71bc6660ea5aa78bd033072c4900688 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 17:11:03 +0200 Subject: [PATCH 15/27] Fix inclusions. --- .../DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl index b554a991c419..0a4745a96fb5 100644 --- a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl @@ -9,9 +9,9 @@ include("Objects/koszul_complexes.jl") include("Objects/degree_zero_complexes.jl") include("Objects/new_complex_template.jl") include("Objects/linear_strands.jl") -include("Objects/cartan_eilenberg_resolutions.jl" include("Morphisms/Types.jl") +include("Objects/cartan_eilenberg_resolution.jl") include("Morphisms/cartan_eilenberg_resolutions.jl") include("Morphisms/ext.jl") include("Morphisms/simplified_complexes.jl") From 105fdd926bb8e3d2b1932aa1697b855f6a9e88e0 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 17:11:22 +0200 Subject: [PATCH 16/27] Fixes to make the code actually run. --- .../Objects/cartan_eilenberg_resolution.jl | 168 ++++++++++++++---- 1 file changed, 131 insertions(+), 37 deletions(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl index 3570bd53a3d8..d1bf4ed8ddf6 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -6,6 +6,8 @@ # # is a bounded below complex. We compute a double complex # +# 0 0 0 +# ↑ ↑ ↑ # 0 ← P₀₀ ← P₀₁ ← P₀₂ ← … # ↑ ↑ ↑ # 0 ← P₁₀ ← P₁₁ ← P₁₂ ← … @@ -14,7 +16,9 @@ # ↑ ↑ ↑ # ⋮ ⋮ ⋮ # -# which is quasi-isomorphic to C via some augmentation map ε. +# which is quasi-isomorphic to C via some augmentation map +# +# ε = (εᵢ : P₀ᵢ → Cᵢ)ᵢ # # The challenge is that if we were only computing resolutions of the Cᵢ's # and lifting the maps, then the rows of the resulting diagrams would @@ -41,58 +45,147 @@ struct CEChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} function CEChainFactory(c::AbsHyperComplex; is_exact::Bool=false) @assert dim(c) == 1 "complex must be 1-dimensional" #@assert has_lower_bound(c, 1) "complex must be bounded from below" - return new{chain_type(c)}(c, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}()) + return new{chain_type(c)}(c, is_exact, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplexMorphism}()) end end -function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) - (i, j) = I +function kernel_resolution(fac::CEChainFactory, i::Int) + if !haskey(fac.kernel_resolutions, i) + Z, _ = kernel(fac.c, i) + fac.kernel_resolutions[i] = free_resolution(SimpleFreeResolution, Z)[1] + end + return fac.kernel_resolutions[i] +end - if iszero(j) - return fac.c[i] +function boundary_resolution(fac::CEChainFactory, i::Int) + if !haskey(fac.boundary_resolutions, i) + Z, _ = boundary(fac.c, i) + fac.boundary_resolutions[i] = free_resolution(SimpleFreeResolution, Z)[1] end + return fac.boundary_resolutions[i] +end - res_Z = getindex!(c.kernel_resolutions, i) do - Z, _ = kernel(fac.c, i) - return free_resolution(SimpleFreeResolution, Z) +function induced_map(fac::CEChainFactory, i::Int) + if !haskey(fac.induced_maps, i) + Z, inc = kernel(fac.c, i) + B, pr = boundary(fac.c, i) + @assert codomain(inc) === ambient_free_module(B) + img_gens = elem_type(Z)[preimage(inc, g) for g in ambient_representatives_generators(B)] + res_Z = kernel_resolution(fac, i) + res_B = boundary_resolution(fac, i) + aug_Z = augmentation_map(res_Z) + aug_B = augmentation_map(res_B) + img_gens = gens(res_B[0]) + @show img_gens + img_gens = aug_B[0].(img_gens) + @show img_gens + img_gens = elem_type(res_Z[0])[preimage(aug_Z[0], preimage(inc, repres(aug_B[0](g)))) for g in gens(res_B[0])] + psi = hom(res_B[0], res_Z[0], img_gens; check=true) # TODO: Set to false + @show i + @assert domain(psi) === boundary_resolution(fac, i)[0] + @assert codomain(psi) === kernel_resolution(fac, i)[0] + fac.induced_maps[i] = lift_map(boundary_resolution(fac, i), kernel_resolution(fac, i), psi; start_index=0) end + return fac.induced_maps[i] +end - if can_compute_map(fac.c, 1, (i,)) - if fac.isexact - res_B = getindex!(c.kernel_resolutions, i-1) do - B, _ = kernel(fac.c, i-1) - return free_resolution(SimpleFreeResolution, B) - end - return direct_sum(res_B[j], res_Z[j]) +function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) + (i, j) = I # i the resolution index, j the index in C + + res_Z = kernel_resolution(fac, j) + + if can_compute_map(fac.c, 1, (j,)) + if fac.is_exact # Use the next kernel directly + res_B = kernel_resolution(fac, j-1) + return direct_sum(res_B[i], res_Z[i])[1] else - res_B = getindex!(c.boundary_resolutions, i-1) do - B, _ = boundary(fac.c, i-1) - return free_resolution(SimpleFreeResolution, B) - end - return direct_sum(res_B[j], res_Z[j]) + res_B = boundary_resolution(fac, j-1) + return direct_sum(res_B[i], res_Z[i])[1] end end - return res_Z[j] + # We may assume that the next map can not be computed and is, hence, zero. + @show i, j + return res_Z[i] end function can_compute(fac::CEChainFactory, self::AbsHyperComplex, I::Tuple) (i, j) = I - can_compute_index(fac.c, (i,)) || return false - return j >= 0 + can_compute_index(fac.c, (j,)) || return false + return i >= 0 end ### Production of the morphisms -struct CEMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} -end +struct CEMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} end function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) (i, j) = I cfac = chain_factory(self) - if p == 1 - j == 0 && return map(cfac.c, 1, (i,)) - elseif p == 2 - if can_compute_map(cfac.c, 1, (i,)) - B_plus_Z = self[I] + if p == 1 # vertical upwards maps + if can_compute_map(cfac.c, 1, (j,)) + # both dom and cod are direct sums in this case + dom = self[I] + cod = self[(i-1, j)] + pr1 = canonical_projection(dom, 1) + pr2 = canonical_projection(dom, 2) + @assert domain(pr1) === domain(pr2) === dom + inc1 = canonical_injection(cod, 1) + inc2 = canonical_injection(cod, 2) + @assert codomain(inc1) === codomain(inc2) === cod + res_Z = kernel_resolution(cfac, j) + @assert domain(map(res_Z, i)) === codomain(pr2) + @assert codomain(map(res_Z, i)) === domain(inc2) + res_B = boundary_resolution(cfac, j-1) + @assert domain(map(res_B, i)) === codomain(pr1) + @assert codomain(map(res_B, i)) === domain(inc1) + return compose(pr1, compose(map(res_B, i), inc1)) + compose(pr2, compose(map(res_Z, i), inc2)) + else + res_Z = kernel_resolution(cfac, j) + return map(res_Z, i) + end + error("execution should never reach this point") + elseif p == 2 # the horizontal maps + dom = self[I] + cod = self[(i, j-1)] + if can_compute_map(cfac.c, 1, (j-1,)) + # the codomain is also a direct sum + if !cfac.is_exact + psi = induced_map(cfac, j-1) + phi = psi[i] + inc = canonical_injection(cod, 2) + pr = canonical_projection(dom, 1) + @show i, j + @show domain(phi) + @show codomain(pr) + @assert codomain(phi) === domain(inc) + @assert codomain(pr) === domain(phi) + return compose(pr, compose(phi, inc)) + else + inc = canonical_injection(cod, 2) + pr = canonical_projection(dom, 1) + return compose(pr, inc) + end + error("execution should never reach this point") + else + # the codomain is just the kernel + if !cfac.is_exact + psi = induced_map(cfac, j-1) + phi = psi[i] + pr = canonical_projection(dom, 1) + @show i, j + @show dom + @show codomain(pr) + @show domain(phi) + return compose(pr, phi) + else + pr = canonical_projection(dom, 1) + return pr + end + error("execution should never reach this point") + end + error("execution should never reach this point") + end + + #= C = cfac.c[(i,)] B, pr_B = boundary(cfac.c, 1, (i-1,)) if cfac.is_exact @@ -133,7 +226,7 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) @assert codomain(inc1) === cod img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) - return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + return hom(B_plus_Z, cod, img_gens; check=true) # TODO: Set to false else res_B = getindex!(cfac.boundary_resolutions, i-1) do B, _ = boundary(cfac.c, i) @@ -145,7 +238,7 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) @assert codomain(inc1) === cod img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) - return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + return hom(B_plus_Z, cod, img_gens; check=true) # TODO: Set to false end error("execution should never reach this point") end @@ -157,15 +250,16 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) return map(res, 1, (j,)) end end + =# error("direction $p out of bounds") end function can_compute(fac::CEMapFactory, self::AbsHyperComplex, p::Int, I::Tuple) (i, j) = I - if p == 1 - return can_compute_map(chain_factory(self).c, 1, (i-1,)) && j >= 0 - elseif p == 2 - return can_compute_index(self, I) && j > 0 + if p == 1 # vertical maps + return i > 0 && can_compute(chain_factory(self).c, j) + elseif p == 2 # horizontal maps + return i >= 0 && can_compute_map(chain_factory(self).c, j) end return false end From 5be5b3a6c4a1abdece8a82f44cc1185ede73bad7 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 08:01:46 +0200 Subject: [PATCH 17/27] Preserve bounds for base changes. --- .../DoubleAndHyperComplexes/src/base_change_types.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/src/base_change_types.jl b/experimental/DoubleAndHyperComplexes/src/base_change_types.jl index fcc44b20a4e5..df187c198f04 100644 --- a/experimental/DoubleAndHyperComplexes/src/base_change_types.jl +++ b/experimental/DoubleAndHyperComplexes/src/base_change_types.jl @@ -58,7 +58,13 @@ end map_fac = BaseChangeMapFactory(phi, orig) d = dim(orig) - internal_complex = HyperComplex(d, chain_fac, map_fac, [direction(orig, i) for i in 1:d]) + upper_bounds = Vector{Union{Int, Nothing}}([(has_upper_bound(orig, i) ? upper_bound(orig, i) : nothing) for i in 1:d]) + lower_bounds = Vector{Union{Int, Nothing}}([(has_lower_bound(orig, i) ? lower_bound(orig, i) : nothing) for i in 1:d]) + internal_complex = HyperComplex(d, chain_fac, map_fac, + [direction(orig, i) for i in 1:d], + lower_bounds = lower_bounds, + upper_bounds = upper_bounds + ) return new{ModuleFP, ModuleFPHom}(orig, internal_complex) end end From 7cda6035b693d3189e478242c46014d7282764a6 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 08:02:31 +0200 Subject: [PATCH 18/27] Add some auxiliary methods for modules and friends. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 81 +++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 08ac74388e5b..a6438f90da9c 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -394,7 +394,8 @@ function multiplication_hash_table(E::ExteriorAlgebra, p::Int, q::Int) end n = rank(E) if !haskey(E.multiplication_hash_tables, (p, q)) - A = [_wedge(ordered_multi_index(i, p, n), ordered_multi_index(j, q, n)) for i in 1:binomial(n, p), j in 1:binomial(n, q)] + #A = [_wedge(ordered_multi_index(i, p, n), ordered_multi_index(j, q, n)) for i in 1:binomial(n, p), j in 1:binomial(n, q)] + A = [_wedge(I, J) for I in OrderedMultiIndexSet(p, n), J in OrderedMultiIndexSet(q, n)] B = [(s, linear_index(k)) for (s, k) in A] E.multiplication_hash_tables[(p, q)] = B end @@ -786,21 +787,25 @@ end function kernel( phi::FreeModuleHom{ModuleType, ModuleType, Nothing}; - degree_bound::Union{Int, Nothing}=0 + upper_degree_bound::Union{Int, Nothing}=0, + lower_degree_bound::Union{Int, Nothing}=nothing ) where {ModuleType <: FreeMod{<:ExtAlgElem}} @vprint :ExteriorAlgebras 1 "computing kernel of map with degrees\n" @vprint :ExteriorAlgebras 1 "$([degree(g; check=false) for g in gens(domain(phi))])\n" @vprint :ExteriorAlgebras 1 "in the domain and degrees\n" @vprint :ExteriorAlgebras 1 "$([degree(g; check=false) for g in gens(codomain(phi))])\n" - @vprint :ExteriorAlgebras 1 "in the codomain up to degree $(degree_bound)\n" + @vprint :ExteriorAlgebras 1 "in the codomain up to degree $(upper_degree_bound)\n" F = domain(phi) iszero(F) && return sub(F, elem_type(F)[]) d0 = minimum([Int(degree(g; check=false)[1]) for g in gens(F)]) E = base_ring(F) n = rank(E) dmax = maximum([Int(degree(g; check=false)[1]) for g in gens(F)]) + n - if degree_bound !== nothing - dmax > degree_bound && (dmax = degree_bound) + if upper_degree_bound !== nothing + dmax > upper_degree_bound && (dmax = upper_degree_bound) + end + if lower_degree_bound !== nothing + d0 < lower_degree_bound && (d0 = lower_degree_bound) end Z = _kernel_part(phi, dmax) return sub(F, gens(Z)) @@ -974,4 +979,70 @@ function _derived_pushforward_BGG(M::ModuleFP{T}; regularity::Int=_regularity_bo return res0 end +include("BGG_complex.jl") + +function twist( + f::FreeModuleHom{MT, MT, Nothing}, d::Int; + domain_twist::FreeModuleHom{MT, MT, Nothing}=twist(domain(f), d)[2], + codomain_twist::FreeModuleHom{MT, MT, Nothing}=twist(codomain(f), d)[2] + ) where {MT <: FreeMod} + new_dom = codomain(domain_twist) + new_cod = codomain(codomain_twist) + img_gens = [new_cod(coordinates(v)) for v in images_of_generators(f)] + return hom(new_dom, new_cod, img_gens) +end + +function twist(F::ModuleFP{T}, d::Int) where {T<:ExtAlgElem} + E = base_ring(F) + return twist(F, d*grading_group(E)[1]) +end + +function twist( + M::SubquoModule{T}, g::FinGenAbGroupElem; + ambient_twist::FreeModuleHom=twist(ambient_free_module(M), g)[2] + ) where {T<:ExtAlgElem} + F = codomain(ambient_twist) + result = SubquoModule(F, ambient_twist.(ambient_representatives_generators(M)), ambient_twist.(relations(M))) + return result, hom(M, result, gens(result); check=false) +end + +function twist(F::FreeMod{T}, g::FinGenAbGroupElem) where {T<:ExtAlgElem} + E = base_ring(F) + @req parent(g) === grading_group(E) "Group element not contained in grading group of base ring" + W = [x-g for x in F.d] + G = graded_free_module(E, rank(F)) + G.d = W + return G, hom(F, G, gens(G)) +end + +function direct_sum(f::Vector{<:ModuleFPHom}; + domain_sum::ModuleFP=begin + domains = domain.(f) + result = direct_sum(domains...) + result[1] + end, + codomain_sum::ModuleFP=begin + codomains = codomain.(f) + result = direct_sum(codomains...) + result[1] + end + ) + result = hom(domain_sum, codomain_sum, [zero(codomain_sum) for i in 1:ngens(domain_sum)]) + for (i, g) in enumerate(f) + pr = canonical_projection(domain_sum, i) + inc = canonical_injection(codomain_sum, i) + img_gens = inc.(g.(images_of_generators(pr))) + result += hom(domain_sum, codomain_sum, img_gens) + end + return result +end + +coefficient_ring(E::ExteriorAlgebra) = base_ring(E) + +# BUG: the following code in the modules does not run generically and must be overwritten manually! +coordinates(v::FreeModElem{ExtAlgElem{T}}, I::SubModuleOfFreeModule{ExtAlgElem{T}}, task::Symbol) where {T} = coordinates(v, I) + +function Base.:*(a::T, f::FreeModuleHom{FreeMod{T}, <:Any, Nothing}) where {T<:NCRingElem} + return hom(domain(f), codomain(f), [a*g for g in images_of_generators(f)]) +end From 9560bd1ba4fb4bfa9602018ba9b614eba828fe37 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 10:36:43 +0200 Subject: [PATCH 19/27] Clean up CA resolutions. --- .../Objects/cartan_eilenberg_resolution.jl | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl index d1bf4ed8ddf6..946850e17183 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -69,19 +69,16 @@ function induced_map(fac::CEChainFactory, i::Int) if !haskey(fac.induced_maps, i) Z, inc = kernel(fac.c, i) B, pr = boundary(fac.c, i) - @assert codomain(inc) === ambient_free_module(B) - img_gens = elem_type(Z)[preimage(inc, g) for g in ambient_representatives_generators(B)] + @assert ambient_free_module(Z) === ambient_free_module(B) + img_gens = elem_type(Z)[Z(g) for g in ambient_representatives_generators(B)] res_Z = kernel_resolution(fac, i) res_B = boundary_resolution(fac, i) aug_Z = augmentation_map(res_Z) aug_B = augmentation_map(res_B) img_gens = gens(res_B[0]) - @show img_gens img_gens = aug_B[0].(img_gens) - @show img_gens - img_gens = elem_type(res_Z[0])[preimage(aug_Z[0], preimage(inc, repres(aug_B[0](g)))) for g in gens(res_B[0])] + img_gens = elem_type(res_Z[0])[preimage(aug_Z[0], Z(repres(aug_B[0](g)))) for g in gens(res_B[0])] psi = hom(res_B[0], res_Z[0], img_gens; check=true) # TODO: Set to false - @show i @assert domain(psi) === boundary_resolution(fac, i)[0] @assert codomain(psi) === kernel_resolution(fac, i)[0] fac.induced_maps[i] = lift_map(boundary_resolution(fac, i), kernel_resolution(fac, i), psi; start_index=0) @@ -104,7 +101,6 @@ function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) end end # We may assume that the next map can not be computed and is, hence, zero. - @show i, j return res_Z[i] end @@ -153,9 +149,6 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) phi = psi[i] inc = canonical_injection(cod, 2) pr = canonical_projection(dom, 1) - @show i, j - @show domain(phi) - @show codomain(pr) @assert codomain(phi) === domain(inc) @assert codomain(pr) === domain(phi) return compose(pr, compose(phi, inc)) @@ -171,10 +164,6 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) psi = induced_map(cfac, j-1) phi = psi[i] pr = canonical_projection(dom, 1) - @show i, j - @show dom - @show codomain(pr) - @show domain(phi) return compose(pr, phi) else pr = canonical_projection(dom, 1) @@ -279,7 +268,7 @@ end map_fac = CEMapFactory{MorphismType}() # TODO: Do proper type inference here! # Assuming d is the dimension of the new complex - internal_complex = HyperComplex(2, chain_fac, map_fac, [:chain, :chain]) + internal_complex = HyperComplex(2, chain_fac, map_fac, [:chain, :chain]; lower_bounds = Union{Int, Nothing}[0, lower_bound(c, 1)]) # Assuming that ChainType and MorphismType are provided by the input return new{ChainType, MorphismType}(internal_complex) end From 80ab1d9e2679d6ab1e9b62f3a0fa23f8c82cbcdc Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 10:37:52 +0200 Subject: [PATCH 20/27] Implement BGG cohomology computation for complexes of free modules. --- .../ExteriorAlgebra/src/BGG_complex.jl | 369 ++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 experimental/ExteriorAlgebra/src/BGG_complex.jl diff --git a/experimental/ExteriorAlgebra/src/BGG_complex.jl b/experimental/ExteriorAlgebra/src/BGG_complex.jl new file mode 100644 index 000000000000..4224e1bf3989 --- /dev/null +++ b/experimental/ExteriorAlgebra/src/BGG_complex.jl @@ -0,0 +1,369 @@ +# Helper structure to transfer back and forth between S- and E-modules +mutable struct BGGHelper{T} + E::ExteriorAlgebra{T} + S::MPolyDecRing{T} + d::Int + + # Fields for caching + ind_to_exp_dom::Vector{Vector{Int}} + exp_to_ind_dom::Dict{Vector{Int}, Int} + ind_to_exp_cod::Vector{Vector{Int}} + exp_to_ind_cod::Dict{Vector{Int}, Int} + + dom::FreeMod{ExtAlgElem{T}} # the (co)domains of the BGG morphism + cod::FreeMod{ExtAlgElem{T}} + phi::FreeModuleHom{FreeMod{ExtAlgElem{T}}, FreeMod{ExtAlgElem{T}}, Nothing} # the actual morphism + K::SubquoModule{ExtAlgElem{T}} # its kernel + inc::SubQuoHom # the inclusion K ↪ dom + res::SimpleFreeResolution # a free resolution P of K + aug::FreeModuleHom # the augmentation map P₀ → K + all_monomials::Vector{<:MPolyDecRingElem} + + function BGGHelper(E::ExteriorAlgebra{T}, S::MPolyDecRing{T}, d::Int) where {T} + return new{T}(E, S, d) + end +end + +function domain(h::BGGHelper) + if !isdefined(h, :dom) + if h.d < 0 + h.dom = graded_free_module(h.E, 0) + else + n = rank(h.E) + h.dom = graded_free_module(h.E, [-h.d for i in 1:length(WeakCompositions(h.d, n))]) + end + end + return h.dom +end + +function codomain(h::BGGHelper) + if !isdefined(h, :cod) + if h.d < -1 + h.cod = graded_free_module(h.E, 0) + else + n = rank(h.E) + h.cod = graded_free_module(h.E, [-(h.d+1) for i in 1:length(WeakCompositions(h.d + 1, n))]) + end + end + return h.cod +end + +function ind_to_exp_dom(h::BGGHelper) + if !isdefined(h, :ind_to_exp_dom) + h.ind_to_exp_dom = [a.c for a in WeakCompositions(h.d, rank(h.E))] + end + return h.ind_to_exp_dom +end + +function exp_to_ind_dom(h::BGGHelper) + if !isdefined(h, :exp_to_ind_hom) + iter = WeakCompositions(h.d, rank(h.E)) + r = length(iter) + h.exp_to_ind_dom = Dict{Vector{Int}, Int}(a.c => i for (i, a) in zip(1:r, iter)) + end + return h.exp_to_ind_dom +end + +function ind_to_exp_cod(h::BGGHelper) + if !isdefined(h, :ind_to_exp_cod) + h.ind_to_exp_cod = [a.c for a in WeakCompositions(h.d+1, rank(h.E))] + end + return h.ind_to_exp_cod +end + +function exp_to_ind_cod(h::BGGHelper) + if !isdefined(h, :exp_to_ind_cod) + iter = WeakCompositions(h.d+1, rank(h.E)) + r = length(iter) + h.exp_to_ind_cod = Dict{Vector{Int}, Int}(a.c => i for (i, a) in zip(1:r, iter)) + end + return h.exp_to_ind_cod +end + +# take a homogeneous polynomial in S and return the SRow corresponding +# element in the domain/codomain of the E-modules +function (h::BGGHelper{T})(f::MPolyDecRingElem{T}) where {T} + degf = Int(degree(f; check=false)[1]) + E = h.E + S = h.S + R = base_ring(S) + if degf == h.d # element of the domain + to_ind_dom = exp_to_ind_dom(h) + return sparse_row(R, [to_ind_dom[e] for e in exponents(f)], + collect(coefficients(f))) + elseif degf == h.d + 1 + to_ind_cod = exp_to_ind_cod(h) + return sparse_row(R, [to_ind_cod[e] for e in exponents(f)], + collect(coefficients(f))) + end + error("execution should never get here") +end + +# take an element in the domain/codomain of h and turn it into a homogeneous polynomial +function (h::BGGHelper)(v::FreeModElem) + E = h.E + S = h.S + n = rank(E) + if parent(v) === domain(h) + return h(coordinates(v), :domain) + elseif parent(v) === codomain(h) + return h(coordinates(v), :codomain) + end + error("execution should never get here") +end + +# for a list of integers indicating basis vectors, +# produce the list of corresponding monomials +function all_monomials(h::BGGHelper) + S = h.S + h.d < 0 && return elem_type(S)[] + R = base_ring(S) + if !isdefined(h, :all_monomials) + exp_list = ind_to_exp_dom(h) + result = sizehint!(elem_type(h.S)[], sizeof(exp_list)) + for e in exp_list + ctx = MPolyBuildCtx(h.S) + push_term!(ctx, one(R), e) + push!(result, finish(ctx)) + end + h.all_monomials = result + end + return h.all_monomials +end + +function (h::BGGHelper)(v::SRow, s::Symbol=:domain) + @assert base_ring(v) === base_ring(h.S) + ctx = MPolyBuildCtx(h.S) + if s == :domain + to_exp_dom = ind_to_exp_dom(h) + for (i, c) in v + push_term!(ctx, c, to_exp_dom[i]) + end + return finish(ctx) + elseif s == :codomain + to_exp_cod = ind_to_exp_cod(h) + for (i, c) in v + push_term!(ctx, c, to_exp_cod[i]) + end + return finish(ctx) + end + error("execution should never get here") +end + +function morphism(h::BGGHelper) + G = codomain(h) + img_gens = elem_type(G)[] + n = rank(h.E) + h.d < 0 && return hom(domain(h), codomain(h), elem_type(codomain(h))[]) + # iterate through the monomials of degree d in n variables + for (i, a) in enumerate(WeakCompositions(h.d, n)) + e = a.c # the exponent vector of the monomial + y = [copy(e) for i in 1:n] + for j in 1:n + y[j][j] += 1 # multiplication by xⱼ + end + to_ind_cod = exp_to_ind_cod(h) + indices = [to_ind_cod[a] for a in y] + push!(img_gens, G(sparse_row(h.E, indices, gens(h.E)))) + end + return hom(domain(h), G, img_gens) +end + +function kernel(h::BGGHelper) + if !isdefined(h, :K) + K, inc = kernel(morphism(h)) + h.K = K + h.inc = inc + end + return h.K +end + +function kernel_inclusion(h::BGGHelper) + if !isdefined(h, :inc) + kernel(h) + end + return h.inc +end + +function resolution(h::BGGHelper) + if !isdefined(h, :res) + res, aug = free_resolution(SimpleFreeResolution, kernel(h)) + h.res = res + h.aug = aug[0] + end + return h.res +end + +function augmentation_map(h::BGGHelper) + if !isdefined(h, :aug) + resolution(h) + end + return h.aug +end + + +### Production of the chains +struct BGGChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} + # Fields needed for production + orig::AbsHyperComplex + S::MPolyDecRing + E::ExteriorAlgebra + d::Int + helper::Dict{Int, <:BGGHelper} + helper_combinations::Dict{<:Tuple, <:Vector{<:BGGHelper}} + bgg_maps::Dict{Tuple, FreeModuleHom} + + function BGGChainFactory( + S::MPolyDecRing, E::ExteriorAlgebra, orig::AbsHyperComplex{T}, d::Int + ) where {T<:ModuleFP{<:MPolyDecRingElem}} + @assert dim(orig) == 1 "complex must be one-dimensional" + @assert direction(orig, 1) == :chain "only chain complexes are supported" + @assert is_z_graded(S) "ring must be standard graded" + return new{ModuleFP{elem_type(E)}}(orig, S, E, d, + Dict{Int, BGGHelper}(), + Dict{Tuple, Vector{<:BGGHelper}}(), + Dict{Tuple, FreeModuleHom}() + ) + end +end + +function get_helper(fac::BGGChainFactory, d::Int) + if !haskey(fac.helper, d) + fac.helper[d] = BGGHelper(fac.E, fac.S, d) + end + return fac.helper[d] +end + +function (fac::BGGChainFactory)(self::AbsHyperComplex, I::Tuple) + return _build_BGG_module!(fac, fac.orig[I], I) +end + +function _build_BGG_module!(fac::BGGChainFactory, F::SubquoModule, I::Tuple) + error("not implemented") +end + +function _build_BGG_module!(fac::BGGChainFactory, F::FreeMod, I::Tuple) + E = fac.E + is_zero(rank(F)) && return graded_free_module(E, 0) + S = fac.S + n = rank(E) + s = fac.d + R = base_ring(E) + @assert ngens(S) == rank(E) + @assert base_ring(S) === R + @assert is_graded(F) + maps = FreeModuleHom[] + kernels = SubquoModule[] + helpers = BGGHelper[] + for (i, d) in enumerate(degrees_of_generators(F)) + dd = Int(d[1]) + helper = get_helper(fac, s-dd) + phi = morphism(helper) + dom_twist, amb_twist = twist(domain(phi), dd) + phi_twist = twist(phi, dd; domain_twist=amb_twist) + K_twist, _ = twist(kernel(helper), d; ambient_twist=amb_twist) + @assert all(Int(e[1]) == -s for e in degrees_of_generators(dom_twist)) + push!(maps, phi_twist) + push!(helpers, helper) + push!(kernels, K_twist) + end + phi = direct_sum(maps) + dom = domain(phi) + cod = codomain(phi) + fac.helper_combinations[I] = helpers + fac.bgg_maps[I] = phi + K = direct_sum(kernels...)[1] + return K +end + +function can_compute(fac::BGGChainFactory, self::AbsHyperComplex, i::Tuple) + return can_compute_index(fac.orig, i) +end + +### Production of the morphisms +struct BGGMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} end + +function (fac::BGGMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) + @assert isone(p) + i = first(I) + cfac = chain_factory(self) + orig = cfac.orig + dom = self[I] + cod = self[(i-1,)] + (is_zero(dom) || is_zero(cod)) && return zero_morphism(dom, cod) + dom_amb = ambient_free_module(dom) + cod_amb = ambient_free_module(cod) + orig_dom = orig[I]::FreeMod + orig_cod = orig[(i-1,)]::FreeMod + orig_phi = map(orig, 1, I) + + # We first build the morphism on the ambient modules + + # transfer these elements to homogeneous S-module elements + helpers = cfac.helper_combinations[I]::Vector{<:BGGHelper} + img_gens = elem_type(orig_dom)[] + w = zero(orig_dom) + for (j, h) in enumerate(helpers) # corresponding to the direct summands + delta = rank(domain(h)) + g = orig_dom[j] + # the generators of the E-module for this summand were just the + # monomials of the S-module in this degree + img_gens = vcat(img_gens, [x*g for x in all_monomials(h)]) + end + + # now we map the monomials over to the other side + img_gens2 = orig_phi.(img_gens) + + # and translate them back + helpers = cfac.helper_combinations[(i-1,)] + img_gens3 = elem_type(cod_amb)[] + offsets = rank.(domain.(helpers)) + for j = length(offsets):-1:2 + offsets[j] = sum(offsets[1:j]) + end + pushfirst!(offsets, 0) + + for v in img_gens2 + w = zero(cod_amb) + for (j, poly) in coordinates(v) + helper = helpers[j] + coord = helper(poly) # convert the polynomial into an element of the summand + coord.pos.+=offsets[j] # a dirty hack to shift the vector to its correct position in the total sum + w += cod_amb(map_entries(cfac.E, coord)) + end + push!(img_gens3, w) + end + + # this gives us the map on the ambient free E-modules + amb_map = hom(dom_amb, cod_amb, img_gens3) + + # finally we need to compute the induced map on the kernels + + img_gens4 = elem_type(cod)[SubquoModuleElem(amb_map(g), cod) for g in ambient_representatives_generators(dom)] + return hom(dom, cod, img_gens4; check=false) +end + +function can_compute(fac::BGGMapFactory, self::AbsHyperComplex, p::Int, i::Tuple) + return can_compute_map(chain_factory(self).orig, p, i) +end + +### The concrete struct +@attributes mutable struct BGGComplex{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} + internal_complex::HyperComplex{ChainType, MorphismType} + + function BGGComplex(E::ExteriorAlgebra{T}, S::MPolyDecRing{T}, orig::AbsHyperComplex{CT, MT}, s::Int) where {T <: RingElem, CT <: ModuleFP, MT<:ModuleFPHom} + chain_fac = BGGChainFactory(S, E, orig, s) + map_fac = BGGMapFactory{ModuleFPHom}() + + # Assuming d is the dimension of the new complex + internal_complex = HyperComplex(1, chain_fac, map_fac, [:chain], + lower_bounds = Union{Int, Nothing}[has_lower_bound(orig, 1) ? lower_bound(orig, 1) : nothing], + upper_bounds = Union{Int, Nothing}[has_upper_bound(orig, 1) ? upper_bound(orig, 1) : nothing]) + # Assuming that ChainType and MorphismType are provided by the input + return new{ModuleFP{ExtAlgElem{T}}, ModuleFPHom}(internal_complex) + end +end + +### Implementing the AbsHyperComplex interface via `underlying_complex` +underlying_complex(c::BGGComplex) = c.internal_complex + From 0551a44f98418ca62f61fd95c263eeac2e22ba91 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 10:38:28 +0200 Subject: [PATCH 21/27] Add tests. --- experimental/ExteriorAlgebra/test/runtests.jl | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/experimental/ExteriorAlgebra/test/runtests.jl b/experimental/ExteriorAlgebra/test/runtests.jl index 96d8b2e118ed..7b057dac5cbc 100644 --- a/experimental/ExteriorAlgebra/test/runtests.jl +++ b/experimental/ExteriorAlgebra/test/runtests.jl @@ -137,3 +137,39 @@ end # @test is_zero(prod21*fac2); # end +# +@testset "BGGHelpers" begin + R, (t,) = polynomial_ring(QQ, [:t]) + S, (x, y, z) = graded_polynomial_ring(R, [:x, :y, :z]) + E = Oscar._exterior_algebra(S) + + h = Oscar.BGGHelper(E, S, 3) + morphism(h) + + f = 2*x^3 + 7*x*y^2 - 5*x*y*z + v = h(f) + ff = h(v) + f == ff + + g = x^3 + v = h(g) + phi = morphism(h) + w = phi(domain(phi)(map_entries(E, v))) + for (i, c) in coordinates(w) + if i == 1 + @test h(sparse_row(R, [i], [one(R)]), :codomain) == x^4 + @test c == gen(E, 1) + elseif i == 2 + @test h(sparse_row(R, [i], [one(R)]), :codomain) == x^3*y + @test c == gen(E, 2) + elseif i == 3 + @test h(sparse_row(R, [i], [one(R)]), :codomain) == x^3*z + @test c == gen(E, 3) + end + end + + K = kernel(phi) + res = resolution(phi) + @test !iszero(res[2]) +end + From 0df246d0642c4841d12406f17adae0b8301ec259 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 13:16:38 +0200 Subject: [PATCH 22/27] Add tests. --- experimental/ExteriorAlgebra/test/runtests.jl | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/experimental/ExteriorAlgebra/test/runtests.jl b/experimental/ExteriorAlgebra/test/runtests.jl index 7b057dac5cbc..dde4e3ea328d 100644 --- a/experimental/ExteriorAlgebra/test/runtests.jl +++ b/experimental/ExteriorAlgebra/test/runtests.jl @@ -168,8 +168,69 @@ end end end - K = kernel(phi) - res = resolution(phi) + K = kernel(h) + res = Oscar.resolution(h) @test !iszero(res[2]) end +@testset "cohomology computation via BGG" begin + A, (t,) = polynomial_ring(QQ, [:t]) + P = projective_space(A, [:x, :y, :z]) + S = homogeneous_coordinate_ring(P) + n = ngens(S) + f1 = sum(x^n for x in gens(S)) + f0 = prod(gens(S)) + #f0 = S[1]^n + # For t=0 this degenerates to three lines and we expect jumps in cohomology there + f = (1-t)*f0 + t*f1 + + # derived pushforward of the structure sheaf of the curve + IPX, inc_X = sub(P, f) + S1 = graded_free_module(S, [0]) + I = ideal(S, f) + IS1, inc = I*S1 + M = cokernel(inc) + # Nothing is expected here to jump: H^0 is always free of rank one and + # so the remainder of this short complex must also be of constant rank. + + phi, _, _ = Oscar._ext_module_map(M, 5) + K, _ = kernel(phi) + res, _ = free_resolution(Oscar.SimpleFreeResolution, K) + res0, _ = change_base_ring(A, res) + @test all(iszero(res0[i]) for i in 0:2) + @test all(rank(res0[i]) == 1 for i in 3:4) + + res, _ = free_resolution(Oscar.SimpleFreeResolution, M) + E = Oscar._exterior_algebra(S) + bgg = Oscar.BGGComplex(E, S, res, 6) + bgg_res = Oscar.CartanEilenbergResolution(bgg) + tot = total_complex(bgg_res) + tot_A, _ = change_base_ring(A, tot) + simp = simplify(tot_A) + @test all(iszero(simp[i]) for i in 0:3) + @test all(rank(simp[i]) == 1 for i in 4:5) + + # derived pushforward of the relative Ω¹ + M1 = Oscar.relative_cotangent_module(IPX) + M, _ = pushforward(inc_X, M1) + + phi, _, _ = Oscar._ext_module_map(M, 5) + K, _ = kernel(phi) + res, _ = free_resolution(Oscar.SimpleFreeResolution, K) + res0, _ = change_base_ring(A, res) + simp = simplify(res0) + @test all(iszero(simp[i]) for i in 0:2) + @test all(rank(simp[i]) == 5 for i in 3:4) + + res, _ = free_resolution(Oscar.SimpleFreeResolution, M) + E = Oscar._exterior_algebra(S) + bgg = Oscar.BGGComplex(E, S, res, 7) + bgg_res = Oscar.CartanEilenbergResolution(bgg) + tot = total_complex(bgg_res) + tot_A, _ = change_base_ring(A, tot) + simp = simplify(tot_A) + @test all(iszero(simp[i]) for i in 0:4) + @test all(rank(simp[i]) == 5 for i in 5:6) + @test all(iszero(simp[i]) for i in 7:8) +end + From 2a3d73a278533b7786cc303be42af072c0062ba5 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 13:17:08 +0200 Subject: [PATCH 23/27] Some tuning. --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index a6438f90da9c..8d81a2286565 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -914,17 +914,17 @@ function _ext_module_map( for (i, g) in enumerate(gens(Fd)) img = zero(Fe_E) for j in 1:nvars(S) - h = g - h = Fd_to_F(h) - h = gen(S, j)*h - h = F_to_Fe(h) - h = Fe_map(h) - h = gen(E, j)*h - img += h + h1 = g + h2 = Fd_to_F(h1) + h3 = gen(S, j)*h2 + h4 = F_to_Fe(h3) + h5 = Fe_map(h4) + h6 = gen(E, j)*h5 + img += h6 end push!(img_gens, img) end - return hom(Fd_E, Fe_E, img_gens), Fd_map, Fe_map + return hom(Fd_E, Fe_E, img_gens; check=false), Fd_map, Fe_map end function _ext_module_map(M::SubquoModule{T}, d::Int) where {T<:MPolyDecRingElem} @@ -947,11 +947,25 @@ function _ext_module_map(M::SubquoModule{T}, d::Int) where {T<:MPolyDecRingElem} @assert domain(phi) === codomain(F0_d_map) @assert codomain(phi) === codomain(F0_e_map) - M_dom, pr_dom = quo(domain(phi), F0_d_map.(ambient_representatives_generators(dom_rels))) - M_cod, pr_cod = quo(codomain(phi), F0_e_map.(ambient_representatives_generators(cod_rels))) - @assert domain(pr_cod) === codomain(phi) + # avoid the computations from set_grading! + # M_dom, pr_dom = quo(domain(phi), F0_d_map.(ambient_representatives_generators(dom_rels))) + # M_cod, pr_cod = quo(codomain(phi), F0_e_map.(ambient_representatives_generators(cod_rels))) - psi = hom(M_dom, M_cod, pr_cod.(phi.(F0_d_map.(gens(F0_d))))) + new_dom_rels = SubModuleOfFreeModule(domain(phi), F0_d_map.(ambient_representatives_generators(dom_rels))) + dom_indices = [i for (i, g) in enumerate(gens(domain(phi))) if !(g in new_dom_rels)] + dom_inv_dict = Dict{Int, Int}(i => k for (k, i) in enumerate(dom_indices)) + new_dom_gens = [gen(domain(phi), i) for i in dom_indices] + M_dom = SubquoModule(SubModuleOfFreeModule(domain(phi), new_dom_gens), new_dom_rels) + new_cod_rels = SubModuleOfFreeModule(codomain(phi), F0_e_map.(ambient_representatives_generators(cod_rels))) + cod_indices = [i for (i, g) in enumerate(gens(codomain(phi))) if !(g in new_cod_rels)] + cod_inv_dict = Dict{Int, Int}(i => k for (k, i) in enumerate(cod_indices)) + new_cod_gens = [gen(codomain(phi), i) for i in cod_indices] + new_cod_gens = [g for g in gens(codomain(phi)) if !(g in new_cod_rels)] + M_cod = SubquoModule(SubModuleOfFreeModule(codomain(phi), new_cod_gens), new_cod_rels) + + img_gens = phi.(F0_d_map.(gens(F0_d)[dom_indices])) + img_gens2 = [sum(c*gen(M_cod, cod_inv_dict[i]) for (i, c) in coordinates(v) if i in keys(cod_inv_dict); init = zero(M_cod)) for v in img_gens] + psi = hom(M_dom, M_cod, img_gens2; check=false) return psi, MapFromFunc(M_dom, M, v->map(pres, 0)(to_F0(repres(v)))), MapFromFunc(M_cod, M, v->map(pres, 0)(to_F0_2(repres(v)))) end From ba6d7ce4f1ccf71ac5c3548c8d890cf6816adbde Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 16 Sep 2024 15:05:29 +0200 Subject: [PATCH 24/27] Extend degree signatures to accept check. --- src/Modules/ModulesGraded.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Modules/ModulesGraded.jl b/src/Modules/ModulesGraded.jl index 2e9458312b0b..02bee40dff94 100644 --- a/src/Modules/ModulesGraded.jl +++ b/src/Modules/ModulesGraded.jl @@ -414,7 +414,7 @@ function set_grading!(M::FreeMod, W::Vector{<:IntegerUnion}) M.d = [W[i] * A[1] for i in 1:length(W)] end -function degrees(M::FreeMod) +function degrees(M::FreeMod; check::Bool=true) @assert is_graded(M) return M.d::Vector{FinGenAbGroupElem} end @@ -437,8 +437,8 @@ julia> degrees_of_generators(F) [0] ``` """ -function degrees_of_generators(F::FreeMod) - return degrees(F) +function degrees_of_generators(F::FreeMod; check::Bool=true) + return degrees(F; check) end ############################################################################### @@ -541,10 +541,11 @@ function is_homogeneous(el::FreeModElem) return isa(el.d, FinGenAbGroupElem) end -const AnyGradedRingElem = Union{<:MPolyDecRingElem, <:MPolyQuoRingElem{<:MPolyDecRingElem}, - <:MPolyLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing}, - <:MPolyQuoLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing} - } +#const AnyGradedRingElem = Union{<:MPolyDecRingElem, <:MPolyQuoRingElem{<:MPolyDecRingElem}, +# <:MPolyLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing}, +# <:MPolyQuoLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing} +# } +const AnyGradedRingElem = NCRingElem @doc raw""" degree(f::FreeModElem{T}; check::Bool=true) where {T<:AnyGradedRingElem} From f7f7c179810c958c89c3918f1c3fb41966c59a09 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 16 Sep 2024 15:18:18 +0200 Subject: [PATCH 25/27] Widen signature for AdmissibleFPRing(Elem) again. --- src/Modules/ModuleTypes.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Modules/ModuleTypes.jl b/src/Modules/ModuleTypes.jl index cdbfe1ed9571..5a53f8654159 100644 --- a/src/Modules/ModuleTypes.jl +++ b/src/Modules/ModuleTypes.jl @@ -4,8 +4,8 @@ import AbstractAlgebra.WeakKeyIdDict # we expect the `ModuleFP` framework to be functional and/or working. # It can gradually be extended, but should not be considered to be # visible or accessible to the outside world. -const AdmissibleModuleFPRingElem = Union{RingElem, PBWAlgQuoElem, PBWAlgElem} -const AdmissibleModuleFPRing = Union{Ring, PBWAlgQuo, PBWAlgRing} +const AdmissibleModuleFPRingElem = NCRingElem +const AdmissibleModuleFPRing = NCRing @doc raw""" ModuleFP{T} From f4e3b51cc268052550af98ec3b0052ae315ed3cf Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 16 Sep 2024 15:18:48 +0200 Subject: [PATCH 26/27] Correct the is_right and is_two_sided methods. --- src/Modules/UngradedModules/FreeModElem.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Modules/UngradedModules/FreeModElem.jl b/src/Modules/UngradedModules/FreeModElem.jl index 6a197b1b64fc..6744abde2b91 100644 --- a/src/Modules/UngradedModules/FreeModElem.jl +++ b/src/Modules/UngradedModules/FreeModElem.jl @@ -279,11 +279,11 @@ is_left(M::ModuleFP) = is_left(typeof(M)) is_left(::Type{T}) where {RET<:RingElem, T<:ModuleFP{RET}} = true is_left(::Type{T}) where {RET<:AdmissibleModuleFPRingElem, T<:ModuleFP{RET}} = true # Left multiplication is generically supported -is_right(M::ModuleFP) = is_right_module(typeof(M)) +is_right(M::ModuleFP) = is_right(typeof(M)) is_right(::Type{T}) where {RET<:RingElem, T<:ModuleFP{RET}} = true is_right(::Type{T}) where {RET<:AdmissibleModuleFPRingElem, T<:ModuleFP{RET}} = false # Right multiplication is not supported by the generic code at the moment, but we plan to do so eventually. -is_two_sided(M::ModuleFP) = is_right_module(typeof(M)) +is_two_sided(M::ModuleFP) = is_two_sided(typeof(M)) is_two_sided(::Type{T}) where {RET<:RingElem, T<:ModuleFP{RET}} = true is_two_sided(::Type{T}) where {RET<:AdmissibleModuleFPRingElem, T<:ModuleFP{RET}} = false # see above From 439784e4c0ec13178f3c686193ff08feac672b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Mon, 16 Sep 2024 17:33:01 +0200 Subject: [PATCH 27/27] Some performance tuning --- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 30 +++++++++---------- experimental/ExteriorAlgebra/src/Types.jl | 4 +-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 8d81a2286565..1ef8b3842318 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -198,12 +198,12 @@ end ### Manual realization of exterior algebras include("Types.jl") -function ExtAlgElem(E::ExteriorAlgebra{T}, a::Vector{Tuple{Int, SRow{T}}}; check::Bool=true) where {T} - return ExtAlgElem(E, Dict{Int, SRow{T}}(i => v for (i, v) in a if !iszero(v))) +function ExtAlgElem(E::ExteriorAlgebra{T}, a::Vector{<:Tuple{Int, SRow{T}}}; check::Bool=true) where {T} + return ExtAlgElem(E, Dict{Int, sparse_row_type(T)}(i => v for (i, v) in a if !iszero(v))) end function ExtAlgElem(E::ExteriorAlgebra{T}, p::Int, v::SRow{T}; check::Bool=true) where {T} - return ExtAlgElem(E, Dict{Int, SRow{T}}(i => v for (i, v) in a if !iszero(v))) + return ExtAlgElem(E, Dict{Int, sparse_row_type(T)}(i => v for (i, v) in a if !iszero(v))) end function mul!(a::T, w::ExtAlgElem{T}) where {T} @@ -219,7 +219,7 @@ function *(a::T, w::ExtAlgElem{T}) where {T <:NCRingElem} @assert parent(a) === base_ring(parent(w)) nc = [(i, a*v) for (i, v) in components(w)] return ExtAlgElem(parent(w), - Dict{Int, SRow{T}}(i => v for (i, v) in nc if !iszero(v)); + Dict{Int, sparse_row_type(T)}(i => v for (i, v) in nc if !iszero(v)); check=false ) end @@ -265,21 +265,21 @@ base_ring(w::ExtAlgElem) = base_ring(parent(w)) parent(w::ExtAlgElem) = w.parent rank(E::ExteriorAlgebra) = E.rank -base_ring(E::ExteriorAlgebra) = E.base_ring +base_ring(E::ExteriorAlgebra{T}) where {T} = E.base_ring::parent_type(T) +base_ring_type(::Type{ExteriorAlgebra{T}}) where {T} = parent_type(T) zero(E::ExteriorAlgebra{T}) where {T} = ExtAlgElem(E) zero(w::ExtAlgElem) = zero(parent(w)) function components(w::ExtAlgElem{T}) where {T} if !isdefined(w, :components) - w.components = Dict{Int, SRow{T}}() + w.components = Dict{Int, sparse_row_type(T)}() end - return w.components + return w.components::Dict{Int, sparse_row_type(T)} end function getindex(w::ExtAlgElem, p::Int) - !haskey(w.components, p) && return sparse_row(base_ring(parent(w))) - return w.components[p] + return get(components(w), p, sparse_row(base_ring(parent(w)))) end function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T<:NCRingElem} @@ -315,7 +315,7 @@ function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T<:NCRingElem} end if !isempty(new_elem) if haskey(components(result), r) - components(result)[r] += sparse_row(R, [(i, v) for (i, v) in new_elem]) + components(result)[r] = Hecke.add_scaled_row!(components(result)[r], sparse_row(R, [(i, v) for (i, v) in new_elem]), one(R)) else components(result)[r] = sparse_row(R, [(i, v) for (i, v) in new_elem]) end @@ -340,12 +340,12 @@ function print_symbols(E::ExteriorAlgebra, p::Int) end function gens(E::ExteriorAlgebra{T}) where {T} - return [ExtAlgElem(E, Dict{Int, SRow{T}}(1 => sparse_row(base_ring(E), + return [ExtAlgElem(E, Dict{Int, sparse_row_type(T)}(1 => sparse_row(base_ring(E), [(i, one(base_ring(E)))]))) for i in 1:rank(E)] end -one(E::ExteriorAlgebra{T}) where {T} = ExtAlgElem(E, Dict{Int, SRow{T}}(0 => sparse_row(base_ring(E), [(1, one(base_ring(E)))])); check=false) +one(E::ExteriorAlgebra{T}) where {T} = ExtAlgElem(E, Dict{Int, sparse_row_type(T)}(0 => sparse_row(base_ring(E), [(1, one(base_ring(E)))])); check=false) function Base.show(io::IO, w::ExtAlgElem) E = parent(w) @@ -536,7 +536,7 @@ function (F::FreeMod{ExtAlgElem{T}})(v::Vector{FreeModElem{T}}, p::Int) where {T v_comp_list = [[G(coordinates(w)[r]) for (G, r) in zip(F_p_parts, ranges)] for w in v] #d = [Int(degree(g; check=false)[1]) for g in gens(F)] d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] - v_comp_coords_list = [[iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, SRow{T}}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] for v_comps in v_comp_list] + v_comp_coords_list = [[iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, sparse_row_type(T)}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] for v_comps in v_comp_list] return elem_type(F)[sum(a*g for (a, g) in zip(v_comp_coords, g_E); init=zero(F)) for v_comp_coords in v_comp_coords_list] end @@ -555,7 +555,7 @@ function (F::FreeMod{ExtAlgElem{T}})(v::FreeModElem{T}, p::Int) where {T} v_comps = [G(coordinates(v)[r]) for (G, r) in zip(F_p_parts, ranges)] #d = [Int(degree(g; check=false)[1]) for g in gens(F)] d = [Int(d[1]) for d in degrees_of_generators(F; check=false)] - v_comp_coords = [iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, SRow{T}}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] + v_comp_coords = [iszero(c.coords) ? E() : ExtAlgElem(E, Dict{Int, sparse_row_type(T)}([p-d[i]=>c.coords])) for (i, c) in enumerate(v_comps)] return sum(a*g for (a, g) in zip(v_comp_coords, g_E); init=zero(F)) end @@ -971,7 +971,7 @@ function _ext_module_map(M::SubquoModule{T}, d::Int) where {T<:MPolyDecRingElem} end function gen(E::ExteriorAlgebra{T}, i::Int) where {T} - return ExtAlgElem(E, Dict{Int, SRow{T}}(1 => sparse_row(base_ring(E), [(i, one(base_ring(E)))]))) + return ExtAlgElem(E, Dict{Int, sparse_row_type(T)}(1 => sparse_row(base_ring(E), [(i, one(base_ring(E)))]))) end diff --git a/experimental/ExteriorAlgebra/src/Types.jl b/experimental/ExteriorAlgebra/src/Types.jl index db9c3d52f64a..fc9407399fdb 100644 --- a/experimental/ExteriorAlgebra/src/Types.jl +++ b/experimental/ExteriorAlgebra/src/Types.jl @@ -15,13 +15,13 @@ end mutable struct ExtAlgElem{T} <: NCRingElem parent::ExteriorAlgebra{T} - components::Dict{Int, SRow{T}} + components::Dict{Int, <:SRow{T}} function ExtAlgElem(E::ExteriorAlgebra{T}) where {T} return new{T}(E) end - function ExtAlgElem(E::ExteriorAlgebra{T}, comp::Dict{Int, SRow{T}}; + function ExtAlgElem(E::ExteriorAlgebra{T}, comp::Dict{Int, <:SRow{T}}; check::Bool=true ) where {T} @assert all(base_ring(x) === base_ring(E) for (_, x) in comp)