diff --git a/Project.toml b/Project.toml index 9f9318fb906e..e4dd0e14b516 100644 --- a/Project.toml +++ b/Project.toml @@ -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" diff --git a/src/Modules/UngradedModules/FreeResolutions.jl b/src/Modules/UngradedModules/FreeResolutions.jl index b0353b9598b3..8af80f5b6b6e 100644 --- a/src/Modules/UngradedModules/FreeResolutions.jl +++ b/src/Modules/UngradedModules/FreeResolutions.jl @@ -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 @@ -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"]) @@ -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 =# @@ -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 diff --git a/src/Modules/UngradedModules/ModuleGens.jl b/src/Modules/UngradedModules/ModuleGens.jl index 2c3ba80e5579..ec47c7dee1fc 100644 --- a/src/Modules/UngradedModules/ModuleGens.jl +++ b/src/Modules/UngradedModules/ModuleGens.jl @@ -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, diff --git a/src/Modules/UngradedModules/Presentation.jl b/src/Modules/UngradedModules/Presentation.jl index 5e70d60ef392..210baa120b44 100644 --- a/src/Modules/UngradedModules/Presentation.jl +++ b/src/Modules/UngradedModules/Presentation.jl @@ -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) @@ -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) diff --git a/test/Modules/MPolyQuo.jl b/test/Modules/MPolyQuo.jl index ef654cacf178..43e639cf6900 100644 --- a/test/Modules/MPolyQuo.jl +++ b/test/Modules/MPolyQuo.jl @@ -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 @@ -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 diff --git a/test/Modules/UngradedModules.jl b/test/Modules/UngradedModules.jl index c417bbc9368c..b696702483b4 100644 --- a/test/Modules/UngradedModules.jl +++ b/test/Modules/UngradedModules.jl @@ -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