From 8109e8b2366d29ea4d3bc2fa1a0eb32f7a3a5657 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 12 Sep 2023 10:47:44 +0200 Subject: [PATCH 1/5] Remove the disabling hack for elements in quotient rings. --- experimental/Schemes/elliptic_surface.jl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/experimental/Schemes/elliptic_surface.jl b/experimental/Schemes/elliptic_surface.jl index 9b4e34bc9104..4d7d66d12b9b 100644 --- a/experimental/Schemes/elliptic_surface.jl +++ b/experimental/Schemes/elliptic_surface.jl @@ -1377,13 +1377,6 @@ end ################################################################################ -# Disable simplification for the usage of (decorated) quotient rings within the -# schemes framework (speedup of ~2). -function simplify(f::MPolyQuoRingElem{<:Union{<:MPolyRingElem, <:MPolyQuoLocRingElem, - <:MPolyQuoRingElem, <:MPolyLocRingElem}}) - return f -end - ######################################################################## # Internal functionality for Weierstrass transformation ######################################################################## From 778f02f40ec0d1029af3bc3fd9fb914e0d6e1132 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 12 Sep 2023 11:49:35 +0200 Subject: [PATCH 2/5] Introduce function to decide existence of Singular backend. --- src/Rings/MPolyQuo.jl | 149 +++++++++++++++++++++++++++++------------- 1 file changed, 102 insertions(+), 47 deletions(-) diff --git a/src/Rings/MPolyQuo.jl b/src/Rings/MPolyQuo.jl index 216f91ffe3a8..08a92a54e4ed 100644 --- a/src/Rings/MPolyQuo.jl +++ b/src/Rings/MPolyQuo.jl @@ -101,6 +101,53 @@ function Base.deepcopy_internal(a::MPolyQuoRingElem, dict::IdDict) return MPolyQuoRingElem(Base.deepcopy_internal(a.f, dict), a.P, a.simplified) end +######################################################################## +# Representatives of elements in quotient rings and normal forms +# +# Elements [a] ∈ P/I in quotients of polynomial rings +# P = 𝕜[x₁,…,xₙ] by ideals I = ⟨f₁,…,fᵣ⟩ admit unique representatives +# a ∈ P whenever a normal form algorithm exists for the polynomial +# ring P. This is really a question about the ring of coefficients 𝕜. +# +# In general, we can not expect a normal form algorithm to exist and, +# in particular, that it is implemented in Singular. However, we wish +# to use Singular as a default backend and this also drives the design +# of the quotient rings to begin with. +# +# To make sure that the data structure for quotient rings can +# nevertheless also accomodate more exotic coefficient rings we +# provide the following functionality to decide the existence and use +# of a singular backend depending on the type. +######################################################################## + +is_coefficient_type_with_singular_normal_form(a::Any) = is_coefficient_type_with_singular_normal_form(typeof(a)) + +# By default we can not expect that there is a singular backend +is_coefficient_type_with_singular_normal_form(::Type{T}) where {T} = false + +# There might be rings for which groebner basis algorithms exist only +# for global orderings, so we make a refinement here +is_coefficient_type_with_groebner_basis_algorithm_in_singular(a::Any) = is_coefficient_type_with_groebner_basis_algorithm_in_singular(typeof(a)) + +function is_coefficient_type_with_groebner_basis_algorithm_in_singular(::Type{T}) where {T} + is_coefficient_type_with_singular_normal_form(T) && return true + return false +end + +# For polynomial rings over fields we expect singular to be able to handle the case +is_coefficient_type_with_singular_normal_form(::Type{T}) where {T<:FieldElem} = true +is_coefficient_type_with_singular_normal_form(::Type{T}) where {T<:Field} = true + +# Singular can also handle groebner bases over the integers +is_coefficient_type_with_groebner_basis_algorithm_in_singular(::Type{T}) where {T<:ZZRingElem} = true +is_coefficient_type_with_groebner_basis_algorithm_in_singular(::Type{T}) where {T<:ZZRing} = true + +# This list can (and should) be extended by eventual new types which +# are supposed to make use of the Singular backend. +# In particular, this decides whether a reasonable Hash function for +# elements in the quotient ring exists. + + ############################################################################## # # Quotient ring ideals @@ -733,48 +780,37 @@ julia> f x^3 + x ``` """ -function simplify(f::MPolyQuoRingElem{T}) where {S<:Union{FieldElem, ZZRingElem}, T<:MPolyRingElem{S}} - f.simplified && return f - R = parent(f) - OR = oscar_origin_ring(R) - SR = singular_origin_ring(R) - G = singular_origin_groebner_basis(R) - g = f.f - f.f = OR(reduce(SR(g), G)) - f.simplified = true - return f::elem_type(R) -end - -# Extra method for quotients of graded rings. -# TODO: Could this be simplified if the type-parameter signature of decorated rings -# was consistent with the one for polynomial rings? I.e. if the first type parameter -# was the one for the coefficient rings and not the one for the underlying polynomial ring? -function simplify(f::MPolyQuoRingElem{<:MPolyDecRingElem{<:FieldElem}}) - f.simplified && return f - R = parent(f) - OR = oscar_origin_ring(R) - SR = singular_origin_ring(R) - G = singular_origin_groebner_basis(R) - g = f.f - f.f = OR(reduce(SR(g), G)) - f.simplified = true - return f::elem_type(R) -end - -# The above methods for `simplify` assume that there is a singular backend which -# can be used. However, we are using (graded) quotient rings also with coefficient -# rings R which can not be translated to Singular; for instance when R is again -# a polynomial ring, or a quotient/localization thereof, or even a `SpecOpenRing`. -# Still in many of those cases, we can use `RingFlattening` to bind a computational -# backend. In particular, this allows us to do ideal_membership tests; see -# the file `flattenings.jl` for details. -# -# The generic method below is a compromise in the sense that `simplify` does not reduce -# a given element to a unique representative as would be the case in a groebner basis reduction, -# but it nevertheless reduces the element to zero in case its representative is -# contained in the modulus. This allows for both, the use of `RingFlattening`s and -# the potential speedup of `iszero` tests. function simplify(f::MPolyQuoRingElem) + Q = parent(f)::MPolyQuoRing + P = base_ring(Q)::MPolyRing + kk = coefficient_ring(P)::Ring + + # The following bracket is for the default assumption + if is_coefficient_type_with_groebner_basis_algorithm_in_singular(kk) + f.simplified && return f + R = parent(f) + OR = oscar_origin_ring(R) + SR = singular_origin_ring(R) + G = singular_origin_groebner_basis(R) + g = f.f + f.f = OR(reduce(SR(g), G)) + f.simplified = true + return f::elem_type(R) + end + + # The above block for `simplify` assume that there is a singular backend which + # can be used. However, we are using (graded) quotient rings also with coefficient + # rings R which can not be translated to Singular; for instance when R is again + # a polynomial ring, or a quotient/localization thereof, or even a `SpecOpenRing`. + # Still in many of those cases, we can use `RingFlattening` to bind a computational + # backend. In particular, this allows us to do ideal_membership tests; see + # the file `flattenings.jl` for details. + # + # The generic block below is a compromise in the sense that `simplify` does not reduce + # a given element to a unique representative as would be the case in a groebner basis reduction, + # but it nevertheless reduces the element to zero in case its representative is + # contained in the modulus. This allows for both, the use of `RingFlattening`s and + # the potential speedup of `iszero` tests. f.simplified && return f if f.f in modulus(parent(f)) f.f = zero(f.f) @@ -783,7 +819,6 @@ function simplify(f::MPolyQuoRingElem) return f::elem_type(parent(f)) end - @doc raw""" ==(f::MPolyQuoRingElem{T}, g::MPolyQuoRingElem{T}) where T @@ -807,9 +842,20 @@ true """ function ==(f::MPolyQuoRingElem{T}, g::MPolyQuoRingElem{T}) where T check_parent(f, g) - simplify(f) - simplify(g) - return f.f == g.f + Q = parent(f)::MPolyQuoRing + P = base_ring(Q)::MPolyRing + kk = coefficient_ring(P)::Ring + + # The following bracket is for the default assumption + if is_coefficient_type_with_groebner_basis_algorithm_in_singular(kk) + f.f == g.f && return true + hash(f) == hash(g) && return true # calls simplify already + return f.f == g.f + end + + # By default we refer to the generic ideal membership routine which + # might be implemented by other means, for instance via a `RingFlattening`. + return f.f - g.f in modulus(Q) end @doc raw""" @@ -1320,8 +1366,17 @@ function grading_group(A::MPolyQuoRing{<:MPolyDecRingElem}) end function hash(w::MPolyQuoRingElem, u::UInt) - simplify(w) - return hash(w.f, u) + Q = parent(w)::MPolyQuoRing + P = base_ring(Q)::MPolyRing + kk = coefficient_ring(P)::Ring + + # The following bracket is for the default assumption + if is_coefficient_type_with_groebner_basis_algorithm_in_singular(kk) + simplify(w) + return hash(w.f, u) + end + + error("hash function not implemented due to lack of unique representatives") end ################################################################ From bb58cdf5e48ed1d1c7074af37842cf5e798fe0ff Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 12 Sep 2023 11:50:06 +0200 Subject: [PATCH 3/5] Add tests. --- test/Rings/MPolyQuo.jl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/Rings/MPolyQuo.jl b/test/Rings/MPolyQuo.jl index 223cdd1ddab8..a06621886882 100644 --- a/test/Rings/MPolyQuo.jl +++ b/test/Rings/MPolyQuo.jl @@ -267,3 +267,38 @@ end Q = MPolyQuo(R, I, o) @test Oscar._divides_hack(one(Q), Q(y))[2] == Q(x) end + +@testset "representatives and hashing" begin + R1, (x1, y1) = QQ["x", "y"] + R2, (x2, y2) = ZZ["x", "y"] + R3, (x3, y3) = GF(7)["x", "y"] + + Q1, p1 = quo(R1, ideal(R1, [x1-3])) + Q2, p2 = quo(R2, ideal(R2, [x2-3])) + Q3, p3 = quo(R3, ideal(R3, [x3-3])) + + + @test hash(p1(x1)) == hash(Q1(3)) + @test hash(p2(x2)) == hash(Q2(3)) + @test hash(p3(x3)) == hash(Q3(3)) + + @test simplify(p1(x1)).f == Q1(3).f + @test simplify(p2(x2)).f == Q2(3).f + @test simplify(p3(x3)).f == Q3(3).f + + # A case that uses the RingFlattening and does not have a singular backend: + P, (u, v) = Q3["u", "v"] + QP, pP = quo(P, ideal(P, [u])) + @test_throws ErrorException hash(pP(v)) # Hashing is forbidden + @test simplify(pP(v-3*u)).f != simplify(pP(v)).f # Simplification does not bring + # representatives to normal form + @test pP(v-3*u) == pP(v) # Equality check works via ideal membership + a = pP(v) # Simplification only checks for being zero + @test !a.simplified + simplify(a) + @test a.simplified + @test !iszero(a.f) + b = pP(u) # Only in case the element is zero, the representative is changed + simplify(b) + @test iszero(b.f) +end From ce23d58c954121998f0cd47fbd70f012b494ea81 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 12 Sep 2023 12:27:13 +0200 Subject: [PATCH 4/5] Repair docstring. --- src/Rings/MPolyQuo.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rings/MPolyQuo.jl b/src/Rings/MPolyQuo.jl index 08a92a54e4ed..94f98a41a22f 100644 --- a/src/Rings/MPolyQuo.jl +++ b/src/Rings/MPolyQuo.jl @@ -754,7 +754,7 @@ function Base.:(==)(a::MPolyQuoIdeal{T}, b::MPolyQuoIdeal{T}) where T end @doc raw""" - simplify(f::MPolyQuoRingElem{T}) where {S<:Union{FieldElem, ZZRingElem}, T<:MPolyRingElem{S}} + simplify(f::MPolyQuoRingElem) If `f` is an element of the quotient of a multivariate polynomial ring `R` by an ideal `I` of `R`, say, replace the internal polynomial representative of `f` by its normal form mod `I` with respect to From 4e0bf930f58e04afbf69a621286bd10a546e0ef3 Mon Sep 17 00:00:00 2001 From: HechtiDerLachs Date: Tue, 12 Sep 2023 12:55:54 +0200 Subject: [PATCH 5/5] Repair documentation. --- docs/src/CommutativeAlgebra/affine_algebras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/CommutativeAlgebra/affine_algebras.md b/docs/src/CommutativeAlgebra/affine_algebras.md index b47e7d4aab26..1c549d786b8d 100644 --- a/docs/src/CommutativeAlgebra/affine_algebras.md +++ b/docs/src/CommutativeAlgebra/affine_algebras.md @@ -156,7 +156,7 @@ true ### Reducing Polynomial Representatives ```@docs -simplify(f::MPolyQuoRingElem{T}) where {S<:Union{FieldElem, ZZRingElem}, T<:MPolyRingElem{S}} +simplify(f::MPolyQuoRingElem) ``` ### Tests on Elements of Affine Algebras