Skip to content

Commit 3253ed9

Browse files
author
Release Manager
committed
gh-36658: `sage.sat`: Update `# needs` <!-- ^^^^^ Please provide a concise, informative and self-explanatory title. Don't put issue numbers in there, do this in the PR body below. For example, instead of "Fixes #1234" use "Introduce new method to calculate 1+1" --> <!-- Describe your changes here in detail --> <!-- Why is this change required? What problem does it solve? --> <!-- If this PR resolves an open issue, please link to it here. For example "Fixes #12345". --> Cherry-picked from #35095. <!-- If your change requires a documentation PR, please link it appropriately. --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!-- Feel free to remove irrelevant items. --> - [x] The title is concise, informative, and self-explanatory. - [ ] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on - #12345: short description why this is a dependency - #34567: ... --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #36658 Reported by: Matthias Köppe Reviewer(s): David Coudert, Matthias Köppe
2 parents b707b29 + d1c6642 commit 3253ed9

File tree

8 files changed

+94
-79
lines changed

8 files changed

+94
-79
lines changed

src/sage/sat/boolean_polynomials.py

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# sage.doctest: optional - pycryptosat, needs sage.modules sage.rings.polynomial.pbori
12
"""
23
SAT Functions for Boolean Polynomials
34
@@ -71,7 +72,7 @@ def solve(F, converter=None, solver=None, n=1, target_variables=None, **kwds):
7172
7273
We construct a very small-scale AES system of equations::
7374
74-
sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True)
75+
sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=True)
7576
sage: while True: # workaround (see :trac:`31891`)
7677
....: try:
7778
....: F, s = sr.polynomial_system()
@@ -81,74 +82,78 @@ def solve(F, converter=None, solver=None, n=1, target_variables=None, **kwds):
8182
8283
and pass it to a SAT solver::
8384
84-
sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - pycryptosat
85-
sage: s = solve_sat(F) # optional - pycryptosat
86-
sage: F.subs(s[0]) # optional - pycryptosat
85+
sage: from sage.sat.boolean_polynomials import solve as solve_sat
86+
sage: s = solve_sat(F)
87+
sage: F.subs(s[0])
8788
Polynomial Sequence with 36 Polynomials in 0 Variables
8889
8990
This time we pass a few options through to the converter and the solver::
9091
91-
sage: s = solve_sat(F, c_max_vars_sparse=4, c_cutting_number=8) # optional - pycryptosat
92-
sage: F.subs(s[0]) # optional - pycryptosat
92+
sage: s = solve_sat(F, c_max_vars_sparse=4, c_cutting_number=8)
93+
sage: F.subs(s[0])
9394
Polynomial Sequence with 36 Polynomials in 0 Variables
9495
95-
We construct a very simple system with three solutions and ask for a specific number of solutions::
96+
We construct a very simple system with three solutions
97+
and ask for a specific number of solutions::
9698
97-
sage: B.<a,b> = BooleanPolynomialRing() # optional - pycryptosat
98-
sage: f = a*b # optional - pycryptosat
99-
sage: l = solve_sat([f],n=1) # optional - pycryptosat
100-
sage: len(l) == 1, f.subs(l[0]) # optional - pycryptosat
99+
sage: B.<a,b> = BooleanPolynomialRing()
100+
sage: f = a*b
101+
sage: l = solve_sat([f],n=1)
102+
sage: len(l) == 1, f.subs(l[0])
101103
(True, 0)
102104
103-
sage: l = solve_sat([a*b],n=2) # optional - pycryptosat
104-
sage: len(l) == 2, f.subs(l[0]), f.subs(l[1]) # optional - pycryptosat
105+
sage: l = solve_sat([a*b],n=2)
106+
sage: len(l) == 2, f.subs(l[0]), f.subs(l[1])
105107
(True, 0, 0)
106108
107-
sage: sorted((d[a], d[b]) for d in solve_sat([a*b],n=3)) # optional - pycryptosat
109+
sage: sorted((d[a], d[b]) for d in solve_sat([a*b], n=3))
108110
[(0, 0), (0, 1), (1, 0)]
109-
sage: sorted((d[a], d[b]) for d in solve_sat([a*b],n=4)) # optional - pycryptosat
111+
sage: sorted((d[a], d[b]) for d in solve_sat([a*b], n=4))
110112
[(0, 0), (0, 1), (1, 0)]
111-
sage: sorted((d[a], d[b]) for d in solve_sat([a*b],n=infinity)) # optional - pycryptosat
113+
sage: sorted((d[a], d[b]) for d in solve_sat([a*b], n=infinity))
112114
[(0, 0), (0, 1), (1, 0)]
113115
114116
In the next example we see how the ``target_variables`` parameter works::
115117
116-
sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - pycryptosat
117-
sage: R.<a,b,c,d> = BooleanPolynomialRing() # optional - pycryptosat
118-
sage: F = [a+b,a+c+d] # optional - pycryptosat
118+
sage: from sage.sat.boolean_polynomials import solve as solve_sat
119+
sage: R.<a,b,c,d> = BooleanPolynomialRing()
120+
sage: F = [a + b, a + c + d]
119121
120122
First the normal use case::
121123
122-
sage: sorted((D[a], D[b], D[c], D[d]) for D in solve_sat(F,n=infinity)) # optional - pycryptosat
124+
sage: sorted((D[a], D[b], D[c], D[d])
125+
....: for D in solve_sat(F, n=infinity))
123126
[(0, 0, 0, 0), (0, 0, 1, 1), (1, 1, 0, 1), (1, 1, 1, 0)]
124127
125128
Now we are only interested in the solutions of the variables a and b::
126129
127-
sage: solve_sat(F,n=infinity,target_variables=[a,b]) # optional - pycryptosat
130+
sage: solve_sat(F, n=infinity, target_variables=[a,b])
128131
[{b: 0, a: 0}, {b: 1, a: 1}]
129132
130133
Here, we generate and solve the cubic equations of the AES SBox (see :trac:`26676`)::
131134
132-
sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence # optional - pycryptosat, long time
133-
sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - pycryptosat, long time
134-
sage: sr = sage.crypto.mq.SR(1, 4, 4, 8, allow_zero_inversions = True) # optional - pycryptosat, long time
135-
sage: sb = sr.sbox() # optional - pycryptosat, long time
136-
sage: eqs = sb.polynomials(degree = 3) # optional - pycryptosat, long time
137-
sage: eqs = PolynomialSequence(eqs) # optional - pycryptosat, long time
138-
sage: variables = map(str, eqs.variables()) # optional - pycryptosat, long time
139-
sage: variables = ",".join(variables) # optional - pycryptosat, long time
140-
sage: R = BooleanPolynomialRing(16, variables) # optional - pycryptosat, long time
141-
sage: eqs = [R(eq) for eq in eqs] # optional - pycryptosat, long time
142-
sage: sls_aes = solve_sat(eqs, n = infinity) # optional - pycryptosat, long time
143-
sage: len(sls_aes) # optional - pycryptosat, long time
135+
sage: # long time
136+
sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
137+
sage: from sage.sat.boolean_polynomials import solve as solve_sat
138+
sage: sr = sage.crypto.mq.SR(1, 4, 4, 8,
139+
....: allow_zero_inversions=True)
140+
sage: sb = sr.sbox()
141+
sage: eqs = sb.polynomials(degree=3)
142+
sage: eqs = PolynomialSequence(eqs)
143+
sage: variables = map(str, eqs.variables())
144+
sage: variables = ",".join(variables)
145+
sage: R = BooleanPolynomialRing(16, variables)
146+
sage: eqs = [R(eq) for eq in eqs]
147+
sage: sls_aes = solve_sat(eqs, n=infinity)
148+
sage: len(sls_aes)
144149
256
145150
146151
TESTS:
147152
148153
Test that :trac:`26676` is fixed::
149154
150155
sage: varl = ['k{0}'.format(p) for p in range(29)]
151-
sage: B = BooleanPolynomialRing(names = varl)
156+
sage: B = BooleanPolynomialRing(names=varl)
152157
sage: B.inject_variables(verbose=False)
153158
sage: keqs = [
154159
....: k0 + k6 + 1,
@@ -162,7 +167,7 @@ def solve(F, converter=None, solver=None, n=1, target_variables=None, **kwds):
162167
....: k9 + k28,
163168
....: k11 + k20]
164169
sage: from sage.sat.boolean_polynomials import solve as solve_sat
165-
sage: solve_sat(keqs, n=1, solver=SAT('cryptominisat')) # optional - pycryptosat
170+
sage: solve_sat(keqs, n=1, solver=SAT('cryptominisat'))
166171
[{k28: 0,
167172
k26: 1,
168173
k24: 0,
@@ -187,7 +192,7 @@ def solve(F, converter=None, solver=None, n=1, target_variables=None, **kwds):
187192
k2: 0,
188193
k1: 0,
189194
k0: 0}]
190-
sage: solve_sat(keqs, n=1, solver=SAT('picosat')) # optional - pycosat
195+
sage: solve_sat(keqs, n=1, solver=SAT('picosat')) # optional - pycosat
191196
[{k28: 0,
192197
k26: 1,
193198
k24: 0,
@@ -336,16 +341,16 @@ def learn(F, converter=None, solver=None, max_learnt_length=3, interreduction=Fa
336341
337342
EXAMPLES::
338343
339-
sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - pycryptosat
344+
sage: from sage.sat.boolean_polynomials import learn as learn_sat
340345
341346
We construct a simple system and solve it::
342347
343-
sage: set_random_seed(2300) # optional - pycryptosat
344-
sage: sr = mq.SR(1,2,2,4,gf2=True,polybori=True) # optional - pycryptosat
345-
sage: F,s = sr.polynomial_system() # optional - pycryptosat
346-
sage: H = learn_sat(F) # optional - pycryptosat
347-
sage: H[-1] # optional - pycryptosat
348-
k033 + 1
348+
sage: set_random_seed(2300)
349+
sage: sr = mq.SR(1, 2, 2, 4, gf2=True, polybori=True)
350+
sage: F,s = sr.polynomial_system()
351+
sage: H = learn_sat(F)
352+
sage: H[-1]
353+
k033 + 1
349354
"""
350355
try:
351356
len(F)

src/sage/sat/converters/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
from sage.misc.lazy_import import lazy_import
2+
13
from .anf2cnf import ANF2CNFConverter
2-
from sage.rings.polynomial.pbori.cnf import CNFEncoder as PolyBoRiCNFEncoder
4+
5+
lazy_import('sage.rings.polynomial.pbori.cnf', 'CNFEncoder', as_='PolyBoRiCNFEncoder')

src/sage/sat/converters/polybori.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# sage.doctest: needs sage.rings.polynomial.pbori
12
"""
23
An ANF to CNF Converter using a Dense/Sparse Strategy
34

src/sage/sat/solvers/cryptominisat.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,13 @@ def __call__(self, assumptions=None):
182182
183183
EXAMPLES::
184184
185+
sage: # optional - pycryptosat
185186
sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat
186-
sage: solver = CryptoMiniSat() # optional - pycryptosat
187-
sage: solver.add_clause((1,2)) # optional - pycryptosat
188-
sage: solver.add_clause((-1,2)) # optional - pycryptosat
189-
sage: solver.add_clause((-1,-2)) # optional - pycryptosat
190-
sage: solver() # optional - pycryptosat
187+
sage: solver = CryptoMiniSat()
188+
sage: solver.add_clause((1,2))
189+
sage: solver.add_clause((-1,2))
190+
sage: solver.add_clause((-1,-2))
191+
sage: solver()
191192
(None, False, True)
192193
193194
sage: solver.add_clause((1,-2)) # optional - pycryptosat
@@ -231,23 +232,25 @@ def clauses(self, filename=None):
231232
232233
EXAMPLES::
233234
235+
sage: # optional - pycryptosat
234236
sage: from sage.sat.solvers import CryptoMiniSat
235-
sage: solver = CryptoMiniSat() # optional - pycryptosat
236-
sage: solver.add_clause((1,2,3,4,5,6,7,8,-9)) # optional - pycryptosat
237-
sage: solver.add_xor_clause((1,2,3,4,5,6,7,8,9), rhs=True) # optional - pycryptosat
238-
sage: solver.clauses() # optional - pycryptosat
237+
sage: solver = CryptoMiniSat()
238+
sage: solver.add_clause((1,2,3,4,5,6,7,8,-9))
239+
sage: solver.add_xor_clause((1,2,3,4,5,6,7,8,9), rhs=True)
240+
sage: solver.clauses()
239241
[((1, 2, 3, 4, 5, 6, 7, 8, -9), False, None),
240242
((1, 2, 3, 4, 5, 6, 7, 8, 9), True, True)]
241243
242244
DIMACS format output::
243245
246+
sage: # optional - pycryptosat
244247
sage: from sage.sat.solvers import CryptoMiniSat
245-
sage: solver = CryptoMiniSat() # optional - pycryptosat
246-
sage: solver.add_clause((1, 2, 4)) # optional - pycryptosat
247-
sage: solver.add_clause((1, 2, -4)) # optional - pycryptosat
248-
sage: fn = tmp_filename() # optional - pycryptosat
249-
sage: solver.clauses(fn) # optional - pycryptosat
250-
sage: print(open(fn).read()) # optional - pycryptosat
248+
sage: solver = CryptoMiniSat()
249+
sage: solver.add_clause((1, 2, 4))
250+
sage: solver.add_clause((1, 2, -4))
251+
sage: fn = tmp_filename()
252+
sage: solver.clauses(fn)
253+
sage: print(open(fn).read())
251254
p cnf 4 2
252255
1 2 4 0
253256
1 2 -4 0

src/sage/sat/solvers/dimacs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,14 @@ def __call__(self, assumptions=None):
490490
TESTS::
491491
492492
sage: from sage.sat.boolean_polynomials import solve as solve_sat
493-
sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True)
494-
sage: while True: # workaround (see :trac:`31891`)
493+
sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=True) # needs sage.rings.finite_rings sage.rings.polynomial.pbori
494+
sage: while True: # workaround (see :trac:`31891`) # needs sage.rings.finite_rings sage.rings.polynomial.pbori
495495
....: try:
496496
....: F, s = sr.polynomial_system()
497497
....: break
498498
....: except ZeroDivisionError:
499499
....: pass
500-
sage: solve_sat(F, solver=sage.sat.solvers.RSat) # optional - RSat
500+
sage: solve_sat(F, solver=sage.sat.solvers.RSat) # optional - rsat, needs sage.rings.finite_rings sage.rings.polynomial.pbori
501501
502502
"""
503503
if assumptions is not None:

src/sage/sat/solvers/picosat.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,13 @@ def __call__(self, assumptions=None):
147147
148148
EXAMPLES::
149149
150+
sage: # optional - pycosat
150151
sage: from sage.sat.solvers.picosat import PicoSAT
151-
sage: solver = PicoSAT() # optional - pycosat
152-
sage: solver.add_clause((1,2)) # optional - pycosat
153-
sage: solver.add_clause((-1,2)) # optional - pycosat
154-
sage: solver.add_clause((-1,-2)) # optional - pycosat
155-
sage: solver() # optional - pycosat
152+
sage: solver = PicoSAT()
153+
sage: solver.add_clause((1,2))
154+
sage: solver.add_clause((-1,2))
155+
sage: solver.add_clause((-1,-2))
156+
sage: solver()
156157
(None, False, True)
157158
158159
sage: solver.add_clause((1,-2)) # optional - pycosat
@@ -207,13 +208,14 @@ def clauses(self, filename=None):
207208
208209
DIMACS format output::
209210
211+
sage: # optional - pycosat
210212
sage: from sage.sat.solvers.picosat import PicoSAT
211-
sage: solver = PicoSAT() # optional - pycosat
212-
sage: solver.add_clause((1, 2, 4)) # optional - pycosat
213-
sage: solver.add_clause((1, 2, -4)) # optional - pycosat
214-
sage: fn = tmp_filename() # optional - pycosat
215-
sage: solver.clauses(fn) # optional - pycosat
216-
sage: print(open(fn).read()) # optional - pycosat
213+
sage: solver = PicoSAT()
214+
sage: solver.add_clause((1, 2, 4))
215+
sage: solver.add_clause((1, 2, -4))
216+
sage: fn = tmp_filename()
217+
sage: solver.clauses(fn)
218+
sage: print(open(fn).read())
217219
p cnf 4 2
218220
1 2 4 0
219221
1 2 -4 0

src/sage/sat/solvers/sat_lp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# sage.doctest: needs sage.numerical.mip
12
r"""
23
Solve SAT problems Integer Linear Programming
34

src/sage/sat/solvers/satsolver.pyx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ cdef class SatSolver:
139139
140140
sage: from io import StringIO
141141
sage: file_object = StringIO("c A sample .cnf file with xor clauses.\np cnf 3 3\n1 2 0\n3 0\nx1 2 3 0")
142-
sage: from sage.sat.solvers.sat_lp import SatLP
143-
sage: solver = SatLP()
144-
sage: solver.read(file_object)
142+
sage: from sage.sat.solvers.sat_lp import SatLP # needs sage.numerical.mip
143+
sage: solver = SatLP() # needs sage.numerical.mip
144+
sage: solver.read(file_object) # needs sage.numerical.mip
145145
Traceback (most recent call last):
146146
...
147147
NotImplementedError: the solver "an ILP-based SAT Solver" does not support xor clauses
@@ -339,7 +339,7 @@ def SAT(solver=None, *args, **kwds):
339339
340340
EXAMPLES::
341341
342-
sage: SAT(solver="LP")
342+
sage: SAT(solver="LP") # needs sage.numerical.mip
343343
an ILP-based SAT Solver
344344
345345
TESTS::
@@ -351,12 +351,12 @@ def SAT(solver=None, *args, **kwds):
351351
352352
Forcing CryptoMiniSat::
353353
354-
sage: SAT(solver="cryptominisat") # optional - pycryptosat
354+
sage: SAT(solver="cryptominisat") # optional - pycryptosat
355355
CryptoMiniSat solver: 0 variables, 0 clauses.
356356
357357
Forcing PicoSat::
358358
359-
sage: SAT(solver="picosat") # optional - pycosat
359+
sage: SAT(solver="picosat") # optional - pycosat
360360
PicoSAT solver: 0 variables, 0 clauses.
361361
362362
Forcing Glucose::

0 commit comments

Comments
 (0)