Skip to content

Commit

Permalink
Adds free resolutions over quotient rings by using Singular.sres (#…
Browse files Browse the repository at this point in the history
…4134)

* adapt module gens to quotient rings

* adjusts signatures of `prune_with_map` and `_presentation_minimal`

* compute free resolutions over quotients with sres
---------

Co-authored-by: Rafael Mohr <Rafael.Mohr@lip6.fr>
Co-authored-by: HechtiDerLachs <zach@mathematik.uni-kl.de>
Co-authored-by: Lars Göttgens <lars.goettgens@gmail.com>
  • Loading branch information
4 people authored Sep 30, 2024
1 parent f091f46 commit 2fe164e
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Polymake = "0.11.20"
Random = "1.6"
RandomExtensions = "0.4.3"
Serialization = "1.6"
Singular = "0.23.4"
Singular = "0.23.8"
TOPCOM_jll = "0.17.8"
UUIDs = "1.6"
cohomCalg_jll = "0.32.0"
Expand Down
29 changes: 20 additions & 9 deletions src/Modules/UngradedModules/FreeResolutions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,15 @@ function _extend_free_resolution(cc::Hecke.ComplexOfMorphisms, idx::Int)
end

@doc raw"""
free_resolution(M::SubquoModule{<:MPolyRingElem};
ordering::ModuleOrdering = default_ordering(M),
length::Int = 0, algorithm::Symbol = :fres
)
free_resolution(M::SubquoModule{T};
length::Int=0,
algorithm::Symbol = T <:MPolyRingElem ? :fres : :sres) where {T <: Union{MPolyRingElem, MPolyQuoRingElem}}
Return a free resolution of `M`.
If `length != 0`, the free resolution is only computed up to the `length`-th free module.
Current options for `algorithm` are `:fres`, `:nres`, and `:mres`.
Current options for `algorithm` are `:fres`, `:nres`, and `:mres` for modules over
polynomial rings and `:sres` for modules over quotients of polynomial rings.
!!! note
The function first computes a presentation of `M`. It then successively computes
Expand All @@ -255,6 +255,10 @@ Current options for `algorithm` are `:fres`, `:nres`, and `:mres`.
[EMSS16](@cite). Typically, this is more efficient than the approaches above, but the
resulting resolution is far from being minimal.
!!! note
If `M` is a module over a quotient of a polynomial ring then the `length` keyword must
be set to a nonzero value.
# Examples
```jldoctest
julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z])
Expand Down Expand Up @@ -392,16 +396,20 @@ julia> matrix(map(FM3, 1))
```
**Note:** Over rings other than polynomial rings, the method will default to a lazy,
**Note:** Over rings other than polynomial rings or quotients of polynomial rings, the method will default to a lazy,
iterative kernel computation.
"""
function free_resolution(M::SubquoModule{<:MPolyRingElem};
ordering::ModuleOrdering = default_ordering(M),
length::Int=0, algorithm::Symbol=:fres)
function free_resolution(M::SubquoModule{T};
length::Int=0,
algorithm::Symbol = T <:MPolyRingElem ? :fres : :sres) where {T <: Union{MPolyRingElem, MPolyQuoRingElem}}

coefficient_ring(base_ring(M)) isa AbstractAlgebra.Field ||
error("Must be defined over a field.")

if T <: MPolyQuoRingElem
!iszero(length) || error("Specify a length up to which a free resolution should be computed")
end

cc_complete = false

#= Start with presentation =#
Expand Down Expand Up @@ -435,6 +443,9 @@ function free_resolution(M::SubquoModule{<:MPolyRingElem};
elseif algorithm == :nres
gbpres = singular_kernel_entry
res = Singular.nres(gbpres, length)
elseif algorithm == :sres && T <: MPolyQuoRingElem
gbpres = Singular.std(singular_kernel_entry)
res = Singular.sres(gbpres, length)
else
error("Unsupported algorithm $algorithm")
end
Expand Down
53 changes: 40 additions & 13 deletions src/Modules/UngradedModules/ModuleGens.jl
Original file line number Diff line number Diff line change
Expand Up @@ -261,22 +261,49 @@ end
Convert a Singular vector to a free module element.
"""
function (F::FreeMod)(s::Singular.svector)
pos = Int[]
values = []
function (F::FreeMod{<:MPolyRingElem})(s::Singular.svector)
Rx = base_ring(F)
R = base_ring(Rx)
for (i, e, c) = s
f = Base.findfirst(==(i), pos)
if f === nothing
push!(values, MPolyBuildCtx(base_ring(F)))
f = length(values)
push!(pos, i)
R = coefficient_ring(Rx)
ctx = MPolyBuildCtx(Rx)

# shortcut in order not to allocate the dictionary
if isone(length(s))
(i, e, c) = first(s)
push_term!(ctx, R(c), e)
return FreeModElem(sparse_row(Rx, [(i, finish(ctx))]), F)
end

cache = IdDict{Int, typeof(ctx)}()
for (i, e, c) in s
ctx = get!(cache, i) do
MPolyBuildCtx(Rx)
end
push_term!(ctx, R(c), e)
end
return FreeModElem(sparse_row(Rx, [(i, finish(ctx)) for (i, ctx) in cache]), F)
end

function (F::FreeMod{<:MPolyQuoRingElem})(s::Singular.svector)
Qx = base_ring(F)::MPolyQuoRing
Rx = base_ring(Qx)::MPolyRing
R = coefficient_ring(Rx)
ctx = MPolyBuildCtx(Rx)

# shortcut in order not to allocate the dictionary
if isone(length(s))
(i, e, c) = first(s)
push_term!(ctx, R(c), e)
return FreeModElem(sparse_row(Qx, [(i, Qx(finish(ctx)))]), F)
end

cache = IdDict{Int, typeof(ctx)}()
for (i, e, c) in s
ctx = get!(cache, i) do
MPolyBuildCtx(Rx)
end
push_term!(values[f], R(c), e)
push_term!(ctx, R(c), e)
end
pv = Tuple{Int, elem_type(Rx)}[(pos[i], base_ring(F)(finish(values[i]))) for i=1:length(pos)]
return FreeModElem(sparse_row(base_ring(F), pv), F)
return FreeModElem(sparse_row(Qx, [(i, Qx(finish(ctx))) for (i, ctx) in cache]), F)
end

# After creating the required infrastruture in Singular,
Expand Down
5 changes: 3 additions & 2 deletions src/Modules/UngradedModules/Presentation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ function prune_with_map(M::ModuleFP)
return N, b
end

function prune_with_map(M::ModuleFP{T}) where {T<:MPolyRingElem{<:FieldElem}} # The case that can be handled by Singular
function prune_with_map(M::ModuleFP{T}) where {T<:Union{MPolyRingElem, MPolyQuoRingElem}} # The case that can be handled by Singular

# Singular presentation
pm = presentation(M)
Expand Down Expand Up @@ -577,7 +577,8 @@ function prune_with_map(M::ModuleFP{T}) where {T<:MPolyRingElem{<:FieldElem}} #
end

function _presentation_minimal(SQ::ModuleFP{T};
minimal_kernel::Bool=true) where {T<:MPolyRingElem{<:FieldElem}}
minimal_kernel::Bool=true) where {T <: Union{MPolyRingElem, MPolyQuoRingElem}}
R = base_ring(SQ)

R = base_ring(SQ)

Expand Down
8 changes: 4 additions & 4 deletions test/Modules/MPolyQuo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ end
A2 = FreeMod(A, 2)
v = [x*A2[1] + y*A2[2], z*A2[1] + (x-1)*A2[2]]
M, _ = quo(A2, v)
p = free_resolution(M)
@test !iszero(p[10])
p = free_resolution(M, length = 11)
@test p[10] isa FreeMod
end

@testset "free resolutions II" begin
Expand All @@ -106,8 +106,8 @@ end
A, _ = quo(R, I)
A1 = FreeMod(A, 1)
M, _ = quo(A1, [y*A1[1], z*A1[1]])
p = free_resolution(M)
@test iszero(p[10])
p = free_resolution(M, length = 11)
@test p[10] isa FreeMod
end

@testset "kernels of FreeMod -> SubquoModule" begin
Expand Down
8 changes: 8 additions & 0 deletions test/Modules/UngradedModules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,14 @@ end
@test relations(C) == [zero(F)]
@test domain(isom) == F
@test codomain(isom) == C

R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);
A, p = quo(R, ideal(R, x^5))
M1 = identity_matrix(A, 2)
M2 = A[-x-y 2*x^2+x; z^4 0; 0 z^4; 8*x^3*y - 4*x^3 - 4*x^2*y + 2*x^2 + 2*x*y - x - y x; x^4 0]
M = SubquoModule(M1, M2)
fr = free_resolution(M, length = 9)
@test all(iszero, homology(fr)[2:end])
end

@testset "Prune With Map" begin
Expand Down

0 comments on commit 2fe164e

Please sign in to comment.