@@ -15,6 +15,8 @@ from flint.flintlib.fmpz cimport fmpz_set
1515from 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)
5967from 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
10531108cdef 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