diff --git a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl index 709dd1973a20..0a4745a96fb5 100644 --- a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl @@ -11,6 +11,7 @@ include("Objects/new_complex_template.jl") include("Objects/linear_strands.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") @@ -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/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, 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..946850e17183 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -0,0 +1,279 @@ +#= Cartan Eilenberg resolutions of 1-dimensional complexes +# +# Suppose +# +# 0 ← C₀ ← C₁ ← C₂ ← … +# +# is a bounded below complex. We compute a double complex +# +# 0 0 0 +# ↑ ↑ ↑ +# 0 ← P₀₀ ← P₀₁ ← P₀₂ ← … +# ↑ ↑ ↑ +# 0 ← P₁₀ ← P₁₁ ← P₁₂ ← … +# ↑ ↑ ↑ +# 0 ← P₂₀ ← P₂₁ ← P₂₂ ← … +# ↑ ↑ ↑ +# ⋮ ⋮ ⋮ +# +# 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 +# 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, is_exact, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplexMorphism}()) + end +end + +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 + +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 + +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 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]) + img_gens = aug_B[0].(img_gens) + 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 + @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 + +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 = boundary_resolution(fac, j-1) + return direct_sum(res_B[i], res_Z[i])[1] + end + end + # We may assume that the next map can not be computed and is, hence, zero. + return res_Z[i] +end + +function can_compute(fac::CEChainFactory, self::AbsHyperComplex, I::Tuple) + (i, j) = I + can_compute_index(fac.c, (j,)) || return false + return i >= 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 # 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) + @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) + 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 + # 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=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=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 # 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 + +### 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]; 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 +end + +### Implementing the AbsHyperComplex interface via `underlying_complex` +underlying_complex(c::CartanEilenbergResolution) = c.internal_complex + 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 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 + diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index b259ba961f09..1ef8b3842318 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -193,3 +193,870 @@ 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, 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, sparse_row_type(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 <:NCRingElem} + @assert parent(a) === base_ring(parent(w)) + nc = [(i, a*v) for (i, v) in components(w)] + return ExtAlgElem(parent(w), + Dict{Int, sparse_row_type(T)}(i => v for (i, v) in nc if !iszero(v)); + check=false + ) +end + +function *(a, w::ExtAlgElem{T}) where {T<:NCRingElem} + return base_ring(w)(a)*w +end + +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] + iszero(components(v)[i]) && delete!(components(v), i) + else + components(v)[i] = components(w)[i] + end + end + return v +end + +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} + return v + (-w) +end + +function +(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T} + return add!(deepcopy(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{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, sparse_row_type(T)}() + end + return w.components::Dict{Int, sparse_row_type(T)} +end + +function getindex(w::ExtAlgElem, p::Int) + return get(components(w), p, sparse_row(base_ring(parent(w)))) +end + +function *(v::ExtAlgElem{T}, w::ExtAlgElem{T}) where {T<:NCRingElem} + E = parent(v) + 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)) + 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 + 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] = 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 + 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, 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, 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) + if is_empty(components(w)) + print(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) + push!(parts, "$(first(v.values))") + #print(io, "0 : $(first(v.values))") + continue + end + #print(io, "$p : ") + p_symb = print_symbols(E, p) + 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) + E = parent(v) + @assert E === parent(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) + 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)] + 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 + return E.multiplication_hash_tables[(p, q)] +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; check::Bool=true) + (!isdefined(w, :components) || isempty(components(w))) && return zero(grading_group(parent(w))) + 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 <: NCRingElem} + 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(g)[1])] for g in gens(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)); 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) + #= + # 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, 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 + +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) + #= + 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, 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 + +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; check=false)[1]) + F = parent(v) + @assert F_p === _graded_part(F, p) + c_v = coordinates(v)::SRow + # 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]]) + result += inc(v_i) + end + 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 + +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(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) + 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 = 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 + 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] + wf = w*f + for (i, a) in enumerate(gens(E)) + 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] = (f, aw) + end + end + 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) + 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 + +function in(v::FreeModElem{ExtAlgElem{T}}, I::SubModuleOfFreeModule{ExtAlgElem{T}}) where {T} + @assert is_homogeneous(v) + iszero(v) && return true + p = Int(degree(v; check=false)[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; check=false)[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 + +@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) + @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; check=false)[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 + @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 = 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}; + 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 $(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 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)) +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]; 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 = 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) + C0, map_to_orig = strand(C, d) + return C0[()], map_to_orig[()] +end + +### 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) + 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) + 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, 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)]) + Fe_map = hom(Fe, Fe_E, gens(Fe_E), 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) + 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; check=false), 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) + + # 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))) + + 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 + +function gen(E::ExteriorAlgebra{T}, i::Int) where {T} + return ExtAlgElem(E, Dict{Int, sparse_row_type(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(S) + R = base_ring(S) + new_symb = Symbol.([string(s)*" ̌" for s in symbols(S)]) + 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 + +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 + diff --git a/experimental/ExteriorAlgebra/src/Types.jl b/experimental/ExteriorAlgebra/src/Types.jl new file mode 100644 index 000000000000..fc9407399fdb --- /dev/null +++ b/experimental/ExteriorAlgebra/src/Types.jl @@ -0,0 +1,33 @@ +mutable struct ExteriorAlgebra{T} <: NCRing + 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}}} + 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)) + end +end + +mutable struct ExtAlgElem{T} <: NCRingElem + 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 + + diff --git a/experimental/ExteriorAlgebra/test/runtests.jl b/experimental/ExteriorAlgebra/test/runtests.jl index 96d8b2e118ed..dde4e3ea328d 100644 --- a/experimental/ExteriorAlgebra/test/runtests.jl +++ b/experimental/ExteriorAlgebra/test/runtests.jl @@ -137,3 +137,100 @@ 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(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 + 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} 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} 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 diff --git a/src/Oscar.jl b/src/Oscar.jl index 10f6865a54b5..47b31f4c8914 100644 --- a/src/Oscar.jl +++ b/src/Oscar.jl @@ -179,6 +179,8 @@ function __init__() add_assertion_scope(:IdealSheaves) add_verbosity_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) if is_dev && VERSION >= v"1.8" && false === (VERSION < v"1.11.0-DEV.1135" ?