passagemath-brial 10.6.33__cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_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.

Files changed (38) hide show
  1. passagemath_brial-10.6.33.dist-info/METADATA +97 -0
  2. passagemath_brial-10.6.33.dist-info/RECORD +38 -0
  3. passagemath_brial-10.6.33.dist-info/WHEEL +6 -0
  4. passagemath_brial-10.6.33.dist-info/top_level.txt +2 -0
  5. passagemath_brial.libs/libbrial-a2b87c7c.so.3.0.7 +0 -0
  6. passagemath_brial.libs/libbrial_groebner-0f8afeeb.so.3.0.7 +0 -0
  7. passagemath_brial.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
  8. passagemath_brial.libs/libm4ri-ffdb5f7f.so.1.0.0 +0 -0
  9. passagemath_brial.libs/libpng16-89198bef.so.16.43.0 +0 -0
  10. sage/all__sagemath_brial.py +9 -0
  11. sage/libs/all__sagemath_brial.py +1 -0
  12. sage/libs/polybori/__init__.pxd +2 -0
  13. sage/libs/polybori/decl.pxd +401 -0
  14. sage/libs/polybori/pb_wrap.h +133 -0
  15. sage/rings/all__sagemath_brial.py +1 -0
  16. sage/rings/polynomial/all__sagemath_brial.py +1 -0
  17. sage/rings/polynomial/pbori/PyPolyBoRi.py +123 -0
  18. sage/rings/polynomial/pbori/__init__.py +44 -0
  19. sage/rings/polynomial/pbori/blocks.py +443 -0
  20. sage/rings/polynomial/pbori/cnf.py +241 -0
  21. sage/rings/polynomial/pbori/easy_polynomials.py +56 -0
  22. sage/rings/polynomial/pbori/fglm.py +93 -0
  23. sage/rings/polynomial/pbori/frontend.py +70 -0
  24. sage/rings/polynomial/pbori/gbcore.py +634 -0
  25. sage/rings/polynomial/pbori/gbrefs.py +127 -0
  26. sage/rings/polynomial/pbori/heuristics.py +35 -0
  27. sage/rings/polynomial/pbori/interpolate.py +115 -0
  28. sage/rings/polynomial/pbori/interred.py +35 -0
  29. sage/rings/polynomial/pbori/ll.py +292 -0
  30. sage/rings/polynomial/pbori/nf.py +662 -0
  31. sage/rings/polynomial/pbori/parallel.py +298 -0
  32. sage/rings/polynomial/pbori/pbori.cpython-311-aarch64-linux-gnu.so +0 -0
  33. sage/rings/polynomial/pbori/pbori.pxd +127 -0
  34. sage/rings/polynomial/pbori/pbori.pyx +8107 -0
  35. sage/rings/polynomial/pbori/randompoly.py +105 -0
  36. sage/rings/polynomial/pbori/rank.py +27 -0
  37. sage/rings/polynomial/pbori/specialsets.py +112 -0
  38. sage/rings/polynomial/pbori/statistics.py +31 -0
@@ -0,0 +1,443 @@
1
+ # sage_setup: distribution = sagemath-brial
2
+ # sage.doctest: needs sage.rings.polynomial.pbori
3
+ import sys
4
+ from itertools import chain, islice
5
+
6
+ from .pbori import VariableBlock
7
+ from .PyPolyBoRi import (Ring, Polynomial, VariableFactory, Variable)
8
+
9
+
10
+ class Block:
11
+ r"""
12
+ The block class represents a block of variables
13
+ <var_name>(start_index,...,start_index+size-1), it is the preferred
14
+ block type for simple one-dimensional variable sets
15
+ """
16
+ def __init__(self, var_name, size, start_index=0, reverse=False):
17
+ indices = range(start_index, start_index + size)
18
+ if reverse:
19
+ indices = reversed(indices)
20
+ self.names = [var_name + "(" + str(i) + ")" for i in indices]
21
+ self.var_name = var_name
22
+ self.start_index = start_index
23
+ self.reverse = reverse
24
+ self.size = size
25
+
26
+ def __iter__(self):
27
+ return iter(self.names)
28
+
29
+ def __getitem__(self, i):
30
+ return self.names[i]
31
+
32
+ def __len__(self):
33
+ return self.size
34
+
35
+ def register(self, start, context):
36
+ ring_context = context
37
+ while isinstance(ring_context, PrefixedDictProxy):
38
+ ring_context = ring_context.wrapped
39
+ ring = ring_context['r']
40
+
41
+ var_func = VariableBlock(self.size, self.start_index, start, self.
42
+ reverse, ring)
43
+ var_func.__name__ = self.var_name
44
+ context[self.var_name] = var_func
45
+
46
+
47
+ class AlternatingBlock:
48
+ r"""
49
+ The Alternating Block class is used for doing tricky variable
50
+ schemes,where base names vary, e.g.
51
+ a(0),b(0),a(1),b(1),a(2),b(2)
52
+ """
53
+ def __init__(self, var_names, size_per_variable, start_index=0,
54
+ reverse=False):
55
+ self.var_names = var_names
56
+ self.size_per_variable = size_per_variable
57
+ self.reverse = reverse
58
+ indices = range(start_index, start_index + size_per_variable)
59
+
60
+ if reverse:
61
+ indices = reversed(indices)
62
+ names = [f"{n}({str(i)})" for i in indices for n in var_names]
63
+ self.indices = indices
64
+ self.index2pos = {v: k for k, v in enumerate(indices)}
65
+ self.names = names
66
+
67
+ def __len__(self):
68
+ return self.size_per_variable * len(self.var_names)
69
+
70
+ def __iter__(self):
71
+ return iter(self.names)
72
+
73
+ def __getitem__(self, i):
74
+ return self.names[i]
75
+
76
+ def register(self, start, context):
77
+ def gen_var_func(var_pos):
78
+
79
+ class var_factory:
80
+ def __init__(self, ring, index2pos, size):
81
+ self.ring = ring
82
+ self.index2pos = index2pos
83
+ self.size = size
84
+
85
+ def __call__(self, idx):
86
+ return self.ring.variable(self.index2pos[idx] * self.size +
87
+ var_pos + start)
88
+ ring_context = context
89
+ while isinstance(ring_context, PrefixedDictProxy):
90
+ ring_context = ring_context.wrapped
91
+ ring = ring_context['r']
92
+
93
+ return var_factory(ring, self.index2pos, len(self.var_names))
94
+
95
+ for (var_pos, n) in enumerate(self.var_names):
96
+ var_func = gen_var_func(var_pos)
97
+ var_func.__name__ = n
98
+ context[n] = var_func
99
+
100
+
101
+ def shift(f, i):
102
+ def g(j):
103
+ return f(i + j)
104
+ g.__name__ = f.__name__
105
+ return g
106
+
107
+
108
+ class AdderBlock(AlternatingBlock):
109
+ def __init__(self, adder_bits, sums='s', carries='c', input1='a',
110
+ input2='b', start_index=0):
111
+ AlternatingBlock.__init__(self, (sums, carries, input1, input2),
112
+ adder_bits, start_index=start_index,
113
+ reverse=True)
114
+ self.input1 = input1
115
+ self.input2 = input2
116
+ self.sums = sums
117
+ self.carries = carries
118
+ self.start_index = start_index
119
+ self.adder_bits = adder_bits
120
+
121
+ def register(self, start, context):
122
+ super().register(start, context)
123
+ a = context[self.input1]
124
+ b = context[self.input2]
125
+ self.s = shift(context[self.sums], self.start_index)
126
+ self.c = shift(context[self.carries], self.start_index)
127
+ a = shift(a, self.start_index)
128
+ b = shift(b, self.start_index)
129
+ carries = [Polynomial(a(0).ring().zero())]
130
+ last = carries[0]
131
+ for i in range(self.adder_bits):
132
+ c = 1 + (1 + a(i) * b(i)) * (1 + last * a(i)) * (1 + last * b(i))
133
+ carries.append(c)
134
+ last = c
135
+
136
+ self.add_results = [a(i) + b(i) + carries[i]
137
+ for i in range(self.adder_bits)]
138
+ self.carries_polys = carries[1:]
139
+
140
+ # def s(i):
141
+ # return self.add_results[i-self.start_index]
142
+ # def c(i):
143
+ # return self.carries_polys[i-self.start_index]
144
+ # context[self.sums]=s
145
+ # context[self.carries]=c
146
+
147
+ def implement(self, equations):
148
+ for i in range(self.adder_bits):
149
+ equations.append(self.s(i) + self.add_results[i])
150
+ equations.append(self.c(i) + self.carries_polys[i])
151
+
152
+
153
+ class HigherOrderBlock:
154
+ r"""
155
+ HigherOrderBlocks are multidimensional blocks of variables.
156
+
157
+ For each dimension a separate start_index and size can be specified.
158
+
159
+ var_name : variables will be called <var_name>(multiindex), where
160
+ multiindex is a tuple of the size <size_tuple>
161
+
162
+ size_tuple : specifies the sizes of the ranges of each component of the
163
+ multi-indices
164
+
165
+ start_index_tuple : the multi-indices will be of the form
166
+ start_index_tuple + a, where a is a multi-index with nonnegative components
167
+ """
168
+ def __init__(self, var_name, size_tuple, start_index_tuple=None,
169
+ reverse=False):
170
+ if start_index_tuple is None:
171
+ start_index_tuple = len(size_tuple) * (0, )
172
+ cart = [()]
173
+ assert len(size_tuple) == len(start_index_tuple)
174
+ outer_indices = reversed(range(len(size_tuple)))
175
+ for i in outer_indices:
176
+ s_i = start_index_tuple[i]
177
+ s = size_tuple[i]
178
+ cart = [(j, ) + c for j in range(s_i, s_i + s) for c in cart]
179
+ if reverse:
180
+ cart.reverse()
181
+ self.cart = cart
182
+ self.cart2index = {v: k for k, v in enumerate(cart)}
183
+ self.var_name = var_name
184
+ self.names = [var_name + str(c) for c in cart]
185
+
186
+ def __getitem__(self, i):
187
+ return self.names[i]
188
+
189
+ def __iter__(self):
190
+ return iter(self.names)
191
+
192
+ def __len__(self):
193
+ return len(self.names)
194
+
195
+ def register(self, start, context):
196
+ def var_func(*indices):
197
+ return Variable(self.cart2index[indices] + start)
198
+ var_func.__name__ = self.var_name
199
+ context[self.var_name] = var_func
200
+
201
+
202
+ class InOutBlock:
203
+ def __init__(self, out_size, in_size, output='out', input='in',
204
+ in_start_index=0, out_start_index=0,
205
+ out_reverse=False, in_reverse=False):
206
+ self.output = Block(var_name=output, start_index=out_start_index,
207
+ size=out_size, reverse=out_reverse)
208
+ self.input = Block(var_name=input, start_index=in_start_index,
209
+ size=in_size, reverse=in_reverse)
210
+ self.out_start_index = out_start_index
211
+
212
+ self.in_start_index = in_start_index
213
+
214
+ def __iter__(self):
215
+ return chain(self.output, self.input)
216
+
217
+ def __getitem__(self, i):
218
+ if i < len(self.output):
219
+ return self.output[i]
220
+ return self.input[i - len(self.output)]
221
+
222
+ def __len__(self):
223
+ return len(self.output) + len(self.input)
224
+
225
+ def register(self, start, context):
226
+ self.output.register(start, context)
227
+ self.input.register(start + len(self.output), context)
228
+ self.out_vars = shift(context[self.output.var_name], self.
229
+ out_start_index)
230
+ self.in_vars = shift(context[self.input.var_name], self.in_start_index)
231
+
232
+
233
+ class MultiBlock:
234
+ def __init__(self, sizes=None, var_names=["v"],
235
+ start_indices=[], reverses=None):
236
+ if reverses is None:
237
+ reverses = []
238
+ if sizes is None:
239
+ sizes = []
240
+ self.start_indices = start_indices + [0] * (len(var_names) -
241
+ len(start_indices))
242
+ reverses += [False] * (len(var_names) - len(reverses))
243
+ sizes += [1] * (len(var_names) - len(sizes))
244
+
245
+ self.blocks = [Block(var_name=var_names[idx], size=sizes[idx],
246
+ start_index=self.start_indices[idx],
247
+ reverse=reverses[idx])
248
+ for idx in range(len(var_names))]
249
+
250
+ def __iter__(self):
251
+ return chain(*self.blocks)
252
+
253
+ def __getitem__(self, i):
254
+ return next(islice(chain(*self.blocks), i, i + 1))
255
+ # sum([bl.names for bl in self.blocks])[i]
256
+
257
+ def __len__(self):
258
+ return sum(len(bl) for bl in self.blocks)
259
+
260
+ def register(self, start, context):
261
+ offset = 0
262
+ for bl in self.blocks:
263
+ bl.register(start + offset, context)
264
+ offset += len(bl)
265
+
266
+ self.vars = [shift(context[self.blocks[idx].var_name],
267
+ self.start_indices[idx])
268
+ for idx in range(len(self.blocks))]
269
+
270
+
271
+ class PrefixedDictProxy:
272
+ """docstring for PrefixedDictProxy"""
273
+
274
+ def __init__(self, wrapped, prefix):
275
+ super().__init__()
276
+ self.wrapped = wrapped
277
+ self.prefix = prefix
278
+
279
+ def __getitem__(self, k):
280
+ try:
281
+ return self.wrapped[self.prefix + k]
282
+ except KeyError:
283
+ print(self.prefix, k, list(self.wrapped))
284
+ raise KeyError
285
+
286
+ def __setitem__(self, k, v):
287
+ self.wrapped[self.prefix + k] = v
288
+
289
+
290
+ class MacroBlock:
291
+ def __init__(self, prefix):
292
+
293
+ self.prefix = prefix
294
+ self.blocks = []
295
+ self.combinations = []
296
+ self.connections = []
297
+
298
+ def declare(self, blocks):
299
+ self.blocks = blocks
300
+
301
+ def connect(self, combinations):
302
+ self.combinations = combinations
303
+
304
+ def __iter__(self):
305
+ return (self.prefix + "_" + n for n in chain(*self.blocks))
306
+
307
+ def __getitem__(self, i):
308
+ return self.prefix + "_" + next(islice(chain(*self.blocks), i, i + 1))
309
+
310
+ def __len__(self):
311
+ return sum(len(bl) for bl in self.blocks)
312
+
313
+ def resolve(self, localname):
314
+ return self.prefix + "_" + localname
315
+
316
+ def register(self, start, context):
317
+ context = PrefixedDictProxy(context, self.prefix + "_")
318
+ offset = 0
319
+ for bl in self.blocks:
320
+ bl.register(start + offset, context)
321
+ offset += len(bl)
322
+
323
+ for ((con1, indices1), (con2, indices2)) in self.combinations:
324
+ for idx in range(min(len(indices1), len(indices2))):
325
+ self.connections += [context[con1](indices1[idx]) + context[
326
+ con2](indices2[idx])]
327
+
328
+ def implement(self, equations):
329
+ for bl in self.blocks:
330
+ if hasattr(bl, "implement"):
331
+ bl.implement(equations)
332
+
333
+ equations += self.connections
334
+
335
+
336
+ class IfThen:
337
+ def __init__(self, ifpart, thenpart, supposed_to_be_valid=True):
338
+ self.ifpart = [Polynomial(p) for p in ifpart]
339
+ self.thenpart = [Polynomial(p) for p in thenpart]
340
+ self.supposedToBeValid = supposed_to_be_valid
341
+
342
+ def __str__(self):
343
+ return ("If(AND(" + ", ".join(f"{p} == 0" for p in self.ifpart) +
344
+ ")), THEN " + ", ".join(f"{p} == 0" for p in self.thenpart))
345
+
346
+
347
+ def if_then(i, t, supposed_to_be_valid=True):
348
+ return IfThen(i, t, supposed_to_be_valid)
349
+
350
+
351
+ def declare_ring(blocks, context=None):
352
+ r"""
353
+ Declare Ring is the preferred function to create a ring and declare a variable scheme,
354
+ the number of variables is automatically determined, usually you pass globals() as context
355
+ argument to store the ring and the variable mapping.
356
+
357
+ EXAMPLES::
358
+
359
+ sage: from sage.rings.polynomial.pbori import *
360
+ sage: declare_ring([Block("x",10),Block("y",5)],globals())
361
+ Boolean PolynomialRing in x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, y0, y1, y2, y3, y4
362
+
363
+ gives a ring with x(0..9),y(0..4) and registers the ring as r, and the variable
364
+ blocks x and y in the context dictionary globals(), which consists of the global
365
+ variables of the python module
366
+ """
367
+ if context is None:
368
+ context = sys.modules['__main__'].__dict__
369
+
370
+ def canonicalize(blocks):
371
+ for elt in blocks:
372
+ if isinstance(elt, str):
373
+ yield elt
374
+ else:
375
+ yield from elt
376
+
377
+ blocks = list(blocks)
378
+ n = 0
379
+
380
+ for b in blocks:
381
+ n = n + 1 if isinstance(b, str) else n + len(b)
382
+
383
+ r = Ring(n, names=canonicalize(blocks))
384
+
385
+ context["internalVariable"] = VariableFactory(r)
386
+ # context["Monomial"] = MonomialFactory(r)
387
+ context["r"] = r
388
+ declare_block_scheme(blocks, context)
389
+ return r
390
+
391
+
392
+ def declare_block_scheme(blocks, context):
393
+ start = 0
394
+ block_starts = []
395
+ for b in blocks:
396
+ if start:
397
+ block_starts.append(start)
398
+ if isinstance(b, str):
399
+ context[b] = context["internalVariable"](start)
400
+ start = start + 1
401
+ else:
402
+ b.register(start, context)
403
+ start = start + len(b)
404
+ context["block_start_hints"] = block_starts
405
+ context["number_of_declared_vars"] = start
406
+
407
+
408
+ def main():
409
+ r = Ring(1000)
410
+ ablock = AlternatingBlock(["a", "b", "c"], 100)
411
+ declare_block_scheme([ablock], globals())
412
+ for i in range(10):
413
+ print(r.variable(i))
414
+
415
+ print(list(ablock))
416
+ declare_block_scheme([Block(var_name="x", size=100),
417
+ HigherOrderBlock("y", (3, 4, 11, 2)),
418
+ AlternatingBlock(["a", "b", "c"], 100)],
419
+ globals())
420
+ for i in range(10):
421
+ print(x(i))
422
+ print(y(0, 0, 0, 0))
423
+ print(y(0, 0, 0, 1))
424
+ print(y(0, 0, 1, 0))
425
+ print(y(0, 0, 1, 1))
426
+ print(a(0), a(1), a(2), b(0), b(1), c(0))
427
+ declare_block_scheme([Block(var_name="x", size=100, reverse=True),
428
+ HigherOrderBlock("y", (3, 4, 11, 2), reverse=True),
429
+ AlternatingBlock(["a", "b", "c"], 100, reverse=True)],
430
+ globals())
431
+ for i in range(10):
432
+ print(x(i))
433
+ print(y(0, 0, 0, 0))
434
+ print(y(0, 0, 0, 1))
435
+ print(y(0, 0, 1, 0))
436
+ print(y(0, 0, 1, 1))
437
+ print(a(0), a(1), a(2), b(0), b(1), c(0))
438
+ declare_block_scheme(["a", "b", "c"], globals())
439
+ print(a, b, c)
440
+
441
+
442
+ if __name__ == '__main__':
443
+ main()
@@ -0,0 +1,241 @@
1
+ # sage_setup: distribution = sagemath-brial
2
+ # sage.doctest: needs sage.rings.polynomial.pbori
3
+ from random import Random
4
+ from sage.rings.polynomial.pbori.pbori import if_then_else as ite
5
+ from .PyPolyBoRi import Polynomial
6
+ from .statistics import used_vars_set
7
+
8
+
9
+ class CNFEncoder:
10
+ def __init__(self, r, random_seed=16):
11
+ self.random_generator = Random(random_seed)
12
+ self.one_set = r.one().set()
13
+ self.empty_set = r.zero().set()
14
+ self.r = r
15
+
16
+ def zero_blocks(self, f):
17
+ r"""
18
+ Divide the zero set of f into blocks.
19
+
20
+ TESTS::
21
+
22
+ sage: from sage.rings.polynomial.pbori import *
23
+ sage: r = declare_ring(["x", "y", "z"], dict())
24
+ sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder
25
+ sage: e = CNFEncoder(r)
26
+ sage: e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2))
27
+ [{z: 0}, {y: 0}, {x: 0}]
28
+ """
29
+ f = Polynomial(f)
30
+ variables = f.vars_as_monomial()
31
+
32
+ space = variables.divisors()
33
+ variables = list(variables.variables())
34
+ zeros = f.zeros_in(space)
35
+ rest = zeros
36
+ res = []
37
+
38
+ # inefficient compared to polynomials lex_lead
39
+ def choose(s):
40
+ indices = []
41
+ assert not s.empty()
42
+ nav = s.navigation()
43
+ while not nav.constant():
44
+ e = nav.else_branch()
45
+ t = nav.then_branch()
46
+ if e.constant() and not e.terminal_one():
47
+ indices.append(nav.value())
48
+ nav = t
49
+ else:
50
+ if self.random_generator.randint(0, 1):
51
+ indices.append(nav.value())
52
+ nav = t
53
+
54
+ else:
55
+ nav = e
56
+ assert nav.terminal_one()
57
+ res = self.one_set
58
+ for i in reversed(indices):
59
+ res = ite(i, res, self.empty_set)
60
+ return next(iter(res))
61
+ while not rest.empty():
62
+ l = choose(rest)
63
+ l_variables = set(l.variables())
64
+
65
+ def get_val(var):
66
+ if var in l_variables:
67
+ return 1
68
+ return 0
69
+ block_dict = {v: get_val(v) for v in variables}
70
+
71
+ l = l.set()
72
+ self.random_generator.shuffle(variables)
73
+ for v in variables:
74
+ candidate = l.change(v.index())
75
+ if candidate.diff(zeros).empty():
76
+ l = l.union(candidate)
77
+ del block_dict[v]
78
+ rest = rest.diff(l)
79
+ res.append(block_dict)
80
+ return res
81
+
82
+ def clauses(self, f):
83
+ r"""
84
+
85
+ TESTS::
86
+
87
+ sage: from sage.rings.polynomial.pbori import *
88
+ sage: r = declare_ring(["x", "y", "z"], dict())
89
+ sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder
90
+ sage: e = CNFEncoder(r)
91
+ sage: sorted(e.clauses(r.variable(0)*r.variable(1)*r.variable(2)), key=lambda d: sorted(d.items()))
92
+ [{z: 0, y: 0, x: 0}]
93
+ sage: sorted(e.clauses(r.variable(Integer(1))+r.variable(Integer(0))), key=lambda d: sorted(d.items()))
94
+ [{y: 0, x: 1}, {y: 1, x: 0}]
95
+ """
96
+ # we form an expression for a var configuration *not* lying in the
97
+ # block it is evaluated to 0 by f, iff it is not lying in any zero
98
+ # block of f+1
99
+ return [{variable: 1 - value for variable, value in b.items()}
100
+ for b in self.zero_blocks(f + 1)]
101
+
102
+ def polynomial_clauses(self, f):
103
+ r"""
104
+
105
+ TESTS::
106
+
107
+ sage: from sage.rings.polynomial.pbori import *
108
+ sage: r = declare_ring(["x", "y", "z"], dict())
109
+ sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder
110
+ sage: e = CNFEncoder(r)
111
+ sage: e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2))
112
+ [x*y*z]
113
+ sage: v = r.variable
114
+ sage: p = v(1)*v(2)+v(2)*v(0)+1
115
+ sage: groebner_basis([p], heuristic = False)==groebner_basis(e.polynomial_clauses(p), heuristic = False)
116
+ True
117
+ """
118
+
119
+ def product(l):
120
+ res = l[0]
121
+ for p in l[1:]:
122
+ res = res * p
123
+ # please care about the order of these multiplications for
124
+ # performance
125
+ return res
126
+ return [product([variable + value for (variable, value)
127
+ in b.items()]) for b in self.clauses(f)]
128
+
129
+ def to_dimacs_index(self, v):
130
+ return v.index() + 1
131
+
132
+ def dimacs_encode_clause(self, c):
133
+ def get_sign(value):
134
+ if value == 1:
135
+ return 1
136
+ return -1
137
+
138
+ items = sorted(c.items(), reverse=True)
139
+ return " ".join([str(v) for v in [
140
+ get_sign(value) * self.to_dimacs_index(variable)
141
+ for (variable, value) in items] + [0]])
142
+
143
+ def dimacs_encode_polynomial(self, p):
144
+ r"""
145
+
146
+ TESTS::
147
+
148
+ sage: from sage.rings.polynomial.pbori import *
149
+ sage: d = {}
150
+ sage: r = declare_ring(["x", "y", "z"], d)
151
+ sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder
152
+ sage: e = CNFEncoder(r)
153
+ sage: sorted(e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"]))
154
+ ['-1 -2 -3 0', '-1 2 3 0', '1 -2 3 0', '1 2 -3 0']
155
+ """
156
+ return [self.dimacs_encode_clause(c) for c in self.clauses(p)]
157
+
158
+ def dimacs_cnf(self, polynomial_system):
159
+ r"""
160
+
161
+ TESTS::
162
+
163
+ sage: from sage.rings.polynomial.pbori import *
164
+ sage: r = declare_ring(["x", "y", "z"], dict())
165
+ sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder
166
+ sage: e = CNFEncoder(r)
167
+ sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)])
168
+ 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0'
169
+ sage: e.dimacs_cnf([r.variable(1)+r.variable(0)])
170
+ 'c cnf generated by PolyBoRi\np cnf 3 2\n-1 2 0\n1 -2 0'
171
+ sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)])
172
+ 'c cnf generated by PolyBoRi\np cnf 3 3\n-1 -2 -3 0\n1 -2 0\n-1 2 0'
173
+ """
174
+ clauses_list = [c for p in polynomial_system for c in self.
175
+ dimacs_encode_polynomial(p)]
176
+ res = ["c cnf generated by PolyBoRi"]
177
+ r = polynomial_system[0].ring()
178
+ n_variables = r.n_variables()
179
+ res.append(f"p cnf {n_variables} {len(clauses_list)}")
180
+ res.extend(clauses_list)
181
+ return "\n".join(res)
182
+
183
+
184
+ class CryptoMiniSatEncoder(CNFEncoder):
185
+ group_counter = 0
186
+
187
+ def dimacs_encode_polynomial(self, p):
188
+ r"""
189
+
190
+ TESTS::
191
+
192
+ sage: from sage.rings.polynomial.pbori import *
193
+ sage: d=dict()
194
+ sage: r = declare_ring(["x", "y", "z"], d)
195
+ sage: from sage.rings.polynomial.pbori.cnf import CryptoMiniSatEncoder
196
+ sage: e = CryptoMiniSatEncoder(r)
197
+ sage: p = d["x"]+d["y"]+d["z"]
198
+ sage: p.deg()
199
+ 1
200
+ sage: len(p)
201
+ 3
202
+ sage: e.dimacs_encode_polynomial(p)
203
+ ['x1 2 3 0\nc g 1 x + y + z']
204
+ sage: e.dimacs_encode_polynomial(p+1)
205
+ ['x1 2 -3 0\nc g 2 x + y + z + 1']
206
+ """
207
+ if p.deg() != 1 or len(p) <= 1:
208
+ res = super().dimacs_encode_polynomial(p)
209
+ else:
210
+ invert_last = bool(p.has_constant_part())
211
+ variables = list(p.vars_as_monomial().variables())
212
+ indices = [self.to_dimacs_index(v) for v in variables]
213
+ if invert_last:
214
+ indices[-1] = -indices[-1]
215
+ indices.append(0)
216
+ res = ["x" + " ".join(str(v) for v in indices)]
217
+ self.group_counter = self.group_counter + 1
218
+ group_comment = f"\nc g {self.group_counter} {str(p)[:30]}"
219
+ return [c + group_comment for c in res]
220
+
221
+ def dimacs_cnf(self, polynomial_system):
222
+ r"""
223
+
224
+ TESTS::
225
+
226
+ sage: from sage.rings.polynomial.pbori import *
227
+ sage: r = declare_ring(["x", "y", "z"], dict())
228
+ sage: from sage.rings.polynomial.pbori.cnf import CryptoMiniSatEncoder
229
+ sage: e = CryptoMiniSatEncoder(r)
230
+ sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)])
231
+ 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0\nc g 1 x*y*z\nc v 1 x\nc v 2 y\nc v 3 z'
232
+ sage: e.dimacs_cnf([r.variable(1)+r.variable(0)])
233
+ 'c cnf generated by PolyBoRi\np cnf 3 1\nx1 2 0\nc g 2 x + y\nc v 1 x\nc v 2 y'
234
+ sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)])
235
+ 'c cnf generated by PolyBoRi\np cnf 3 2\n-1 -2 -3 0\nc g 3 x*y*z\nx1 2 0\nc g 4 x + y\nc v 1 x\nc v 2 y\nc v 3 z'
236
+ """
237
+ uv = list(used_vars_set(polynomial_system).variables())
238
+ res = super().dimacs_cnf(polynomial_system)
239
+ res += "\n" + "\n".join(f"c v {self.to_dimacs_index(v)} {v}"
240
+ for v in uv)
241
+ return res