Skip to content

Commit

Permalink
Eagon northcott complex (#4327)
Browse files Browse the repository at this point in the history
 Work on hypercomplexes:
    strands of complexes of graded modules for ZZ^m-gradings
    Eagon-Northcott complexes
  • Loading branch information
HechtiDerLachs authored Jan 10, 2025
1 parent 8dd3b60 commit dd36f4f
Show file tree
Hide file tree
Showing 23 changed files with 1,074 additions and 145 deletions.
10 changes: 10 additions & 0 deletions docs/oscar_references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,16 @@ @Article{EMSS16
zbmath = {6517845}
}

@Article{EN62,
author = {Eagon, J. and Northcott, D.G.},
title = {Ideals defined by matrices, and a certain complex associated to them},
journal = {Proc. Royal Soc.},
volume = {269},
pages = {188--204},
year = {1962},
doi = {10.1098/rspa.1962.0170}
}

@Article{ES96,
author = {Eisenbud, David and Sturmfels, Bernd},
title = {Binomial ideals},
Expand Down
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,9 @@ function (fac::SimplifiedChainFactory)(d::AbsHyperComplex, Ind::Tuple)
for i in 1:m
w = Sinv[i]
v = zero(new_dom)
for j in 1:length(I)
a = w[I[j]]
!iszero(a) && (v += a*new_dom[j])
for (j, ind) in enumerate(I)
success, a = _has_index(w, ind)
success && (v += a*new_dom[j])
end
push!(img_gens_dom, v)
end
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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)))
# 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(AbstractAlgebra.coefficients(b), AbstractAlgebra.exponent_vectors(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


# 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(ZZ, 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

102 changes: 7 additions & 95 deletions experimental/DoubleAndHyperComplexes/src/Morphisms/strands.jl
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
=#
31 changes: 31 additions & 0 deletions experimental/DoubleAndHyperComplexes/src/Objects/Methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,34 @@ 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.
# Warning: If the user provides their own parent, they need to make sure that things
# are compatible. As this is an internal function, no sanity checks are done.
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


Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
Loading

0 comments on commit dd36f4f

Please sign in to comment.