Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement change_base_ring for hypercomplexes #3794

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)

Check warning on line 8 in experimental/DoubleAndHyperComplexes/src/base_change.jl

View check run for this annotation

Codecov / codecov/patch

experimental/DoubleAndHyperComplexes/src/base_change.jl#L8

Added line #L8 was not covered by tests
# The field is not set when using the internal constructor of the type.
if !isdefined(comp, :red_map)
comp.red_map = BaseChangeMorphism(comp)

Check warning on line 11 in experimental/DoubleAndHyperComplexes/src/base_change.jl

View check run for this annotation

Codecov / codecov/patch

experimental/DoubleAndHyperComplexes/src/base_change.jl#L10-L11

Added lines #L10 - L11 were not covered by tests
end
return comp.red_map::BaseChangeMorphism

Check warning on line 13 in experimental/DoubleAndHyperComplexes/src/base_change.jl

View check run for this annotation

Codecov / codecov/patch

experimental/DoubleAndHyperComplexes/src/base_change.jl#L13

Added line #L13 was not covered by tests
end

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

Check warning on line 17 in experimental/DoubleAndHyperComplexes/src/base_change.jl

View check run for this annotation

Codecov / codecov/patch

experimental/DoubleAndHyperComplexes/src/base_change.jl#L16-L17

Added lines #L16 - L17 were not covered by tests
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)

Check warning on line 47 in experimental/DoubleAndHyperComplexes/src/base_change_types.jl

View check run for this annotation

Codecov / codecov/patch

experimental/DoubleAndHyperComplexes/src/base_change_types.jl#L46-L47

Added lines #L46 - L47 were not covered by tests
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 @@
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

Check warning on line 25 in src/Modules/UngradedModules/FreeModuleHom.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/FreeModuleHom.jl#L24-L25

Added lines #L24 - L25 were not covered by tests
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 @@
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
Loading