Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/sat/solvers/cryptominisat/cryptominisat.pyx
8823 views
1
"""
2
CryptoMiniSat.
3
4
"CryptoMiniSat is an LGPL-licenced SAT solver that aims to become a
5
premier SAT solver with all the features and speed of successful SAT
6
solvers, such as MiniSat and PrecoSat. The long-term goals of
7
CryptoMiniSat are to be an efficient sequential, parallel and
8
distributed solver. There are solvers that are good at one or the
9
other, e.g. ManySat (parallel) or PSolver (distributed), but we wish
10
to excel at all." -- http://www.msoos.org/cryptominisat2/
11
12
.. note::
13
14
Our SAT solver interfaces are 1-based, i.e., literals start at
15
1. This is consistent with the popular DIMACS format for SAT
16
solving but not with Pythion's 0-based convention. However, this
17
also allows to construct clauses using simple integers.
18
19
AUTHORS:
20
21
- Martin Albrecht (2012): first version
22
"""
23
##############################################################################
24
# Copyright (C) 2012 Martin Albrecht <[email protected]>
25
# Distributed under the terms of the GNU General Public License (GPL)
26
# The full text of the GPL is available at:
27
# http://www.gnu.org/licenses/
28
##############################################################################
29
30
include "sage/ext/stdsage.pxi"
31
include "sage/ext/interrupt.pxi"
32
33
from libc.stdint cimport uint32_t
34
from decl cimport lbool, Var, Lit, Clause, l_Undef, l_False, RetClause
35
from decl cimport vec, vector
36
from decl cimport GaussConf
37
from solverconf cimport SolverConf
38
39
from sage.misc.misc import get_verbose
40
41
cdef extern from "cryptominisat_helper.h":
42
# Cython doesn't handle cdef vec[Lit] foo = solver.get_unitary_learnts() propertly. It will
43
# declare foo first and then assign the answer of get_unitary_learnts() to foo. This requires
44
# that operator= is available which isn't necessarily the case.
45
cdef uint32_t* get_unitary_learnts_helper(Solver* solver, uint32_t* num)
46
cdef uint32_t** get_sorted_learnts_helper(Solver* solver, uint32_t* num)
47
48
cdef class CryptoMiniSat(SatSolver):
49
"""
50
The CryptoMiniSat solver.
51
52
EXAMPLE::
53
54
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
55
sage: cms = CryptoMiniSat() # optional - cryptominisat
56
sage: cms.add_clause((1,2,-3)) # optional - cryptominisat
57
sage: cms() # optional - cryptominisat
58
(None, True, True, False)
59
60
.. note::
61
62
Do not import 'sage.sat.solvers.cryptominisat.cryptominisat'
63
directly, but use 'sage.sat.solvers.cryptominisat' which
64
throws a friendlier error message if the CryptoMiniSat SPKG is
65
not installed. Also, 'CryptoMiniSat' will be available in
66
'sage.sat.solvers' if the CryptoMiniSat SPKG is installed.
67
"""
68
def __cinit__(self, SolverConf sc=None, **kwds):
69
"""
70
Construct a new CryptoMiniSat instance.
71
72
INPUT:
73
74
- ``SolverConf`` - a :cls:`sage.sat.solvers.cryptominisat.SolverConf` instance
75
- ``**kwds`` - passed to :cls:`sage.sat.solvers.cryptominisat.SolverConf`
76
77
EXAMPLE::
78
79
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
80
sage: cms = CryptoMiniSat() # optional - cryptominisat
81
82
CryptoMiniSat accepts a :cls:`sage.sat.solvers.cryptominisat.SolverConf` instance as parameter::
83
84
sage: from sage.sat.solvers.cryptominisat import SolverConf # optional - cryptominisat
85
sage: sc = SolverConf(verbosity=2) # optional - cryptominisat
86
sage: cms = CryptoMiniSat(sc=sc) # optional - cryptominisat
87
88
However, parameters passed directly tot he solver overwrite
89
any options passed to the :cls:`sage.sat.solvers.cryptominisat.SolverConf` instance::
90
91
sage: from sage.sat.solvers.cryptominisat import SolverConf # optional - cryptominisat
92
sage: sc = SolverConf(verbosity=2) # optional - cryptominisat
93
sage: cms = CryptoMiniSat(sc=sc, verbosity=3) # optional - cryptominisat
94
"""
95
cdef SolverConf _sc
96
if sc is not None:
97
_sc = sc.__copy__()
98
else:
99
_sc = SolverConf()
100
cdef GaussConf gc
101
102
for k,v in kwds.iteritems():
103
_sc[k] = v
104
105
self._solver = new Solver(_sc._conf[0], gc)
106
107
def __dealloc__(self):
108
"""
109
TESTS::
110
111
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
112
sage: cms = CryptoMiniSat() # optional - cryptominisat
113
sage: del cms # optional - cryptominisat
114
"""
115
sig_on()
116
del self
117
sig_off()
118
119
def __repr__(self):
120
"""
121
TESTS::
122
123
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
124
sage: cms = CryptoMiniSat() # optional - cryptominisat
125
sage: cms.add_clause((1,2,-3)) # optional - cryptominisat
126
sage: cms # optional - cryptominisat
127
CryptoMiniSat
128
#vars: 3, #lits: 3, #clauses: 1, #learnt: 0, #assigns: 0
129
"""
130
s = """CryptoMiniSat
131
#vars: %7d, #lits: %7d, #clauses: %7d, #learnt: %7d, #assigns: %7d
132
"""%(self._solver.nVars(), self._solver.nLiterals(), self._solver.nClauses(), self._solver.nLearnts(), self._solver.nAssigns())
133
return s
134
135
def var(self, decision=None):
136
"""
137
Return a *new* generator.
138
139
INPUT:
140
141
- ``decision`` - if ``True`` this variable will be used for
142
decisions (default: ``None``, let the solver decide.)
143
144
EXAMPLE::
145
146
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
147
sage: cms = CryptoMiniSat() # optional - cryptominisat
148
sage: cms.var() # optional - cryptominisat
149
1
150
sage: cms.var(decision=True) # optional - cryptominisat
151
2
152
153
"""
154
assert(self._solver.okay())
155
156
cdef Var var
157
if decision is None:
158
var = self._solver.newVar()
159
else:
160
var = self._solver.newVar(bool(decision))
161
return int(var+1)
162
163
def nvars(self):
164
"""
165
Return the number of variables.
166
167
EXAMPLE::
168
169
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
170
sage: cms = CryptoMiniSat() # optional - cryptominisat
171
sage: cms.var() # optional - cryptominisat
172
1
173
sage: cms.var(decision=True) # optional - cryptominisat
174
2
175
sage: cms.nvars() # optional - cryptominisat
176
2
177
"""
178
return int(self._solver.nVars())
179
180
def add_clause(self, lits):
181
"""
182
Add a new clause to set of clauses.
183
184
INPUT:
185
186
- ``lits`` - a tuple of integers != 0
187
188
.. note::
189
190
If any element ``e`` in ``lits`` has ``abs(e)`` greater
191
than the number of variables generated so far, then new
192
variables are created automatically.
193
194
EXAMPLE::
195
196
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
197
sage: cms = CryptoMiniSat() # optional - cryptominisat
198
sage: cms.var() # optional - cryptominisat
199
1
200
sage: cms.var(decision=True) # optional - cryptominisat
201
2
202
sage: cms.add_clause( (1, -2 , 3) ) # optional - cryptominisat
203
sage: cms # optional - cryptominisat
204
CryptoMiniSat
205
#vars: 3, #lits: 3, #clauses: 1, #learnt: 0, #assigns: 0
206
"""
207
assert(self._solver.okay())
208
209
cdef vec[Lit] l
210
for lit in lits:
211
lit = int(lit)
212
while abs(lit) > self._solver.nVars():
213
self._solver.newVar()
214
l.push(Lit(abs(lit)-1,lit<0))
215
self._solver.addClause(l)
216
217
def add_xor_clause(self, lits, isfalse):
218
"""
219
Add a new XOR clause to set of clauses.
220
221
INPUT:
222
223
- ``lits`` - a tuple of integers != 0
224
- ``isfalse`` - set to ``True`` if the XOR chain should evaluate to ``False``
225
226
.. note::
227
228
If any element ``e`` in ``lits`` has ``abs(e)`` greater
229
than the number of variables generated so far, then new
230
variables are created automatically.
231
232
EXAMPLE::
233
234
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
235
sage: cms = CryptoMiniSat() # optional - cryptominisat
236
sage: cms.var() # optional - cryptominisat
237
1
238
sage: cms.var(decision=True) # optional - cryptominisat
239
2
240
sage: cms.add_xor_clause( (1, -2 , 3), True ) # optional - cryptominisat
241
sage: cms # optional - cryptominisat
242
CryptoMiniSat
243
#vars: 3, #lits: 3, #clauses: 1, #learnt: 0, #assigns: 0
244
"""
245
assert(self._solver.okay())
246
247
cdef vec[Lit] l
248
for lit in lits:
249
while abs(lit) > self._solver.nVars():
250
self._solver.newVar()
251
l.push(Lit(abs(lit)-1,lit<0))
252
self._solver.addXorClause(l, bool(isfalse))
253
254
def __call__(self, assumptions=None):
255
"""
256
Solve this instance.
257
258
INPUT:
259
260
- ``assumptions`` - assumed variable assignments (default: ``None``)
261
262
OUTPUT:
263
264
- If this instance is SAT: A tuple of length ``nvars()+1``
265
where the ``i``-th entry holds an assignment for the
266
``i``-th variables (the ``0``-th entry is always ``None``).
267
268
- If this instance is UNSAT: ``False``
269
270
- If the solver was interrupted before deciding satisfiability
271
``None``.
272
273
EXAMPLE:
274
275
We construct a simple example::
276
277
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
278
sage: cms = CryptoMiniSat() # optional - cryptominisat
279
sage: cms.add_clause( (1, 2) ) # optional - cryptominisat
280
281
282
First, we do not assume anything, note that the first entry is
283
``None`` because there is not ``0``-th variable::
284
285
sage: cms() # optional - cryptominisat
286
(None, True, False)
287
288
Now, we make assumptions which make this instance UNSAT::
289
290
sage: cms( (-1, -2) ) # optional - cryptominisat
291
False
292
sage: cms.conflict_clause() # optional - cryptominisat
293
(2, 1)
294
295
Finally, we use assumptions to decide on a solution::
296
297
sage: cms( (-1,) ) # optional - cryptominisat
298
(None, False, True)
299
"""
300
cdef vec[Lit] l
301
cdef lbool r
302
if assumptions is None:
303
sig_on()
304
r = self._solver.solve()
305
sig_off()
306
else:
307
for lit in assumptions:
308
while abs(lit) > self._solver.nVars():
309
self._solver.newVar()
310
l.push(Lit(abs(lit)-1,lit<0))
311
sig_on()
312
r = self._solver.solve(l)
313
sig_off()
314
315
if r == l_False:
316
return False
317
if r == l_Undef:
318
return None
319
320
return (None, ) + tuple([self._solver.model[i].getBool() for i in range(self._solver.model.size())])
321
322
def conflict_clause(self):
323
"""
324
Return conflict clause if this instance is UNSAT and the last
325
call used assumptions.
326
327
EXAMPLE::
328
329
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
330
sage: cms = CryptoMiniSat() # optional - cryptominisat
331
sage: cms.add_clause( (1,2) ) # optional - cryptominisat
332
sage: cms.add_clause( (1,-2) ) # optional - cryptominisat
333
sage: cms.add_clause( (-1,) ) # optional - cryptominisat
334
sage: cms.conflict_clause() # optional - cryptominisat
335
()
336
337
We solve again, but this time with an explicit assumption::
338
339
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
340
sage: cms = CryptoMiniSat() # optional - cryptominisat
341
sage: cms.add_clause( (1,2) ) # optional - cryptominisat
342
sage: cms.add_clause( (1,-2) ) # optional - cryptominisat
343
sage: cms( (-1,) ) # optional - cryptominisat
344
False
345
sage: cms.conflict_clause() # optional - cryptominisat
346
(1,)
347
348
A more elaborate example using small-scale AES where we set 80 variables to ``1``::
349
350
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
351
sage: from sage.sat.converters.polybori import CNFEncoder # optional - cryptominisat
352
sage: set_random_seed( 22 ) # optional - cryptominisat
353
sage: sr = mq.SR(1,4,4,4,gf2=True,polybori=True) # optional - cryptominisat
354
sage: F,s = sr.polynomial_system() # optional - cryptominisat
355
sage: cms = CryptoMiniSat() # optional - cryptominisat
356
sage: phi = CNFEncoder(cms, F.ring())(F) # optional - cryptominisat
357
sage: cms( range(1,120) ) # optional - cryptominisat
358
False
359
360
This guess was wrong and we need to flip one of the following variables::
361
362
sage: cms.conflict_clause() # optional - cryptominisat
363
(-119, -118, -117, -116, -114, -113, -112, -110, -109, -100, -98, -97, -96, -94, -93, -92, -91, -76, -75, -71, -70, -69)
364
"""
365
cdef Lit l
366
r = []
367
for i in range(self._solver.conflict.size()):
368
l = self._solver.conflict[i]
369
r.append( (-1)**l.sign() * (l.var()+1) )
370
return tuple(r)
371
372
def learnt_clauses(self, unitary_only=False):
373
"""
374
Return learnt clauses.
375
376
INPUT:
377
378
- ``unitary_only`` - return only unitary learnt clauses (default: ``False``)
379
380
EXAMPLE::
381
382
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
383
sage: from sage.sat.converters.polybori import CNFEncoder # optional - cryptominisat
384
sage: set_random_seed( 22 ) # optional - cryptominisat
385
sage: sr = mq.SR(1,4,4,4,gf2=True,polybori=True) # optional - cryptominisat
386
sage: F,s = sr.polynomial_system() # optional - cryptominisat
387
sage: cms = CryptoMiniSat(maxrestarts=10,verbosity=0) # optional - cryptominisat
388
sage: phi = CNFEncoder(cms, F.ring())(F) # optional - cryptominisat
389
sage: cms() # optional - cryptominisat
390
sage: sorted(cms.learnt_clauses())[0] # optional - cryptominisat, output random
391
(-592, -578, -68, 588, 94, 579, 584, 583)
392
393
394
An example for unitary clauses::
395
396
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
397
sage: from sage.sat.converters.polybori import CNFEncoder # optional - cryptominisat
398
sage: set_random_seed( 22 ) # optional - cryptominisat
399
sage: sr = mq.SR(1,4,4,4,gf2=True,polybori=True) # optional - cryptominisat
400
sage: F,s = sr.polynomial_system() # optional - cryptominisat
401
sage: cms = CryptoMiniSat(maxrestarts=10,verbosity=0) # optional - cryptominisat
402
sage: phi = CNFEncoder(cms, F.ring())(F) # optional - cryptominisat
403
sage: cms() # optional - cryptominisat
404
sage: cms.learnt_clauses(unitary_only=True) # optional - cryptominisat
405
()
406
407
.. todo::
408
409
Find a more useful example for unitary learnt clauses.
410
"""
411
cdef uint32_t num = 0
412
cdef uint32_t *learnt1 = get_unitary_learnts_helper(self._solver,&num)
413
414
r = []
415
for i in range(num):
416
r.append( (-1)**int(learnt1[i]&1) * (int(learnt1[i]>>1)+1) )
417
sage_free(learnt1)
418
419
if unitary_only:
420
return tuple(r)
421
422
cdef uint32_t **learnt = get_sorted_learnts_helper(self._solver,&num)
423
cdef uint32_t *clause = NULL
424
425
r = []
426
for i in range(num):
427
clause = learnt[i]
428
C = [(-1)**int(clause[j]&1) * (int(clause[j]>>1)+1) for j in range(1,clause[0]+1)]
429
sage_free(clause)
430
r.append(tuple(C))
431
sage_free(learnt)
432
return tuple(r)
433
434
def clauses(self, filename=None):
435
"""
436
Return (possibly simplified) original clauses.
437
438
INPUT:
439
440
- ``filename'' - if not ``None`` clauses are written to ``filename`` in
441
CryptoMinisat's extended DIMACS format (default: ``None``)
442
443
OUTPUT:
444
445
If ``filename`` is ``None`` then a list of ``lits, is_xor, rhs``
446
tuples is returned, where ``lits`` is a tuple of literals,
447
``is_xor`` indicates whether the clause is an xor clause and ``rhs``
448
is either ``True`` or ``False`` for xor clauses and ``None``
449
otherwise.
450
451
If ``filename`` points to a writable file, then the list of original
452
clauses is written to that file in CryptoMiniSat's extended DIMACS
453
format.
454
455
EXAMPLES::
456
457
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
458
sage: cms = CryptoMiniSat() # optional - cryptominisat
459
sage: cms.add_xor_clause((1,2,3,4,5,6,7,8,9), isfalse=False) # optional - cryptominisat
460
sage: cms.add_clause((1,2,3,4,5,6,7,8,-9)) # optional - cryptominisat
461
sage: cms.clauses() # optional - cryptominisat
462
[((1, 2, 3, 4, 5, 6, 7, 8, -9), False, None),
463
((1, 2, 3, 4, 5, 6, 7, 8, 9), True, True)]
464
465
Clauses may have been simplified already::
466
467
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
468
sage: cms = CryptoMiniSat() # optional - cryptominisat
469
sage: cms.add_xor_clause((1,2), isfalse=False) # optional - cryptominisat
470
sage: cms.add_clause((1,2)) # optional - cryptominisat
471
sage: cms.clauses() # optional - cryptominisat
472
[((2, 1), True, True),
473
((-1, -2), False, None),
474
((1, 2), False, None),
475
((1, 2), False, None)]
476
477
DIMACS format output::
478
479
sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat
480
sage: cms = CryptoMiniSat() # optional - cryptominisat
481
sage: cms.add_xor_clause((1,2), isfalse=False) # optional - cryptominisat
482
sage: cms.add_clause((1,2)) # optional - cryptominisat
483
sage: fn = tmp_filename() # optional - cryptominisat
484
sage: cms.clauses(fn) # optional - cryptominisat
485
sage: print open(fn).read() # optional - cryptominisat
486
p cnf 2 4
487
x2 1 0
488
-1 -2 0
489
1 2 0
490
1 2 0
491
<BLANKLINE>
492
"""
493
cdef vector[RetClause] v = self._solver.dumpOrigClauses()
494
cdef vector[Lit] l
495
cdef list original = []
496
497
for i in range(v.size()):
498
l = v[i].lits
499
L = tuple([(-1)**l[j].sign() * (l[j].var()+1) for j in range(l.size())])
500
original.append( (L, v[i].is_xor, v[i].right_hand_side if v[i].is_xor else None ) )
501
502
if filename is None:
503
return original
504
else:
505
from sage.sat.solvers.dimacs import DIMACS
506
DIMACS.render_dimacs(original, filename, self._solver.nVars())
507
508