From 51ae7664d2c93e18f32b5401e56c46aac5199591 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 26 Nov 2024 07:48:52 +0100 Subject: [PATCH 1/7] Work on hypercomplexes. --- .../src/DoubleAndHyperComplexes.jl | 5 + .../src/Morphisms/simplified_complexes.jl | 14 +- .../src/Morphisms/strand_functionality.jl | 109 +++++++++ .../src/Morphisms/strands.jl | 102 +-------- .../src/Objects/Methods.jl | 29 +++ .../Objects/cartan_eilenberg_resolution.jl | 6 +- .../src/Objects/eagon_northcott_complex.jl | 145 ++++++++++++ .../src/Objects/induced_ENC.jl | 96 ++++++++ .../src/Objects/new_koszul_complexes.jl | 81 +++++++ .../Objects/tensor_product_functionality.jl | 16 ++ .../src/Objects/tensor_products.jl | 15 -- .../test/eagon_northcott_complexes.jl | 53 +++++ .../DoubleAndHyperComplexes/test/runtests.jl | 1 + .../Schemes/src/DerivedPushforward.jl | 91 +++++++- src/Modules/Iterators.jl | 206 ++++++++++++++++-- .../UngradedModules/HomologicalAlgebra.jl | 2 +- src/Modules/UngradedModules/Methods.jl | 4 +- src/Modules/UngradedModules/Presentation.jl | 1 + 18 files changed, 834 insertions(+), 142 deletions(-) create mode 100644 experimental/DoubleAndHyperComplexes/src/Morphisms/strand_functionality.jl create mode 100644 experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl create mode 100644 experimental/DoubleAndHyperComplexes/src/Objects/induced_ENC.jl create mode 100644 experimental/DoubleAndHyperComplexes/src/Objects/new_koszul_complexes.jl create mode 100644 experimental/DoubleAndHyperComplexes/src/Objects/tensor_product_functionality.jl create mode 100644 experimental/DoubleAndHyperComplexes/test/eagon_northcott_complexes.jl diff --git a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl index 0a4745a96fb5..3bfd924d4c7e 100644 --- a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl @@ -1,14 +1,18 @@ include("Objects/Types.jl") include("Objects/Attributes.jl") include("Objects/tensor_products.jl") +include("Objects/tensor_product_functionality.jl") include("Objects/tor.jl") include("Objects/vector_spaces.jl") include("Objects/Constructors.jl") include("Objects/total_complexes.jl") include("Objects/koszul_complexes.jl") +include("Objects/new_koszul_complexes.jl") include("Objects/degree_zero_complexes.jl") include("Objects/new_complex_template.jl") include("Objects/linear_strands.jl") +include("Objects/eagon_northcott_complex.jl") +include("Objects/induced_ENC.jl") include("Morphisms/Types.jl") include("Objects/cartan_eilenberg_resolution.jl") @@ -17,6 +21,7 @@ include("Morphisms/ext.jl") include("Morphisms/simplified_complexes.jl") include("Morphisms/free_resolutions.jl") include("Morphisms/strands.jl") +include("Morphisms/strand_functionality.jl") include("Morphisms/koszul_complexes.jl") include("Morphisms/morphism_from_maps.jl") include("Morphisms/linear_strands.jl") diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl index e0e9ef965493..fdd561fb4cfa 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl @@ -209,8 +209,8 @@ function (fac::SimplifiedChainFactory)(d::AbsHyperComplex, Ind::Tuple) w = Sinv[i] v = zero(new_dom) for j in 1:length(I) - a = w[I[j]] - !iszero(a) && (v += a*new_dom[j]) + success, a = _has_index(w, I[j]) + success && (v += a*new_dom[j]) end push!(img_gens_dom, v) end @@ -401,13 +401,13 @@ function _simplify_matrix!(A::SMat; find_pivot=nothing) # Initialize the base change matrices in domain and codomain S = sparse_matrix(R, m, m) for i in 1:m - S[i] = sparse_row(R, [(i, one(R))]) + S[i] = sparse_row(R, [(i, one(R))]; sort=false) end Sinv_transp = deepcopy(S) T = sparse_matrix(R, n, n) for i in 1:n - T[i] = sparse_row(R, [(i, one(R))]) + T[i] = sparse_row(R, [(i, one(R))]; sort=false) end Tinv_transp = deepcopy(T) @@ -479,7 +479,7 @@ function _simplify_matrix!(A::SMat; find_pivot=nothing) # been treated. a_row = deepcopy(A[p]) - a_row_del = a_row - sparse_row(R, [(q, u)]) + a_row_del = a_row - sparse_row(R, [(q, u)]; sort=false) col_entries = Vector{Tuple{Int, elem_type(R)}}() for i in 1:m @@ -488,8 +488,8 @@ function _simplify_matrix!(A::SMat; find_pivot=nothing) success, c = _has_index(A, i, q) success && push!(col_entries, (i, c::elem_type(R))) end - a_col = sparse_row(R, col_entries) - a_col_del = a_col - sparse_row(R, [(p, u)]) + a_col = sparse_row(R, col_entries; sort=false) + a_col_del = a_col - sparse_row(R, [(p, u)]; sort=false) uinv = inv(u) diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/strand_functionality.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/strand_functionality.jl new file mode 100644 index 000000000000..c104a8535c49 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/strand_functionality.jl @@ -0,0 +1,109 @@ +function (fac::StrandChainFactory)(c::AbsHyperComplex, i::Tuple) + M = fac.orig[i] + @assert is_graded(M) "module must be graded" + R = base_ring(M) + kk = coefficient_ring(R) + return FreeMod(kk, length(all_exponents(fac.orig[i], fac.d))) +end + +function can_compute(fac::StrandChainFactory, c::AbsHyperComplex, i::Tuple) + return can_compute_index(fac.orig, i) +end + + +function (fac::StrandMorphismFactory)(c::AbsHyperComplex, p::Int, i::Tuple) + I = collect(i) + Next = I + (direction(c, p) == :chain ? -1 : 1)*[(k==p ? 1 : 0) for k in 1:dim(c)] + next = Tuple(Next) + orig_dom = fac.orig[i] + orig_cod = fac.orig[next] + dom = c[i] + cod = c[next] + + orig_map = map(fac.orig, p, i) + + # Use a dictionary for fast mapping of the monomials to the + # generators of `cod`. + cod_dict = Dict{Tuple{Vector{Int}, Int}, elem_type(cod)}(m=>cod[k] for (k, m) in enumerate(all_exponents(orig_cod, fac.d))) + #cod_dict = Dict{Tuple{Vector{Int}, Int}, elem_type(cod)}(first(exponents(m))=>cod[k] for (k, m) in enumerate(all_monomials(orig_cod, fac.d))) + # Hashing of FreeModElem's can not be assumed to be non-trivial. Hence we use the exponents directly. + img_gens_res = elem_type(cod)[] + R = base_ring(orig_dom) + vv = gens(R) + for (e, i) in all_exponents(orig_dom, fac.d) # iterate through the generators of `dom` + m = prod(x^k for (x, k) in zip(vv, e); init=one(R))*orig_dom[i] + v = orig_map(m) # map the monomial + # take preimage of the result using the previously built dictionary. + # TODO: Iteration over the terms of v is VERY slow due to its suboptimal implementation. + # We have to iterate manually. This saves us roughly 2/3 of the memory consumption and + # it also runs three times as fast. + w = zero(cod) + for (i, b) in coordinates(v) + #g = orig_cod[i] + w += sum(c*cod_dict[(n, i)] for (c, n) in zip(coefficients(b), exponents(b)); init=zero(cod)) + end + push!(img_gens_res, w) + end + return hom(dom, cod, img_gens_res) +end + +function can_compute(fac::StrandMorphismFactory, c::AbsHyperComplex, p::Int, i::Tuple) + return can_compute_map(fac.orig, p, i) +end + +### User facing constructor +function strand(c::AbsHyperComplex{T}, d::Union{Int, FinGenAbGroupElem}) where {T<:ModuleFP} + result = StrandComplex(c, d) + inc = StrandInclusionMorphism(result) + result.inclusion_map = inc + return result, inc +end + +### Some missing methods +# (Disabled for the moment because the use case was disabled due to slowness) +#= +function sparse_matrix(phi::SubQuoHom{<:SubquoModule, <:ModuleFP, Nothing}) + R = base_ring(domain(phi)) + m = ngens(domain(phi)) + n = ngens(codomain(phi)) + result = sparse_matrix(R, m, n) + for (i, g) in enumerate(gens(domain(phi))) + result[i] = coordinates(phi(g)) + end + return result +end + +function sparse_matrix(phi::FreeModuleHom{FreeMod{T}, SubquoModule{T}, Nothing}) where {T} + V = domain(phi) + W = codomain(phi) + kk = base_ring(V) + m = ngens(V) + n = ngens(W) + result = sparse_matrix(kk, m, n) + for (i, g) in enumerate(gens(V)) + result[i] = coordinates(phi(g)) + end + return result +end +=# + +# TODO: Code duplicated from `monomial_basis`. Clean this up! +function all_exponents(W::MPolyDecRing, d::FinGenAbGroupElem) + D = W.D + is_free(D) || error("Grading group must be free") + h = hom(free_abelian_group(ngens(W)), W.d) + fl, p = has_preimage_with_preimage(h, d) + R = base_ring(W) + B = Vector{Int}[] + if fl + k, im = kernel(h) + #need the positive elements in there... + #Ax = b, Cx >= 0 + C = identity_matrix(FlintZZ, ngens(W)) + A = reduce(vcat, [x.coeff for x = W.d]) + k = solve_mixed(transpose(A), transpose(d.coeff), C) + B = Vector{Int}[k[ee, :] for ee in 1:nrows(k)] + end + return B +end + diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/strands.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/strands.jl index 93d7adcacbb1..4c67213f0329 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/strands.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/strands.jl @@ -1,5 +1,5 @@ ######################################################################## -# Strands of hyper complexs of graded free modules. +# Strands of hyper complexes of graded free modules. # # Suppose S = ๐•œ[xโ‚€,โ€ฆ,xโ‚™] is a standard graded ring and C a # hypercomplex of modules over it with all morphisms of degree @@ -14,89 +14,36 @@ ### Production of the chains struct StrandChainFactory{ChainType<:ModuleFP} <: HyperComplexChainFactory{ChainType} orig::AbsHyperComplex - d::Int + d::Union{Int, FinGenAbGroupElem} function StrandChainFactory( - orig::AbsHyperComplex{ChainType}, d::Int + orig::AbsHyperComplex{ChainType}, d::Union{Int, FinGenAbGroupElem} ) where {ChainType<:ModuleFP} return new{FreeMod}(orig, d) # TODO: Specify the chain type better end end -function (fac::StrandChainFactory)(c::AbsHyperComplex, i::Tuple) - M = fac.orig[i] - @assert is_graded(M) "module must be graded" - R = base_ring(M) - @assert is_standard_graded(R) "the base ring must be standard graded" - kk = coefficient_ring(R) - return FreeMod(kk, length(all_exponents(fac.orig[i], fac.d))) -end - -function can_compute(fac::StrandChainFactory, c::AbsHyperComplex, i::Tuple) - return can_compute_index(fac.orig, i) -end - ### Production of the morphisms struct StrandMorphismFactory{MorphismType<:ModuleFPHom} <: HyperComplexMapFactory{MorphismType} orig::AbsHyperComplex - d::Int + d::Union{Int, FinGenAbGroupElem} monomial_mappings::Dict{<:Tuple{<:Tuple, Int}, <:Map} - function StrandMorphismFactory(orig::AbsHyperComplex, d::Int) + function StrandMorphismFactory(orig::AbsHyperComplex, d::Union{Int, FinGenAbGroupElem}) monomial_mappings = Dict{Tuple{Tuple, Int}, Map}() return new{FreeModuleHom}(orig, d, monomial_mappings) end end -function (fac::StrandMorphismFactory)(c::AbsHyperComplex, p::Int, i::Tuple) - I = collect(i) - Next = I + (direction(c, p) == :chain ? -1 : 1)*[(k==p ? 1 : 0) for k in 1:dim(c)] - next = Tuple(Next) - orig_dom = fac.orig[i] - orig_cod = fac.orig[next] - dom = c[i] - cod = c[next] - - orig_map = map(fac.orig, p, i) - - # Use a dictionary for fast mapping of the monomials to the - # generators of `cod`. - cod_dict = Dict{Tuple{Vector{Int}, Int}, elem_type(cod)}(m=>cod[k] for (k, m) in enumerate(all_exponents(orig_cod, fac.d))) - #cod_dict = Dict{Tuple{Vector{Int}, Int}, elem_type(cod)}(first(exponents(m))=>cod[k] for (k, m) in enumerate(all_monomials(orig_cod, fac.d))) - # Hashing of FreeModElem's can not be assumed to be non-trivial. Hence we use the exponents directly. - img_gens_res = elem_type(cod)[] - R = base_ring(orig_dom) - vv = gens(R) - for (e, i) in all_exponents(orig_dom, fac.d) # iterate through the generators of `dom` - m = prod(x^k for (x, k) in zip(vv, e); init=one(R))*orig_dom[i] - v = orig_map(m) # map the monomial - # take preimageof the result using the previously built dictionary. - # TODO: Iteration over the terms of v is VERY slow due to its suboptimal implementation. - # We have to iterate manually. This saves us roughly 2/3 of the memory consumption and - # it also runs three times as fast. - w = zero(cod) - for (i, b) in coordinates(v) - #g = orig_cod[i] - w += sum(c*cod_dict[(n, i)] for (c, n) in zip(coefficients(b), exponents(b)); init=zero(cod)) - end - push!(img_gens_res, w) - end - return hom(dom, cod, img_gens_res) -end - -function can_compute(fac::StrandMorphismFactory, c::AbsHyperComplex, p::Int, i::Tuple) - return can_compute_map(fac.orig, p, i) -end - ### The concrete struct @attributes mutable struct StrandComplex{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} internal_complex::HyperComplex{ChainType, MorphismType} original_complex::AbsHyperComplex - d::Int + d::Union{Int, FinGenAbGroupElem} inclusion_map::AbsHyperComplexMorphism function StrandComplex( - orig::AbsHyperComplex{ChainType, MorphismType}, d::Int + orig::AbsHyperComplex{ChainType, MorphismType}, d::Union{Int, FinGenAbGroupElem} ) where {ChainType <: ModuleFP, MorphismType <: ModuleFPHom} chain_fac = StrandChainFactory(orig, d) map_fac = StrandMorphismFactory(orig, d) @@ -160,38 +107,3 @@ end underlying_morphism(phi::StrandInclusionMorphism) = phi.internal_morphism -### User facing constructor -function strand(c::AbsHyperComplex{T}, d::Int) where {T<:ModuleFP} - result = StrandComplex(c, d) - inc = StrandInclusionMorphism(result) - result.inclusion_map = inc - return result, inc -end - -### Some missing methods -# (Disabled for the moment because the use case was disabled due to slowness) -#= -function sparse_matrix(phi::SubQuoHom{<:SubquoModule, <:ModuleFP, Nothing}) - R = base_ring(domain(phi)) - m = ngens(domain(phi)) - n = ngens(codomain(phi)) - result = sparse_matrix(R, m, n) - for (i, g) in enumerate(gens(domain(phi))) - result[i] = coordinates(phi(g)) - end - return result -end - -function sparse_matrix(phi::FreeModuleHom{FreeMod{T}, SubquoModule{T}, Nothing}) where {T} - V = domain(phi) - W = codomain(phi) - kk = base_ring(V) - m = ngens(V) - n = ngens(W) - result = sparse_matrix(kk, m, n) - for (i, g) in enumerate(gens(V)) - result[i] = coordinates(phi(g)) - end - return result -end -=# diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/Methods.jl b/experimental/DoubleAndHyperComplexes/src/Objects/Methods.jl index 90b12d4107cc..201265e422f2 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/Methods.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/Methods.jl @@ -340,3 +340,32 @@ function _free_show(io::IO, C::AbsHyperComplex) end end +### Koszul contraction +# Given a free `R`-module `F`, a morphism ฯ†: F โ†’ R, and an element `v` in โ‹€ แต– F, +# compute the contraction ฯ†(v) โˆˆ โ‹€ แต–โปยน F. +# Note: For this internal method ฯ† is only represented as a dense (column) vector. +function _contract( + v::FreeModElem{T}, phi::Vector{T}; + parent::FreeMod{T}=begin + success, F0, p = _is_exterior_power(Oscar.parent(v)) + @req success "parent is not an exterior power" + exterior_power(F0, p-1) + end + ) where {T} + success, F0, p = _is_exterior_power(Oscar.parent(v)) + @req success "parent is not an exterior power" + @assert length(phi) == ngens(F0) "lengths are incompatible" + result = zero(parent) + n = ngens(F0) + for (i, ind) in enumerate(OrderedMultiIndexSet(p, n)) + is_zero(v[i]) && continue + for j in 1:p + I = deleteat!(copy(indices(ind)), j) + new_ind = OrderedMultiIndex(I, n) + result = result + (-1)^j * v[i] * phi[ind[j]] * parent[linear_index(new_ind)] + end + end + return result +end + + diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl index 982d058b5931..dbdd3ed4cfc3 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -45,7 +45,7 @@ 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, is_exact, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplexMorphism}()) + return new{FreeMod}(c, is_exact, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplexMorphism}()) end end @@ -198,12 +198,12 @@ end @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! + map_fac = CEMapFactory{FreeModuleHom}() # 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) + return new{FreeMod, FreeModuleHom}(internal_complex) end end diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl b/experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl new file mode 100644 index 000000000000..728fdce60776 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl @@ -0,0 +1,145 @@ +### Production of the chains +struct ENCChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} + A::MatrixElem + exps::Vector{Vector{Vector{Int}}} # a hashtable for indexing + F::ChainType # the original free module for the Koszul complex + sym_powers::Dict{Int, ChainType} + ext_powers::Dict{Int, ChainType} + + function ENCChainFactory( + A::MatrixElem{T}, + F::FreeMod + ) where {T<:RingElem} + n = ncols(A) + k = nrows(A) + exps = Vector{Vector{Vector{Int}}}() + for i in 0:(n-k+1) + push!(exps, [a.c for a in WeakCompositions(i, k)]) + end + ChainType = FreeMod{T} + return new{ChainType}(A, exps, F, Dict{Int, ChainType}(), Dict{Int, ChainType}()) + end +end + +function (fac::ENCChainFactory)(self::AbsHyperComplex, I::Tuple) + i = first(I) + R = base_ring(fac.A) + if is_zero(i) + return is_graded(R) ? graded_free_module(R, [zero(grading_group(R))]) : FreeMod(R, 1) + end + Sd = is_graded(R) ? graded_free_module(R, [zero(grading_group(R)) for _ in 1:length(fac.exps[i])]) : FreeMod(R, length(fac.exps[i])) + n = ncols(fac.A) + k = nrows(fac.A) + wedge_power, _ = exterior_power(fac.F, k + i - 1) + fac.ext_powers[i] = wedge_power + fac.sym_powers[i] = Sd + return tensor_product(Sd, wedge_power) +end + +function can_compute(fac::ENCChainFactory, self::AbsHyperComplex, I::Tuple) + is_one(length(I)) || return false + i = first(I) + i >= 0 || return false + return i <= ncols(fac.A) - nrows(fac.A) + 1 +end + +### Production of the morphisms +struct ENCMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} + function ENCMapFactory() + return new{FreeModuleHom}() + end +end + +function (fac::ENCMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) + cfac = chain_factory(self) + A = cfac.A + F = cfac.F + n = ncols(A) + k = nrows(A) + i = first(I) + dom = self[i] + cod = self[i-1] + + if isone(i) + # TODO: Do we need to take signs into account? + dets = [det(A[:, indices(ind)]) for ind in OrderedMultiIndexSet(k, n)] + return hom(dom, cod, [a*cod[1] for a in dets]) + end + + ext_dom = exterior_power(F, k + i - 1) # both are cached in F + ext_cod = exterior_power(F, k + i - 2) + + img_gens = elem_type(cod)[] + dom_dec = tensor_generator_decompose_function(dom) + dom_sym_power = cfac.sym_powers[i] + dom_ext_power = cfac.ext_powers[i] + cod_sym_power = cfac.sym_powers[i-1] + cod_ext_power = cfac.ext_powers[i-1] + for g in gens(dom) + img_gen = zero(cod) + a, b = dom_dec(g) + @assert parent(b) === dom_ext_power + @assert parent(a) === dom_sym_power + alpha = cfac.exps[i][first(coordinates(a))[1]] + for (k, e) in enumerate(alpha) + is_zero(e) && continue + beta = copy(alpha) + beta[k] -= 1 + aa = cod_sym_power[findfirst(gamma==beta for gamma in cfac.exps[i-1])] + bb = _contract(b, A[k, :]; parent=cod_ext_power) + img_gen = img_gen + tensor_pure_function(cod)((aa, bb)) + end + push!(img_gens, img_gen) + end + return hom(dom, cod, img_gens) +end + +function can_compute(fac::ENCMapFactory, self::AbsHyperComplex, p::Int, I::Tuple) + is_one(p) || return false + is_one(length(I)) || return false + i = first(I) + 0 < i || return false + cfac = chain_factory(self) + return i <= ncols(cfac.A) - nrows(cfac.A) + 1 +end + +### The concrete struct +@attributes mutable struct EagonNorthcottComplex{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} + internal_complex::HyperComplex{ChainType, MorphismType} + + function EagonNorthcottComplex( + A::MatrixElem{T}; + F::FreeMod{T}=begin + R = base_ring(A) + F = is_graded(R) ? graded_free_module(R, [zero(grading_group(R)) for _ in 1:ncols(A)]) : FreeMod(R, ncols(A)) + end + ) where {T} + chain_fac = ENCChainFactory(A, F) + map_fac = ENCMapFactory() + + internal_complex = HyperComplex(1, chain_fac, map_fac, [:chain]; + upper_bounds = Union{Int, Nothing}[ncols(A) - nrows(A) + 1], + lower_bounds = Union{Int, Nothing}[0] + ) + # Assuming that ChainType and MorphismType are provided by the input + return new{FreeMod{T}, FreeModuleHom}(internal_complex) + end +end + +### Implementing the AbsHyperComplex interface via `underlying_complex` +underlying_complex(c::EagonNorthcottComplex) = c.internal_complex + +original_module(c::EagonNorthcottComplex) = chain_factory(c).F +matrix(c::EagonNorthcottComplex) = chain_factory(c).A + +# return the exterior power used for the i-th entry +function _exterior_power(c::EagonNorthcottComplex, i::Int) + c[i] # fill the cache + return chain_factory(c).ext_powers[i] +end + +function _symmetric_power(c::EagonNorthcottComplex, i::Int) + c[i] # fill the cache + return chain_factory(c).sym_powers[i] +end + diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/induced_ENC.jl b/experimental/DoubleAndHyperComplexes/src/Objects/induced_ENC.jl new file mode 100644 index 000000000000..355e4153cf86 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Objects/induced_ENC.jl @@ -0,0 +1,96 @@ +### Production of the chains +struct InducedENCChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} + enc::EagonNorthcottComplex + I::ChainType + ext_powers::Dict{Int, ChainType} + + function InducedENCChainFactory(enc::EagonNorthcottComplex, I::SubquoModule) + @assert ambient_free_module(I) === original_module(enc) + ChainType = typeof(I) + return new{ChainType}(enc, I, Dict{Int, ChainType}()) + end +end + +function (fac::InducedENCChainFactory)(self::AbsHyperComplex, ind::Tuple) + i = first(ind) + enc = fac.enc + is_zero(i) && return sub(enc[0], [enc[0][1]])[1] + F0 = original_module(enc) + n = ngens(F0) + k = nrows(matrix(enc)) + I0 = fac.I + k + i - 1 > ngens(I0) && return sub(enc[i], elem_type(enc[i])[])[1] + amb_ext_power = _exterior_power(enc, i) + new_gens = elem_type(amb_ext_power)[] + for a in OrderedMultiIndexSet(k+i-1, ngens(I0)) + push!(new_gens, wedge([repres(gen(I0, i)) for i in indices(a)]; parent=amb_ext_power)) + end + #filter!(!iszero, new_gens) + Ip, _ = sub(amb_ext_power, new_gens) + fac.ext_powers[i] = Ip + result = tensor_product(_symmetric_power(enc, i), Ip) + return result +end + +function can_compute(fac::InducedENCChainFactory, self::AbsHyperComplex, i::Tuple) + return can_compute_index(fac.enc, i) +end + +### Production of the morphisms +struct InducedENCMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} + function InducedENCMapFactory(enc::EagonNorthcottComplex, I::SubquoModule) + return new{SubQuoHom}() + end +end + +function (fac::InducedENCMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) + i = first(I) + cfac = chain_factory(self) + enc = cfac.enc + dom = self[i] + cod = self[i-1] + + if i == 1 # evaluation of the determinants on the generators + A = matrix(enc) + R = base_ring(A) + I0 = chain_factory(self).I + B = matrix_space(R, ncols(A), ngens(I0))([repres(g)[i] for i in 1:ncols(A), g in gens(I0)]) + C = A*B + dets = [det(C[:, indices(ind)]) for ind in OrderedMultiIndexSet(nrows(C), ncols(C))] + return hom(dom, cod, [a*cod[1] for a in dets]) + end + + # TODO: This can probably be sped up by lifting directly on the exterior powers. + img_gens = elem_type(cod)[] + ambient_map = map(enc, i) + for g in gens(dom) + rep = repres(g) + image_g = ambient_map(rep) + push!(img_gens, cod(image_g)) + end + return hom(dom, cod, img_gens) +end + +function can_compute(fac::InducedENCMapFactory, self::AbsHyperComplex, p::Int, i::Tuple) + return can_compute_map(chain_factory(self).enc, p, i) +end + +### The concrete struct +@attributes mutable struct InducedENC{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} + internal_complex::HyperComplex{ChainType, MorphismType} + + function InducedENC(enc::EagonNorthcottComplex, I::SubquoModule) + chain_fac = InducedENCChainFactory(enc, I) + map_fac = InducedENCMapFactory(enc, I) + + internal_complex = HyperComplex(1, chain_fac, map_fac, [:chain]; + upper_bounds=enc.internal_complex.upper_bounds, + lower_bounds=enc.internal_complex.lower_bounds + ) + return new{typeof(I), SubQuoHom}(internal_complex) + end +end + +### Implementing the AbsHyperComplex interface via `underlying_complex` +underlying_complex(c::InducedENC) = c.internal_complex + diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/new_koszul_complexes.jl b/experimental/DoubleAndHyperComplexes/src/Objects/new_koszul_complexes.jl new file mode 100644 index 000000000000..241011907fbf --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Objects/new_koszul_complexes.jl @@ -0,0 +1,81 @@ +### Production of the chains +struct HomogKoszulComplexChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} + S::Ring + seq::Vector{<:RingElem} + degs::Vector{FinGenAbGroupElem} + + function HomogKoszulComplexChainFactory(S::Ring, seq::Vector{<:RingElem}; check::Bool=true) + @check all(is_homogeneous(a) for a in seq) "elements must be homogeneous" + return new{FreeMod{elem_type(S)}}(S, seq, [degree(a; check) for a in seq]) + end +end + +function (fac::HomogKoszulComplexChainFactory)(self::AbsHyperComplex, I::Tuple) + i = first(I) + G = grading_group(fac.S) + is_zero(i) && return graded_free_module(fac.S, [zero(G)]) + n = length(fac.seq) + degs = elem_type(G)[-sum(fac.degs[indices(omi)]; init=zero(G)) for omi in OrderedMultiIndexSet(i, n)] + result = graded_free_module(fac.S, degs) +end + +function can_compute(fac::HomogKoszulComplexChainFactory, self::AbsHyperComplex, i::Tuple) + return 0 <= first(i) <= length(fac.seq) +end + +### Production of the morphisms +struct HomogKoszulComplexMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} + function HomogKoszulComplexMapFactory(S::Ring) + return new{FreeModuleHom}() + end +end + +function (fac::HomogKoszulComplexMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) + i = first(I) + cfac = chain_factory(self) + n = length(cfac.seq) + dom = self[I] + cod = self[i+1] + img_gens = elem_type(cod)[] + inds = [OrderedMultiIndex([k], n) for k in 1:n] + for omi in OrderedMultiIndexSet(i, n) + img = zero(cod) + for (m, v) in zip(inds, cfac.seq) + sign, mult_ind = _wedge(omi, m) + is_zero(sign) && continue + res_ind = linear_index(mult_ind) + img += sign*v*cod[res_ind] + end + push!(img_gens, img) + end + return hom(dom, cod, img_gens) +end + +function can_compute(fac::HomogKoszulComplexMapFactory, self::AbsHyperComplex, p::Int, i::Tuple) + isone(p) || return false + return 0 <= first(i) < length(chain_factory(self).seq) +end + +### The concrete struct +@attributes mutable struct HomogKoszulComplex{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} + internal_complex::HyperComplex{ChainType, MorphismType} + + function HomogKoszulComplex(R::Ring, seq::Vector{<:RingElem}; check::Bool=true) + @assert is_graded(R) + @assert all(parent(a) === R for a in seq) + chain_fac = HomogKoszulComplexChainFactory(R, seq; check) + map_fac = HomogKoszulComplexMapFactory(R) + + # Assuming d is the dimension of the new complex + internal_complex = HyperComplex(1, chain_fac, map_fac, [:cochain], + upper_bounds=Union{Int, Nothing}[length(seq)], + lower_bounds=Union{Int, Nothing}[0] + ) + # Assuming that ChainType and MorphismType are provided by the input + return new{FreeMod{elem_type(R)}, FreeModuleHom}(internal_complex) + end +end + +### Implementing the AbsHyperComplex interface via `underlying_complex` +underlying_complex(c::HomogKoszulComplex) = c.internal_complex + diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/tensor_product_functionality.jl b/experimental/DoubleAndHyperComplexes/src/Objects/tensor_product_functionality.jl new file mode 100644 index 000000000000..2c790ace4345 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Objects/tensor_product_functionality.jl @@ -0,0 +1,16 @@ + +function can_compute(fac::HCTensorProductMapFactory, c::AbsHyperComplex, p::Int, I::Tuple) + i = collect(I) + # decompose I into its individual indices + j = Vector{Vector{Int}}() + k = 0 + for f in fac.factors + push!(j, i[k+1:k+dim(f)]) + k = k + dim(f) + end + d = length.(j) + l = findfirst(l->sum(d[1:l]; init=0)>=p, 1:length(j)) + l === nothing && error("mapping direction out of bounds") + return can_compute_map(fac.factors[l], p - sum(d[1:l-1]; init=0), Tuple(j[l])) +end + diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/tensor_products.jl b/experimental/DoubleAndHyperComplexes/src/Objects/tensor_products.jl index f5cdf64357df..8b421b1ee608 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/tensor_products.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/tensor_products.jl @@ -315,21 +315,6 @@ function (fac::HCTensorProductMapFactory{MorphismType})(c::AbsHyperComplex, p::I return tensor_product(c[I], c[J], maps) end -function can_compute(fac::HCTensorProductMapFactory, c::AbsHyperComplex, p::Int, I::Tuple) - i = collect(I) - # decompose I into its individual indices - j = Vector{Vector{Int}}() - k = 0 - for f in fac.factors - push!(j, i[k+1:k+dim(f)]) - k = k + dim(f) - end - d = length.(j) - l = findfirst(l->sum(d[1:l]; init=0)>=p, 1:length(j)) - l === nothing && error("mapping direction out of bounds") - return can_compute_map(fac.factors[l], p - sum(d[1:l-1]; init=0), Tuple(j[l])) -end - ### The concrete struct @attributes mutable struct HCTensorProductComplex{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} internal_complex::HyperComplex{ChainType, MorphismType} diff --git a/experimental/DoubleAndHyperComplexes/test/eagon_northcott_complexes.jl b/experimental/DoubleAndHyperComplexes/test/eagon_northcott_complexes.jl new file mode 100644 index 000000000000..c2e510810513 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/test/eagon_northcott_complexes.jl @@ -0,0 +1,53 @@ +@testset "Eagon-Northcott complexes and their restrictions" begin + R, (x, y, z, w) = QQ[:x, :y, :z, :w] + + A = R[x y z; y z w] + enc = Oscar.EagonNorthcottComplex(A) + @test ngens(enc[0]) == 1 + @test ngens(enc[1]) == 3 + @test ngens(enc[2]) == 2 + @test is_zero(compose(map(enc, 2), map(enc, 1))) + @test is_zero(homology(enc, 1)[1]) + + F0 = Oscar.original_module(enc) + I, _ = sub(F0, [F0[1], F0[2], x*F0[1]]) + ind = Oscar.InducedENC(enc, I) + @test is_zero(homology(ind, 1)[1]) + + m = 3 + n = 7 + r = 4 + A = matrix_space(R, m, n)([rand(R, 0:r, 0:r, 0:r) for i in 1:m, j in 1:n]) + enc = Oscar.EagonNorthcottComplex(A) + + for i in 2:n-m+1 + @test is_zero(compose(map(enc, i), map(enc, i-1))) + end + + F0 = Oscar.original_module(enc) + I, _ = sub(F0, [F0[1], F0[2], x*F0[3], y*F0[5], F0[7]]) + ind = Oscar.InducedENC(enc, I) + for i in 2:m-n+1 + @test is_zero(compose(map(ind, i), map(ind, i-1))) + end + + m = 2 + n = 4 + R, x = QQ[["x_$(i)_$j" for i in 1:m for j in 1:n]...] + A = matrix_space(R, m, n)(x) + enc = Oscar.EagonNorthcottComplex(A) + + for i in 2:n-m+1 + @test is_zero(compose(map(enc, i), map(enc, i-1))) + @test is_zero(homology(enc, i-1)[1]) + end + + F0 = Oscar.original_module(enc) + I, _ = sub(F0, [7*x[5]*F0[1], x[7]*F0[2] + F0[4], F0[n-1], F0[n]]) + ind = Oscar.InducedENC(enc, I) + for i in 2:m-n+1 + @test is_zero(compose(map(ind, i), map(ind, i-1))) + @test is_zero(homology(ind, i-1)[1]) + end + +end diff --git a/experimental/DoubleAndHyperComplexes/test/runtests.jl b/experimental/DoubleAndHyperComplexes/test/runtests.jl index 591a3023f363..403792743bf6 100644 --- a/experimental/DoubleAndHyperComplexes/test/runtests.jl +++ b/experimental/DoubleAndHyperComplexes/test/runtests.jl @@ -20,3 +20,4 @@ include("printing.jl") include("degree_zero_complex.jl") include("base_change.jl") include("cartan_eilenberg_resolutions.jl") +include("eagon_northcott_complexes.jl") diff --git a/experimental/Schemes/src/DerivedPushforward.jl b/experimental/Schemes/src/DerivedPushforward.jl index 6d4b23ba9ce5..e06e63d56d65 100644 --- a/experimental/Schemes/src/DerivedPushforward.jl +++ b/experimental/Schemes/src/DerivedPushforward.jl @@ -1,8 +1,31 @@ +function _maximum(v::Vector{FinGenAbGroupElem}) + @assert !is_empty(v) + G = parent(first(v)) + m = Vector{Int}() + for i in 1:ngens(G) + push!(m, maximum([w[i] for w in v])) + end + return sum(a*G[i] for (i, a) in enumerate(m); init=zero(G)) +end + +function _regularity_bound(F::FreeMod) + @assert is_graded(F) + is_zero(ngens(F)) && return zero(grading_group(F)) + degs = degrees_of_generators(F) + result = _maximum(degs) +end + +function _regularity_bound(comp::AbsHyperComplex, rng::UnitRange) + @assert isone(dim(comp)) + m = [_regularity_bound(comp[i]) for i in rng] + return _maximum(m) +end + function _derived_pushforward(M::FreeMod) S = base_ring(M) n = ngens(S)-1 - d = _regularity_bound(M) - n + d = Int(_regularity_bound(M)[1]) - n d = (d < 0 ? 0 : d) Sd = graded_free_module(S, [0 for i in 1:ngens(S)]) @@ -32,8 +55,63 @@ function _derived_pushforward(M::SubquoModule) d = _regularity_bound(M) - n d = (d < 0 ? 0 : d) + return _derived_pushforward(M, d) +end + +# Method for the multigraded case +function _derived_pushforward(M::FreeMod{T}, seq::Vector{T}) where {T <: RingElem} + return _derived_pushforward(ZeroDimensionalComplex(M), seq) +end + +function _derived_pushforward(M::SubquoModule{T}, seq::Vector{T}) where {T <: RingElem} + res, _ = free_resolution(Oscar.SimpleFreeResolution, M) + return _derived_pushforward(res, seq) + S = base_ring(M) + @assert all(parent(x) === S for x in seq) + n = length(seq) + Sd = graded_free_module(S, [0 for i in 1:n]) + v = sum(seq[i]*Sd[i] for i in 1:n; init=zero(Sd)) + kosz = koszul_complex(Oscar.KoszulComplex, v) + K = shift(Oscar.DegreeZeroComplex(kosz)[1:n+1], 1) + + res, _ = free_resolution(Oscar.SimpleFreeResolution, M) + KoM = hom(K, res) + tot = total_complex(KoM) + tot_simp = simplify(tot) + + G = grading_group(M) + st = strand(tot_simp, zero(G)) + return st[1] +end + +function _derived_pushforward(comp::AbsHyperComplex, seq::Vector{T}) where {T <: RingElem} + @assert dim(comp) <= 1 + @assert !isempty(seq) + S = parent(first(seq)) + @assert all(parent(x) === S for x in seq) + n = length(seq) + Sd = graded_free_module(S, [0 for i in 1:n]) + v = sum(seq[i]*Sd[i] for i in 1:n; init=zero(Sd)) + kosz = koszul_complex(Oscar.KoszulComplex, v) + K = shift(Oscar.DegreeZeroComplex(kosz)[1:n+1], 1) + + KoM = hom(K, comp) + tot = total_complex(KoM) + tot_simp = simplify(tot) + + G = grading_group(S) + st, _ = strand(tot_simp, zero(G)) + return simplify(st) +end + + +# Method for the standard graded case +function _derived_pushforward(M::SubquoModule, bound::Int) + S = base_ring(M) + n = ngens(S)-1 + Sd = graded_free_module(S, [0 for i in 1:ngens(S)]) - v = sum(x^d*Sd[i] for (i, x) in enumerate(gens(S)); init=zero(Sd)) + v = sum(x^bound*Sd[i] for (i, x) in enumerate(gens(S)); init=zero(Sd)) kosz = koszul_complex(Oscar.KoszulComplex, v) K = shift(Oscar.DegreeZeroComplex(kosz)[1:n+1], 1) @@ -79,7 +157,7 @@ function rank(phi::FreeModuleHom{FreeMod{T}, FreeMod{T}, Nothing}) where {T<:Fie end -_regularity_bound(F::FreeMod) = maximum(Int(degree(a; check=false)[1]) for a in gens(F)) +#_regularity_bound(F::FreeMod) = maximum(Int(degree(a; check=false)[1]) for a in gens(F)) @doc raw""" simplify(c::ComplexOfMorphisms{ChainType}) where {ChainType<:ModuleFP} @@ -172,3 +250,10 @@ function simplify(c::ComplexOfMorphisms{ChainType}) where {ChainType<:ModuleFP} return result, phi, psi end +function _vdim(M::SubquoModule) + F = ambient_free_module(M) + all(repres(x) in gens(F) for x in gens(M)) || return _vdim(presentation(M)[-1]) + + return Singular.vdim(Singular.std(singular_generators(M.quo.gens))) +end + diff --git a/src/Modules/Iterators.jl b/src/Modules/Iterators.jl index 0562f9a79162..e568e5fb25d0 100644 --- a/src/Modules/Iterators.jl +++ b/src/Modules/Iterators.jl @@ -1,26 +1,36 @@ ### Iteration over monomials in modules of a certain degree -struct AllModuleMonomials{ModuleType<:FreeMod} +struct AllModuleMonomials{ModuleType<:FreeMod, DegreeType<:Union{Int, FinGenAbGroupElem}} F::ModuleType - d::Int + d::DegreeType + # Cache for the exponent vectors of the `base_ring` of the module in degree `d` + # For multigradings we're using polymake and this doesn't return iterators. + # Since the computation is expensive, we don't want to do it over and over again. + exp_cache::Dict{FinGenAbGroupElem, Vector{Vector{Int}}} function AllModuleMonomials(F::FreeMod{T}, d::Int) where {T <: MPolyDecRingElem} is_graded(F) || error("module must be graded") S = base_ring(F) is_standard_graded(S) || error("iterator implemented only for the standard graded case") - return new{typeof(F)}(F, d) + return new{typeof(F), Int}(F, d) + end + function AllModuleMonomials(F::FreeMod{T}, d::FinGenAbGroupElem) where {T <: MPolyDecRingElem} + is_graded(F) || error("module must be graded") + S = base_ring(F) + is_zm_graded(S) || error("iterator implemented only for the ZZ^m-graded case") + return new{typeof(F), FinGenAbGroupElem}(F, d, Dict{FinGenAbGroupElem, Vector{Vector{Int}}}()) end end underlying_module(amm::AllModuleMonomials) = amm.F degree(amm::AllModuleMonomials) = amm.d -function all_monomials(F::FreeMod{T}, d::Int) where {T<:MPolyDecRingElem} +function all_monomials(F::FreeMod{T}, d::Union{Int, FinGenAbGroupElem}) where {T<:MPolyDecRingElem} return AllModuleMonomials(F, d) end -Base.eltype(::Type{AllModuleMonomials{T}}) where {T} = elem_type(T) +Base.eltype(::Type{<:AllModuleMonomials{T}}) where {T} = elem_type(T) -function Base.length(amm::AllModuleMonomials) +function Base.length(amm::AllModuleMonomials{<:FreeMod, Int}) F = underlying_module(amm) r = rank(F) R = base_ring(F) @@ -34,7 +44,25 @@ function Base.length(amm::AllModuleMonomials) return result end -function Base.iterate(amm::AllModuleMonomials, state::Nothing = nothing) +function Base.length(amm::AllModuleMonomials{<:FreeMod, FinGenAbGroupElem}) + F = underlying_module(amm) + r = rank(F) + R = base_ring(F) + d = degree(amm)::FinGenAbGroupElem + result = 0 + for i in 1:r + d_loc = d - degree(F[i]; check=false) + any(Int(d_loc[i]) < 0 for i in 1:ngens(parent(d)))&& continue + if !haskey(amm.exp_cache, d_loc) + amm.exp_cache[d_loc] = all_exponents(R, d_loc) + end + exps = amm.exp_cache[d_loc] + result = result + length(exps) + end + return result +end + +function Base.iterate(amm::AllModuleMonomials{<:FreeMod, Int}, state::Nothing = nothing) i = 1 F = underlying_module(amm) d = degree(amm) @@ -52,7 +80,10 @@ function Base.iterate(amm::AllModuleMonomials, state::Nothing = nothing) return x*F[i], (i, mon_it, s) end -function Base.iterate(amm::AllModuleMonomials, state::Tuple{Int, AllMonomials, Vector{Int}}) +function Base.iterate( + amm::AllModuleMonomials{<:FreeMod, Int}, + state::Tuple{Int, AllMonomials, Vector{Int}} + ) F = underlying_module(amm) d = degree(amm) R = base_ring(F) @@ -76,29 +107,98 @@ function Base.iterate(amm::AllModuleMonomials, state::Tuple{Int, AllMonomials, V return x*F[i], (i, mon_it, s) end +function Base.iterate(amm::AllModuleMonomials{<:FreeMod, FinGenAbGroupElem}, state::Nothing = nothing) + i = 1 + F = underlying_module(amm) + d = degree(amm)::FinGenAbGroupElem + R = base_ring(F) + + i = findfirst(i -> all(d[k] >= degree(F[i]; check=false)[k] for k in 1:ngens(parent(d))), 1:ngens(F)) + i === nothing && return nothing + d_loc = d - degree(F[i]; check=false) + + if !haskey(amm.exp_cache, d_loc) + amm.exp_cache[d_loc] = all_exponents(R, d_loc) + end + + exps = amm.exp_cache[d_loc] + res = iterate(exps) + res === nothing && i == ngens(F) && return nothing + + e, s = res + poly_ctx = MPolyBuildCtx(R) + push_term!(poly_ctx, one(coefficient_ring(R)), e) + return (finish(poly_ctx)*F[i])::elem_type(F), (i, exps, s) +end + +function Base.iterate( + amm::AllModuleMonomials{<:FreeMod, FinGenAbGroupElem}, + state::Tuple{Int, Vector{Vector{Int}}, Int} + ) + F = underlying_module(amm) + d = degree(amm) + R = base_ring(F) + + i, exps, s = state + res = iterate(exps, s) + + if res === nothing + # Monomials have been exhausted for this component of the module; + # move on to the next one if any + i = findnext(i -> all(d[k] >= degree(F[i]; check=false)[k] for k in 1:ngens(parent(d))), 1:ngens(F), i+1) + i === nothing && return nothing + d_loc = d - degree(F[i]; check=false) + + if !haskey(amm.exp_cache, d_loc) + amm.exp_cache[d_loc] = all_exponents(R, d_loc) + end + + exps = amm.exp_cache[d_loc] + res_loc = iterate(exps) + res_loc === nothing && i == ngens(F) && return nothing + + e, s = res_loc + poly_ctx = MPolyBuildCtx(R) + push_term!(poly_ctx, one(coefficient_ring(R)), e) + return finish(poly_ctx)*F[i], (i, exps, s) + end + + e, s = res + poly_ctx = MPolyBuildCtx(R) + push_term!(poly_ctx, one(coefficient_ring(R)), e) + return (finish(poly_ctx)*F[i])::elem_type(F), (i, exps, s) +end + ### The same for module exponents -struct AllModuleExponents{ModuleType<:FreeMod} +struct AllModuleExponents{ModuleType<:FreeMod, DegreeType<:Union{Int, FinGenAbGroupElem}} F::ModuleType - d::Int + d::DegreeType + exp_cache::Dict{FinGenAbGroupElem, Vector{Vector{Int}}} function AllModuleExponents(F::FreeMod{T}, d::Int) where {T <: MPolyDecRingElem} is_graded(F) || error("module must be graded") S = base_ring(F) is_standard_graded(S) || error("iterator implemented only for the standard graded case") - return new{typeof(F)}(F, d) + return new{typeof(F), Int}(F, d) + end + function AllModuleExponents(F::FreeMod{T}, d::FinGenAbGroupElem) where {T <: MPolyDecRingElem} + is_graded(F) || error("module must be graded") + S = base_ring(F) + is_zm_graded(S) || error("iterator implemented only for the ZZ^m-graded case") + return new{typeof(F), FinGenAbGroupElem}(F, d, Dict{FinGenAbGroupElem, Vector{Vector{Int}}}()) end end underlying_module(amm::AllModuleExponents) = amm.F degree(amm::AllModuleExponents) = amm.d -function all_exponents(F::FreeMod{T}, d::Int) where {T<:MPolyDecRingElem} +function all_exponents(F::FreeMod{T}, d::Union{Int, FinGenAbGroupElem}) where {T<:MPolyDecRingElem} return AllModuleExponents(F, d) end -Base.eltype(::Type{AllModuleExponents{T}}) where {T} = Tuple{Vector{Int}, Int} +Base.eltype(::Type{<:AllModuleExponents{T}}) where {T} = Tuple{Vector{Int}, Int} -function Base.length(amm::AllModuleExponents) +function Base.length(amm::AllModuleExponents{<:FreeMod, Int}) F = underlying_module(amm) r = rank(F) R = base_ring(F) @@ -113,7 +213,7 @@ function Base.length(amm::AllModuleExponents) return result end -function Base.iterate(amm::AllModuleExponents, state::Nothing = nothing) +function Base.iterate(amm::AllModuleExponents{<:FreeMod, Int}, state::Nothing = nothing) i = 1 F = underlying_module(amm) d = degree(amm) @@ -132,7 +232,7 @@ function Base.iterate(amm::AllModuleExponents, state::Nothing = nothing) return (data(e), i), (i, exp_it, s) end -function Base.iterate(amm::AllModuleExponents, state::Tuple{Int, WeakCompositions{Int}, Vector{Int}}) +function Base.iterate(amm::AllModuleExponents{<:FreeMod, Int}, state::Tuple{Int, WeakCompositions{Int}, Vector{Int}}) F = underlying_module(amm) d = degree(amm) R = base_ring(F) @@ -157,6 +257,80 @@ function Base.iterate(amm::AllModuleExponents, state::Tuple{Int, WeakComposition return (data(e), i), (i, exp_it, s) end +function Base.length(amm::AllModuleExponents{<:FreeMod, FinGenAbGroupElem}) + F = underlying_module(amm) + r = rank(F) + R = base_ring(F) + d = degree(amm)::FinGenAbGroupElem + result = 0 + for i in 1:r + d_loc = d - degree(F[i]; check=false) + any(Int(d_loc[i]) < 0 for i in 1:ngens(parent(d)))&& continue + if !haskey(amm.exp_cache, d_loc) + amm.exp_cache[d_loc] = all_exponents(R, d_loc) + end + exps = amm.exp_cache[d_loc] + result = result + length(exps) + end + return result +end + +function Base.iterate(amm::AllModuleExponents{<:FreeMod, FinGenAbGroupElem}, state::Nothing = nothing) + i = 1 + F = underlying_module(amm) + d = degree(amm)::FinGenAbGroupElem + R = base_ring(F) + + i = findfirst(i -> all(d[k] >= degree(F[i]; check=false)[k] for k in 1:ngens(parent(d))), 1:ngens(F)) + i === nothing && return nothing + d_loc = d - degree(F[i]; check=false) + + if !haskey(amm.exp_cache, d_loc) + amm.exp_cache[d_loc] = all_exponents(R, d_loc) + end + + exps = amm.exp_cache[d_loc] + res = iterate(exps) + res === nothing && i == ngens(F) && return nothing + + e, s = res + return (e, i), (i, exps, s) +end + +function Base.iterate( + amm::AllModuleExponents{<:FreeMod, FinGenAbGroupElem}, + state::Tuple{Int, Vector{Vector{Int}}, Int} + ) + F = underlying_module(amm) + d = degree(amm) + R = base_ring(F) + + i, exps, s = state + res = iterate(exps, s) + + if res === nothing + # Monomials have been exhausted for this component of the module; + # move on to the next one if any + i = findnext(i -> all(d[k] >= degree(F[i]; check=false)[k] for k in 1:ngens(parent(d))), 1:ngens(F), i+1) + i === nothing && return nothing + d_loc = d - degree(F[i]; check=false) + + if !haskey(amm.exp_cache, d_loc) + amm.exp_cache[d_loc] = all_exponents(R, d_loc) + end + + exps = amm.exp_cache[d_loc] + res_loc = iterate(exps) + res_loc === nothing && i == ngens(F) && return nothing + + e, s = res_loc + return (e, i), (i, exps, s) + end + + e, s = res + return (e, i), (i, exps, s) +end + ### Iteration over monomials in Subquos # #= Disabled for the moment and continued soon (10.1.2024) diff --git a/src/Modules/UngradedModules/HomologicalAlgebra.jl b/src/Modules/UngradedModules/HomologicalAlgebra.jl index 21a77b1d3286..2bb3937f1fa4 100644 --- a/src/Modules/UngradedModules/HomologicalAlgebra.jl +++ b/src/Modules/UngradedModules/HomologicalAlgebra.jl @@ -79,7 +79,7 @@ function hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{<:ModuleFPHom{<:ModuleFP res = pure_N(image_as_tuple) return res end - return hom(M, N, Vector{elem_type(N)}(map(map_gen, gens(M)))) + return hom(M, N, Vector{elem_type(N)}(map(map_gen, gens(M))); check=false) end # the case of a non-trivial base change diff --git a/src/Modules/UngradedModules/Methods.jl b/src/Modules/UngradedModules/Methods.jl index 822b9036ea85..d12f52eee88b 100644 --- a/src/Modules/UngradedModules/Methods.jl +++ b/src/Modules/UngradedModules/Methods.jl @@ -379,7 +379,7 @@ end function change_base_ring(S::Ring, F::FreeMod) R = base_ring(F) r = ngens(F) - FS = FreeMod(S, F.S) # the symbols of F + FS = is_graded(F) ? graded_free_module(S, degrees_of_generators(F)) : FreeMod(S, F.S) # the symbols of F map = hom(F, FS, gens(FS), MapFromFunc(R, S, S)) return FS, map end @@ -388,7 +388,7 @@ function change_base_ring(f::Map{DomType, CodType}, F::FreeMod) where {DomType<: domain(f) == base_ring(F) || error("ring map not compatible with the module") S = codomain(f) r = ngens(F) - FS = FreeMod(S, F.S) + FS = is_graded(F) ? graded_free_module(S, degrees_of_generators(F)) : FreeMod(S, F.S) # the symbols of F map = hom(F, FS, gens(FS), f) return FS, map end diff --git a/src/Modules/UngradedModules/Presentation.jl b/src/Modules/UngradedModules/Presentation.jl index dd04827f498c..f15139544efb 100644 --- a/src/Modules/UngradedModules/Presentation.jl +++ b/src/Modules/UngradedModules/Presentation.jl @@ -95,6 +95,7 @@ function presentation(SQ::SubquoModule; end function _presentation_graded(SQ::SubquoModule) + #any(iszero(a) for a in gens(SQ)) && error("generators must not be zero for presentations in the graded case") R = base_ring(SQ) # Create the free module for the presentation From a77ea15592b23c5bf2f47520a95e419c2eae9f21 Mon Sep 17 00:00:00 2001 From: Matthias Zach <85350711+HechtiDerLachs@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:28:54 +0100 Subject: [PATCH 2/7] Update experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl Co-authored-by: Benjamin Lorenz --- .../src/Objects/eagon_northcott_complex.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl b/experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl index 728fdce60776..6c5e20546999 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/eagon_northcott_complex.jl @@ -85,7 +85,7 @@ function (fac::ENCMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) is_zero(e) && continue beta = copy(alpha) beta[k] -= 1 - aa = cod_sym_power[findfirst(gamma==beta for gamma in cfac.exps[i-1])] + aa = cod_sym_power[findfirst(==(beta), cfac.exps[i-1])] bb = _contract(b, A[k, :]; parent=cod_ext_power) img_gen = img_gen + tensor_pure_function(cod)((aa, bb)) end From da2e9670556dd3f14c583b89b4e8b12c27c1f018 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 26 Nov 2024 21:42:40 +0100 Subject: [PATCH 3/7] Add tests for iterating over monomials in modules. --- test/Modules/Iterators.jl | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/Modules/Iterators.jl diff --git a/test/Modules/Iterators.jl b/test/Modules/Iterators.jl new file mode 100644 index 000000000000..98f59959110a --- /dev/null +++ b/test/Modules/Iterators.jl @@ -0,0 +1,8 @@ +@testset "iterators over monomials in modules" begin + IP1 = projective_space(NormalToricVariety, 1) + X = IP1*IP1 + S = cox_ring(X) + G = grading_group(S) + F = graded_free_module(S, [zero(G), G[1], 3*G[2]]) + @test length(collect(Oscar.all_monomials(F, 7*G[2]+3*G[1]))) == 76 +end From 447b7b0680191886b58a126ad32176cbafe150ba Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 26 Nov 2024 21:47:00 +0100 Subject: [PATCH 4/7] Add tests for exponents. --- test/Modules/Iterators.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Modules/Iterators.jl b/test/Modules/Iterators.jl index 98f59959110a..cabc4bd1c10d 100644 --- a/test/Modules/Iterators.jl +++ b/test/Modules/Iterators.jl @@ -5,4 +5,5 @@ G = grading_group(S) F = graded_free_module(S, [zero(G), G[1], 3*G[2]]) @test length(collect(Oscar.all_monomials(F, 7*G[2]+3*G[1]))) == 76 + @test length(collect(Oscar.all_exponents(F, 7*G[2]+3*G[1]))) == 76 end From ec83b05330fb7d2fc78e0c8f1a9af1a5d72c0c77 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 26 Nov 2024 22:08:25 +0100 Subject: [PATCH 5/7] Add tests from the paper. --- .../test/LeGreuelFormulaOnStratifiedSpaces.jl | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl diff --git a/experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl b/experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl new file mode 100644 index 000000000000..f320eaa21c64 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl @@ -0,0 +1,164 @@ +######################################################################## +# This file contains the code to compute Examples 5.1 and 5.2 from +# arXiv:2411.02682. +# +# Above that it serves to test the strand complexes for multigraded +# rings and modules and the Eagon-Northcott complexes. +######################################################################## + +@testset "Example 5.1" begin + # We create a ring with the variables of the ambient space and the parameters. + P, a = QQ[vcat(["a_{$(i), $j}" for i in 1:2 for j in 1:2], [:x, :y, :z, :w])...] + a = a[1:4] + (x, y, z, w) = gens(P)[5:end] + + # We create a multigraded ring for IP^1 x IP^1 over that space. + S, _ = P[:s, :t, :u, :v] + S, (s, t, u, v) = grade(S, [[1, 0], [1, 0], [0, 1], [0, 1]]) + + # We set up graded modules to represent the required bundles. + Z2 = grading_group(S) + S2 = graded_free_module(S, [zero(Z2) for _ in 1:2]) + S2x2 = tensor_product(S2, S2); + tm = Oscar.tensor_pure_function(S2x2) + + A = S[a[1] a[2] a[3] a[4]] + enc = Oscar.EagonNorthcottComplex(A; F=S2x2); + + T1 = s*S2[1] + t*S2[2] # tautological bundle of the first factor + T2 = u*S2[1] + v*S2[2] # tautological bundle of the second factor + # the sub-bundle serving as the Nash-bundle for the generic determinantal variety + T, inc = sub(S2x2, [tm(T1, S2[1]), tm(T1, S2[2]), tm(S2[1], T2), tm(S2[2], T2)]) + + # We compute the induced Eagon-Northcott complex on the sub-bundle + rest = Oscar.InducedENC(enc, T); + # and a free resolution thereof. + res = Oscar.CartanEilenbergResolution(rest); + tot_res = total_complex(res); + tot_res_simp = simplify(tot_res); + + # equations for the Nash transform + S1 = graded_free_module(S, [zero(Z2)]) + M1 = S[s x y; t z w] + I1 = ideal(S, minors(M1, 2)) + M2 = S[u v; x y; z w] + I2 = ideal(S, minors(M2, 2)) + + J, inc = (I1 + I2)*S1 + OX = cokernel(inc) + OX_res, _ = free_resolution(Oscar.SimpleFreeResolution, OX); + + # restriction of the resolution of the Nash bundle + all_res = tensor_product(OX_res, tot_res_simp); + tot = simplify(total_complex(all_res)); + + tot_0 = Oscar.StrandComplex(tot, zero(Z2)) + + # To compute the direct image we need to know where to truncate. + bnd = Oscar._regularity_bound(tot, 0:10) + + k = Int(bnd[1]) - 1 + irr = ideal(S, [s^k, t^k]) + + k = Int(bnd[2]) - 1 + irr = irr*ideal(S, [u^k, v^k]) + + # the generic direct image complex + coh = Oscar._derived_pushforward(tot, gens(irr)); + + # We substitute for a specific function. + R, (x, y, z, w) = QQ[:x, :y, :z, :w] + k = 5 + l = [-k*x^(k-1), 0, 0, 1] + subs = hom(P, R, vcat(R.(l), gens(R))) + + tot_0_subs, _ = change_base_ring(subs, tot_0); + subs_coh, _ = change_base_ring(subs, coh); + @time H0, _ = simplify(homology(subs_coh, 0)[1]); + @test vector_space_dimension(H0) == 4 + + R, (x, y, z, w) = QQ[:x, :y, :z, :w] + k = 5 + l = [1, 0, 0, 1] + subs = hom(P, R, vcat(R.(l), gens(R))) + subs_coh, _ = change_base_ring(subs, coh); + @test all(is_zero(homology(subs_coh, i)[1]) for i in 0:5) +end + +@testset "Example 5.2" begin + # All steps similar to the above. + P, v = QQ[vcat(["a_{$(i), $j}" for i in 1:2 for j in 1:2], ["b_{$(i), $j}" for i in 1:2 for j in 1:2], ["c"], [:x, :y, :z, :w])...] + a = v[1:4] + b = v[5:8] + c = v[9] + (x, y, z, w) = gens(P)[10:end] + S, _ = P[:s, :t, :u, :v] + S, (s, t, u, v) = grade(S, [[1, 0], [1, 0], [0, 1], [0, 1]]) + + Z2 = grading_group(S) + S2 = graded_free_module(S, [zero(Z2) for _ in 1:2]) + S2x2 = tensor_product(S2, S2); + tm = Oscar.tensor_pure_function(S2x2) + + A = S[a[1] a[2] a[3] a[4]; b[1] b[2] b[3] b[4]] + enc = Oscar.EagonNorthcottComplex(A; F=S2x2); + + T1 = s*S2[1] + t*S2[2] + T2 = u*S2[1] + v*S2[2] + T, inc = sub(S2x2, [tm(T1, S2[1]), tm(T1, S2[2]), tm(S2[1], T2), tm(S2[2], T2)]) + + rest = Oscar.InducedENC(enc, T); + res = Oscar.CartanEilenbergResolution(rest); + tot_res = total_complex(res); + tot_res_simp = simplify(tot_res); + + S1 = graded_free_module(S, [zero(Z2)]) + C = c*S1[1] + Kc = koszul_complex(Oscar.KoszulComplex, C); + K = tensor_product(Kc, tot_res_simp); + + tot = simplify(total_complex(K)); + + S1 = graded_free_module(S, [zero(Z2)]) + M1 = S[s x y; t z w] + I1 = ideal(S, minors(M1, 2)) + M2 = S[u v; x y; z w] + I2 = ideal(S, minors(M2, 2)) + + J, inc = (I1 + I2)*S1 + OX = cokernel(inc) + OX_res, _ = free_resolution(Oscar.SimpleFreeResolution, OX); + all_res = tensor_product(OX_res, tot); + tot = simplify(total_complex(all_res)); + + bnd = Oscar._regularity_bound(tot, 0:10) + + k = Int(bnd[1]) - 1 + irr = ideal(S, [s^k, t^k]) + + k = Int(bnd[2]) - 1 + irr = irr*ideal(S, [u^k, v^k]) + + coh = Oscar._derived_pushforward(tot, gens(irr)); + + R, (x, y, z, w) = QQ[:x, :y, :z, :w] + k = 5 + c = [y - z] + a = [0, 1, -1, 0] + b = [-k*x^(k-1), 0, 0, 1] + subs = hom(P, R, vcat(R.(a), R.(b), R.(c), gens(R))) + subs_coh, _ = change_base_ring(subs, coh); + @time H0, _ = simplify(homology(subs_coh, 0)[1]); + @test vector_space_dimension(H0) == 6 + + R, (x, y, z, w) = QQ[:x, :y, :z, :w] + k = 5 + c = [y - z] + a = [0, 1, -1, 0] + b = [-1, 0, 0, 1] + subs = hom(P, R, vcat(R.(a), R.(b), R.(c), gens(R))) + subs_coh, _ = change_base_ring(subs, coh); + @time H0, _ = simplify(homology(subs_coh, 0)[1]); + @test vector_space_dimension(H0) == 2 +end + From e05578c1238e53ef695244af427c3412e76cfb3f Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Wed, 27 Nov 2024 14:25:19 +0100 Subject: [PATCH 6/7] Disable some tests for the sake of CI-timings. --- .../test/LeGreuelFormulaOnStratifiedSpaces.jl | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl b/experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl index f320eaa21c64..7d1d302512b1 100644 --- a/experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl +++ b/experimental/DoubleAndHyperComplexes/test/LeGreuelFormulaOnStratifiedSpaces.jl @@ -65,7 +65,14 @@ # the generic direct image complex coh = Oscar._derived_pushforward(tot, gens(irr)); - + + @test [ngens(coh.original_complex[i]) for i in -4:10] == [0, 169, 1817, 8412, 22848, 40967, 51448, 46849, 31338, 15027, 4749, 830, 54, 0, 0] + + #= The code below takes too long for the CI. + # It is kept here for the purpose of reproducibility of the paper's results. + + @test [ngens(coh[i]) for i in 11:-1:-2] == [0, 0, 0, 0, 0, 0, 0, 1, 9, 16, 9, 1, 0, 0] + # We substitute for a specific function. R, (x, y, z, w) = QQ[:x, :y, :z, :w] k = 5 @@ -83,8 +90,15 @@ subs = hom(P, R, vcat(R.(l), gens(R))) subs_coh, _ = change_base_ring(subs, coh); @test all(is_zero(homology(subs_coh, i)[1]) for i in 0:5) + + =# end +#= The following tests are disabled to cut down on cost for CI. +# +# We keep them here to test against regression and for the purpose of +# accessibility of code for reproduction of the paper's results +# in accordance with the MarDi principles. @testset "Example 5.2" begin # All steps similar to the above. P, v = QQ[vcat(["a_{$(i), $j}" for i in 1:2 for j in 1:2], ["b_{$(i), $j}" for i in 1:2 for j in 1:2], ["c"], [:x, :y, :z, :w])...] @@ -161,4 +175,5 @@ end @time H0, _ = simplify(homology(subs_coh, 0)[1]); @test vector_space_dimension(H0) == 2 end +=# From ed2cf5aa153f55c27ed41e1376d769ddcf06ed14 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Wed, 27 Nov 2024 14:33:17 +0100 Subject: [PATCH 7/7] Add some extra tests for the homogeneous Koszul complexes. --- .../DoubleAndHyperComplexes/test/koszul_complexes.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/experimental/DoubleAndHyperComplexes/test/koszul_complexes.jl b/experimental/DoubleAndHyperComplexes/test/koszul_complexes.jl index 103d79b8d2ec..87ae4cae9995 100644 --- a/experimental/DoubleAndHyperComplexes/test/koszul_complexes.jl +++ b/experimental/DoubleAndHyperComplexes/test/koszul_complexes.jl @@ -49,3 +49,13 @@ end @test compose(f[3], map(KG, 2)) == compose(map(KF, 3), f[2]) @test compose(f[2], map(KG, 1)) == compose(map(KF, 2), f[1]) end + +@testset "homogeneous Koszul complexes" begin + R, (x, y) = graded_polynomial_ring(QQ, [:x, :y]) + kosz = Oscar.HomogKoszulComplex(R, [x, y]) + @test [ngens(kosz[i]) for i in 0:2] == [1, 2, 1] + G = grading_group(R) + @test [degrees_of_generators(kosz[i]) for i in 0:2] == [[zero(G)], [-G[1], -G[1]], [-2*G[1]]] + [map(kosz, i) for i in 0:1] +end +