From 1d039b7533690327e753c83a5b59a8dd40133764 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 2 Aug 2024 15:34:06 +0200 Subject: [PATCH 01/10] Allow zero dimensional complexes. --- experimental/DoubleAndHyperComplexes/src/Objects/Types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl b/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl index 3c08eea07bf5..a6f4b235b873 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/Types.jl @@ -93,7 +93,7 @@ end upper_bounds::Vector=[nothing for i in 1:d], lower_bounds::Vector=[nothing for i in 1:d] ) where {ChainType, MorphismType} - @assert d > 0 "can not create zero or negative dimensional hypercomplex" + @assert d >= 0 "can not create negative dimensional hypercomplex" chains = Dict{Tuple, ChainType}() morphisms = Dict{Tuple, Dict{Int, <:MorphismType}}() return new{ChainType, MorphismType}(d, chains, morphisms, From ab26ebf3d321f8afffede7c9f2691f7b51db2524 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 14:28:52 +0200 Subject: [PATCH 02/10] Raw implementation of CA resolutions. --- .../src/DoubleAndHyperComplexes.jl | 2 + .../src/Morphisms/Types.jl | 20 ++ .../Morphisms/cartan_eilenberg_resolutions.jl | 23 +- .../Objects/cartan_eilenberg_resolution.jl | 196 ++++++++++++++++++ 4 files changed, 222 insertions(+), 19 deletions(-) create mode 100644 experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl diff --git a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl index 709dd1973a20..b554a991c419 100644 --- a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl @@ -9,6 +9,7 @@ include("Objects/koszul_complexes.jl") include("Objects/degree_zero_complexes.jl") include("Objects/new_complex_template.jl") include("Objects/linear_strands.jl") +include("Objects/cartan_eilenberg_resolutions.jl" include("Morphisms/Types.jl") include("Morphisms/cartan_eilenberg_resolutions.jl") @@ -25,3 +26,4 @@ include("Exports.jl") include("base_change_types.jl") include("base_change.jl") + diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl index 9e5a5c1ac05b..0551379623d6 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl @@ -142,3 +142,23 @@ end underlying_complex(c::SimpleFreeResolution) = c.underlying_complex original_module(c::SimpleFreeResolution) = c.M +### Lifting morphisms through projective resolutions +mutable struct MapLifter{MorphismType} <: HyperComplexMorphismFactory{MorphismType} + dom::AbsHyperComplex + cod::AbsHyperComplex + orig_map::Map + start_index::Int + offset::Int + check::Bool + + function MapLifter(::Type{MorphismType}, dom::AbsHyperComplex, cod::AbsHyperComplex, phi::Map; + start_index::Int=0, offset::Int=0, check::Bool=true + ) where {MorphismType} + @assert dim(dom) == 1 && dim(cod) == 1 "lifting of maps is only implemented in dimension one" + @assert (is_chain_complex(dom) && is_chain_complex(cod)) || (is_cochain_complex(dom) && is_cochain_complex(cod)) + @assert domain(phi) === dom[start_index] + @assert codomain(phi) === cod[start_index + offset] + return new{MorphismType}(dom, cod, phi, start_index, offset) + end +end + diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl index 9dbf5a1078dd..1823bdf37c54 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl @@ -1,22 +1,4 @@ -mutable struct MapLifter{MorphismType} <: HyperComplexMorphismFactory{MorphismType} - dom::AbsHyperComplex - cod::AbsHyperComplex - orig_map::Map - start_index::Int - offset::Int - check::Bool - - function MapLifter(::Type{MorphismType}, dom::AbsHyperComplex, cod::AbsHyperComplex, phi::Map; - start_index::Int=0, offset::Int=0, check::Bool=true - ) where {MorphismType} - @assert dim(dom) == 1 && dim(cod) == 1 "lifting of maps is only implemented in dimension one" - @assert (is_chain_complex(dom) && is_chain_complex(cod)) || (is_cochain_complex(dom) && is_cochain_complex(cod)) - @assert domain(phi) === dom[start_index] - @assert codomain(phi) === cod[start_index + offset] - return new{MorphismType}(dom, cod, phi, start_index, offset) - end -end - +### Lifting maps through projective resolutions is_chain_complex(c::AbsHyperComplex) = (dim(c) == 1 ? direction(c, 1) == (:chain) : error("complex is not one-dimensional")) is_cochain_complex(c::AbsHyperComplex) = (dim(c) == 1 ? direction(c, 1) == (:cochain) : error("complex is not one-dimensional")) @@ -57,3 +39,6 @@ function lift_map(dom::AbsHyperComplex{DomChainType}, cod::AbsHyperComplex{CodCh end morphism_type(::Type{T1}, ::Type{T2}) where {T1<:ModuleFP, T2<:ModuleFP} = ModuleFPHom{<:T1, <:T2} + +### Cartan Eilenberg resolutions + diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl new file mode 100644 index 000000000000..3570bd53a3d8 --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -0,0 +1,196 @@ +#= Cartan Eilenberg resolutions of 1-dimensional complexes +# +# Suppose +# +# 0 ← C₀ ← C₁ ← C₂ ← … +# +# is a bounded below complex. We compute a double complex +# +# 0 ← P₀₀ ← P₀₁ ← P₀₂ ← … +# ↑ ↑ ↑ +# 0 ← P₁₀ ← P₁₁ ← P₁₂ ← … +# ↑ ↑ ↑ +# 0 ← P₂₀ ← P₂₁ ← P₂₂ ← … +# ↑ ↑ ↑ +# ⋮ ⋮ ⋮ +# +# which is quasi-isomorphic to C via some augmentation map ε. +# +# The challenge is that if we were only computing resolutions of the Cᵢ's +# and lifting the maps, then the rows of the resulting diagrams would +# not necessarily form complexes. To accomplish that, we split the original +# complex into short exact sequences +# +# 0 ← Bᵢ ← Cᵢ ← Zᵢ ← 0 +# +# and apply the Horse shoe lemma to these. Together with the induced maps +# from Bᵢ ↪ Zᵢ₋₁ we get the desired double complex. +# +# If the original complex C is known to be exact, then there is no need +# to compute the resolutions of both Bᵢ and Zᵢ and we can shorten the procedure. +=# +### Production of the chains +struct CEChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} + c::AbsHyperComplex + is_exact::Bool + kernel_resolutions::Dict{Int, <:AbsHyperComplex} # the kernels of Cᵢ → Cᵢ₋₁ + boundary_resolutions::Dict{Int, <:AbsHyperComplex} # the boundaries of Cᵢ₊₁ → Cᵢ + induced_maps::Dict{Int, <:AbsHyperComplexMorphism} # the induced maps from the free + # resolutions of the boundary and kernel + + function CEChainFactory(c::AbsHyperComplex; is_exact::Bool=false) + @assert dim(c) == 1 "complex must be 1-dimensional" + #@assert has_lower_bound(c, 1) "complex must be bounded from below" + return new{chain_type(c)}(c, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}()) + end +end + +function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) + (i, j) = I + + if iszero(j) + return fac.c[i] + end + + res_Z = getindex!(c.kernel_resolutions, i) do + Z, _ = kernel(fac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + + if can_compute_map(fac.c, 1, (i,)) + if fac.isexact + res_B = getindex!(c.kernel_resolutions, i-1) do + B, _ = kernel(fac.c, i-1) + return free_resolution(SimpleFreeResolution, B) + end + return direct_sum(res_B[j], res_Z[j]) + else + res_B = getindex!(c.boundary_resolutions, i-1) do + B, _ = boundary(fac.c, i-1) + return free_resolution(SimpleFreeResolution, B) + end + return direct_sum(res_B[j], res_Z[j]) + end + end + return res_Z[j] +end + +function can_compute(fac::CEChainFactory, self::AbsHyperComplex, I::Tuple) + (i, j) = I + can_compute_index(fac.c, (i,)) || return false + return j >= 0 +end + +### Production of the morphisms +struct CEMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} +end + +function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) + (i, j) = I + cfac = chain_factory(self) + if p == 1 + j == 0 && return map(cfac.c, 1, (i,)) + elseif p == 2 + if can_compute_map(cfac.c, 1, (i,)) + B_plus_Z = self[I] + C = cfac.c[(i,)] + B, pr_B = boundary(cfac.c, 1, (i-1,)) + if cfac.is_exact + # use the kernel instead; we have to adjust the map + Z, inc_Z = kernel(cfac.c, 1, (i-1,)) + B = Z + phi = map(cfac.c, 1, (i,)) + img_gens = [preimage(inc_Z, phi(g)) for g in gens(C)] + pr_B = hom(C, B, img_gens; check=true) # TODO: Set to false + end + Z, inc_Z = kernel(cfac.c, 1, (i,)) + @assert domain(pr_B) === C + @assert codomain(inc_Z) === C + if j == 1 + img_gens = elem_type(C)[preimage(pr_B, g) for g in gens(B)] + img_gens = vcat(img_gens, elem_type(C)[inc_Z(g) for g in gens(Z)]) + return hom(B_plus_Z, C, img_gens; check=true) # TODO: Set to false + else + # may assume j > 1 + cod = self[(i, j-1)] + res_Z = getindex!(cfac.kernel_resolutions, i) do + Z, _ = kernel(fac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + b = map(res_Z, 1, (j,)) + inc2 = canonical_injection(cod, 2) + @assert domain(inc2) === res_Z[j-1] === codomain(b) + @assert codomain(inc2) === cod + + if cfac.isexact + res_Z1 = getindex!(cfac.kernel_resolutions, i-1) do + Z, _ = kernel(cfac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + a = map(res_Z1, 1, (j,)) + inc1 = canonical_injection(cod, 1) + @assert domain(inc1) === res_Z1[j-1] === codomain(a) + @assert codomain(inc1) === cod + img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] + img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) + return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + else + res_B = getindex!(cfac.boundary_resolutions, i-1) do + B, _ = boundary(cfac.c, i) + return free_resolution(SimpleFreeResolution, B) + end + a = map(res_B, 1, (j,)) + inc1 = canonical_injection(cod, 1) + @assert domain(inc1) === res_B[j-1] === codomain(a) + @assert codomain(inc1) === cod + img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] + img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) + return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + end + error("execution should never reach this point") + end + else + res = getindex!(cfac.kernel_resolutions, i) do + Z, _ = kernel(fac.c, i) + return free_resolution(SimpleFreeResolution, Z) + end + return map(res, 1, (j,)) + end + end + error("direction $p out of bounds") +end + +function can_compute(fac::CEMapFactory, self::AbsHyperComplex, p::Int, I::Tuple) + (i, j) = I + if p == 1 + return can_compute_map(chain_factory(self).c, 1, (i-1,)) && j >= 0 + elseif p == 2 + return can_compute_index(self, I) && j > 0 + end + return false +end + +### The concrete struct +@attributes mutable struct CartanEilenbergResolution{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType} + internal_complex::HyperComplex{ChainType, MorphismType} + + function CartanEilenbergResolution( + c::AbsHyperComplex{ChainType, MorphismType}; + is_exact::Bool=false + ) where {ChainType, MorphismType} + @assert dim(c) == 1 "complexes must be 1-dimensional" + @assert has_lower_bound(c, 1) "complexes must be bounded from below" + @assert direction(c, 1) == :chain "resolutions are only implemented for chain complexes" + chain_fac = CEChainFactory(c; is_exact) + map_fac = CEMapFactory{MorphismType}() # TODO: Do proper type inference here! + + # Assuming d is the dimension of the new complex + internal_complex = HyperComplex(2, chain_fac, map_fac, [:chain, :chain]) + # Assuming that ChainType and MorphismType are provided by the input + return new{ChainType, MorphismType}(internal_complex) + end +end + +### Implementing the AbsHyperComplex interface via `underlying_complex` +underlying_complex(c::CartanEilenbergResolution) = c.internal_complex + From abe02bbd7650d55d5e944fe658eb56244e866f7d Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 17:11:03 +0200 Subject: [PATCH 03/10] Fix inclusions. --- .../DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl index b554a991c419..0a4745a96fb5 100644 --- a/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl @@ -9,9 +9,9 @@ include("Objects/koszul_complexes.jl") include("Objects/degree_zero_complexes.jl") include("Objects/new_complex_template.jl") include("Objects/linear_strands.jl") -include("Objects/cartan_eilenberg_resolutions.jl" include("Morphisms/Types.jl") +include("Objects/cartan_eilenberg_resolution.jl") include("Morphisms/cartan_eilenberg_resolutions.jl") include("Morphisms/ext.jl") include("Morphisms/simplified_complexes.jl") From e46bfb8418f20e5725a02263f162bc9d09b6204d Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Sat, 3 Aug 2024 17:11:22 +0200 Subject: [PATCH 04/10] Fixes to make the code actually run. --- .../Objects/cartan_eilenberg_resolution.jl | 168 ++++++++++++++---- 1 file changed, 131 insertions(+), 37 deletions(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl index 3570bd53a3d8..d1bf4ed8ddf6 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -6,6 +6,8 @@ # # is a bounded below complex. We compute a double complex # +# 0 0 0 +# ↑ ↑ ↑ # 0 ← P₀₀ ← P₀₁ ← P₀₂ ← … # ↑ ↑ ↑ # 0 ← P₁₀ ← P₁₁ ← P₁₂ ← … @@ -14,7 +16,9 @@ # ↑ ↑ ↑ # ⋮ ⋮ ⋮ # -# which is quasi-isomorphic to C via some augmentation map ε. +# which is quasi-isomorphic to C via some augmentation map +# +# ε = (εᵢ : P₀ᵢ → Cᵢ)ᵢ # # The challenge is that if we were only computing resolutions of the Cᵢ's # and lifting the maps, then the rows of the resulting diagrams would @@ -41,58 +45,147 @@ struct CEChainFactory{ChainType} <: HyperComplexChainFactory{ChainType} function CEChainFactory(c::AbsHyperComplex; is_exact::Bool=false) @assert dim(c) == 1 "complex must be 1-dimensional" #@assert has_lower_bound(c, 1) "complex must be bounded from below" - return new{chain_type(c)}(c, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}()) + return new{chain_type(c)}(c, is_exact, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplexMorphism}()) end end -function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) - (i, j) = I +function kernel_resolution(fac::CEChainFactory, i::Int) + if !haskey(fac.kernel_resolutions, i) + Z, _ = kernel(fac.c, i) + fac.kernel_resolutions[i] = free_resolution(SimpleFreeResolution, Z)[1] + end + return fac.kernel_resolutions[i] +end - if iszero(j) - return fac.c[i] +function boundary_resolution(fac::CEChainFactory, i::Int) + if !haskey(fac.boundary_resolutions, i) + Z, _ = boundary(fac.c, i) + fac.boundary_resolutions[i] = free_resolution(SimpleFreeResolution, Z)[1] end + return fac.boundary_resolutions[i] +end - res_Z = getindex!(c.kernel_resolutions, i) do - Z, _ = kernel(fac.c, i) - return free_resolution(SimpleFreeResolution, Z) +function induced_map(fac::CEChainFactory, i::Int) + if !haskey(fac.induced_maps, i) + Z, inc = kernel(fac.c, i) + B, pr = boundary(fac.c, i) + @assert codomain(inc) === ambient_free_module(B) + img_gens = elem_type(Z)[preimage(inc, g) for g in ambient_representatives_generators(B)] + res_Z = kernel_resolution(fac, i) + res_B = boundary_resolution(fac, i) + aug_Z = augmentation_map(res_Z) + aug_B = augmentation_map(res_B) + img_gens = gens(res_B[0]) + @show img_gens + img_gens = aug_B[0].(img_gens) + @show img_gens + img_gens = elem_type(res_Z[0])[preimage(aug_Z[0], preimage(inc, repres(aug_B[0](g)))) for g in gens(res_B[0])] + psi = hom(res_B[0], res_Z[0], img_gens; check=true) # TODO: Set to false + @show i + @assert domain(psi) === boundary_resolution(fac, i)[0] + @assert codomain(psi) === kernel_resolution(fac, i)[0] + fac.induced_maps[i] = lift_map(boundary_resolution(fac, i), kernel_resolution(fac, i), psi; start_index=0) end + return fac.induced_maps[i] +end - if can_compute_map(fac.c, 1, (i,)) - if fac.isexact - res_B = getindex!(c.kernel_resolutions, i-1) do - B, _ = kernel(fac.c, i-1) - return free_resolution(SimpleFreeResolution, B) - end - return direct_sum(res_B[j], res_Z[j]) +function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) + (i, j) = I # i the resolution index, j the index in C + + res_Z = kernel_resolution(fac, j) + + if can_compute_map(fac.c, 1, (j,)) + if fac.is_exact # Use the next kernel directly + res_B = kernel_resolution(fac, j-1) + return direct_sum(res_B[i], res_Z[i])[1] else - res_B = getindex!(c.boundary_resolutions, i-1) do - B, _ = boundary(fac.c, i-1) - return free_resolution(SimpleFreeResolution, B) - end - return direct_sum(res_B[j], res_Z[j]) + res_B = boundary_resolution(fac, j-1) + return direct_sum(res_B[i], res_Z[i])[1] end end - return res_Z[j] + # We may assume that the next map can not be computed and is, hence, zero. + @show i, j + return res_Z[i] end function can_compute(fac::CEChainFactory, self::AbsHyperComplex, I::Tuple) (i, j) = I - can_compute_index(fac.c, (i,)) || return false - return j >= 0 + can_compute_index(fac.c, (j,)) || return false + return i >= 0 end ### Production of the morphisms -struct CEMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} -end +struct CEMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} end function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) (i, j) = I cfac = chain_factory(self) - if p == 1 - j == 0 && return map(cfac.c, 1, (i,)) - elseif p == 2 - if can_compute_map(cfac.c, 1, (i,)) - B_plus_Z = self[I] + if p == 1 # vertical upwards maps + if can_compute_map(cfac.c, 1, (j,)) + # both dom and cod are direct sums in this case + dom = self[I] + cod = self[(i-1, j)] + pr1 = canonical_projection(dom, 1) + pr2 = canonical_projection(dom, 2) + @assert domain(pr1) === domain(pr2) === dom + inc1 = canonical_injection(cod, 1) + inc2 = canonical_injection(cod, 2) + @assert codomain(inc1) === codomain(inc2) === cod + res_Z = kernel_resolution(cfac, j) + @assert domain(map(res_Z, i)) === codomain(pr2) + @assert codomain(map(res_Z, i)) === domain(inc2) + res_B = boundary_resolution(cfac, j-1) + @assert domain(map(res_B, i)) === codomain(pr1) + @assert codomain(map(res_B, i)) === domain(inc1) + return compose(pr1, compose(map(res_B, i), inc1)) + compose(pr2, compose(map(res_Z, i), inc2)) + else + res_Z = kernel_resolution(cfac, j) + return map(res_Z, i) + end + error("execution should never reach this point") + elseif p == 2 # the horizontal maps + dom = self[I] + cod = self[(i, j-1)] + if can_compute_map(cfac.c, 1, (j-1,)) + # the codomain is also a direct sum + if !cfac.is_exact + psi = induced_map(cfac, j-1) + phi = psi[i] + inc = canonical_injection(cod, 2) + pr = canonical_projection(dom, 1) + @show i, j + @show domain(phi) + @show codomain(pr) + @assert codomain(phi) === domain(inc) + @assert codomain(pr) === domain(phi) + return compose(pr, compose(phi, inc)) + else + inc = canonical_injection(cod, 2) + pr = canonical_projection(dom, 1) + return compose(pr, inc) + end + error("execution should never reach this point") + else + # the codomain is just the kernel + if !cfac.is_exact + psi = induced_map(cfac, j-1) + phi = psi[i] + pr = canonical_projection(dom, 1) + @show i, j + @show dom + @show codomain(pr) + @show domain(phi) + return compose(pr, phi) + else + pr = canonical_projection(dom, 1) + return pr + end + error("execution should never reach this point") + end + error("execution should never reach this point") + end + + #= C = cfac.c[(i,)] B, pr_B = boundary(cfac.c, 1, (i-1,)) if cfac.is_exact @@ -133,7 +226,7 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) @assert codomain(inc1) === cod img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) - return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + return hom(B_plus_Z, cod, img_gens; check=true) # TODO: Set to false else res_B = getindex!(cfac.boundary_resolutions, i-1) do B, _ = boundary(cfac.c, i) @@ -145,7 +238,7 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) @assert codomain(inc1) === cod img_gens = elem_type(cod)[inc1(a(g)) for g in gens(res_Z1[j])] img_gens = vcat(img_gens, elem_type(cod)[inc2(b(g)) for g in gens(res_Z[j])]) - return hom(B_plus_Z, cod, img_gens; check::Bool=true) # TODO: Set to false + return hom(B_plus_Z, cod, img_gens; check=true) # TODO: Set to false end error("execution should never reach this point") end @@ -157,15 +250,16 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) return map(res, 1, (j,)) end end + =# error("direction $p out of bounds") end function can_compute(fac::CEMapFactory, self::AbsHyperComplex, p::Int, I::Tuple) (i, j) = I - if p == 1 - return can_compute_map(chain_factory(self).c, 1, (i-1,)) && j >= 0 - elseif p == 2 - return can_compute_index(self, I) && j > 0 + if p == 1 # vertical maps + return i > 0 && can_compute(chain_factory(self).c, j) + elseif p == 2 # horizontal maps + return i >= 0 && can_compute_map(chain_factory(self).c, j) end return false end From b2a05fc08f6de0b5c35636a1288c749d44958e26 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 08:01:46 +0200 Subject: [PATCH 05/10] Preserve bounds for base changes. --- .../DoubleAndHyperComplexes/src/base_change_types.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/experimental/DoubleAndHyperComplexes/src/base_change_types.jl b/experimental/DoubleAndHyperComplexes/src/base_change_types.jl index fcc44b20a4e5..df187c198f04 100644 --- a/experimental/DoubleAndHyperComplexes/src/base_change_types.jl +++ b/experimental/DoubleAndHyperComplexes/src/base_change_types.jl @@ -58,7 +58,13 @@ end map_fac = BaseChangeMapFactory(phi, orig) d = dim(orig) - internal_complex = HyperComplex(d, chain_fac, map_fac, [direction(orig, i) for i in 1:d]) + upper_bounds = Vector{Union{Int, Nothing}}([(has_upper_bound(orig, i) ? upper_bound(orig, i) : nothing) for i in 1:d]) + lower_bounds = Vector{Union{Int, Nothing}}([(has_lower_bound(orig, i) ? lower_bound(orig, i) : nothing) for i in 1:d]) + internal_complex = HyperComplex(d, chain_fac, map_fac, + [direction(orig, i) for i in 1:d], + lower_bounds = lower_bounds, + upper_bounds = upper_bounds + ) return new{ModuleFP, ModuleFPHom}(orig, internal_complex) end end From ebbe2b30172e2b0477e04d9c1089885df7e19ca6 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 5 Aug 2024 10:36:43 +0200 Subject: [PATCH 06/10] Clean up CA resolutions. --- .../Objects/cartan_eilenberg_resolution.jl | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl index d1bf4ed8ddf6..946850e17183 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -69,19 +69,16 @@ function induced_map(fac::CEChainFactory, i::Int) if !haskey(fac.induced_maps, i) Z, inc = kernel(fac.c, i) B, pr = boundary(fac.c, i) - @assert codomain(inc) === ambient_free_module(B) - img_gens = elem_type(Z)[preimage(inc, g) for g in ambient_representatives_generators(B)] + @assert ambient_free_module(Z) === ambient_free_module(B) + img_gens = elem_type(Z)[Z(g) for g in ambient_representatives_generators(B)] res_Z = kernel_resolution(fac, i) res_B = boundary_resolution(fac, i) aug_Z = augmentation_map(res_Z) aug_B = augmentation_map(res_B) img_gens = gens(res_B[0]) - @show img_gens img_gens = aug_B[0].(img_gens) - @show img_gens - img_gens = elem_type(res_Z[0])[preimage(aug_Z[0], preimage(inc, repres(aug_B[0](g)))) for g in gens(res_B[0])] + img_gens = elem_type(res_Z[0])[preimage(aug_Z[0], Z(repres(aug_B[0](g)))) for g in gens(res_B[0])] psi = hom(res_B[0], res_Z[0], img_gens; check=true) # TODO: Set to false - @show i @assert domain(psi) === boundary_resolution(fac, i)[0] @assert codomain(psi) === kernel_resolution(fac, i)[0] fac.induced_maps[i] = lift_map(boundary_resolution(fac, i), kernel_resolution(fac, i), psi; start_index=0) @@ -104,7 +101,6 @@ function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple) end end # We may assume that the next map can not be computed and is, hence, zero. - @show i, j return res_Z[i] end @@ -153,9 +149,6 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) phi = psi[i] inc = canonical_injection(cod, 2) pr = canonical_projection(dom, 1) - @show i, j - @show domain(phi) - @show codomain(pr) @assert codomain(phi) === domain(inc) @assert codomain(pr) === domain(phi) return compose(pr, compose(phi, inc)) @@ -171,10 +164,6 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) psi = induced_map(cfac, j-1) phi = psi[i] pr = canonical_projection(dom, 1) - @show i, j - @show dom - @show codomain(pr) - @show domain(phi) return compose(pr, phi) else pr = canonical_projection(dom, 1) @@ -279,7 +268,7 @@ end map_fac = CEMapFactory{MorphismType}() # TODO: Do proper type inference here! # Assuming d is the dimension of the new complex - internal_complex = HyperComplex(2, chain_fac, map_fac, [:chain, :chain]) + internal_complex = HyperComplex(2, chain_fac, map_fac, [:chain, :chain]; lower_bounds = Union{Int, Nothing}[0, lower_bound(c, 1)]) # Assuming that ChainType and MorphismType are provided by the input return new{ChainType, MorphismType}(internal_complex) end From 1e804fc4dddfc426387cc97642779c438cef56b8 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Mon, 16 Sep 2024 15:05:29 +0200 Subject: [PATCH 07/10] Extend degree signatures to accept check. --- src/Modules/ModulesGraded.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Modules/ModulesGraded.jl b/src/Modules/ModulesGraded.jl index 08cf10fda0a7..27d841ec35a6 100644 --- a/src/Modules/ModulesGraded.jl +++ b/src/Modules/ModulesGraded.jl @@ -414,7 +414,7 @@ function set_grading!(M::FreeMod, W::Vector{<:IntegerUnion}) M.d = [W[i] * A[1] for i in 1:length(W)] end -function degrees(M::FreeMod) +function degrees(M::FreeMod; check::Bool=true) @assert is_graded(M) return M.d::Vector{FinGenAbGroupElem} end @@ -437,8 +437,8 @@ julia> degrees_of_generators(F) [0] ``` """ -function degrees_of_generators(F::FreeMod) - return degrees(F) +function degrees_of_generators(F::FreeMod; check::Bool=true) + return degrees(F; check) end ############################################################################### @@ -541,10 +541,11 @@ function is_homogeneous(el::FreeModElem) return isa(el.d, FinGenAbGroupElem) end -const AnyGradedRingElem = Union{<:MPolyDecRingElem, <:MPolyQuoRingElem{<:MPolyDecRingElem}, - <:MPolyLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing}, - <:MPolyQuoLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing} - } +#const AnyGradedRingElem = Union{<:MPolyDecRingElem, <:MPolyQuoRingElem{<:MPolyDecRingElem}, +# <:MPolyLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing}, +# <:MPolyQuoLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing} +# } +const AnyGradedRingElem = NCRingElem @doc raw""" degree(f::FreeModElem{T}; check::Bool=true) where {T<:AnyGradedRingElem} From 6727736ef3749d84b2e7db1b453af15c5b57c6b4 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 25 Oct 2024 15:36:48 +0200 Subject: [PATCH 08/10] Fix dirty WIP. --- src/Modules/ModulesGraded.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Modules/ModulesGraded.jl b/src/Modules/ModulesGraded.jl index 27d841ec35a6..8c383813da63 100644 --- a/src/Modules/ModulesGraded.jl +++ b/src/Modules/ModulesGraded.jl @@ -541,11 +541,10 @@ 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 = NCRingElem +const AnyGradedRingElem = Union{<:MPolyDecRingElem, <:MPolyQuoRingElem{<:MPolyDecRingElem}, + <:MPolyLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing}, + <:MPolyQuoLocRingElem{<:Ring, <:RingElem, <:MPolyDecRing} + } @doc raw""" degree(f::FreeModElem{T}; check::Bool=true) where {T<:AnyGradedRingElem} From ea0ad2510ed6d9708d9d455d8f762493689f16d2 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 25 Oct 2024 15:37:03 +0200 Subject: [PATCH 09/10] Remove deprecated code. --- .../Objects/cartan_eilenberg_resolution.jl | 69 +------------------ 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl index 946850e17183..982d058b5931 100644 --- a/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl +++ b/experimental/DoubleAndHyperComplexes/src/Objects/cartan_eilenberg_resolution.jl @@ -16,7 +16,7 @@ # ↑ ↑ ↑ # ⋮ ⋮ ⋮ # -# which is quasi-isomorphic to C via some augmentation map +# whose total complex is quasi-isomorphic to C via some augmentation map # # ε = (εᵢ : P₀ᵢ → Cᵢ)ᵢ # @@ -173,73 +173,6 @@ function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple) 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 From d5eaa1e60d0874292ccd25b0107278cccef09c4a Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Fri, 25 Oct 2024 15:53:17 +0200 Subject: [PATCH 10/10] Add tests. --- .../test/cartan_eilenberg_resolutions.jl | 25 +++++++++++++++++++ .../DoubleAndHyperComplexes/test/runtests.jl | 1 + 2 files changed, 26 insertions(+) create mode 100644 experimental/DoubleAndHyperComplexes/test/cartan_eilenberg_resolutions.jl diff --git a/experimental/DoubleAndHyperComplexes/test/cartan_eilenberg_resolutions.jl b/experimental/DoubleAndHyperComplexes/test/cartan_eilenberg_resolutions.jl new file mode 100644 index 000000000000..7e51b008980f --- /dev/null +++ b/experimental/DoubleAndHyperComplexes/test/cartan_eilenberg_resolutions.jl @@ -0,0 +1,25 @@ +@testset "Cartan-Eilenberg resolutions" begin + R, (x, y, z, w) = QQ[:x, :y, :z, :w] + + A = R[x y z; y z w] + + R1 = free_module(R, 1) + I, inc = sub(R1, [a*R1[1] for a in minors(A, 2)]) + M = cokernel(inc) + R4 = free_module(R, 4) + theta = sum(a*g for (a, g) in zip(gens(R), gens(R4)); init=zero(R4)) + K = koszul_complex(Oscar.KoszulComplex, theta) + + comp = tensor_product(K, Oscar.ZeroDimensionalComplex(M)) + res = Oscar.CartanEilenbergResolution(comp); + tot = total_complex(res); + tot_simp = simplify(tot); + + res_M, _ = free_resolution(Oscar.SimpleFreeResolution, M) + comp2 = tensor_product(K, res_M) + tot2 = total_complex(comp2) + tot_simp2 = simplify(tot2); + + @test [ngens(tot_simp[i]) for i in 0:5] == [ngens(tot_simp2[i]) for i in 0:5] +end + diff --git a/experimental/DoubleAndHyperComplexes/test/runtests.jl b/experimental/DoubleAndHyperComplexes/test/runtests.jl index 727dae3712a1..591a3023f363 100644 --- a/experimental/DoubleAndHyperComplexes/test/runtests.jl +++ b/experimental/DoubleAndHyperComplexes/test/runtests.jl @@ -19,3 +19,4 @@ include("linear_strands.jl") include("printing.jl") include("degree_zero_complex.jl") include("base_change.jl") +include("cartan_eilenberg_resolutions.jl")