Skip to content

Commit 5581734

Browse files
authored
Merge pull request #69 from ederc/gb-qq
GBs over QQ
2 parents 67cf4d4 + 1184fb4 commit 5581734

File tree

6 files changed

+154
-36
lines changed

6 files changed

+154
-36
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "AlgebraicSolving"
22
uuid = "66b61cbe-0446-4d5d-9090-1ff510639f9d"
33
authors = ["ederc <[email protected]>", "Mohab Safey El Din <[email protected]", "Rafael Mohr <[email protected]>"]
4-
version = "0.5.1"
4+
version = "0.6.0"
55

66
[deps]
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
@@ -24,4 +24,4 @@ Random = "1.6"
2424
StaticArrays = "1"
2525
Test = "1.6"
2626
julia = "1.6"
27-
msolve_jll = "0.600.600"
27+
msolve_jll = "0.700.100"

docs/Project.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,3 @@ Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a"
88

99
[compat]
1010
Documenter = "0.26"
11-
msolve_jll = "0.600.501"

src/algorithms/groebner-bases.jl

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import msolve_jll: libneogb
1+
import msolve_jll: libneogb, libmsolve
22

33
export groebner_basis, eliminate
44

@@ -11,7 +11,7 @@ the first `eliminate` variables.
1111
1212
At the moment the underlying algorithm is based on variants of Faugère's F4 Algorithm.
1313
14-
**Note**: At the moment only ground fields of characteristic `p`, `p` prime, `p < 2^{31}` are supported.
14+
**Note**: At the moment only ground fields of characteristic `p`, `p` prime, `p < 2^{31}` and the rationals are supported.
1515
1616
# Arguments
1717
- `I::Ideal{T} where T <: MPolyRingElem`: input generators.
@@ -21,7 +21,9 @@ At the moment the underlying algorithm is based on variants of Faugère's F4 Alg
2121
- `nr_thrds::Int=1`: number of threads for parallel linear algebra.
2222
- `max_nr_pairs::Int=0`: maximal number of pairs per matrix, only bounded by minimal degree if `0`.
2323
- `la_option::Int=2`: linear algebra option: exact sparse-dense (`1`), exact sparse (`2`, default), probabilistic sparse-dense (`42`), probabilistic sparse(`44`).
24-
- `complete_reduction::Bool=true`: compute a reduced Gröbner basis for `I`
24+
- `complete_reduction::Bool=true`: compute a reduced Gröbner basis for `I`.
25+
- `normalize::Bool=false`: normalize generators of Gröbner basis for `I`, only applicable when working over the rationals.
26+
- `truncate_lifting::Int=0`: truncates the lifting process to given number of elements, only applicable when working over the rationals.
2527
- `info_level::Int=0`: info level printout: off (`0`, default), summary (`1`), detailed (`2`).
2628
2729
# Examples
@@ -48,6 +50,8 @@ function eliminate(
4850
max_nr_pairs::Int=0,
4951
la_option::Int=2,
5052
complete_reduction::Bool=true,
53+
normalize::Bool=false,
54+
truncate_lifting::Int=0,
5155
info_level::Int=0
5256
)
5357
if eliminate <= 0
@@ -57,6 +61,8 @@ function eliminate(
5761
max_nr_pairs=max_nr_pairs, la_option=la_option,
5862
eliminate=eliminate, intersect=intersect,
5963
complete_reduction=complete_reduction,
64+
normalize = normalize,
65+
truncate_lifting = truncate_lifting,
6066
info_level=info_level)
6167
end
6268
end
@@ -67,7 +73,7 @@ end
6773
Compute a Groebner basis of the ideal `I` w.r.t. to the degree reverse lexicographical monomial ordering using Faugère's F4 algorithm.
6874
At the moment the underlying algorithm is based on variants of Faugère's F4 Algorithm.
6975
70-
**Note**: At the moment only ground fields of characteristic `p`, `p` prime, `p < 2^{31}` are supported.
76+
**Note**: At the moment only ground fields of characteristic `p`, `p` prime, `p < 2^{31}` and the rationals are supported.
7177
7278
# Arguments
7379
- `I::Ideal{T} where T <: MPolyRingElem`: input generators.
@@ -77,7 +83,9 @@ At the moment the underlying algorithm is based on variants of Faugère's F4 Alg
7783
- `la_option::Int=2`: linear algebra option: exact sparse-dense (`1`), exact sparse (`2`, default), probabilistic sparse-dense (`42`), probabilistic sparse(`44`).
7884
- `eliminate::Int=0`: size of first block of variables to be eliminated.
7985
- `intersect::Bool=true`: compute the `eliminate`-th elimination ideal.
80-
- `complete_reduction::Bool=true`: compute a reduced Gröbner basis for `I`
86+
- `complete_reduction::Bool=true`: compute a reduced Gröbner basis for `I`.
87+
- `normalize::Bool=false`: normalize generators of Gröbner basis for `I`, only applicable when working over the rationals.
88+
- `truncate_lifting::Int=0`: truncates the lifting process to given number of elements, only applicable when working over the rationals.
8189
- `info_level::Int=0`: info level printout: off (`0`, default), summary (`1`), detailed (`2`).
8290
8391
# Examples
@@ -111,6 +119,8 @@ function groebner_basis(
111119
eliminate::Int=0,
112120
intersect::Bool=true,
113121
complete_reduction::Bool=true,
122+
normalize::Bool=false,
123+
truncate_lifting::Int=0,
114124
info_level::Int=0
115125
)
116126

@@ -119,6 +129,8 @@ function groebner_basis(
119129
max_nr_pairs = max_nr_pairs, la_option = la_option,
120130
eliminate = eliminate, intersect = intersect,
121131
complete_reduction = complete_reduction,
132+
normalize = normalize,
133+
truncate_lifting = truncate_lifting,
122134
info_level = info_level)
123135
end
124136
end
@@ -132,6 +144,8 @@ function _core_groebner_basis(
132144
eliminate::Int=0,
133145
intersect::Bool=true,
134146
complete_reduction::Bool=true,
147+
normalize::Bool=false,
148+
truncate_lifting::Int=0,
135149
info_level::Int=0
136150
)
137151

@@ -146,11 +160,17 @@ function _core_groebner_basis(
146160

147161
mon_order = 0
148162
elim_block_size = eliminate
163+
if elim_block_size >= nr_vars
164+
error("Number of variables to be eliminated is bigger than number of variables in ring.")
165+
end
149166
reduce_gb = Int(complete_reduction)
150167

151168
# convert ideal to flattened arrays of ints
152-
if !(is_probable_prime(field_char))
153-
error("At the moment we only support finite fields.")
169+
170+
if !(field_char == 0)
171+
if !(is_probable_prime(field_char))
172+
error("At the moment we only support finite fields or the rationals.")
173+
end
154174
end
155175

156176
# nr_gens might change if F contains zero polynomials
@@ -161,29 +181,52 @@ function _core_groebner_basis(
161181
gb_exp = Ref(Ptr{Cint}(0))
162182
gb_cf = Ref(Ptr{Cvoid}(0))
163183

164-
nr_terms = ccall((:export_f4, libneogb), Int,
165-
(Ptr{Nothing}, Ptr{Cint}, Ptr{Ptr{Cint}}, Ptr{Ptr{Cint}}, Ptr{Ptr{Cvoid}},
166-
Ptr{Cint}, Ptr{Cint}, Ptr{Cvoid}, Cint, Cint, Cint, Cint, Cint, Cint,
167-
Cint, Cint, Cint, Cint, Cint, Cint, Cint),
168-
cglobal(:jl_malloc), gb_ld, gb_len, gb_exp, gb_cf, lens, exps, cfs,
169-
field_char, mon_order, elim_block_size, nr_vars, nr_gens, initial_hts,
170-
nr_thrds, max_nr_pairs, 0, la_option, reduce_gb, 0, info_level)
184+
if field_char == 0
185+
nr_terms = ccall((:export_groebner_qq, libmsolve), Int,
186+
(Ptr{Nothing}, Ptr{Cint}, Ptr{Ptr{Cint}}, Ptr{Ptr{Cint}}, Ptr{Ptr{Cvoid}},
187+
Ptr{Cint}, Ptr{Cint}, Ptr{Cvoid}, Cint, Cint, Cint, Cint, Cint, Cint,
188+
Cint, Cint, Cint, Cint, Cint, Cint, Cint, Cint),
189+
cglobal(:jl_malloc), gb_ld, gb_len, gb_exp, gb_cf, lens, exps, cfs,
190+
field_char, mon_order, elim_block_size, nr_vars, nr_gens, initial_hts,
191+
nr_thrds, max_nr_pairs, 0, la_option, reduce_gb, 0, truncate_lifting, info_level)
192+
193+
else
194+
nr_terms = ccall((:export_f4, libneogb), Int,
195+
(Ptr{Nothing}, Ptr{Cint}, Ptr{Ptr{Cint}}, Ptr{Ptr{Cint}}, Ptr{Ptr{Cvoid}},
196+
Ptr{Cint}, Ptr{Cint}, Ptr{Cvoid}, Cint, Cint, Cint, Cint, Cint, Cint,
197+
Cint, Cint, Cint, Cint, Cint, Cint, Cint),
198+
cglobal(:jl_malloc), gb_ld, gb_len, gb_exp, gb_cf, lens, exps, cfs,
199+
field_char, mon_order, elim_block_size, nr_vars, nr_gens, initial_hts,
200+
nr_thrds, max_nr_pairs, 0, la_option, reduce_gb, 0, info_level)
201+
end
171202

172203
# convert to julia array, also give memory management to julia
173204
jl_ld = gb_ld[]
174205
jl_len = Base.unsafe_wrap(Array, gb_len[], jl_ld)
175206
jl_exp = Base.unsafe_wrap(Array, gb_exp[], nr_terms*nr_vars)
176-
ptr = reinterpret(Ptr{Int32}, gb_cf[])
177-
jl_cf = Base.unsafe_wrap(Array, ptr, nr_terms)
178207

179-
if intersect == true
180-
basis = _convert_finite_field_array_to_abstract_algebra(
181-
jl_ld, jl_len, jl_cf, jl_exp, R, eliminate)
208+
# coefficient handling depending on field characteristic
209+
if field_char == 0
210+
ptr = reinterpret(Ptr{BigInt}, gb_cf[])
211+
jl_cf = [QQFieldElem(unsafe_load(ptr, i)) for i in 1:nr_terms]
182212
else
183-
basis = _convert_finite_field_array_to_abstract_algebra(
184-
jl_ld, jl_len, jl_cf, jl_exp, R, 0)
213+
ptr = reinterpret(Ptr{Int32}, gb_cf[])
214+
jl_cf = Base.unsafe_wrap(Array, ptr, nr_terms)
215+
end
216+
217+
# shall eliminated variables be removed?
218+
if !intersect
219+
eliminate = 0
185220
end
186221

222+
# convert to basis
223+
if field_char == 0
224+
basis = _convert_rational_array_to_abstract_algebra(
225+
jl_ld, jl_len, jl_cf, jl_exp, R, normalize, eliminate)
226+
else
227+
basis = _convert_finite_field_array_to_abstract_algebra(
228+
jl_ld, jl_len, jl_cf, jl_exp, R, eliminate)
229+
end
187230
ccall((:free_f4_julia_result_data, libneogb), Nothing ,
188231
(Ptr{Nothing}, Ptr{Ptr{Cint}}, Ptr{Ptr{Cint}},
189232
Ptr{Ptr{Cvoid}}, Int, Int),

src/interfaces/nemo.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,54 @@ function _convert_finite_field_array_to_abstract_algebra(
124124

125125
return basis
126126
end
127+
128+
function _convert_rational_array_to_abstract_algebra(
129+
bld::Int32,
130+
blen::Vector{Int32},
131+
bcf::Vector{QQFieldElem},
132+
bexp::Vector{Int32},
133+
R::MPolyRing,
134+
normalize::Bool=false,
135+
eliminate::Int=0
136+
)
137+
138+
# Note: Over QQ msolve already returns the eliminated basis
139+
# only in order to save memory due to possible huge
140+
# coefficient sizes.
141+
if characteristic(R) != 0
142+
error("We assume QQ as base field here.")
143+
end
144+
145+
nr_gens = bld
146+
nr_vars = nvars(R)
147+
CR = coefficient_ring(R)
148+
149+
basis = (typeof(R(0)))[]
150+
151+
len = 0
152+
153+
for i in 1:nr_gens
154+
if bcf[len+1] == 0
155+
push!(basis, R(0))
156+
else
157+
g = MPolyBuildCtx(R)
158+
lc = bcf[len+1]
159+
160+
if normalize && lc != 1
161+
for j in 1:blen[i]
162+
push_term!(g, bcf[len+j]/lc,
163+
convert(Vector{Int}, bexp[(len+j-1)*nr_vars+1:(len+j)*nr_vars]))
164+
end
165+
else
166+
for j in 1:blen[i]
167+
push_term!(g, bcf[len+j],
168+
convert(Vector{Int}, bexp[(len+j-1)*nr_vars+1:(len+j)*nr_vars]))
169+
end
170+
end
171+
push!(basis, finish(g))
172+
end
173+
len += blen[i]
174+
end
175+
176+
return basis
177+
end

test/algorithms/groebner-bases.jl

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
@testset "Algorithms -> Gröbner bases" begin
2-
R, (x,y,z) = polynomial_ring(QQ,["x","y","z"], internal_ordering=:degrevlex)
3-
F = [x^2+1-3, x*y-z, x*z^2-3*y^2]
4-
#= not a finite field =#
5-
@test_throws ErrorException groebner_basis(Ideal(F))
62
R, (x,y,z) = polynomial_ring(GF(101),["x","y","z"], internal_ordering=:degrevlex)
73
I = Ideal([x+2*y+2*z-1, x^2+2*y^2+2*z^2-x, 2*x*y+2*y*z-y])
84
G = groebner_basis(I)
@@ -38,6 +34,29 @@
3834
I = Ideal([R(0)])
3935
G = groebner_basis(I)
4036
@test G == [R(0)]
37+
38+
R, (x1, x2) = polynomial_ring(QQ, ["x1", "x2"])
39+
I = Ideal([3*x1^2 + ZZRingElem(2)^100, 2*x1*x2 + 5*x1 + ZZRingElem(2)^100])
40+
G = groebner_basis(I)
41+
H = MPolyRingElem[
42+
3*x1 - 2*x2 - 5
43+
4*x2^2 + 20*x2 + 3802951800684688204490109616153
44+
]
45+
@test G == H
46+
J = Ideal([3*x1^2 + ZZRingElem(2)^100, 2*x1*x2 + 5*x1 + ZZRingElem(2)^100])
47+
G = groebner_basis(J, normalize=true)
48+
H = MPolyRingElem[
49+
x1 - 2//3*x2 - 5//3
50+
x2^2 + 5*x2 + 3802951800684688204490109616153//4
51+
]
52+
@test G == H
53+
R, (x,y,z) = polynomial_ring(QQ,["x","y","z"], internal_ordering=:degrevlex)
54+
I = Ideal([x+2*y+2*z-1, x^2+2*y^2+2*z^2-x, 2*x*y+2*y*z-y])
55+
G = eliminate(I, 2)
56+
H = MPolyRingElem[
57+
84*z^4 - 40*z^3 + z^2 + z
58+
]
59+
@test G == H
4160
end
4261

4362
@testset "Algorithms -> Sig Gröbner bases" begin

test/interfaces/nemo.jl

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@
99
cmp = (Int32[1], BigInt[1], Int32[1, 0], 1)
1010
@test AlgebraicSolving._convert_to_msolve(I.gens) == cmp
1111
for _GF in [GF, AlgebraicSolving.Nemo.Native.GF]
12-
R, (x,y,z) = polynomial_ring(_GF(2147483659),["x","y","z"], internal_ordering=:degrevlex)
13-
F = [x^2+1-3, x*y-z, x*z^2-3*y^2]
14-
# prime is bigger than 2^31, should throw an error
15-
@test_throws ErrorException AlgebraicSolving._convert_to_msolve(F)
16-
R, (x,y,z) = polynomial_ring(_GF(101),["x","y","z"], internal_ordering=:degrevlex)
17-
F = [x^2+1-3, x*y-z, x*z^2-3*y^2]
18-
res = AlgebraicSolving._convert_to_msolve(F)
19-
@test AlgebraicSolving._convert_finite_field_array_to_abstract_algebra(Int32(3), res[1], res[2], res[3], R) == F
12+
R, (x,y,z) = polynomial_ring(_GF(2147483659),["x","y","z"], internal_ordering=:degrevlex)
13+
F = [x^2+1-3, x*y-z, x*z^2-3*y^2]
14+
# prime is bigger than 2^31, should throw an error
15+
@test_throws ErrorException AlgebraicSolving._convert_to_msolve(F)
16+
R, (x,y,z) = polynomial_ring(_GF(101),["x","y","z"], internal_ordering=:degrevlex)
17+
F = [x^2+1-3, x*y-z, x*z^2-3*y^2]
18+
@show F
19+
res = AlgebraicSolving._convert_to_msolve(F)
20+
@test AlgebraicSolving._convert_finite_field_array_to_abstract_algebra(Int32(3), res[1], res[2], res[3], R) == F
2021
end
22+
R, (x,y,z) = polynomial_ring(QQ,["x","y","z"], internal_ordering=:degrevlex)
23+
F = [x^2+1-3, x*y-z, x*z^2-3*y^2]
24+
res = AlgebraicSolving._convert_to_msolve(F)
25+
res_qq =AlgebraicSolving.QQFieldElem[res[2][i] for i in 1:2:length(res[2])]
26+
@test AlgebraicSolving._convert_rational_array_to_abstract_algebra(Int32(3), res[1], res_qq, res[3], R) == F
2127
end

0 commit comments

Comments
 (0)