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..445269c65b83 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)) # TODO: length doesn't work! +# (i, e, c) = first(s) +# push_term!(ctx, R(c), e) +# return FreeModElem(sparse_row(Qx, [(i, finish(ctx))])) +# 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(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)) # TODO: length doesn't work! +# (i, e, c) = first(s) +# push_term!(ctx, R(c), e) +# return FreeModElem(sparse_row(Qx, [(i, Qx(finish(ctx)))])) +# 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(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/src/Modules/mpolyquo.jl b/src/Modules/mpolyquo.jl index e2aca6b078ef..375cac6eb658 100644 --- a/src/Modules/mpolyquo.jl +++ b/src/Modules/mpolyquo.jl @@ -10,18 +10,18 @@ DomainType<:FreeMod{<:MPolyQuoRingElem}, CodomainType<:FreeMod{<:MPolyQuoRingElem} } + @show "this kernel" R = base_ring(codomain(f)) - P = base_ring(R) - F = _poly_module(domain(f)) - M = _as_poly_module(codomain(f)) - id = _iso_with_poly_module(codomain(f)) - # Why does img_gens(f) return a list of SubQuoElems??? - phi = _lifting_iso(codomain(f)) - g = hom(F, M, phi.(f.(gens(domain(f))))) - K, inc = kernel(g) - tr = compose(inc, _poly_module_restriction(domain(f))) - KK, inc2 = sub(domain(f), unique!(filter!(!iszero, tr.(gens(K))))) - return KK, inc2 + SR = singular_poly_ring(R) + F = domain(f) + G = codomain(f) + img_gens = images_of_generators(f) + MG = ModuleGens(img_gens, G) + singular_assure(MG) + sing_ker_gens = Singular.syz(singular_generators(MG)) + @show "done with syzygy computations" + KG = ModuleGens(F, sing_ker_gens) + return sub(F, oscar_generators(KG)) end function Base.in(a::FreeModElem{T}, M::SubModuleOfFreeModule{T}) where {T<:MPolyQuoRingElem} diff --git a/src/Rings/MPolyMap/MPolyAnyMap.jl b/src/Rings/MPolyMap/MPolyAnyMap.jl index 873365b9ad5f..03384f28d7ad 100644 --- a/src/Rings/MPolyMap/MPolyAnyMap.jl +++ b/src/Rings/MPolyMap/MPolyAnyMap.jl @@ -42,17 +42,37 @@ const _DomainTypes = Union{MPolyRing, MPolyQuoRing} coeff_map::U img_gens::Vector{V} temp_ring # temporary ring used when evaluating maps + variable_indices::Vector{Int} # a table where the i-th entry contains the + # index of the variable where it's mapped to + # in case the mapping takes such a particularly + # simple form. function MPolyAnyMap{D, C, U, V}(domain::D, - codomain::C, - coeff_map::U, - img_gens::Vector{V}) where {D, C, U, V} - @assert V === elem_type(C) - for g in img_gens - @assert parent(g) === codomain "elements does not have the correct parent" - end + codomain::C, + coeff_map::U, + img_gens::Vector{V}) where {D, C, U, V} + @assert V === elem_type(C) + for g in img_gens + @assert parent(g) === codomain "elements does not have the correct parent" + end return new{D, C, U, V}(domain, codomain, coeff_map, img_gens) end + function MPolyAnyMap{D, C, U, V}(domain::D, + codomain::C, + coeff_map::U, + img_gens::Vector{V}) where {D <: Union{<:MPolyRing, <:MPolyQuoRing}, + C <: Union{<:MPolyRing, <:MPolyQuoRing}, + U, V} + @assert V === elem_type(C) + for g in img_gens + @assert parent(g) === codomain "elements does not have the correct parent" + end + result = new{D, C, U, V}(domain, codomain, coeff_map, img_gens) + if all(is_gen(x) for x in img_gens) && allunique(img_gens) + result.variable_indices = [findfirst(x==y for y in gens(codomain)) for x in img_gens] + end + return result + end end function MPolyAnyMap(d::D, c::C, cm::U, ig::Vector{V}) where {D, C, U, V} diff --git a/src/Rings/MPolyMap/MPolyRing.jl b/src/Rings/MPolyMap/MPolyRing.jl index 4a14a083baae..d248208f2cd9 100644 --- a/src/Rings/MPolyMap/MPolyRing.jl +++ b/src/Rings/MPolyMap/MPolyRing.jl @@ -132,17 +132,101 @@ end # ################################################################################ +# Some additional methods needed for the test in the constructor for MPolyAnyMap +is_gen(x::MPolyQuoRingElem) = is_gen(lift(x)) +is_gen(x::MPolyDecRingElem) = is_gen(forget_grading(x)) + +function _evaluate_plain(F::MPolyAnyMap{<:MPolyRing, <:MPolyRing}, u) + if isdefined(F, :variable_indices) + S = codomain(F)::MPolyRing + r = ngens(S) + ctx = MPolyBuildCtx(S) + for (c, e) in zip(AbstractAlgebra.coefficients(u), AbstractAlgebra.exponent_vectors(u)) + ee = [0 for _ in 1:r] + for (i, k) in enumerate(e) + ee[F.variable_indices[i]] = k + end + push_term!(ctx, c, ee) + end + return finish(ctx) + end + + return evaluate(u, F.img_gens) +end + function _evaluate_plain(F::MPolyAnyMap{<: MPolyRing}, u) return evaluate(u, F.img_gens) end # See the comment in MPolyQuo.jl function _evaluate_plain(F::MPolyAnyMap{<:MPolyRing, <:MPolyQuoRing}, u) + if isdefined(F, :variable_indices) + S = base_ring(codomain(F))::MPolyRing + r = ngens(S) + ctx = MPolyBuildCtx(S) + for (c, e) in zip(coefficients(u), exponents(u)) + ee = [0 for _ in 1:r] + for (i, k) in enumerate(e) + ee[F.variable_indices[i]] = k + end + push_term!(ctx, c, ee) + end + return codomain(F)(finish(ctx)) + end A = codomain(F) v = evaluate(lift(u), lift.(_images(F))) return simplify(A(v)) end +function _evaluate_general(F::MPolyAnyMap{<:MPolyRing, <:MPolyRing}, u) + if domain(F) === codomain(F) && coefficient_map(F) === nothing + return evaluate(map_coefficients(coefficient_map(F), u, + parent = domain(F)), F.img_gens) + else + S = temp_ring(F) + if S !== nothing + if !isdefined(F, :variable_indices) || coefficient_ring(S) !== codomain(F) + return evaluate(map_coefficients(coefficient_map(F), u, + parent = S), F.img_gens) + else + tmp_poly = map_coefficients(coefficient_map(F), u, parent = S) + cod_ring = codomain(F)::MPolyRing + r = ngens(cod_ring) + ctx = MPolyBuildCtx(cod_ring) + for (p, e) in zip(coefficients(tmp_poly), exponents(tmp_poly)) + ee = [0 for _ in 1:r] + for (i, k) in enumerate(e) + ee[F.variable_indices[i]] = k + end + p::MPolyRingElem + @assert parent(p) === cod_ring + for (c, d) in zip(coefficients(p), exponents(p)) + push_term!(ctx, c, ee+d) + end + end + return finish(ctx) + end + else + if !isdefined(F, :variable_indices) + return evaluate(map_coefficients(coefficient_map(F), u), F.img_gens) + else + tmp_poly = map_coefficients(coefficient_map(F), u) + cod_ring = codomain(F)::MPolyRing + r = ngens(cod_ring) + ctx = MPolyBuildCtx(cod_ring) + for (c, e) in zip(coefficients(tmp_poly), exponents(tmp_poly)) + ee = [0 for _ in 1:r] + for (i, k) in enumerate(e) + ee[F.variable_indices[i]] = k + end + push_term!(ctx, c, ee) + end + return finish(ctx) + end + end + end +end + function _evaluate_general(F::MPolyAnyMap{<: MPolyRing}, u) if domain(F) === codomain(F) && coefficient_map(F) === nothing return evaluate(map_coefficients(coefficient_map(F), u, @@ -151,7 +235,7 @@ function _evaluate_general(F::MPolyAnyMap{<: MPolyRing}, u) S = temp_ring(F) if S !== nothing return evaluate(map_coefficients(coefficient_map(F), u, - parent = S), F.img_gens) + parent = S), F.img_gens) else return evaluate(map_coefficients(coefficient_map(F), u), F.img_gens) end diff --git a/test/Modules/MPolyQuo.jl b/test/Modules/MPolyQuo.jl index ef654cacf178..4505b9e6c3a0 100644 --- a/test/Modules/MPolyQuo.jl +++ b/test/Modules/MPolyQuo.jl @@ -96,7 +96,7 @@ 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) + p = free_resolution(M, length = 11) @test !iszero(p[10]) end 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