@@ -15,6 +15,8 @@ from flint.flintlib.fmpz cimport fmpz_set
15
15
from flint.flintlib.fmpz_mpoly cimport (
16
16
fmpz_mpoly_add,
17
17
fmpz_mpoly_add_fmpz,
18
+ fmpz_mpoly_buchberger_naive,
19
+ fmpz_mpoly_buchberger_naive_with_limits,
18
20
fmpz_mpoly_clear,
19
21
fmpz_mpoly_compose_fmpz_mpoly,
20
22
fmpz_mpoly_ctx_init,
@@ -41,20 +43,26 @@ from flint.flintlib.fmpz_mpoly cimport (
41
43
fmpz_mpoly_neg,
42
44
fmpz_mpoly_pow_fmpz,
43
45
fmpz_mpoly_push_term_fmpz_ffmpz,
46
+ fmpz_mpoly_reduction_primitive_part,
44
47
fmpz_mpoly_scalar_divides_fmpz,
45
48
fmpz_mpoly_scalar_mul_fmpz,
46
49
fmpz_mpoly_set,
47
50
fmpz_mpoly_set_coeff_fmpz_fmpz,
48
51
fmpz_mpoly_set_fmpz,
49
52
fmpz_mpoly_set_str_pretty,
50
53
fmpz_mpoly_sort_terms,
54
+ fmpz_mpoly_spoly,
51
55
fmpz_mpoly_sqrt_heap,
52
56
fmpz_mpoly_sub,
53
57
fmpz_mpoly_sub_fmpz,
54
58
fmpz_mpoly_total_degree_fmpz,
59
+ fmpz_mpoly_vec_autoreduction,
60
+ fmpz_mpoly_vec_autoreduction_groebner,
55
61
fmpz_mpoly_vec_clear,
56
62
fmpz_mpoly_vec_entry,
57
63
fmpz_mpoly_vec_init,
64
+ fmpz_mpoly_vec_is_autoreduced,
65
+ fmpz_mpoly_vec_is_groebner,
58
66
)
59
67
from flint.flintlib.fmpz_mpoly_factor cimport (
60
68
fmpz_mpoly_factor,
@@ -924,13 +932,61 @@ cdef class fmpz_mpoly(flint_mpoly):
924
932
fmpz_mpoly_integral(res.val, scale.val, self .val, i, self .ctx.val)
925
933
return scale, res
926
934
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
+
927
981
928
982
cdef class fmpz_mpoly_vec:
929
983
"""
930
984
A class representing a vector of fmpz_mpolys.
931
985
"""
932
986
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
+ ):
934
990
if isinstance (iterable_or_len, int ):
935
991
length = iterable_or_len
936
992
else :
@@ -940,7 +996,9 @@ cdef class fmpz_mpoly_vec:
940
996
fmpz_mpoly_vec_init(self .val, length, self .ctx.val)
941
997
942
998
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
+ )
944
1002
if self .double_indirect is NULL :
945
1003
raise MemoryError (" malloc returned a null pointer" ) # pragma: no cover
946
1004
@@ -978,7 +1036,9 @@ cdef class fmpz_mpoly_vec:
978
1036
elif (< fmpz_mpoly> y).ctx is not self .ctx:
979
1037
raise IncompatibleContextError(f" {(<fmpz_mpoly>y).ctx} is not {self.ctx}" )
980
1038
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
+ )
982
1042
983
1043
def __len__ (self ):
984
1044
return self .val.length
@@ -994,5 +1054,166 @@ cdef class fmpz_mpoly_vec:
994
1054
def __repr__ (self ):
995
1055
return f" fmpz_mpoly_vec({self}, ctx={self.ctx})"
996
1056
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
+
997
1071
def to_tuple (self ):
998
1072
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