passagemath-brial 10.6.31rc3__cp314-cp314-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-brial might be problematic. Click here for more details.
- passagemath_brial-10.6.31rc3.dist-info/METADATA +97 -0
- passagemath_brial-10.6.31rc3.dist-info/RECORD +39 -0
- passagemath_brial-10.6.31rc3.dist-info/WHEEL +5 -0
- passagemath_brial-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_brial.libs/libbrial-83985df5.so.3.0.7 +0 -0
- passagemath_brial.libs/libbrial_groebner-a6504217.so.3.0.7 +0 -0
- passagemath_brial.libs/libgcc_s-2d945d6c.so.1 +0 -0
- passagemath_brial.libs/libm4ri-5e907cd2.so.1.0.0 +0 -0
- passagemath_brial.libs/libpng16-09496a15.so.16.43.0 +0 -0
- passagemath_brial.libs/libstdc++-85f2cd6d.so.6.0.33 +0 -0
- sage/all__sagemath_brial.py +9 -0
- sage/libs/all__sagemath_brial.py +1 -0
- sage/libs/polybori/__init__.pxd +2 -0
- sage/libs/polybori/decl.pxd +401 -0
- sage/libs/polybori/pb_wrap.h +133 -0
- sage/rings/all__sagemath_brial.py +1 -0
- sage/rings/polynomial/all__sagemath_brial.py +1 -0
- sage/rings/polynomial/pbori/PyPolyBoRi.py +123 -0
- sage/rings/polynomial/pbori/__init__.py +44 -0
- sage/rings/polynomial/pbori/blocks.py +443 -0
- sage/rings/polynomial/pbori/cnf.py +241 -0
- sage/rings/polynomial/pbori/easy_polynomials.py +56 -0
- sage/rings/polynomial/pbori/fglm.py +93 -0
- sage/rings/polynomial/pbori/frontend.py +70 -0
- sage/rings/polynomial/pbori/gbcore.py +634 -0
- sage/rings/polynomial/pbori/gbrefs.py +127 -0
- sage/rings/polynomial/pbori/heuristics.py +35 -0
- sage/rings/polynomial/pbori/interpolate.py +115 -0
- sage/rings/polynomial/pbori/interred.py +35 -0
- sage/rings/polynomial/pbori/ll.py +292 -0
- sage/rings/polynomial/pbori/nf.py +662 -0
- sage/rings/polynomial/pbori/parallel.py +298 -0
- sage/rings/polynomial/pbori/pbori.cpython-314-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/pbori/pbori.pxd +127 -0
- sage/rings/polynomial/pbori/pbori.pyx +8107 -0
- sage/rings/polynomial/pbori/randompoly.py +105 -0
- sage/rings/polynomial/pbori/rank.py +27 -0
- sage/rings/polynomial/pbori/specialsets.py +112 -0
- sage/rings/polynomial/pbori/statistics.py +31 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-brial
|
|
2
|
+
# sage.doctest: needs sage.rings.polynomial.pbori
|
|
3
|
+
import contextlib
|
|
4
|
+
from copy import copy
|
|
5
|
+
from itertools import chain
|
|
6
|
+
from inspect import getfullargspec as getargspec
|
|
7
|
+
|
|
8
|
+
from .nf import GeneratorLimitExceeded, symmGB_F2_C, symmGB_F2_python
|
|
9
|
+
from .pbori import GroebnerStrategy, ll_red_nf_redsb
|
|
10
|
+
from .PyPolyBoRi import (Monomial, Polynomial,
|
|
11
|
+
OrderCode)
|
|
12
|
+
from .ll import eliminate, ll_encode
|
|
13
|
+
from .statistics import used_vars_set
|
|
14
|
+
from .heuristics import dense_system, gauss_on_linear
|
|
15
|
+
from .easy_polynomials import easy_linear_polynomials
|
|
16
|
+
from .interpolate import lex_groebner_basis_for_polynomial_via_variety
|
|
17
|
+
from .fglm import _fglm
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_options_from_function(f):
|
|
21
|
+
(argnames, varargs, varopts, defaults) = getargspec(f)[:4]
|
|
22
|
+
return dict(zip(argnames[-len(defaults):], defaults))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def filter_oldstyle_options(**options):
|
|
26
|
+
filtered = {}
|
|
27
|
+
for key in options:
|
|
28
|
+
newkey = key
|
|
29
|
+
for prefix in ['use_', 'opt_allow_', 'opt_']:
|
|
30
|
+
newkey = newkey.replace(prefix, '')
|
|
31
|
+
filtered[newkey] = options[key]
|
|
32
|
+
return filtered
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def filter_newstyle_options(func, **options):
|
|
36
|
+
allowed = get_options_from_function(func).keys()
|
|
37
|
+
filtered = {}
|
|
38
|
+
for key in options:
|
|
39
|
+
for prefix in ['', 'use_', 'opt_', 'opt_allow_']:
|
|
40
|
+
if prefix + key in allowed:
|
|
41
|
+
filtered[prefix + key] = options[key]
|
|
42
|
+
|
|
43
|
+
return filtered
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def want_interpolation_gb(G):
|
|
47
|
+
if not G or G[0].ring().get_order_code() != OrderCode.lp or len(G) != 1:
|
|
48
|
+
return False
|
|
49
|
+
p = Polynomial(G[0])
|
|
50
|
+
return not (p.lead_deg() <= 1 or p.set().n_nodes() > 1000)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def ll_is_good(I):
|
|
54
|
+
lex_lead = set()
|
|
55
|
+
for p in I:
|
|
56
|
+
if not p.is_zero():
|
|
57
|
+
m = p.lex_lead()
|
|
58
|
+
if m.deg() == 1:
|
|
59
|
+
lex_lead.add(next(iter(m.variables())).index())
|
|
60
|
+
if len(lex_lead) >= 0.8 * len(I):
|
|
61
|
+
uv = used_vars_set(I).deg() # don't use len here, which will yield 1
|
|
62
|
+
if len(lex_lead) > 0.9 * uv:
|
|
63
|
+
if uv - len(lex_lead) > 16:
|
|
64
|
+
return "llfirstonthefly"
|
|
65
|
+
return "llfirst"
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def ll_heuristic(d):
|
|
70
|
+
d = copy(d)
|
|
71
|
+
I = d["I"]
|
|
72
|
+
if ("llfirstonthefly" not in d) and ("llfirst" not in d):
|
|
73
|
+
hint = ll_is_good(I)
|
|
74
|
+
if hint:
|
|
75
|
+
d[hint] = True
|
|
76
|
+
return d
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def change_order_heuristic(d):
|
|
80
|
+
d_orig = d
|
|
81
|
+
d = copy(d)
|
|
82
|
+
I = d["I"]
|
|
83
|
+
if not I:
|
|
84
|
+
return d
|
|
85
|
+
switch_table = {OrderCode.lp: OrderCode.dp_asc, OrderCode.dlex: OrderCode.
|
|
86
|
+
dp_asc}
|
|
87
|
+
if "other_ordering_first" not in d:
|
|
88
|
+
# TODO after ll situation might look much different, so heuristic is on
|
|
89
|
+
# wrong place
|
|
90
|
+
code = next(iter(I)).ring().get_order_code()
|
|
91
|
+
if code in switch_table:
|
|
92
|
+
max_non_linear = len(I) // 2
|
|
93
|
+
non_linear = 0
|
|
94
|
+
if code == OrderCode.lp:
|
|
95
|
+
for p in I:
|
|
96
|
+
if p.lead_deg() > 1:
|
|
97
|
+
non_linear = non_linear + 1
|
|
98
|
+
if non_linear > max_non_linear:
|
|
99
|
+
break
|
|
100
|
+
if (non_linear > max_non_linear) or (code != OrderCode.lp):
|
|
101
|
+
other_ordering_opts = copy(d_orig)
|
|
102
|
+
other_ordering_opts["switch_to"] = switch_table[code]
|
|
103
|
+
d["other_ordering_first"] = other_ordering_opts
|
|
104
|
+
return d
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def interpolation_gb_heuristic(d):
|
|
108
|
+
d = copy(d)
|
|
109
|
+
I = d["I"]
|
|
110
|
+
if not d.get("other_ordering_opts", False) and want_interpolation_gb(I):
|
|
111
|
+
d["interpolation_gb"] = True
|
|
112
|
+
d["other_ordering_first"] = False
|
|
113
|
+
return d
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def linear_algebra_heuristic(d):
|
|
117
|
+
d = copy(d)
|
|
118
|
+
I = d["I"]
|
|
119
|
+
|
|
120
|
+
def want_la():
|
|
121
|
+
if not I:
|
|
122
|
+
return False
|
|
123
|
+
n_used_vars = None
|
|
124
|
+
bound = None
|
|
125
|
+
if next(iter(I)).ring().has_degree_order():
|
|
126
|
+
new_bound = 200
|
|
127
|
+
n_used_vars = used_vars_set(I, bound=new_bound).deg()
|
|
128
|
+
if n_used_vars < new_bound:
|
|
129
|
+
return True
|
|
130
|
+
bound = new_bound
|
|
131
|
+
if dense_system(I):
|
|
132
|
+
new_bound = 100
|
|
133
|
+
if not (bound and new_bound < bound):
|
|
134
|
+
n_used_vars = used_vars_set(I, bound=new_bound).deg()
|
|
135
|
+
bound = new_bound
|
|
136
|
+
if n_used_vars < bound:
|
|
137
|
+
return True
|
|
138
|
+
return False
|
|
139
|
+
if not (("faugere" in d and not d["faugere"]) or
|
|
140
|
+
("noro" in d and d["noro"])):
|
|
141
|
+
if ("faugere" in d and d["faugere"]) or want_la():
|
|
142
|
+
|
|
143
|
+
d["faugere"] = True
|
|
144
|
+
if "red_tail" not in d:
|
|
145
|
+
d["red_tail"] = False
|
|
146
|
+
if "selection_size" not in d:
|
|
147
|
+
d["selection_size"] = 10000
|
|
148
|
+
if "ll" not in d:
|
|
149
|
+
d["ll"] = True
|
|
150
|
+
|
|
151
|
+
return d
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def trivial_heuristic(d):
|
|
155
|
+
return d
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class HeuristicalFunction:
|
|
159
|
+
def __call__(self, *args, **kwds):
|
|
160
|
+
complete_dict = copy(kwds)
|
|
161
|
+
heuristic = True
|
|
162
|
+
with contextlib.suppress(KeyError):
|
|
163
|
+
heuristic = complete_dict["heuristic"]
|
|
164
|
+
for (k, v) in zip(self.argnames, args):
|
|
165
|
+
complete_dict[k] = v
|
|
166
|
+
if heuristic:
|
|
167
|
+
complete_dict = self.heuristicFunction(complete_dict)
|
|
168
|
+
return self.f(**complete_dict)
|
|
169
|
+
|
|
170
|
+
def __init__(self, f, heuristic_function):
|
|
171
|
+
self.argnames, self.varargs, self.varopts, self.defaults = getargspec(f)[:4]
|
|
172
|
+
if hasattr(f, "options"):
|
|
173
|
+
self.options = f.options
|
|
174
|
+
else:
|
|
175
|
+
self.options = dict(zip(self.argnames[-len(self.defaults):], self.
|
|
176
|
+
defaults))
|
|
177
|
+
self.heuristicFunction = heuristic_function
|
|
178
|
+
self.f = f
|
|
179
|
+
self.__doc__ = f.__doc__
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def with_heuristic(heuristic_function):
|
|
183
|
+
def make_wrapper(f):
|
|
184
|
+
wrapped = HeuristicalFunction(f, heuristic_function)
|
|
185
|
+
wrapped.__name__ = f.__name__
|
|
186
|
+
return wrapped
|
|
187
|
+
return make_wrapper
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def clean_polys_pre(I):
|
|
191
|
+
wrap = (Polynomial(p) for p in I)
|
|
192
|
+
return (list({p for p in wrap if not p.is_zero()}), None)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def gb_with_pre_post_option(option, pre=None,
|
|
196
|
+
post=None, if_not_option=(),
|
|
197
|
+
default=False):
|
|
198
|
+
def make_wrapper(f):
|
|
199
|
+
def wrapper(I, **kwds):
|
|
200
|
+
prot = kwds.get("prot", False)
|
|
201
|
+
for o in if_not_option:
|
|
202
|
+
if (o in kwds and kwds[o]) or (o not in kwds and
|
|
203
|
+
groebner_basis.options[o]):
|
|
204
|
+
option_set = False
|
|
205
|
+
if "option_set" not in locals():
|
|
206
|
+
option_set = kwds.get(option, default)
|
|
207
|
+
kwds = {o: kwds[o] for o in kwds if o != option}
|
|
208
|
+
state = None
|
|
209
|
+
|
|
210
|
+
if option_set and pre:
|
|
211
|
+
pre_args = getargspec(pre)[0]
|
|
212
|
+
if prot:
|
|
213
|
+
print("preprocessing for option:", option)
|
|
214
|
+
|
|
215
|
+
local_symbols = copy(locals())
|
|
216
|
+
(I, state) = pre(**{k: v for (k, v) in local_symbols.items()
|
|
217
|
+
if k in pre_args})
|
|
218
|
+
I = f(I, **kwds)
|
|
219
|
+
if option_set and post:
|
|
220
|
+
post_args = getargspec(post)[0]
|
|
221
|
+
if prot:
|
|
222
|
+
print("postprocessing for option:", option)
|
|
223
|
+
local_symbols = copy(locals())
|
|
224
|
+
I = post(**{k: v for (k, v) in local_symbols.items()
|
|
225
|
+
if k in post_args})
|
|
226
|
+
|
|
227
|
+
return I
|
|
228
|
+
wrapper.__name__ = f.__name__
|
|
229
|
+
wrapper.__doc__ = f.__doc__
|
|
230
|
+
if hasattr(f, "options"):
|
|
231
|
+
wrapper.options = copy(f.options)
|
|
232
|
+
else:
|
|
233
|
+
|
|
234
|
+
wrapper.options = get_options_from_function(f)
|
|
235
|
+
|
|
236
|
+
wrapper.options[option] = default
|
|
237
|
+
return wrapper
|
|
238
|
+
return make_wrapper
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def redsb_post(I, state):
|
|
242
|
+
if not I:
|
|
243
|
+
return []
|
|
244
|
+
return I.minimalize_and_tail_reduce()
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def minsb_post(I, state):
|
|
248
|
+
if not I:
|
|
249
|
+
return []
|
|
250
|
+
return I.minimalize()
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def invert_all(I):
|
|
254
|
+
return [p.map_every_x_to_x_plus_one() for p in I]
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def invert_all_pre(I):
|
|
258
|
+
return (invert_all(I), None)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def invert_all_post(I, state):
|
|
262
|
+
return invert_all(I)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def llfirst_pre(I, prot):
|
|
266
|
+
(eliminated, llnf, I) = eliminate(I, on_the_fly=False, prot=prot)
|
|
267
|
+
return (I, eliminated)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def ll_constants_pre(I):
|
|
271
|
+
ll_res = []
|
|
272
|
+
|
|
273
|
+
while any(p.lex_lead_deg() == 1 and (p + p.lex_lead()).constant()
|
|
274
|
+
for p in I):
|
|
275
|
+
I_new = []
|
|
276
|
+
ll = []
|
|
277
|
+
leads = set()
|
|
278
|
+
for p in I:
|
|
279
|
+
if p.lex_lead_deg() == 1:
|
|
280
|
+
l = p.lead()
|
|
281
|
+
if l not in leads and p.is_singleton_or_pair():
|
|
282
|
+
tail = p + l
|
|
283
|
+
if tail.deg() <= 0:
|
|
284
|
+
ll.append(p)
|
|
285
|
+
leads.add(l)
|
|
286
|
+
continue
|
|
287
|
+
I_new.append(p)
|
|
288
|
+
encoded = ll_encode(ll)
|
|
289
|
+
reduced = []
|
|
290
|
+
for p in I_new:
|
|
291
|
+
rp = ll_red_nf_redsb(p, encoded)
|
|
292
|
+
if not rp.is_zero():
|
|
293
|
+
reduced.append(rp)
|
|
294
|
+
I = reduced
|
|
295
|
+
ll_res.extend(ll)
|
|
296
|
+
return (I, ll_res)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def variety_size_from_gb(I):
|
|
300
|
+
"""
|
|
301
|
+
TESTS::
|
|
302
|
+
|
|
303
|
+
sage: from sage.rings.polynomial.pbori import Ring, Monomial, Polynomial
|
|
304
|
+
sage: from sage.rings.polynomial.pbori.gbcore import variety_size_from_gb
|
|
305
|
+
sage: r = Ring(100)
|
|
306
|
+
sage: x = r.variable
|
|
307
|
+
sage: variety_size_from_gb([])
|
|
308
|
+
1
|
|
309
|
+
sage: variety_size_from_gb([Polynomial(0, r)])
|
|
310
|
+
1
|
|
311
|
+
sage: variety_size_from_gb([Polynomial(1, r)])
|
|
312
|
+
0.0
|
|
313
|
+
sage: variety_size_from_gb([x(1)])
|
|
314
|
+
1.0
|
|
315
|
+
sage: variety_size_from_gb([x(1), x(2)])
|
|
316
|
+
1.0
|
|
317
|
+
sage: variety_size_from_gb([x(1), x(2)*x(3)])
|
|
318
|
+
3.0
|
|
319
|
+
sage: variety_size_from_gb([x(1), x(1)*x(4), x(2)*x(3)])
|
|
320
|
+
6.0
|
|
321
|
+
sage: variety_size_from_gb([x(1)*x(2), x(2)*x(3)])
|
|
322
|
+
5.0
|
|
323
|
+
sage: mons = [Monomial([r.variable(i) for i in range(100) if i!=j])\
|
|
324
|
+
....: for j in range(100)]
|
|
325
|
+
sage: variety_size_from_gb(mons)
|
|
326
|
+
1.2676506002282294e+30
|
|
327
|
+
"""
|
|
328
|
+
I = (Polynomial(p) for p in I)
|
|
329
|
+
I = [p for p in I if not p.is_zero()]
|
|
330
|
+
if not I:
|
|
331
|
+
return 1
|
|
332
|
+
# # TODO Here's something wrong! See the example with 5 solutions.
|
|
333
|
+
# # (reverting for now)
|
|
334
|
+
# number_of_used_vars = used_vars_set(I).deg()
|
|
335
|
+
# leads = set([p.lead() for p in I])
|
|
336
|
+
# minimal_leads = BooleSet(leads).minimal_elements()
|
|
337
|
+
# number_of_used_vars_minimal_leads =\
|
|
338
|
+
# minimal_leads.vars().deg()
|
|
339
|
+
# standard_monomials =\
|
|
340
|
+
# minimal_leads.include_divisors().diff(minimal_leads)
|
|
341
|
+
# return standard_monomials.size_double()*\
|
|
342
|
+
# 2**(number_of_used_vars-number_of_used_vars_minimal_leads)
|
|
343
|
+
|
|
344
|
+
sm = Monomial(used_vars_set(I)).divisors()
|
|
345
|
+
for p in I:
|
|
346
|
+
m = p.lead()
|
|
347
|
+
sm = sm.diff(sm.multiples_of(m))
|
|
348
|
+
return sm.size_double()
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def other_ordering_pre(I, option_set, kwds):
|
|
352
|
+
"""
|
|
353
|
+
TESTS::
|
|
354
|
+
|
|
355
|
+
sage: from sage.rings.polynomial.pbori.blocks import declare_ring
|
|
356
|
+
sage: r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals())
|
|
357
|
+
sage: id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4]
|
|
358
|
+
sage: from sage.rings.polynomial.pbori.gbcore import groebner_basis
|
|
359
|
+
sage: groebner_basis(id)
|
|
360
|
+
[1]
|
|
361
|
+
"""
|
|
362
|
+
if not I:
|
|
363
|
+
return (I, None)
|
|
364
|
+
|
|
365
|
+
main_kwds = kwds
|
|
366
|
+
options = option_set
|
|
367
|
+
|
|
368
|
+
old_ring = next(iter(I)).ring()
|
|
369
|
+
try:
|
|
370
|
+
new_ring = old_ring.clone(ordering=options["switch_to"])
|
|
371
|
+
|
|
372
|
+
kwds = {k: options[k] for k in options
|
|
373
|
+
if k not in ("other_ordering_first", "switch_to", "I")}
|
|
374
|
+
kwds["redsb"] = True
|
|
375
|
+
I = groebner_basis([new_ring(poly) for poly in I], **kwds)
|
|
376
|
+
variety_size = variety_size_from_gb(I)
|
|
377
|
+
|
|
378
|
+
fglm_bound = options.get("fglm_bound") or groebner_basis.options["fglm_bound"]
|
|
379
|
+
if variety_size < fglm_bound:
|
|
380
|
+
main_kwds["convert_with_fglm_from_ring"] = new_ring
|
|
381
|
+
main_kwds["convert_with_fglm_to_ring"] = old_ring
|
|
382
|
+
else:
|
|
383
|
+
I = [old_ring(poly) for poly in I]
|
|
384
|
+
finally:
|
|
385
|
+
pass
|
|
386
|
+
|
|
387
|
+
return (I, None)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def llfirstonthefly_pre(I, prot):
|
|
391
|
+
(eliminated, llnf, I) = eliminate(I, on_the_fly=True)
|
|
392
|
+
return (I, eliminated)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def gauss_on_linear_pre(I, prot):
|
|
396
|
+
return (gauss_on_linear(I), None)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def easy_linear_polynomials_pre(I):
|
|
400
|
+
res = []
|
|
401
|
+
for p in I:
|
|
402
|
+
res.append(p)
|
|
403
|
+
res.extend(easy_linear_polynomials(p))
|
|
404
|
+
|
|
405
|
+
return (list(set(res)), None)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def llfirst_post(I, state, prot, kwds):
|
|
409
|
+
eliminated = state
|
|
410
|
+
for p in I:
|
|
411
|
+
if p.is_one():
|
|
412
|
+
return [p]
|
|
413
|
+
|
|
414
|
+
if eliminated:
|
|
415
|
+
I = list(chain(I, eliminated))
|
|
416
|
+
# redsb just for safety, as don't know how option is set
|
|
417
|
+
kwds = copy(kwds)
|
|
418
|
+
kwds.update(
|
|
419
|
+
{'llfirst': False,
|
|
420
|
+
'llfirstonthefly': False,
|
|
421
|
+
'll_constants': False,
|
|
422
|
+
'deg_bound': False,
|
|
423
|
+
'other_ordering_first': False,
|
|
424
|
+
'eliminate_identical_variables': False, 'redsb': True})
|
|
425
|
+
I = groebner_basis(I, **kwds)
|
|
426
|
+
return I
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def ll_constants_post(I, state):
|
|
430
|
+
eliminated = state
|
|
431
|
+
for p in I:
|
|
432
|
+
if p.is_one():
|
|
433
|
+
return [p]
|
|
434
|
+
if eliminated:
|
|
435
|
+
I = list(chain(I, eliminated))
|
|
436
|
+
# redsb just for safety, as don't know how option is set
|
|
437
|
+
return I
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def result_to_list_post(I, state):
|
|
441
|
+
return list(I)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def fix_deg_bound_post(I, state):
|
|
445
|
+
if isinstance(I, GroebnerStrategy):
|
|
446
|
+
return I.all_generators()
|
|
447
|
+
return I
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def incremental_pre(I, prot, kwds):
|
|
451
|
+
def sort_key(p):
|
|
452
|
+
p = Polynomial(p)
|
|
453
|
+
return (p.navigation().value(), -p.deg())
|
|
454
|
+
I = sorted(I, key=sort_key)
|
|
455
|
+
inc_sys = []
|
|
456
|
+
kwds = copy(kwds)
|
|
457
|
+
kwds['incremental'] = False
|
|
458
|
+
|
|
459
|
+
for p in I[:-1]:
|
|
460
|
+
inc_sys.append(p)
|
|
461
|
+
inc_sys = groebner_basis(inc_sys, **kwds)
|
|
462
|
+
if prot:
|
|
463
|
+
print("incrementally calculating GB, adding generator:", p)
|
|
464
|
+
inc_sys.append(I[:-1])
|
|
465
|
+
return (inc_sys, None)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def eliminate_identical_variables_pre(I, prot):
|
|
469
|
+
changed = True
|
|
470
|
+
ll_system = []
|
|
471
|
+
treated_linears = set()
|
|
472
|
+
while changed:
|
|
473
|
+
changed = False
|
|
474
|
+
rules = {}
|
|
475
|
+
for p in I:
|
|
476
|
+
t = p + p.lead()
|
|
477
|
+
if p.lead_deg() == 1:
|
|
478
|
+
l = p.lead()
|
|
479
|
+
if l in treated_linears:
|
|
480
|
+
continue
|
|
481
|
+
treated_linears.add(l)
|
|
482
|
+
if t.deg() > 0:
|
|
483
|
+
rules.setdefault(t, [])
|
|
484
|
+
leads = rules[t]
|
|
485
|
+
leads.append(l)
|
|
486
|
+
|
|
487
|
+
def my_sort_key(l):
|
|
488
|
+
return l.navigation().value()
|
|
489
|
+
|
|
490
|
+
for t, leads in rules.items():
|
|
491
|
+
if len(leads) > 1:
|
|
492
|
+
changed = True
|
|
493
|
+
sleads = sorted(leads, key=my_sort_key, reverse=True)
|
|
494
|
+
chosen = sleads[0]
|
|
495
|
+
ll_system.extend(chosen + v for v in sleads[1:])
|
|
496
|
+
if ll_system:
|
|
497
|
+
ll_encoded = ll_encode(ll_system, reduce=True)
|
|
498
|
+
I = {ll_red_nf_redsb(p, ll_encoded) for p in I}
|
|
499
|
+
return (I, ll_system)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
@gb_with_pre_post_option("clean_arguments", pre=clean_polys_pre, default=True)
|
|
503
|
+
@gb_with_pre_post_option("easy_linear_polynomials",
|
|
504
|
+
pre=easy_linear_polynomials_pre, default=True)
|
|
505
|
+
@gb_with_pre_post_option("result_to_list", post=result_to_list_post,
|
|
506
|
+
default=True)
|
|
507
|
+
@with_heuristic(interpolation_gb_heuristic)
|
|
508
|
+
@gb_with_pre_post_option("invert", pre=invert_all_pre,
|
|
509
|
+
post=invert_all_post, default=False)
|
|
510
|
+
@gb_with_pre_post_option("gauss_on_linear", pre=gauss_on_linear_pre,
|
|
511
|
+
default=True)
|
|
512
|
+
@gb_with_pre_post_option("ll_constants", pre=ll_constants_pre,
|
|
513
|
+
post=ll_constants_post, default=True)
|
|
514
|
+
@gb_with_pre_post_option("eliminate_identical_variables",
|
|
515
|
+
pre=eliminate_identical_variables_pre,
|
|
516
|
+
post=llfirst_post, default=True)
|
|
517
|
+
@with_heuristic(ll_heuristic)
|
|
518
|
+
@gb_with_pre_post_option("llfirst", if_not_option=["llfirstonthefly"],
|
|
519
|
+
pre=llfirst_pre, post=llfirst_post, default=False)
|
|
520
|
+
@gb_with_pre_post_option("llfirstonthefly", pre=llfirstonthefly_pre,
|
|
521
|
+
post=llfirst_post, default=False)
|
|
522
|
+
@gb_with_pre_post_option("incremental", pre=incremental_pre)
|
|
523
|
+
@with_heuristic(change_order_heuristic)
|
|
524
|
+
@gb_with_pre_post_option("other_ordering_first", if_not_option=[
|
|
525
|
+
"interpolation_gb"], pre=other_ordering_pre, default=False)
|
|
526
|
+
@with_heuristic(linear_algebra_heuristic)
|
|
527
|
+
@gb_with_pre_post_option("fix_deg_bound", if_not_option=["interpolation_gb"],
|
|
528
|
+
post=fix_deg_bound_post, default=True)
|
|
529
|
+
@gb_with_pre_post_option("minsb", post=minsb_post,
|
|
530
|
+
if_not_option=["redsb", "deg_bound",
|
|
531
|
+
"interpolation_gb",
|
|
532
|
+
"convert_with_fglm_from_ring"],
|
|
533
|
+
default=True)
|
|
534
|
+
@gb_with_pre_post_option("redsb", post=redsb_post,
|
|
535
|
+
if_not_option=["deg_bound", "interpolation_gb",
|
|
536
|
+
"convert_with_fglm_from_ring"],
|
|
537
|
+
default=True)
|
|
538
|
+
def groebner_basis(I, heuristic=True, unique_ideal_generator=False,
|
|
539
|
+
interpolation_gb=False, clean_and_restart_algorithm=False,
|
|
540
|
+
convert_with_fglm_from_ring=None,
|
|
541
|
+
convert_with_fglm_to_ring=None,
|
|
542
|
+
fglm_bound=40000,
|
|
543
|
+
modified_linear_algebra=True, preprocessor=None,
|
|
544
|
+
deg_bound=False,
|
|
545
|
+
implementation='Python', full_prot=False, prot=False,
|
|
546
|
+
draw_matrices=False, preprocess_only=False, **impl_options):
|
|
547
|
+
"""Computes a Groebner basis of a given ideal I, w.r.t options."""
|
|
548
|
+
|
|
549
|
+
if not I:
|
|
550
|
+
return I
|
|
551
|
+
|
|
552
|
+
if full_prot:
|
|
553
|
+
prot = True
|
|
554
|
+
if prot:
|
|
555
|
+
print("number of passed generators:", len(I))
|
|
556
|
+
if convert_with_fglm_from_ring is not None:
|
|
557
|
+
from_ring = convert_with_fglm_from_ring
|
|
558
|
+
to_ring = convert_with_fglm_to_ring
|
|
559
|
+
return _fglm(I, from_ring, to_ring)
|
|
560
|
+
|
|
561
|
+
if interpolation_gb:
|
|
562
|
+
first = next(iter(I))
|
|
563
|
+
if len(I) != 1 or first.ring().get_order_code() != OrderCode.lp:
|
|
564
|
+
raise ValueError
|
|
565
|
+
return lex_groebner_basis_for_polynomial_via_variety(first)
|
|
566
|
+
if deg_bound is False:
|
|
567
|
+
deg_bound = 100000000
|
|
568
|
+
I = [Polynomial(p) for p in I if not p.is_zero()]
|
|
569
|
+
if unique_ideal_generator and I:
|
|
570
|
+
prod = 1
|
|
571
|
+
for p in I:
|
|
572
|
+
prod = (p + 1) * prod
|
|
573
|
+
I = [prod + 1]
|
|
574
|
+
|
|
575
|
+
implementation = symmGB_F2_python if implementation == 'Python' else symmGB_F2_C
|
|
576
|
+
|
|
577
|
+
# custom preprocessing
|
|
578
|
+
if preprocessor:
|
|
579
|
+
I = preprocessor(I)
|
|
580
|
+
|
|
581
|
+
if preprocess_only:
|
|
582
|
+
for p in I:
|
|
583
|
+
print(p)
|
|
584
|
+
import sys
|
|
585
|
+
sys.exit(0)
|
|
586
|
+
|
|
587
|
+
def call_algorithm(I, max_generators=None):
|
|
588
|
+
return implementation(I,
|
|
589
|
+
deg_bound=deg_bound,
|
|
590
|
+
full_prot=full_prot,
|
|
591
|
+
prot=prot,
|
|
592
|
+
max_generators=max_generators,
|
|
593
|
+
draw_matrices=draw_matrices,
|
|
594
|
+
**filter_newstyle_options(implementation,
|
|
595
|
+
**impl_options))
|
|
596
|
+
|
|
597
|
+
if clean_and_restart_algorithm:
|
|
598
|
+
for max_generators in [1000, 10000, 50000, 100000, 200000, 300000,
|
|
599
|
+
400000, None]:
|
|
600
|
+
try:
|
|
601
|
+
return call_algorithm(I, max_generators=max_generators)
|
|
602
|
+
except GeneratorLimitExceeded as e:
|
|
603
|
+
I = list(e.strat.all_generators())
|
|
604
|
+
del e.strat
|
|
605
|
+
if prot:
|
|
606
|
+
print("generator limit exceeded:", max_generators,
|
|
607
|
+
"restarting algorithm")
|
|
608
|
+
else:
|
|
609
|
+
return call_algorithm(I)
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def build_groebner_basis_doc_string():
|
|
613
|
+
additional_options_from_buchberger = filter_oldstyle_options(
|
|
614
|
+
**get_options_from_function(symmGB_F2_python))
|
|
615
|
+
for k in list(additional_options_from_buchberger):
|
|
616
|
+
if k in groebner_basis.options:
|
|
617
|
+
del additional_options_from_buchberger[k]
|
|
618
|
+
|
|
619
|
+
gdoc = groebner_basis.__doc__
|
|
620
|
+
gdoc += "\nOptions are:\n"
|
|
621
|
+
gdoc += "\n".join(k + " : " + repr(groebner_basis.options[k])
|
|
622
|
+
for k in groebner_basis.options)
|
|
623
|
+
gdoc += """
|
|
624
|
+
|
|
625
|
+
Turn off heuristic by setting heuristic=False
|
|
626
|
+
Additional options come from the actual buchberger implementation.
|
|
627
|
+
In case of our standard Python implementation these are the following:
|
|
628
|
+
"""
|
|
629
|
+
gdoc += "\n".join(k + " : " + repr(additional_options_from_buchberger[k])
|
|
630
|
+
for k in additional_options_from_buchberger)
|
|
631
|
+
groebner_basis.__doc__ = gdoc
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
build_groebner_basis_doc_string()
|