passagemath-brial 10.6.31rc3__cp314-cp314-musllinux_1_2_x86_64.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-c481f38e.so.3.0.7 +0 -0
- passagemath_brial.libs/libbrial_groebner-f1cf2801.so.3.0.7 +0 -0
- passagemath_brial.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_brial.libs/libm4ri-b8433175.so.1.0.0 +0 -0
- passagemath_brial.libs/libpng16-1c631ee1.so.16.43.0 +0 -0
- passagemath_brial.libs/libstdc++-5d72f927.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-x86_64-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,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
|