Skip to content

Commit 83279d9

Browse files
committed
Add Groebner basis related functions for fmpz_mpoly_vec
1 parent 464a276 commit 83279d9

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

src/flint/types/fmpz_mpoly.pyx

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ from flint.flintlib.fmpz cimport fmpz_set
1515
from flint.flintlib.fmpz_mpoly cimport (
1616
fmpz_mpoly_add,
1717
fmpz_mpoly_add_fmpz,
18+
fmpz_mpoly_buchberger_naive,
19+
fmpz_mpoly_buchberger_naive_with_limits,
1820
fmpz_mpoly_clear,
1921
fmpz_mpoly_compose_fmpz_mpoly,
2022
fmpz_mpoly_ctx_init,
@@ -41,20 +43,26 @@ from flint.flintlib.fmpz_mpoly cimport (
4143
fmpz_mpoly_neg,
4244
fmpz_mpoly_pow_fmpz,
4345
fmpz_mpoly_push_term_fmpz_ffmpz,
46+
fmpz_mpoly_reduction_primitive_part,
4447
fmpz_mpoly_scalar_divides_fmpz,
4548
fmpz_mpoly_scalar_mul_fmpz,
4649
fmpz_mpoly_set,
4750
fmpz_mpoly_set_coeff_fmpz_fmpz,
4851
fmpz_mpoly_set_fmpz,
4952
fmpz_mpoly_set_str_pretty,
5053
fmpz_mpoly_sort_terms,
54+
fmpz_mpoly_spoly,
5155
fmpz_mpoly_sqrt_heap,
5256
fmpz_mpoly_sub,
5357
fmpz_mpoly_sub_fmpz,
5458
fmpz_mpoly_total_degree_fmpz,
59+
fmpz_mpoly_vec_autoreduction,
60+
fmpz_mpoly_vec_autoreduction_groebner,
5561
fmpz_mpoly_vec_clear,
5662
fmpz_mpoly_vec_entry,
5763
fmpz_mpoly_vec_init,
64+
fmpz_mpoly_vec_is_autoreduced,
65+
fmpz_mpoly_vec_is_groebner,
5866
)
5967
from flint.flintlib.fmpz_mpoly_factor cimport (
6068
fmpz_mpoly_factor,
@@ -1049,6 +1057,53 @@ cdef class fmpz_mpoly(flint_mpoly):
10491057
fmpz_mpoly_integral(res.val, scale.val, self.val, i, self.ctx.val)
10501058
return scale, res
10511059

1060+
def spoly(self, g):
1061+
"""
1062+
Compute the S-polynomial of `self` and `g`, scaled to an integer polynomial by computing the LCM of the
1063+
leading coefficients.
1064+
1065+
>>> from flint import Ordering
1066+
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
1067+
>>> f = ctx.from_dict({(2, 0): 1, (0, 1): -1})
1068+
>>> g = ctx.from_dict({(3, 0): 1, (1, 0): -1})
1069+
>>> f.spoly(g)
1070+
-x*y + x
1071+
1072+
"""
1073+
cdef fmpz_mpoly res = create_fmpz_mpoly(self.ctx)
1074+
1075+
if not typecheck(g, fmpz_mpoly):
1076+
raise TypeError(f"expected fmpz_mpoly, got {type(g)}")
1077+
1078+
self.ctx.compatible_context_check((<fmpz_mpoly>g).ctx)
1079+
fmpz_mpoly_spoly(res.val, self.val, (<fmpz_mpoly>g).val, self.ctx.val)
1080+
return res
1081+
1082+
def reduction_primitive_part(self, vec):
1083+
"""
1084+
Compute the the primitive part of the reduction (remainder of multivariate quasi-division with remainder)
1085+
with respect to the polynomials `vec`.
1086+
1087+
>>> from flint import Ordering
1088+
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
1089+
>>> f = ctx.from_dict({(3, 0): 2, (2, 1): -1, (0, 3): 1, (0, 1): 3})
1090+
>>> g1 = ctx.from_dict({(2, 0): 1, (0, 2): 1, (0, 0): 1})
1091+
>>> g2 = ctx.from_dict({(1, 1): 1, (0, 0): -2})
1092+
>>> vec = fmpz_mpoly_vec([g1, g2], ctx)
1093+
>>> vec
1094+
fmpz_mpoly_vec([x^2 + y^2 + 1, x*y - 2], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1095+
>>> f.reduction_primitive_part(vec)
1096+
x - y^3
1097+
1098+
"""
1099+
cdef fmpz_mpoly res = create_fmpz_mpoly(self.ctx)
1100+
if not typecheck(vec, fmpz_mpoly_vec):
1101+
raise TypeError(f"expected fmpz_mpoly, got {type(vec)}")
1102+
1103+
self.ctx.compatible_context_check((<fmpz_mpoly_vec>vec).ctx)
1104+
fmpz_mpoly_reduction_primitive_part(res.val, self.val, (<fmpz_mpoly_vec>vec).val, self.ctx.val)
1105+
return res
1106+
10521107

10531108
cdef class fmpz_mpoly_vec:
10541109
"""
@@ -1121,3 +1176,131 @@ cdef class fmpz_mpoly_vec:
11211176

11221177
def to_tuple(self):
11231178
return tuple(self[i] for i in range(self.val.length))
1179+
1180+
def is_groebner(self, other=None) -> bool:
1181+
"""
1182+
Check if self is a Gröbner basis. If `other` is not None then check if self is a Gröbner basis for `other`.
1183+
1184+
>>> from flint import Ordering
1185+
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
1186+
>>> f = ctx.from_dict({(2, 0): 1, (0, 1): -1})
1187+
>>> g = ctx.from_dict({(3, 0): 1, (1, 0): -1})
1188+
>>> k = ctx.from_dict({(1, 1): 1, (1, 0): -1})
1189+
>>> h = ctx.from_dict({(0, 2): 1, (0, 1): -1})
1190+
>>> vec = fmpz_mpoly_vec([f, k, h], ctx)
1191+
>>> vec
1192+
fmpz_mpoly_vec([x^2 - y, x*y - x, y^2 - y], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1193+
>>> vec.is_groebner()
1194+
True
1195+
>>> vec.is_groebner(fmpz_mpoly_vec([f, g], ctx))
1196+
True
1197+
>>> vec.is_groebner(fmpz_mpoly_vec([f, ctx.from_dict({(3, 0): 1})], ctx))
1198+
False
1199+
1200+
"""
1201+
if other is None:
1202+
return <bint>fmpz_mpoly_vec_is_groebner(self.val, NULL, self.ctx.val)
1203+
elif typecheck(other, fmpz_mpoly_vec):
1204+
self.ctx.compatible_context_check((<fmpz_mpoly_vec>other).ctx)
1205+
return <bint>fmpz_mpoly_vec_is_groebner(self.val, (<fmpz_mpoly_vec>other).val, self.ctx.val)
1206+
else:
1207+
raise TypeError(f"expected either None or a fmpz_mpoly_vec, got {type(other)}")
1208+
1209+
def is_autoreduced(self) -> bool:
1210+
"""
1211+
Check if self is auto-reduced (or inter-reduced).
1212+
1213+
>>> from flint import Ordering
1214+
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
1215+
>>> f = ctx.from_dict({(2, 0): 3, (0, 1): -1})
1216+
>>> k1 = ctx.from_dict({(1, 1): 1, (1, 0): -1})
1217+
>>> k2 = ctx.from_dict({(1, 1): 3, (1, 0): -3})
1218+
>>> h = ctx.from_dict({(0, 2): 1, (0, 1): -1})
1219+
>>> vec = fmpz_mpoly_vec([f, k1, h], ctx)
1220+
>>> vec
1221+
fmpz_mpoly_vec([3*x^2 - y, x*y - x, y^2 - y], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1222+
>>> vec.is_autoreduced()
1223+
True
1224+
>>> vec = fmpz_mpoly_vec([f, k2, h], ctx)
1225+
>>> vec
1226+
fmpz_mpoly_vec([3*x^2 - y, 3*x*y - 3*x, y^2 - y], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1227+
>>> vec.is_autoreduced()
1228+
False
1229+
1230+
"""
1231+
return <bint>fmpz_mpoly_vec_is_autoreduced(self.val, self.ctx.val)
1232+
1233+
def autoreduction(self, groebner=False) -> fmpz_mpoly_vec:
1234+
"""
1235+
Compute the autoreduction of `self`. If `groebner` is True and `self` is a Gröbner basis, compute the reduced
1236+
reduced Gröbner basis of `self`, throws an `RuntimeError` otherwise.
1237+
1238+
>>> from flint import Ordering
1239+
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
1240+
>>> f = ctx.from_dict({(2, 0): 3, (0, 1): -1})
1241+
>>> k2 = ctx.from_dict({(1, 1): 3, (1, 0): -3})
1242+
>>> h = ctx.from_dict({(0, 2): 1, (0, 1): -1})
1243+
>>> vec = fmpz_mpoly_vec([f, k2, h], ctx)
1244+
>>> vec
1245+
fmpz_mpoly_vec([3*x^2 - y, 3*x*y - 3*x, y^2 - y], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1246+
>>> vec.is_autoreduced()
1247+
False
1248+
>>> vec2 = vec.autoreduction()
1249+
>>> vec2.is_autoreduced()
1250+
True
1251+
>>> vec2
1252+
fmpz_mpoly_vec([3*x^2 - y, x*y - x, y^2 - y], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1253+
1254+
"""
1255+
1256+
cdef fmpz_mpoly_vec h = fmpz_mpoly_vec(0, self.ctx)
1257+
1258+
if groebner:
1259+
if not self.is_groebner():
1260+
raise RuntimeError("reduced Gröbner basis construction requires that `self` is a Gröbner basis.")
1261+
fmpz_mpoly_vec_autoreduction_groebner(h.val, self.val, self.ctx.val)
1262+
else:
1263+
fmpz_mpoly_vec_autoreduction(h.val, self.val, self.ctx.val)
1264+
1265+
return h
1266+
1267+
def buchberger_naive(self, limits=None):
1268+
"""
1269+
Compute the Gröbner basis of `self` using a naive implementation of Buchberger’s algorithm.
1270+
1271+
Provide `limits` in the form of a tuple of `(ideal_len_limit, poly_len_limit, poly_bits_limit)` to halt
1272+
execution if the length of the ideal basis set exceeds `ideal_len_limit`, the length of any polynomial exceeds
1273+
`poly_len_limit`, or the size of the coefficients of any polynomial exceeds `poly_bits_limit`.
1274+
1275+
If limits is provided return a tuple of `(result, success)`. If `success` is False then `result` is a valid
1276+
basis for `self`, but it may not be a Gröbner basis.
1277+
1278+
>>> from flint import Ordering
1279+
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
1280+
>>> f = ctx.from_dict({(2, 0): 1, (0, 1): -1})
1281+
>>> g = ctx.from_dict({(3, 1): 1, (1, 0): -1})
1282+
>>> vec = fmpz_mpoly_vec([f, g], ctx)
1283+
>>> vec
1284+
fmpz_mpoly_vec([x^2 - y, x^3*y - x], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1285+
>>> vec.is_groebner()
1286+
False
1287+
>>> vec.buchberger_naive()
1288+
fmpz_mpoly_vec([x^2 - y, x^3*y - x, x*y^2 - x, y^3 - y], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1289+
>>> vec.buchberger_naive(limits=(2, 2, 512))
1290+
(fmpz_mpoly_vec([x^2 - y, x^3*y - x], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y'))), False)
1291+
1292+
"""
1293+
1294+
cdef:
1295+
fmpz_mpoly_vec g = fmpz_mpoly_vec(0, self.ctx)
1296+
slong ideal_len_limit, poly_len_limit, poly_bits_limit
1297+
1298+
if limits is not None:
1299+
ideal_len_limit, poly_len_limit, poly_bits_limit = limits
1300+
if fmpz_mpoly_buchberger_naive_with_limits(g.val, self.val, ideal_len_limit, poly_len_limit, poly_bits_limit, self.ctx.val):
1301+
return g, True
1302+
else:
1303+
return g, False
1304+
else:
1305+
fmpz_mpoly_buchberger_naive(g.val, self.val, self.ctx.val)
1306+
return g

0 commit comments

Comments
 (0)