Skip to content

Commit

Permalink
Certificate for modular Gröbner basis computations (#2652)
Browse files Browse the repository at this point in the history
* adds first version of certification for modular groebner basis

* fixes non certify modular groebner computation

* adds tests for modular groebner basis certification checker
  • Loading branch information
ederc authored Aug 11, 2023
1 parent 98403e0 commit c66d764
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 29 deletions.
91 changes: 62 additions & 29 deletions src/Rings/groebner.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,8 @@ end

# modular gröbner basis techniques using Singular
@doc raw"""
groebner_basis_modular(I::MPolyIdeal{fmpq_mpoly}; ordering::MonomialOrdering = default_ordering(base_ring(I))
groebner_basis_modular(I::MPolyIdeal{fmpq_mpoly}; ordering::MonomialOrdering = default_ordering(base_ring(I)),
certify::Bool = false)
Compute the reduced Gröbner basis of `I` w.r.t. `ordering` using a
multi-modular strategy.
Expand All @@ -1508,7 +1509,8 @@ with respect to the ordering
degrevlex([x, y, z])
```
"""
function groebner_basis_modular(I::MPolyIdeal{fmpq_mpoly}; ordering::MonomialOrdering = default_ordering(base_ring(I)))
function groebner_basis_modular(I::MPolyIdeal{fmpq_mpoly}; ordering::MonomialOrdering = default_ordering(base_ring(I)),
certify::Bool = false)

# small function to get a canonically sorted reduced gb
sorted_gb = idl -> begin
Expand Down Expand Up @@ -1536,38 +1538,48 @@ function groebner_basis_modular(I::MPolyIdeal{fmpq_mpoly}; ordering::MonomialOrd
n_stable_primes = 0
d = fmpz(p)
unlucky_primes_in_a_row = 0
while n_stable_primes < 2
p = iterate(primes, p)[1]
Rt, t = PolynomialRing(GF(p), [string(s) for s = symbols(Qt)], cached = false)
std_basis_mod_p_lifted = map(x->lift(Zt, x), sorted_gb(ideal(Rt, gens(I))))

# test for unlucky prime
if any(((i, p), ) -> leading_monomial(p) != leading_monomial(std_basis_crt_previous[i]),
enumerate(std_basis_mod_p_lifted))
unlucky_primes_in_a_row += 1
# if we get unlucky twice in a row we assume that
# we started with an unlucky prime
if unlucky_primes_in_a_row == 2
std_basis_crt_previous = std_basis_mod_p_lifted
done = false
while !done
while n_stable_primes < 2
p = iterate(primes, p)[1]
Rt, t = PolynomialRing(GF(p), [string(s) for s = symbols(Qt)], cached = false)
std_basis_mod_p_lifted = map(x->lift(Zt, x), sorted_gb(ideal(Rt, gens(I))))

# test for unlucky prime
if any(((i, p), ) -> leading_monomial(p) != leading_monomial(std_basis_crt_previous[i]),
enumerate(std_basis_mod_p_lifted))
unlucky_primes_in_a_row += 1
# if we get unlucky twice in a row we assume that
# we started with an unlucky prime
if unlucky_primes_in_a_row == 2
std_basis_crt_previous = std_basis_mod_p_lifted
end
continue
end
continue
end
unlucky_primes_in_a_row = 0

is_stable = true
for (i, f) in enumerate(std_basis_mod_p_lifted)
if !iszero(f - std_basis_crt_previous[i])
std_basis_crt_previous[i], _ = induce_crt(std_basis_crt_previous[i], d, f, fmpz(p), true)
stable = false
unlucky_primes_in_a_row = 0

is_stable = true
for (i, f) in enumerate(std_basis_mod_p_lifted)
if !iszero(f - std_basis_crt_previous[i])
std_basis_crt_previous[i], _ = induce_crt(std_basis_crt_previous[i], d, f, fmpz(p), true)
stable = false
end
end
if is_stable
n_stable_primes += 1
end
d *= fmpz(p)
end
if is_stable
n_stable_primes += 1
final_gb = fmpq_mpoly[induce_rational_reconstruction(f, d, parent = base_ring(I)) for f in std_basis_crt_previous]

I.gb[ordering] = IdealGens(final_gb, ordering)
if certify
done = _certify_modular_groebner_basis(I, ordering)
else
done = true
end
d *= fmpz(p)
end
final_gb = fmpq_mpoly[induce_rational_reconstruction(f, d, parent = base_ring(I)) for f in std_basis_crt_previous]
I.gb[ordering] = IdealGens(final_gb, ordering, isGB = true)
I.gb[ordering].isGB = true
return I.gb[ordering]
end

Expand All @@ -1580,3 +1592,24 @@ function induce_rational_reconstruction(f::fmpz_mpoly, d::fmpz; parent = 1)
return finish(g)
end

function _certify_modular_groebner_basis(I::MPolyIdeal, ordering::MonomialOrdering)
@req haskey(I.gb, ordering) "There exists no standard basis w.r.t. the given ordering."
ctr = 0
singular_generators(I.gb[ordering])
SR = I.gb[ordering].gens.Sx
SG = I.gb[ordering].gens.S

#= test if I is included in <G> =#
for f in I.gens
if Singular.reduce(SR(f), SG) != 0
break
end
ctr += 1
end
if ctr != ngens(I)
return false
end

#= test if G is a standard basis of <G> w.r.t. ordering =#
return is_standard_basis(I.gb[ordering], ordering=ordering)
end
5 changes: 5 additions & 0 deletions test/Rings/groebner.jl
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,9 @@ end
@test is_groebner_basis(I.gb[degrevlex(R)], ordering = degrevlex(R))
@test all(iszero, Oscar.reduce(groebner_basis(I), gb))
@test all(iszero, Oscar.reduce(gb, groebner_basis(I)))
J = ideal(R, [x+y^2, x*y+y^3])
J.gb[degrevlex(R)] = Oscar.IdealGens(R, [x^3])
@test Oscar._certify_modular_groebner_basis(J, degrevlex(R)) == false
groebner_basis_modular(J, ordering=wdegrevlex(R,[2,1]), certify=true)
@test gens(J.gb[wdegrevlex([x, y], [2, 1])]) == QQMPolyRingElem[x+y^2]
end

0 comments on commit c66d764

Please sign in to comment.