Skip to content

Commit ea8bd99

Browse files
Cartan eilenberg resolutions (#4248)
1 parent f849adc commit ea8bd99

File tree

9 files changed

+275
-24
lines changed

9 files changed

+275
-24
lines changed

experimental/DoubleAndHyperComplexes/src/DoubleAndHyperComplexes.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ include("Objects/new_complex_template.jl")
1111
include("Objects/linear_strands.jl")
1212

1313
include("Morphisms/Types.jl")
14+
include("Objects/cartan_eilenberg_resolution.jl")
1415
include("Morphisms/cartan_eilenberg_resolutions.jl")
1516
include("Morphisms/ext.jl")
1617
include("Morphisms/simplified_complexes.jl")
@@ -25,3 +26,4 @@ include("Exports.jl")
2526

2627
include("base_change_types.jl")
2728
include("base_change.jl")
29+

experimental/DoubleAndHyperComplexes/src/Morphisms/Types.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,23 @@ end
142142
underlying_complex(c::SimpleFreeResolution) = c.underlying_complex
143143
original_module(c::SimpleFreeResolution) = c.M
144144

145+
### Lifting morphisms through projective resolutions
146+
mutable struct MapLifter{MorphismType} <: HyperComplexMorphismFactory{MorphismType}
147+
dom::AbsHyperComplex
148+
cod::AbsHyperComplex
149+
orig_map::Map
150+
start_index::Int
151+
offset::Int
152+
check::Bool
153+
154+
function MapLifter(::Type{MorphismType}, dom::AbsHyperComplex, cod::AbsHyperComplex, phi::Map;
155+
start_index::Int=0, offset::Int=0, check::Bool=true
156+
) where {MorphismType}
157+
@assert dim(dom) == 1 && dim(cod) == 1 "lifting of maps is only implemented in dimension one"
158+
@assert (is_chain_complex(dom) && is_chain_complex(cod)) || (is_cochain_complex(dom) && is_cochain_complex(cod))
159+
@assert domain(phi) === dom[start_index]
160+
@assert codomain(phi) === cod[start_index + offset]
161+
return new{MorphismType}(dom, cod, phi, start_index, offset)
162+
end
163+
end
164+

experimental/DoubleAndHyperComplexes/src/Morphisms/cartan_eilenberg_resolutions.jl

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,4 @@
1-
mutable struct MapLifter{MorphismType} <: HyperComplexMorphismFactory{MorphismType}
2-
dom::AbsHyperComplex
3-
cod::AbsHyperComplex
4-
orig_map::Map
5-
start_index::Int
6-
offset::Int
7-
check::Bool
8-
9-
function MapLifter(::Type{MorphismType}, dom::AbsHyperComplex, cod::AbsHyperComplex, phi::Map;
10-
start_index::Int=0, offset::Int=0, check::Bool=true
11-
) where {MorphismType}
12-
@assert dim(dom) == 1 && dim(cod) == 1 "lifting of maps is only implemented in dimension one"
13-
@assert (is_chain_complex(dom) && is_chain_complex(cod)) || (is_cochain_complex(dom) && is_cochain_complex(cod))
14-
@assert domain(phi) === dom[start_index]
15-
@assert codomain(phi) === cod[start_index + offset]
16-
return new{MorphismType}(dom, cod, phi, start_index, offset)
17-
end
18-
end
19-
1+
### Lifting maps through projective resolutions
202
is_chain_complex(c::AbsHyperComplex) = (dim(c) == 1 ? direction(c, 1) == (:chain) : error("complex is not one-dimensional"))
213
is_cochain_complex(c::AbsHyperComplex) = (dim(c) == 1 ? direction(c, 1) == (:cochain) : error("complex is not one-dimensional"))
224

@@ -57,3 +39,6 @@ function lift_map(dom::AbsHyperComplex{DomChainType}, cod::AbsHyperComplex{CodCh
5739
end
5840

5941
morphism_type(::Type{T1}, ::Type{T2}) where {T1<:ModuleFP, T2<:ModuleFP} = ModuleFPHom{<:T1, <:T2}
42+
43+
### Cartan Eilenberg resolutions
44+

experimental/DoubleAndHyperComplexes/src/Objects/Types.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ end
9393
upper_bounds::Vector=[nothing for i in 1:d],
9494
lower_bounds::Vector=[nothing for i in 1:d]
9595
) where {ChainType, MorphismType}
96-
@assert d > 0 "can not create zero or negative dimensional hypercomplex"
96+
@assert d >= 0 "can not create negative dimensional hypercomplex"
9797
chains = Dict{Tuple, ChainType}()
9898
morphisms = Dict{Tuple, Dict{Int, <:MorphismType}}()
9999
return new{ChainType, MorphismType}(d, chains, morphisms,
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
#= Cartan Eilenberg resolutions of 1-dimensional complexes
2+
#
3+
# Suppose
4+
#
5+
# 0 ← C₀ ← C₁ ← C₂ ← …
6+
#
7+
# is a bounded below complex. We compute a double complex
8+
#
9+
# 0 0 0
10+
# ↑ ↑ ↑
11+
# 0 ← P₀₀ ← P₀₁ ← P₀₂ ← …
12+
# ↑ ↑ ↑
13+
# 0 ← P₁₀ ← P₁₁ ← P₁₂ ← …
14+
# ↑ ↑ ↑
15+
# 0 ← P₂₀ ← P₂₁ ← P₂₂ ← …
16+
# ↑ ↑ ↑
17+
# ⋮ ⋮ ⋮
18+
#
19+
# whose total complex is quasi-isomorphic to C via some augmentation map
20+
#
21+
# ε = (εᵢ : P₀ᵢ → Cᵢ)ᵢ
22+
#
23+
# The challenge is that if we were only computing resolutions of the Cᵢ's
24+
# and lifting the maps, then the rows of the resulting diagrams would
25+
# not necessarily form complexes. To accomplish that, we split the original
26+
# complex into short exact sequences
27+
#
28+
# 0 ← Bᵢ ← Cᵢ ← Zᵢ ← 0
29+
#
30+
# and apply the Horse shoe lemma to these. Together with the induced maps
31+
# from Bᵢ ↪ Zᵢ₋₁ we get the desired double complex.
32+
#
33+
# If the original complex C is known to be exact, then there is no need
34+
# to compute the resolutions of both Bᵢ and Zᵢ and we can shorten the procedure.
35+
=#
36+
### Production of the chains
37+
struct CEChainFactory{ChainType} <: HyperComplexChainFactory{ChainType}
38+
c::AbsHyperComplex
39+
is_exact::Bool
40+
kernel_resolutions::Dict{Int, <:AbsHyperComplex} # the kernels of Cᵢ → Cᵢ₋₁
41+
boundary_resolutions::Dict{Int, <:AbsHyperComplex} # the boundaries of Cᵢ₊₁ → Cᵢ
42+
induced_maps::Dict{Int, <:AbsHyperComplexMorphism} # the induced maps from the free
43+
# resolutions of the boundary and kernel
44+
45+
function CEChainFactory(c::AbsHyperComplex; is_exact::Bool=false)
46+
@assert dim(c) == 1 "complex must be 1-dimensional"
47+
#@assert has_lower_bound(c, 1) "complex must be bounded from below"
48+
return new{chain_type(c)}(c, is_exact, Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplex}(), Dict{Int, AbsHyperComplexMorphism}())
49+
end
50+
end
51+
52+
function kernel_resolution(fac::CEChainFactory, i::Int)
53+
if !haskey(fac.kernel_resolutions, i)
54+
Z, _ = kernel(fac.c, i)
55+
fac.kernel_resolutions[i] = free_resolution(SimpleFreeResolution, Z)[1]
56+
end
57+
return fac.kernel_resolutions[i]
58+
end
59+
60+
function boundary_resolution(fac::CEChainFactory, i::Int)
61+
if !haskey(fac.boundary_resolutions, i)
62+
Z, _ = boundary(fac.c, i)
63+
fac.boundary_resolutions[i] = free_resolution(SimpleFreeResolution, Z)[1]
64+
end
65+
return fac.boundary_resolutions[i]
66+
end
67+
68+
function induced_map(fac::CEChainFactory, i::Int)
69+
if !haskey(fac.induced_maps, i)
70+
Z, inc = kernel(fac.c, i)
71+
B, pr = boundary(fac.c, i)
72+
@assert ambient_free_module(Z) === ambient_free_module(B)
73+
img_gens = elem_type(Z)[Z(g) for g in ambient_representatives_generators(B)]
74+
res_Z = kernel_resolution(fac, i)
75+
res_B = boundary_resolution(fac, i)
76+
aug_Z = augmentation_map(res_Z)
77+
aug_B = augmentation_map(res_B)
78+
img_gens = gens(res_B[0])
79+
img_gens = aug_B[0].(img_gens)
80+
img_gens = elem_type(res_Z[0])[preimage(aug_Z[0], Z(repres(aug_B[0](g)))) for g in gens(res_B[0])]
81+
psi = hom(res_B[0], res_Z[0], img_gens; check=true) # TODO: Set to false
82+
@assert domain(psi) === boundary_resolution(fac, i)[0]
83+
@assert codomain(psi) === kernel_resolution(fac, i)[0]
84+
fac.induced_maps[i] = lift_map(boundary_resolution(fac, i), kernel_resolution(fac, i), psi; start_index=0)
85+
end
86+
return fac.induced_maps[i]
87+
end
88+
89+
function (fac::CEChainFactory)(self::AbsHyperComplex, I::Tuple)
90+
(i, j) = I # i the resolution index, j the index in C
91+
92+
res_Z = kernel_resolution(fac, j)
93+
94+
if can_compute_map(fac.c, 1, (j,))
95+
if fac.is_exact # Use the next kernel directly
96+
res_B = kernel_resolution(fac, j-1)
97+
return direct_sum(res_B[i], res_Z[i])[1]
98+
else
99+
res_B = boundary_resolution(fac, j-1)
100+
return direct_sum(res_B[i], res_Z[i])[1]
101+
end
102+
end
103+
# We may assume that the next map can not be computed and is, hence, zero.
104+
return res_Z[i]
105+
end
106+
107+
function can_compute(fac::CEChainFactory, self::AbsHyperComplex, I::Tuple)
108+
(i, j) = I
109+
can_compute_index(fac.c, (j,)) || return false
110+
return i >= 0
111+
end
112+
113+
### Production of the morphisms
114+
struct CEMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType} end
115+
116+
function (fac::CEMapFactory)(self::AbsHyperComplex, p::Int, I::Tuple)
117+
(i, j) = I
118+
cfac = chain_factory(self)
119+
if p == 1 # vertical upwards maps
120+
if can_compute_map(cfac.c, 1, (j,))
121+
# both dom and cod are direct sums in this case
122+
dom = self[I]
123+
cod = self[(i-1, j)]
124+
pr1 = canonical_projection(dom, 1)
125+
pr2 = canonical_projection(dom, 2)
126+
@assert domain(pr1) === domain(pr2) === dom
127+
inc1 = canonical_injection(cod, 1)
128+
inc2 = canonical_injection(cod, 2)
129+
@assert codomain(inc1) === codomain(inc2) === cod
130+
res_Z = kernel_resolution(cfac, j)
131+
@assert domain(map(res_Z, i)) === codomain(pr2)
132+
@assert codomain(map(res_Z, i)) === domain(inc2)
133+
res_B = boundary_resolution(cfac, j-1)
134+
@assert domain(map(res_B, i)) === codomain(pr1)
135+
@assert codomain(map(res_B, i)) === domain(inc1)
136+
return compose(pr1, compose(map(res_B, i), inc1)) + compose(pr2, compose(map(res_Z, i), inc2))
137+
else
138+
res_Z = kernel_resolution(cfac, j)
139+
return map(res_Z, i)
140+
end
141+
error("execution should never reach this point")
142+
elseif p == 2 # the horizontal maps
143+
dom = self[I]
144+
cod = self[(i, j-1)]
145+
if can_compute_map(cfac.c, 1, (j-1,))
146+
# the codomain is also a direct sum
147+
if !cfac.is_exact
148+
psi = induced_map(cfac, j-1)
149+
phi = psi[i]
150+
inc = canonical_injection(cod, 2)
151+
pr = canonical_projection(dom, 1)
152+
@assert codomain(phi) === domain(inc)
153+
@assert codomain(pr) === domain(phi)
154+
return compose(pr, compose(phi, inc))
155+
else
156+
inc = canonical_injection(cod, 2)
157+
pr = canonical_projection(dom, 1)
158+
return compose(pr, inc)
159+
end
160+
error("execution should never reach this point")
161+
else
162+
# the codomain is just the kernel
163+
if !cfac.is_exact
164+
psi = induced_map(cfac, j-1)
165+
phi = psi[i]
166+
pr = canonical_projection(dom, 1)
167+
return compose(pr, phi)
168+
else
169+
pr = canonical_projection(dom, 1)
170+
return pr
171+
end
172+
error("execution should never reach this point")
173+
end
174+
error("execution should never reach this point")
175+
end
176+
error("direction $p out of bounds")
177+
end
178+
179+
function can_compute(fac::CEMapFactory, self::AbsHyperComplex, p::Int, I::Tuple)
180+
(i, j) = I
181+
if p == 1 # vertical maps
182+
return i > 0 && can_compute(chain_factory(self).c, j)
183+
elseif p == 2 # horizontal maps
184+
return i >= 0 && can_compute_map(chain_factory(self).c, j)
185+
end
186+
return false
187+
end
188+
189+
### The concrete struct
190+
@attributes mutable struct CartanEilenbergResolution{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType}
191+
internal_complex::HyperComplex{ChainType, MorphismType}
192+
193+
function CartanEilenbergResolution(
194+
c::AbsHyperComplex{ChainType, MorphismType};
195+
is_exact::Bool=false
196+
) where {ChainType, MorphismType}
197+
@assert dim(c) == 1 "complexes must be 1-dimensional"
198+
@assert has_lower_bound(c, 1) "complexes must be bounded from below"
199+
@assert direction(c, 1) == :chain "resolutions are only implemented for chain complexes"
200+
chain_fac = CEChainFactory(c; is_exact)
201+
map_fac = CEMapFactory{MorphismType}() # TODO: Do proper type inference here!
202+
203+
# Assuming d is the dimension of the new complex
204+
internal_complex = HyperComplex(2, chain_fac, map_fac, [:chain, :chain]; lower_bounds = Union{Int, Nothing}[0, lower_bound(c, 1)])
205+
# Assuming that ChainType and MorphismType are provided by the input
206+
return new{ChainType, MorphismType}(internal_complex)
207+
end
208+
end
209+
210+
### Implementing the AbsHyperComplex interface via `underlying_complex`
211+
underlying_complex(c::CartanEilenbergResolution) = c.internal_complex
212+

experimental/DoubleAndHyperComplexes/src/base_change_types.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,13 @@ end
5858
map_fac = BaseChangeMapFactory(phi, orig)
5959

6060
d = dim(orig)
61-
internal_complex = HyperComplex(d, chain_fac, map_fac, [direction(orig, i) for i in 1:d])
61+
upper_bounds = Vector{Union{Int, Nothing}}([(has_upper_bound(orig, i) ? upper_bound(orig, i) : nothing) for i in 1:d])
62+
lower_bounds = Vector{Union{Int, Nothing}}([(has_lower_bound(orig, i) ? lower_bound(orig, i) : nothing) for i in 1:d])
63+
internal_complex = HyperComplex(d, chain_fac, map_fac,
64+
[direction(orig, i) for i in 1:d],
65+
lower_bounds = lower_bounds,
66+
upper_bounds = upper_bounds
67+
)
6268
return new{ModuleFP, ModuleFPHom}(orig, internal_complex)
6369
end
6470
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@testset "Cartan-Eilenberg resolutions" begin
2+
R, (x, y, z, w) = QQ[:x, :y, :z, :w]
3+
4+
A = R[x y z; y z w]
5+
6+
R1 = free_module(R, 1)
7+
I, inc = sub(R1, [a*R1[1] for a in minors(A, 2)])
8+
M = cokernel(inc)
9+
R4 = free_module(R, 4)
10+
theta = sum(a*g for (a, g) in zip(gens(R), gens(R4)); init=zero(R4))
11+
K = koszul_complex(Oscar.KoszulComplex, theta)
12+
13+
comp = tensor_product(K, Oscar.ZeroDimensionalComplex(M))
14+
res = Oscar.CartanEilenbergResolution(comp);
15+
tot = total_complex(res);
16+
tot_simp = simplify(tot);
17+
18+
res_M, _ = free_resolution(Oscar.SimpleFreeResolution, M)
19+
comp2 = tensor_product(K, res_M)
20+
tot2 = total_complex(comp2)
21+
tot_simp2 = simplify(tot2);
22+
23+
@test [ngens(tot_simp[i]) for i in 0:5] == [ngens(tot_simp2[i]) for i in 0:5]
24+
end
25+

experimental/DoubleAndHyperComplexes/test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ include("linear_strands.jl")
1919
include("printing.jl")
2020
include("degree_zero_complex.jl")
2121
include("base_change.jl")
22+
include("cartan_eilenberg_resolutions.jl")

src/Modules/ModulesGraded.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ function set_grading!(M::FreeMod, W::Vector{<:IntegerUnion})
414414
M.d = [W[i] * A[1] for i in 1:length(W)]
415415
end
416416

417-
function degrees(M::FreeMod)
417+
function degrees(M::FreeMod; check::Bool=true)
418418
@assert is_graded(M)
419419
return M.d::Vector{FinGenAbGroupElem}
420420
end
@@ -437,8 +437,8 @@ julia> degrees_of_generators(F)
437437
[0]
438438
```
439439
"""
440-
function degrees_of_generators(F::FreeMod)
441-
return degrees(F)
440+
function degrees_of_generators(F::FreeMod; check::Bool=true)
441+
return degrees(F; check)
442442
end
443443

444444
###############################################################################

0 commit comments

Comments
 (0)