Skip to content

Commit

Permalink
Implement change_base_ring for hypercomplexes (oscar-system#3794)
Browse files Browse the repository at this point in the history
  • Loading branch information
HechtiDerLachs authored Sep 11, 2024
1 parent aca84ef commit 85af063
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ then `compose(a, b)` refers to the composition `b` $\circ$ `a`. If an isomorphis
`a` is given, then `inv(a)` refers to its inverse.

```@docs
hom_product(M::ModuleFP, N::ModuleFP, A::Matrix{<: ModuleFPHom})
hom_product(M::ModuleFP, N::ModuleFP, A::Matrix{<:ModuleFPHom{<:ModuleFP, <:ModuleFP, Nothing}})
```

```@docs
hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{ <: ModuleFPHom})
hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{<:ModuleFPHom})
```

```@docs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ include("Morphisms/linear_strands.jl")
include("Morphisms/methods.jl")
include("Objects/Methods.jl")
include("Exports.jl")

include("base_change_types.jl")
include("base_change.jl")
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ function (fac::SimplifiedChainFactory)(d::AbsHyperComplex, Ind::Tuple)
end

function can_compute(fac::SimplifiedChainFactory, c::AbsHyperComplex, I::Tuple)
@assert dim(c) == 1
i = first(I)
can_compute_index(original_complex(fac), I) || return false
I_p = (i+1,)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ end

# directions are inverted by reflection
dirs = [(direction(original_complex, i) == :chain ? (:cochain) : (:chain)) for i in 1:dim(original_complex)]
upper_bounds = Union{Int, Nothing}[has_lower_bound(original_complex, i) ? -lower_bound(original_complex, i) : nothing for i in 1:dim(original_complex)]
lower_bounds = Union{Int, Nothing}[has_upper_bound(original_complex, i) ? -upper_bound(original_complex, i) : nothing for i in 1:dim(original_complex)]

# Create an instance of `HyperComplex` using your factories.
#
Expand All @@ -171,7 +173,9 @@ end
internal_complex = HyperComplex(
dim(original_complex), # the reflected complex has the same dimension
chain_fac, map_fac, # use your factories
dirs #, cached=true # an optional argument to switch off caching
dirs;
upper_bounds,
lower_bounds #, cached=true # an optional argument to switch off caching
# and only use the factories.
# Use only if you know what you're doing!
)
Expand Down
19 changes: 19 additions & 0 deletions experimental/DoubleAndHyperComplexes/src/base_change.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function change_base_ring(phi::Any, C::AbsHyperComplex)
res = BaseChangeComplex(phi, C)
red_map = BaseChangeMorphism(res)
res.red_map = red_map
return res, red_map
end

function base_change_map(comp::BaseChangeComplex)
# The field is not set when using the internal constructor of the type.
if !isdefined(comp, :red_map)
comp.red_map = BaseChangeMorphism(comp)
end
return comp.red_map::BaseChangeMorphism
end

function original_complex(comp::BaseChangeComplex)
return comp.orig
end

106 changes: 106 additions & 0 deletions experimental/DoubleAndHyperComplexes/src/base_change_types.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# change of base rings for complexes of morphisms

### Production of the chains
struct BaseChangeChainFactory{ChainType} <: HyperComplexChainFactory{ChainType}
phi::Any # Whatever serves as a base change map
orig::AbsHyperComplex
red_map_cache::Dict{Tuple, Any}

function BaseChangeChainFactory(phi::Any, orig::AbsHyperComplex)
# TODO: Can we be more specific about the type?
return new{ModuleFP}(phi, orig, Dict{Tuple, Any}())
end
end

function (fac::BaseChangeChainFactory)(self::AbsHyperComplex, i::Tuple)
res, m = change_base_ring(fac.phi, fac.orig[i])
fac.red_map_cache[i] = m
return res
end

function can_compute(fac::BaseChangeChainFactory, self::AbsHyperComplex, i::Tuple)
return can_compute_index(fac.orig, i)
end

### Production of the morphisms
struct BaseChangeMapFactory{MorphismType} <: HyperComplexMapFactory{MorphismType}
phi::Any # Whatever serves as a base change map
orig::AbsHyperComplex

function BaseChangeMapFactory(phi::Any, orig::AbsHyperComplex)
return new{ModuleFPHom}(phi, orig)
end
end

function (fac::BaseChangeMapFactory)(self::AbsHyperComplex, p::Int, i::Tuple)
f = map(fac.orig, p, i)
next = _codomain_index(self, p, i)
# Fill the cache
self[i]
self[next]
dom_bc = chain_factory(self).red_map_cache[i]
cod_bc = chain_factory(self).red_map_cache[next]
res = change_base_ring(fac.phi, f; domain_base_change=dom_bc, codomain_base_change=cod_bc)
end

function can_compute(fac::BaseChangeMapFactory, self::AbsHyperComplex, p::Int, i::Tuple)
return can_compute_map(fac.orig, p, i)
end

### The concrete struct
@attributes mutable struct BaseChangeComplex{ChainType, MorphismType} <: AbsHyperComplex{ChainType, MorphismType}
orig::AbsHyperComplex
internal_complex::HyperComplex{ChainType, MorphismType}
red_map::AbsHyperComplexMorphism

function BaseChangeComplex(phi::Any, orig::AbsHyperComplex{CT, MT}) where {CT<:ModuleFP, MT<:ModuleFPHom}
chain_fac = BaseChangeChainFactory(phi, orig)
map_fac = BaseChangeMapFactory(phi, orig)

d = dim(orig)
internal_complex = HyperComplex(d, chain_fac, map_fac, [direction(orig, i) for i in 1:d])
return new{ModuleFP, ModuleFPHom}(orig, internal_complex)
end
end

### Implementing the AbsHyperComplex interface via `underlying_complex`
underlying_complex(c::BaseChangeComplex) = c.internal_complex

########################################################################
# Type for the base change morphism
########################################################################
struct BaseChangeMorphismFactory{MorphismType} <: HyperComplexMorphismFactory{MorphismType}
cod::BaseChangeComplex

function BaseChangeMorphismFactory(comp::BaseChangeComplex{CT, MT}) where {CT, MT}
# TODO: Can we do more about the type?
return new{ModuleFPHom}(comp)
end
end

function (fac::BaseChangeMorphismFactory)(self::AbsHyperComplexMorphism, i::Tuple)
# fill the cache
fac.cod[i]

return chain_factory(fac.cod).red_map_cache[i]
end

function can_compute(fac::BaseChangeMorphismFactory, self::AbsHyperComplexMorphism, i::Tuple)
return can_compute_index(fac.cod, i)
end


@attributes mutable struct BaseChangeMorphism{DomainType, CodomainType, MorphismType} <: AbsHyperComplexMorphism{DomainType, CodomainType, MorphismType, BaseChangeMorphism{DomainType, CodomainType, MorphismType}}
internal_morphism::HyperComplexMorphism{DomainType, CodomainType, MorphismType}

function BaseChangeMorphism(cod::BaseChangeComplex)
map_factory = BaseChangeMorphismFactory(cod)
dom = cod.orig

internal_morphism = HyperComplexMorphism(dom, cod, map_factory, cached=true, offset=[0 for i in 1:dim(dom)])
return new{typeof(cod.orig), typeof(cod), ModuleFPHom}(internal_morphism)
end
end

underlying_morphism(phi::BaseChangeMorphism) = phi.internal_morphism

36 changes: 36 additions & 0 deletions experimental/DoubleAndHyperComplexes/test/base_change.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@testset "base change" begin
R, (x, y) = QQ[:x, :y]
R2 = FreeMod(R, 2)
C = koszul_complex(Oscar.KoszulComplex, x*R2[1] + y*R2[2])

U = complement_of_point_ideal(R, [0, 0])

L, loc = localization(R, U)

CC, red = change_base_ring(loc, C)
@test !is_zero(CC[0])
@test !is_zero(CC[1])
@test !is_zero(CC[2])
a = compose(map(C, 1), red[0])
b = compose(red[1], map(CC, 1))
@test all(a(g) == b(g) for g in gens(C[1]))
a = compose(map(C, 2), red[1])
b = compose(red[2], map(CC, 2))
@test all(a(g) == b(g) for g in gens(C[2]))

F1 = FreeMod(ZZ, 2)
v = 15*F1[1] + 21*F1[2]
C = koszul_complex(Oscar.KoszulComplex, v)

CC, red = change_base_ring(QQ, C)
@test !is_zero(CC[0])
@test !is_zero(CC[1])
@test !is_zero(CC[2])
a = compose(map(C, 1), red[0])
b = compose(red[1], map(CC, 1))
@test all(a(g) == b(g) for g in gens(C[1]))
a = compose(map(C, 2), red[1])
b = compose(red[2], map(CC, 2))
@test all(a(g) == b(g) for g in gens(C[2]))
end

2 changes: 1 addition & 1 deletion experimental/DoubleAndHyperComplexes/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ include("free_resolutions.jl")
include("linear_strands.jl")
include("printing.jl")
include("degree_zero_complex.jl")

include("base_change.jl")
9 changes: 5 additions & 4 deletions src/Modules/UngradedModules/FreeModuleHom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ img_gens(f::FreeModuleHom) = images_of_generators(f)
images_of_generators(f::FreeModuleHom) = f.imgs_of_gens::Vector{elem_type(codomain(f))}
image_of_generator(phi::FreeModuleHom, i::Int) = phi.imgs_of_gens[i]::elem_type(codomain(phi))
base_ring_map(f::FreeModuleHom) = f.ring_map
@attr Map function base_ring_map(f::FreeModuleHom{<:SubquoModule, <:ModuleFP, Nothing})
return identity_map(base_ring(domain(f)))
function base_ring_map(f::FreeModuleHom{<:SubquoModule, <:ModuleFP, Nothing})
return nothing
end
base_ring_map(f::SubQuoHom) = f.ring_map
@attr Map function base_ring_map(f::SubQuoHom{<:SubquoModule, <:ModuleFP, Nothing})
return identity_map(base_ring(domain(f)))
function base_ring_map(f::SubQuoHom{<:SubquoModule, <:ModuleFP, Nothing})
return nothing
end

@doc raw"""
Expand Down Expand Up @@ -232,6 +232,7 @@ scalars in `base_ring(F)` to their images under `h`.
the homomorphism under consideration as a *homogeneous module homomorphism*.
"""
hom(F::FreeMod, M::ModuleFP{T}, V::Vector{<:ModuleFPElem{T}}, h::RingMapType; check::Bool=true) where {T, RingMapType} = FreeModuleHom(F, M, V, h; check)
hom(F::FreeMod, M::ModuleFP{T}, V::Vector{<:ModuleFPElem{T}}, h::Nothing; check::Bool=true) where {T} = FreeModuleHom(F, M, V; check)
hom(F::FreeMod, M::ModuleFP{T}, A::MatElem{T}, h::RingMapType; check::Bool=true) where {T, RingMapType} = FreeModuleHom(F, M, A, h; check)

@doc raw"""
Expand Down
35 changes: 31 additions & 4 deletions src/Modules/UngradedModules/HomologicalAlgebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,30 @@ function cochain_complex(V::Vector{<:ModuleFPHom}; seed::Int = 0)
end


function hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{<:ModuleFPHom{<:ModuleFP, <:ModuleFP, Nothing}})
tM = get_attribute(M, :tensor_product)
tM === nothing && error("both modules must be tensor products")
tN = get_attribute(N, :tensor_product)
tN === nothing && error("both modules must be tensor products")
@assert length(tM) == length(tN) == length(V)
@assert all(i-> domain(V[i]) === tM[i] && codomain(V[i]) === tN[i], 1:length(V))
#gens of M are M[i][j] tensor M[h][l] for i != h and all j, l
#such a pure tensor is mapped to V[i](M[i][j]) tensor V[h](M[j][l])
#thus need the pure map - and re-create the careful ordering of the generators as in the
# constructor
#store the maps? and possibly more data, like the ordeing
decompose_M = get_attribute(M, :tensor_generator_decompose_function)
pure_N = get_attribute(N, :tensor_pure_function)
function map_gen(g) # Is there something that generalizes FreeModElem and SubquoModuleElem?
g_decomposed = decompose_M(g)
image_as_tuple = Tuple(f(x) for (f,x) in zip(V,g_decomposed))
res = pure_N(image_as_tuple)
return res
end
return hom(M, N, Vector{elem_type(N)}(map(map_gen, gens(M))))
end

# the case of a non-trivial base change
@doc raw"""
hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{<:ModuleFPHom})
Expand All @@ -67,7 +91,7 @@ say $M = M_1 \otimes \cdots \otimes M_r$, $N = N_1 \otimes \cdots \otimes N_r$,
and given a vector `V` of homomorphisms $a_i : M_i \to N_i$, return
$a_1 \otimes \cdots \otimes a_r$.
"""
function hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{ <: ModuleFPHom})
function hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{<:ModuleFPHom})
tM = get_attribute(M, :tensor_product)
tM === nothing && error("both modules must be tensor products")
tN = get_attribute(N, :tensor_product)
Expand All @@ -87,18 +111,21 @@ function hom_tensor(M::ModuleFP, N::ModuleFP, V::Vector{ <: ModuleFPHom})
res = pure_N(image_as_tuple)
return res
end
return hom(M, N, Vector{elem_type(N)}(map(map_gen, gens(M))))
bc_map = base_ring_map(first(V))
bc_map !== nothing && @assert all(domain(g) === domain(bc_map) && codomain(g) === codomain(bc_map) for g in base_ring_map.(V))
return hom(M, N, Vector{elem_type(N)}(map(map_gen, gens(M))), bc_map)
end


@doc raw"""
hom_product(M::ModuleFP, N::ModuleFP, A::Matrix{<:ModuleFPHom})
hom_product(M::ModuleFP, N::ModuleFP, A::Matrix{<:ModuleFPHom{<:ModuleFP, <:ModuleFP, Nothing}})
Given modules `M` and `N` which are products with `r` respective `s` factors,
say $M = \prod_{i=1}^r M_i$, $N = \prod_{j=1}^s N_j$, and given a $r \times s$ matrix
`A` of homomorphisms $a_{ij} : M_i \to N_j$, return the homomorphism
$M \to N$ with $ij$-components $a_{ij}$.
"""
function hom_product(M::ModuleFP, N::ModuleFP, A::Matrix{<:ModuleFPHom})
function hom_product(M::ModuleFP, N::ModuleFP, A::Matrix{<:ModuleFPHom{<:ModuleFP, <:ModuleFP, Nothing}})
tM = get_attribute(M, :direct_product)
tM === nothing && error("both modules must be direct products")
tN = get_attribute(N, :direct_product)
Expand Down
19 changes: 15 additions & 4 deletions src/Modules/UngradedModules/Methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,16 @@ function change_base_ring(f::Map{DomType, CodType}, M::SubquoModule) where {DomT
return MS, map
end

function change_base_ring(phi::Any, f::ModuleFPHom;
domain_base_change=change_base_ring(phi, domain(f))[2],
codomain_base_change=change_base_ring(phi, codomain(f))[2]
)
new_dom = codomain(domain_base_change)
new_cod = codomain(codomain_base_change)
return hom(new_dom, new_cod, codomain_base_change.(f.(gens(domain(f)))))
end


### Duals of modules
@doc raw"""
dual(M::ModuleFP; codomain::Union{FreeMod, Nothing}=nothing)
Expand Down Expand Up @@ -801,9 +811,10 @@ end
# constructor of induced maps.
tensor_product(dom::ModuleFP, cod::ModuleFP, maps::Vector{<:ModuleFPHom}) = hom_tensor(dom, cod, maps)

function tensor_product(maps::Vector{<:ModuleFPHom})
dom = tensor_product([domain(f) for f in maps])
cod = tensor_product([codomain(f) for f in maps])
return tensor_product(dom, cod, maps)
function tensor_product(maps::Vector{<:ModuleFPHom};
domain::ModuleFP = tensor_product([domain(f) for f in maps]),
codomain::ModuleFP = tensor_product([codomain(f) for f in maps])
)
return tensor_product(domain, codomain, maps)
end

2 changes: 1 addition & 1 deletion src/Modules/UngradedModules/SubQuoHom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ function matrix(f::SubQuoHom)
if !isdefined(f, :matrix)
D = domain(f)
C = codomain(f)
R = base_ring(D)
R = base_ring(C)
matrix = zero_matrix(R, ngens(D), ngens(C))
for i=1:ngens(D), j=1:ngens(C)
matrix[i,j] = f.im[i][j]
Expand Down

0 comments on commit 85af063

Please sign in to comment.