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

Repair quotient rings #2788

Closed
Closed
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
2 changes: 1 addition & 1 deletion docs/src/CommutativeAlgebra/affine_algebras.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 0 additions & 7 deletions experimental/Schemes/elliptic_surface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
########################################################################
Expand Down
151 changes: 103 additions & 48 deletions src/Rings/MPolyQuo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,53 @@
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))

Check warning on line 123 in src/Rings/MPolyQuo.jl

View check run for this annotation

Codecov / codecov/patch

src/Rings/MPolyQuo.jl#L123

Added line #L123 was not covered by tests

# 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

Check warning on line 138 in src/Rings/MPolyQuo.jl

View check run for this annotation

Codecov / codecov/patch

src/Rings/MPolyQuo.jl#L138

Added line #L138 was not covered by tests
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

Check warning on line 142 in src/Rings/MPolyQuo.jl

View check run for this annotation

Codecov / codecov/patch

src/Rings/MPolyQuo.jl#L142

Added line #L142 was not covered by tests
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
Expand Down Expand Up @@ -707,7 +754,7 @@
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
Expand All @@ -733,48 +780,37 @@
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)
Expand All @@ -783,7 +819,6 @@
return f::elem_type(parent(f))
end


@doc raw"""
==(f::MPolyQuoRingElem{T}, g::MPolyQuoRingElem{T}) where T

Expand All @@ -807,9 +842,20 @@
"""
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
Comment on lines +851 to +853
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks wrong to me. Please double check

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. What was I thinking...? Corrected in #2789 .

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"""
Expand Down Expand Up @@ -1320,8 +1366,17 @@
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

################################################################
Expand Down
35 changes: 35 additions & 0 deletions test/Rings/MPolyQuo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading