@@ -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,
@@ -924,13 +932,61 @@ cdef class fmpz_mpoly(flint_mpoly):
924932 fmpz_mpoly_integral(res.val, scale.val, self .val, i, self .ctx.val)
925933 return scale, res
926934
935+ def spoly (self , g ):
936+ """
937+ Compute the S-polynomial of `self` and `g`, scaled to an integer polynomial
938+ by computing the LCM of the leading coefficients.
939+
940+ >>> from flint import Ordering
941+ >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
942+ >>> f = ctx.from_dict({(2, 0): 1, (0, 1): -1})
943+ >>> g = ctx.from_dict({(3, 0): 1, (1, 0): -1})
944+ >>> f.spoly(g)
945+ -x*y + x
946+ """
947+ cdef fmpz_mpoly res = create_fmpz_mpoly(self .ctx)
948+
949+ if not typecheck(g, fmpz_mpoly):
950+ raise TypeError (f" expected fmpz_mpoly, got {type(g)}" )
951+
952+ self .ctx.compatible_context_check((< fmpz_mpoly> g).ctx)
953+ fmpz_mpoly_spoly(res.val, self .val, (< fmpz_mpoly> g).val, self .ctx.val)
954+ return res
955+
956+ def reduction_primitive_part (self , vec ):
957+ """
958+ Compute the the primitive part of the reduction (remainder of multivariate
959+ quasi-division with remainder) with respect to the polynomials `vec`.
960+
961+ >>> from flint import Ordering
962+ >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
963+ >>> x, y = ctx.gens()
964+ >>> f = 2 * x**3 -x**2 * y + y**3 + 3 * y
965+ >>> g1 = x**2 + y**2 + 1
966+ >>> g2 = x * y - 2
967+ >>> vec = fmpz_mpoly_vec([g1, g2], ctx)
968+ >>> vec
969+ fmpz_mpoly_vec([x^2 + y^2 + 1, x*y - 2], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
970+ >>> f.reduction_primitive_part(vec)
971+ x - y^3
972+ """
973+ cdef fmpz_mpoly res = create_fmpz_mpoly(self .ctx)
974+ if not typecheck(vec, fmpz_mpoly_vec):
975+ raise TypeError (f" expected fmpz_mpoly, got {type(vec)}" )
976+
977+ self .ctx.compatible_context_check((< fmpz_mpoly_vec> vec).ctx)
978+ fmpz_mpoly_reduction_primitive_part(res.val, self .val, (< fmpz_mpoly_vec> vec).val, self .ctx.val)
979+ return res
980+
927981
928982cdef class fmpz_mpoly_vec:
929983 """
930984 A class representing a vector of fmpz_mpolys.
931985 """
932986
933- def __cinit__ (self , iterable_or_len , fmpz_mpoly_ctx ctx , bint double_indirect = False ):
987+ def __cinit__ (
988+ self , iterable_or_len , fmpz_mpoly_ctx ctx , bint double_indirect = False
989+ ):
934990 if isinstance (iterable_or_len, int ):
935991 length = iterable_or_len
936992 else :
@@ -940,7 +996,9 @@ cdef class fmpz_mpoly_vec:
940996 fmpz_mpoly_vec_init(self .val, length, self .ctx.val)
941997
942998 if double_indirect:
943- self .double_indirect = < fmpz_mpoly_struct ** > libc.stdlib.malloc(length * sizeof(fmpz_mpoly_struct * ))
999+ self .double_indirect = < fmpz_mpoly_struct ** > libc.stdlib.malloc(
1000+ length * sizeof(fmpz_mpoly_struct * )
1001+ )
9441002 if self .double_indirect is NULL :
9451003 raise MemoryError (" malloc returned a null pointer" ) # pragma: no cover
9461004
@@ -978,7 +1036,9 @@ cdef class fmpz_mpoly_vec:
9781036 elif (< fmpz_mpoly> y).ctx is not self .ctx:
9791037 raise IncompatibleContextError(f" {(<fmpz_mpoly>y).ctx} is not {self.ctx}" )
9801038
981- fmpz_mpoly_set(fmpz_mpoly_vec_entry(self .val, x), (< fmpz_mpoly> y).val, self .ctx.val)
1039+ fmpz_mpoly_set(
1040+ fmpz_mpoly_vec_entry(self .val, x), (< fmpz_mpoly> y).val, self .ctx.val
1041+ )
9821042
9831043 def __len__ (self ):
9841044 return self .val.length
@@ -994,5 +1054,166 @@ cdef class fmpz_mpoly_vec:
9941054 def __repr__ (self ):
9951055 return f" fmpz_mpoly_vec({self}, ctx={self.ctx})"
9961056
1057+ def __richcmp__ (self , other , int op ):
1058+ if not (op == Py_EQ or op == Py_NE):
1059+ return NotImplemented
1060+ elif typecheck(other, fmpz_mpoly_vec):
1061+ if (
1062+ (< fmpz_mpoly_vec> self ).ctx is (< fmpz_mpoly_vec> other).ctx
1063+ and len (self ) == len (other)
1064+ ):
1065+ return (op == Py_NE) ^ all (x == y for x, y in zip (self , other))
1066+ else :
1067+ return op == Py_NE
1068+ else :
1069+ return NotImplemented
1070+
9971071 def to_tuple (self ):
9981072 return tuple (self [i] for i in range (self .val.length))
1073+
1074+ def is_groebner (self , other = None ) -> bool:
1075+ """
1076+ Check if self is a Gröbner basis. If `other` is not None then check if self
1077+ is a Gröbner basis for `other`.
1078+
1079+ >>> from flint import Ordering
1080+ >>> ctx = fmpz_mpoly_ctx.get_context(2 , Ordering.lex, nametup = (' x' , ' y' ))
1081+ >>> x , y = ctx.gens()
1082+ >>> f = x** 2 - y
1083+ >>> g = x** 3 - x
1084+ >>> k = x* y - x
1085+ >>> h = y** 2 - y
1086+ >>> vec = fmpz_mpoly_vec([f, k, h], ctx)
1087+ >>> vec.is_groebner()
1088+ True
1089+ >>> vec.is_groebner(fmpz_mpoly_vec([f , g], ctx ))
1090+ True
1091+ >>> vec.is_groebner(fmpz_mpoly_vec([f , x**3], ctx ))
1092+ False
1093+ """
1094+ if other is None:
1095+ return <bint>fmpz_mpoly_vec_is_groebner(self.val , NULL , self.ctx.val )
1096+ elif typecheck(other , fmpz_mpoly_vec ):
1097+ self .ctx.compatible_context_check((< fmpz_mpoly_vec> other).ctx)
1098+ return < bint> fmpz_mpoly_vec_is_groebner(
1099+ self .val, (< fmpz_mpoly_vec> other).val, self .ctx.val
1100+ )
1101+ else :
1102+ raise TypeError (
1103+ f" expected either None or a fmpz_mpoly_vec, got {type(other)}"
1104+ )
1105+
1106+ def is_autoreduced (self ) -> bool:
1107+ """
1108+ Check if self is auto-reduced (or inter-reduced ).
1109+
1110+ >>> from flint import Ordering
1111+ >>> ctx = fmpz_mpoly_ctx.get_context(2 , Ordering.lex, nametup = (' x' , ' y' ))
1112+ >>> x , y = ctx.gens()
1113+ >>> f2 = 3 * x** 2 - y
1114+ >>> k = x* y - x
1115+ >>> k2 = 3 * x* y - 3 * x
1116+ >>> h = y** 2 - y
1117+ >>> vec = fmpz_mpoly_vec([f2, k, h], ctx)
1118+ >>> vec.is_autoreduced()
1119+ True
1120+ >>> vec = fmpz_mpoly_vec([f2, k2, h], ctx)
1121+ >>> vec.is_autoreduced()
1122+ False
1123+
1124+ """
1125+ return <bint>fmpz_mpoly_vec_is_autoreduced(self.val , self.ctx.val )
1126+
1127+ def autoreduction(self , groebner = False ) -> fmpz_mpoly_vec:
1128+ """
1129+ Compute the autoreduction of `self`. If `groebner` is True and `self` is a
1130+ Gröbner basis , compute the reduced reduced Gröbner basis of `self`, throws an
1131+ `RuntimeError` otherwise.
1132+
1133+ >>> from flint import Ordering
1134+ >>> ctx = fmpz_mpoly_ctx.get_context(2 , Ordering.lex, nametup = (' x' , ' y' ))
1135+ >>> x , y = ctx.gens()
1136+ >>> f2 = 3 * x** 2 - y
1137+ >>> k2 = 3 * x* y - 3 * x
1138+ >>> h = y** 2 - y
1139+ >>> vec = fmpz_mpoly_vec([f2, k2, h], ctx)
1140+ >>> vec.is_autoreduced()
1141+ False
1142+ >>> vec2 = vec.autoreduction()
1143+ >>> vec2.is_autoreduced()
1144+ True
1145+ >>> vec2
1146+ fmpz_mpoly_vec([3*x^2 - y , x*y - x , y^2 - y], ctx = fmpz_mpoly_ctx(2 , ' <Ordering.lex: 0>' , (' x' , ' y' )))
1147+ """
1148+
1149+ cdef fmpz_mpoly_vec h = fmpz_mpoly_vec(0 , self .ctx)
1150+
1151+ if groebner:
1152+ if not self.is_groebner():
1153+ raise RuntimeError (
1154+ " reduced Gröbner basis construction requires that `self` is a "
1155+ " Gröbner basis."
1156+ )
1157+ fmpz_mpoly_vec_autoreduction_groebner(h.val, self .val, self .ctx.val)
1158+ else :
1159+ fmpz_mpoly_vec_autoreduction(h.val, self .val, self .ctx.val)
1160+
1161+ return h
1162+
1163+ def buchberger_naive (self , limits = None ):
1164+ """
1165+ Compute the Gröbner basis of `self` using a naive implementation of
1166+ Buchberger’s algorithm.
1167+
1168+ Provide `limits` in the form of a tuple of `(ideal_len_limit, poly_len_limit,
1169+ poly_bits_limit)` to halt execution if the length of the ideal basis set exceeds
1170+ `ideal_len_limit`, the length of any polynomial exceeds `poly_len_limit`, or the
1171+ size of the coefficients of any polynomial exceeds `poly_bits_limit`.
1172+
1173+ If limits is provided return a tuple of `(result, success)`. If `success` is
1174+ False then `result` is a valid basis for `self`, but it may not be a Gröbner
1175+ basis.
1176+
1177+ NOTE: This function is exposed only for convenience, it is a naive
1178+ implementation and does not compute a reduced basis. To construct a reduced
1179+ basis use `autoreduce`.
1180+
1181+ >>> from flint import Ordering
1182+ >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y'))
1183+ >>> x, y = ctx.gens()
1184+ >>> f = x**2 - y
1185+ >>> g = x**3*y - x
1186+ >>> vec = fmpz_mpoly_vec([f, g], ctx)
1187+ >>> vec.is_groebner()
1188+ False
1189+ >>> vec2 = vec.buchberger_naive()
1190+ >>> vec2
1191+ 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')))
1192+ >>> vec.buchberger_naive(limits=(2, 2, 512))
1193+ (fmpz_mpoly_vec([x^2 - y, x^3*y - x], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y'))), False)
1194+ >>> vec2.is_autoreduced()
1195+ False
1196+ >>> vec2.autoreduction()
1197+ fmpz_mpoly_vec([x^2 - y, y^3 - y, x*y^2 - x], ctx=fmpz_mpoly_ctx(2, '<Ordering.lex: 0>', ('x', 'y')))
1198+ """
1199+
1200+ cdef:
1201+ fmpz_mpoly_vec g = fmpz_mpoly_vec(0 , self .ctx)
1202+ slong ideal_len_limit, poly_len_limit, poly_bits_limit
1203+
1204+ if limits is not None :
1205+ ideal_len_limit, poly_len_limit, poly_bits_limit = limits
1206+ if fmpz_mpoly_buchberger_naive_with_limits(
1207+ g.val,
1208+ self .val,
1209+ ideal_len_limit,
1210+ poly_len_limit,
1211+ poly_bits_limit,
1212+ self .ctx.val
1213+ ):
1214+ return g, True
1215+ else :
1216+ return g, False
1217+ else :
1218+ fmpz_mpoly_buchberger_naive(g.val, self .val, self .ctx.val)
1219+ return g
0 commit comments