python-sat 1.8.dev22__cp314-cp314t-macosx_10_13_x86_64.whl → 1.8.dev24__cp314-cp314t-macosx_10_13_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 python-sat might be problematic. Click here for more details.
- pycard.cpython-314t-darwin.so +0 -0
- pysat/__init__.py +1 -1
- pysat/_fileio.py +1 -1
- pysat/examples/bbscan.py +119 -115
- pysolvers.cpython-314t-darwin.so +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/bbscan.py +119 -115
- {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/METADATA +1 -1
- {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/RECORD +24 -24
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/approxmc.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/bica.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/fm.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/genhard.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/lbx.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/lsu.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/mcsls.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/models.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/musx.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/optux.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/primer.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/rc2.py +0 -0
- {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/unigen.py +0 -0
- {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/WHEEL +0 -0
- {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/licenses/LICENSE.txt +0 -0
- {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/top_level.txt +0 -0
pycard.cpython-314t-darwin.so
CHANGED
|
Binary file
|
pysat/__init__.py
CHANGED
pysat/_fileio.py
CHANGED
pysat/examples/bbscan.py
CHANGED
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
>>> cnf = CNF(from_file='formula.wcnf.xz')
|
|
74
74
|
>>>
|
|
75
75
|
>>> # creating BBScan and running it
|
|
76
|
-
>>> with BBScan(cnf, solver='g3',
|
|
76
|
+
>>> with BBScan(cnf, solver='g3', rotate=True) as bbscan:
|
|
77
77
|
... bbone = bbscan.compute(algorithm='core')
|
|
78
78
|
...
|
|
79
79
|
... # outputting the results
|
|
@@ -81,10 +81,10 @@
|
|
|
81
81
|
... print(bbone)
|
|
82
82
|
[1, -3]
|
|
83
83
|
|
|
84
|
-
Each of the available algorithms can be augmented with
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
Each of the available algorithms can be augmented with a simple heuristic
|
|
85
|
+
aiming at reducing satisfying assignments and, thus, filtering out
|
|
86
|
+
unnecessary literals: namely, filtering rotatable literals. The heuristic
|
|
87
|
+
is described in the aforementioned paper.
|
|
88
88
|
|
|
89
89
|
Note that most of the methods of the class :class:`BBScan` are made
|
|
90
90
|
private. Therefore, the tool provides a minimalistic interface via which a
|
|
@@ -113,44 +113,42 @@ class BBScan:
|
|
|
113
113
|
"""
|
|
114
114
|
A solver for computing all backbone literals of a given Boolean
|
|
115
115
|
formula. It implements 6 algorithms for backbone computation described
|
|
116
|
-
in [1]_ augmented with
|
|
117
|
-
process.
|
|
116
|
+
in [1]_ augmented with a heuristic that can be speed up the process.
|
|
118
117
|
|
|
119
118
|
Note that the input formula can be a :class:`.CNF` object but also any
|
|
120
119
|
object of class :class:`.Formula`, thus the tool can used for
|
|
121
120
|
computing backbone literals of non-clausal formulas.
|
|
122
121
|
|
|
123
122
|
A user can select one of the SAT solvers available at hand
|
|
124
|
-
(``'glucose3'`` is used by default). The optional
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
(``'glucose3'`` is used by default). The optional heuristic can be
|
|
124
|
+
also specified as a Boolean input arguments ``rotate`` (turned off by
|
|
125
|
+
default).
|
|
127
126
|
|
|
128
127
|
The list of initialiser's arguments is as follows:
|
|
129
128
|
|
|
130
129
|
:param formula: input formula whose backbone is sought
|
|
131
130
|
:param solver: SAT oracle name
|
|
132
|
-
:param lift: apply literal lifting heuristic
|
|
133
131
|
:param rotate: apply rotatable literal filtering
|
|
134
132
|
:param verbose: verbosity level
|
|
135
133
|
|
|
136
134
|
:type formula: :class:`.Formula` or :class:`.CNF`
|
|
137
135
|
:type solver: str
|
|
138
|
-
:type lift: bool
|
|
139
136
|
:type rotate: bool
|
|
140
137
|
:type verbose: int
|
|
141
138
|
"""
|
|
142
139
|
|
|
143
|
-
def __init__(self, formula, solver='g3',
|
|
140
|
+
def __init__(self, formula, solver='g3', rotate=False, verbose=0):
|
|
144
141
|
"""
|
|
145
142
|
Constructor.
|
|
146
143
|
"""
|
|
147
144
|
|
|
148
145
|
self.formula = formula
|
|
146
|
+
self.focuscl = list(range(len(formula.clauses))) if rotate else []
|
|
149
147
|
self.verbose = verbose
|
|
150
148
|
self.oracle = None
|
|
151
149
|
|
|
152
150
|
# implicant reduction heuristics
|
|
153
|
-
self.
|
|
151
|
+
self.rotate = rotate
|
|
154
152
|
|
|
155
153
|
# basic stats
|
|
156
154
|
self.calls, self.filtered = 0, 0
|
|
@@ -204,13 +202,16 @@ class BBScan:
|
|
|
204
202
|
|
|
205
203
|
If the chunking algorithm is selected (either ``'chunk'`` or
|
|
206
204
|
``'corechunk'``), the user may specify the size of the chunk,
|
|
207
|
-
which is set to 100 by default.
|
|
205
|
+
which is set to 100 by default. Note that the chunk_size can be a
|
|
206
|
+
floating-point number in the interval (0, 1], which will set the
|
|
207
|
+
actual chunk_size relative to the total number of literals of
|
|
208
|
+
interest.
|
|
208
209
|
|
|
209
210
|
Finally, one may opt for computing backbone literals out of a
|
|
210
|
-
particular subset of literals
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
particular subset of literals, which may run faster than computing
|
|
212
|
+
the entire formula's backbone. To focus on a particular set of
|
|
213
|
+
literals, the user should use the parameter ``focus_on``, which is
|
|
214
|
+
set to ``None`` by default.
|
|
214
215
|
|
|
215
216
|
.. note::
|
|
216
217
|
|
|
@@ -222,20 +223,38 @@ class BBScan:
|
|
|
222
223
|
:param focus_on: a list of literals to focus on
|
|
223
224
|
|
|
224
225
|
:type algorithm: str
|
|
225
|
-
:type chunk_size: int
|
|
226
|
+
:type chunk_size: int or float
|
|
226
227
|
:type focus_on: iterable(int)
|
|
227
228
|
"""
|
|
228
229
|
|
|
229
230
|
self.calls += 1
|
|
231
|
+
trivial = []
|
|
230
232
|
|
|
231
233
|
if self.oracle.solve():
|
|
232
|
-
self.model = self.oracle.get_model()
|
|
234
|
+
self.model = set(self.oracle.get_model())
|
|
233
235
|
if focus_on is not None:
|
|
234
|
-
model
|
|
235
|
-
|
|
236
|
+
self.model &= set(focus_on)
|
|
237
|
+
|
|
238
|
+
# if filtering rotatable literals is requested then
|
|
239
|
+
# we should be clever about the clauses to traverse
|
|
240
|
+
if self.rotate:
|
|
241
|
+
self.focuscl = []
|
|
242
|
+
for i, cl in enumerate(self.formula):
|
|
243
|
+
for l in cl:
|
|
244
|
+
if l in self.model:
|
|
245
|
+
if len(cl) == 1:
|
|
246
|
+
trivial.append(l)
|
|
247
|
+
self.model.remove(l)
|
|
248
|
+
else:
|
|
249
|
+
self.focuscl.append(i)
|
|
250
|
+
break
|
|
236
251
|
else:
|
|
237
252
|
raise ValueError('Unsatisfiable formula')
|
|
238
253
|
|
|
254
|
+
if isinstance(chunk_size, float):
|
|
255
|
+
assert 0 < chunk_size <= 1, f'Wrong chunk proportion {chunk_size}'
|
|
256
|
+
chunk_size = int(min(self.formula.nv, len(self.model)) * chunk_size)
|
|
257
|
+
|
|
239
258
|
if algorithm == 'enum':
|
|
240
259
|
result = self._compute_enum()
|
|
241
260
|
elif algorithm == 'iter':
|
|
@@ -251,50 +270,36 @@ class BBScan:
|
|
|
251
270
|
else:
|
|
252
271
|
assert 0, f'Unknown algorithm: {algorithm}'
|
|
253
272
|
|
|
254
|
-
return sorted(result, key=lambda l: abs(l))
|
|
255
|
-
|
|
256
|
-
def _get_implicant(self, model):
|
|
257
|
-
"""
|
|
258
|
-
Simple literal lifting used to reduce a given model.
|
|
259
|
-
"""
|
|
260
|
-
|
|
261
|
-
res, model = set(), set(model)
|
|
262
|
-
|
|
263
|
-
# traversing the clauses and collecting all literals
|
|
264
|
-
# that satisfy at least one clause of the formula
|
|
265
|
-
for cl in self.formula:
|
|
266
|
-
res |= set([l for l in cl if l in model])
|
|
267
|
-
|
|
268
|
-
return list(res)
|
|
273
|
+
return sorted(trivial + result, key=lambda l: abs(l))
|
|
269
274
|
|
|
270
275
|
def _filter_rotatable(self, model):
|
|
271
276
|
"""
|
|
272
277
|
Filter out rotatable literals.
|
|
273
278
|
"""
|
|
274
279
|
|
|
275
|
-
|
|
280
|
+
def get_unit(cl):
|
|
281
|
+
# this is supposed to be a faster alternative to the
|
|
282
|
+
# complete clause traversal with a conditional inside
|
|
283
|
+
unit = None
|
|
284
|
+
for l in cl:
|
|
285
|
+
if l in model:
|
|
286
|
+
if unit:
|
|
287
|
+
return
|
|
288
|
+
else:
|
|
289
|
+
unit = l
|
|
290
|
+
return unit
|
|
291
|
+
|
|
292
|
+
# result of this procedure
|
|
293
|
+
units = set([])
|
|
276
294
|
|
|
277
295
|
# determining unit literals
|
|
278
|
-
for
|
|
279
|
-
|
|
280
|
-
if
|
|
281
|
-
units.add(
|
|
296
|
+
for i in self.focuscl:
|
|
297
|
+
unit = get_unit(self.formula.clauses[i])
|
|
298
|
+
if unit:
|
|
299
|
+
units.add(unit)
|
|
282
300
|
|
|
283
301
|
self.filtered += len(model) - len(units)
|
|
284
|
-
return
|
|
285
|
-
|
|
286
|
-
def _process_model(self, model):
|
|
287
|
-
"""
|
|
288
|
-
Heuristically reduce a model.
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
if self.lift:
|
|
292
|
-
model = self._get_implicant(model)
|
|
293
|
-
|
|
294
|
-
if self.rotate:
|
|
295
|
-
model = self._filter_rotatable(model)
|
|
296
|
-
|
|
297
|
-
return model
|
|
302
|
+
return units
|
|
298
303
|
|
|
299
304
|
def _compute_enum(self, focus_on=None):
|
|
300
305
|
"""
|
|
@@ -305,7 +310,7 @@ class BBScan:
|
|
|
305
310
|
print('c using enumeration-based algorithm')
|
|
306
311
|
|
|
307
312
|
# initial backbone estimate contains all literals
|
|
308
|
-
bbone =
|
|
313
|
+
bbone = self.model if focus_on is None else focus_on
|
|
309
314
|
|
|
310
315
|
while bbone:
|
|
311
316
|
# counting the number of calls
|
|
@@ -315,17 +320,14 @@ class BBScan:
|
|
|
315
320
|
if not self.oracle.solve():
|
|
316
321
|
break
|
|
317
322
|
|
|
318
|
-
self.model = self.oracle.get_model()
|
|
319
|
-
# if self.lift:
|
|
320
|
-
# self.model = self._get_implicant(self.model)
|
|
321
|
-
|
|
322
|
-
self.model = self._process_model(self.model)
|
|
323
|
-
|
|
324
323
|
# updating backbone estimate - intersection
|
|
325
|
-
bbone &= set(self.
|
|
324
|
+
bbone &= set(self.oracle.get_model())
|
|
325
|
+
|
|
326
|
+
if self.rotate:
|
|
327
|
+
bbone = self._filter_rotatable(bbone)
|
|
326
328
|
|
|
327
329
|
# blocking the previously found implicant
|
|
328
|
-
self.oracle.add_clause([-l for l in
|
|
330
|
+
self.oracle.add_clause([-l for l in bbone])
|
|
329
331
|
|
|
330
332
|
return list(bbone)
|
|
331
333
|
|
|
@@ -337,6 +339,8 @@ class BBScan:
|
|
|
337
339
|
if self.verbose:
|
|
338
340
|
print('c using iterative algorithm')
|
|
339
341
|
|
|
342
|
+
# initial estimate
|
|
343
|
+
# using sets for model and assumps to save filtering time
|
|
340
344
|
bbone, model = [], self.model if focus_on is None else focus_on
|
|
341
345
|
|
|
342
346
|
while model:
|
|
@@ -352,9 +356,10 @@ class BBScan:
|
|
|
352
356
|
else:
|
|
353
357
|
# it isn't and we've got a counterexample
|
|
354
358
|
# => using it to filter out more literals
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
359
|
+
model &= set(self.oracle.get_model())
|
|
360
|
+
|
|
361
|
+
if self.rotate:
|
|
362
|
+
model = self._filter_rotatable(model)
|
|
358
363
|
|
|
359
364
|
return bbone
|
|
360
365
|
|
|
@@ -367,7 +372,7 @@ class BBScan:
|
|
|
367
372
|
print('c using complement of backbone estimate algorithm')
|
|
368
373
|
|
|
369
374
|
# initial estimate
|
|
370
|
-
bbone =
|
|
375
|
+
bbone = self.model if focus_on is None else focus_on
|
|
371
376
|
|
|
372
377
|
# iterating until we find the backbone or determine there is none
|
|
373
378
|
while bbone:
|
|
@@ -380,22 +385,23 @@ class BBScan:
|
|
|
380
385
|
if self.oracle.solve() == False:
|
|
381
386
|
break
|
|
382
387
|
else:
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
388
|
+
bbone &= set(self.oracle.get_model())
|
|
389
|
+
|
|
390
|
+
if self.rotate:
|
|
391
|
+
bbone = self._filter_rotatable(bbone)
|
|
386
392
|
|
|
387
393
|
return list(bbone)
|
|
388
394
|
|
|
389
395
|
def _compute_chunking(self, chunk_size=100, focus_on=None):
|
|
390
396
|
"""
|
|
391
|
-
|
|
397
|
+
Chunking algorithm.
|
|
392
398
|
"""
|
|
393
399
|
|
|
394
400
|
if self.verbose:
|
|
395
401
|
print('c using chunking algorithm, with chunk size:', chunk_size)
|
|
396
402
|
|
|
397
403
|
# initial estimate
|
|
398
|
-
bbone, model = [], self.model if focus_on is None else focus_on
|
|
404
|
+
bbone, model = [], list(self.model if focus_on is None else focus_on)
|
|
399
405
|
|
|
400
406
|
# we are going to use clause selectors
|
|
401
407
|
vpool = IDPool(start_from=self.formula.nv + 1)
|
|
@@ -421,7 +427,9 @@ class BBScan:
|
|
|
421
427
|
else:
|
|
422
428
|
coex = set(self.oracle.get_model())
|
|
423
429
|
model = [l for l in model if l in coex]
|
|
424
|
-
|
|
430
|
+
|
|
431
|
+
if self.rotate:
|
|
432
|
+
model = list(self._filter_rotatable(set(model)))
|
|
425
433
|
|
|
426
434
|
return bbone
|
|
427
435
|
|
|
@@ -434,21 +442,24 @@ class BBScan:
|
|
|
434
442
|
print('c using core-based algorithm')
|
|
435
443
|
|
|
436
444
|
# initial estimate
|
|
445
|
+
# using sets for model and assumps to save filtering time
|
|
437
446
|
bbone, model = [], self.model if focus_on is None else focus_on
|
|
438
447
|
|
|
439
448
|
# iterating until we find the backbone or determine there is none
|
|
440
449
|
while model:
|
|
441
450
|
# flipping all the literals
|
|
442
|
-
assumps =
|
|
451
|
+
assumps = {-l for l in model}
|
|
443
452
|
|
|
444
453
|
# getting unsatisfiable cores with them
|
|
445
454
|
while True:
|
|
446
455
|
self.calls += 1
|
|
447
456
|
|
|
448
457
|
if self.oracle.solve(assumptions=assumps):
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
458
|
+
model &= set(self.oracle.get_model())
|
|
459
|
+
|
|
460
|
+
if self.rotate:
|
|
461
|
+
model = self._filter_rotatable(model)
|
|
462
|
+
|
|
452
463
|
break
|
|
453
464
|
|
|
454
465
|
else:
|
|
@@ -458,18 +469,12 @@ class BBScan:
|
|
|
458
469
|
self.oracle.add_clause([-core[0]])
|
|
459
470
|
|
|
460
471
|
# remove from the working model
|
|
461
|
-
|
|
462
|
-
if indx < len(model) - 1:
|
|
463
|
-
model[indx] = model.pop()
|
|
464
|
-
else:
|
|
465
|
-
model.pop()
|
|
472
|
+
model.remove(-core[0])
|
|
466
473
|
|
|
467
474
|
# filtering out unnecessary flipped literals
|
|
468
|
-
|
|
469
|
-
assumps = [l for l in assumps if l not in core]
|
|
475
|
+
assumps -= set(core)
|
|
470
476
|
|
|
471
477
|
if not assumps:
|
|
472
|
-
# resorting to the iterative traversal algorithm
|
|
473
478
|
self.model = model
|
|
474
479
|
return bbone + self._compute_iter()
|
|
475
480
|
|
|
@@ -484,7 +489,7 @@ class BBScan:
|
|
|
484
489
|
print('c using core-based chunking, with chunk size:', chunk_size)
|
|
485
490
|
|
|
486
491
|
# initial estimate
|
|
487
|
-
bbone, model = [], self.model if focus_on is None else focus_on
|
|
492
|
+
bbone, model = [], list(self.model if focus_on is None else focus_on)
|
|
488
493
|
|
|
489
494
|
# we are going to use clause selectors
|
|
490
495
|
vpool = IDPool(start_from=self.formula.nv + 1)
|
|
@@ -495,7 +500,7 @@ class BBScan:
|
|
|
495
500
|
size = min(chunk_size, len(model))
|
|
496
501
|
|
|
497
502
|
# flipping all the literals
|
|
498
|
-
assumps, skipped =
|
|
503
|
+
assumps, skipped = {-model.pop() for i in range(size)}, set()
|
|
499
504
|
|
|
500
505
|
# getting unsatisfiable cores with them
|
|
501
506
|
while True:
|
|
@@ -504,7 +509,9 @@ class BBScan:
|
|
|
504
509
|
if self.oracle.solve(assumptions=assumps):
|
|
505
510
|
coex = set(self.oracle.get_model())
|
|
506
511
|
model = [l for l in model if l in coex]
|
|
507
|
-
|
|
512
|
+
|
|
513
|
+
if self.rotate:
|
|
514
|
+
model = list(self._filter_rotatable(set(model)))
|
|
508
515
|
|
|
509
516
|
if skipped:
|
|
510
517
|
bbone += self._compute_iter(focus_on=skipped)
|
|
@@ -520,11 +527,10 @@ class BBScan:
|
|
|
520
527
|
self.oracle.add_clause([-core[0]])
|
|
521
528
|
else:
|
|
522
529
|
# all removed literals are going to be tested later
|
|
523
|
-
skipped
|
|
530
|
+
skipped |= {-l for l in core}
|
|
524
531
|
|
|
525
532
|
# filtering out unnecessary flipped literals
|
|
526
|
-
|
|
527
|
-
assumps = [l for l in assumps if l not in core]
|
|
533
|
+
assumps -= set(core)
|
|
528
534
|
|
|
529
535
|
if not assumps:
|
|
530
536
|
# resorting to the iterative traversal algorithm
|
|
@@ -555,11 +561,10 @@ def parse_options():
|
|
|
555
561
|
|
|
556
562
|
try:
|
|
557
563
|
opts, args = getopt.getopt(sys.argv[1:],
|
|
558
|
-
'a:c:
|
|
564
|
+
'a:c:hrs:v',
|
|
559
565
|
['algo=',
|
|
560
566
|
'chunk=',
|
|
561
567
|
'help',
|
|
562
|
-
'lift',
|
|
563
568
|
'rotate',
|
|
564
569
|
'solver=',
|
|
565
570
|
'verbose'])
|
|
@@ -570,7 +575,6 @@ def parse_options():
|
|
|
570
575
|
|
|
571
576
|
algo = 'iter'
|
|
572
577
|
chunk = 100
|
|
573
|
-
lift = False
|
|
574
578
|
rotate = False
|
|
575
579
|
solver = 'g3'
|
|
576
580
|
verbose = 0
|
|
@@ -580,12 +584,14 @@ def parse_options():
|
|
|
580
584
|
algo = str(arg)
|
|
581
585
|
assert algo in ('enum', 'iter', 'compl', 'chunk', 'core', 'corechunk'), 'Unknown algorithm'
|
|
582
586
|
elif opt in ('-c', '--chunk'):
|
|
583
|
-
chunk =
|
|
587
|
+
chunk = float(arg)
|
|
588
|
+
if chunk.is_integer():
|
|
589
|
+
chunk = int(chunk)
|
|
590
|
+
else:
|
|
591
|
+
assert 0 < chunk <= 1, f'Wrong chunk proportion {chunk_size}'
|
|
584
592
|
elif opt in ('-h', '--help'):
|
|
585
593
|
usage()
|
|
586
594
|
sys.exit(0)
|
|
587
|
-
elif opt in ('-l', '--lift'):
|
|
588
|
-
lift = True
|
|
589
595
|
elif opt in ('-r', '--rotate'):
|
|
590
596
|
rotate = True
|
|
591
597
|
elif opt in ('-s', '--solver'):
|
|
@@ -595,7 +601,7 @@ def parse_options():
|
|
|
595
601
|
else:
|
|
596
602
|
assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
|
|
597
603
|
|
|
598
|
-
return algo, chunk,
|
|
604
|
+
return algo, chunk, rotate, solver, verbose, args
|
|
599
605
|
|
|
600
606
|
|
|
601
607
|
#
|
|
@@ -607,22 +613,21 @@ def usage():
|
|
|
607
613
|
|
|
608
614
|
print('Usage:', os.path.basename(sys.argv[0]), '[options] file')
|
|
609
615
|
print('Options:')
|
|
610
|
-
print(' -a, --algo=<string>
|
|
611
|
-
print('
|
|
612
|
-
print(' -c, --chunk=<int>
|
|
613
|
-
print('
|
|
614
|
-
print(' -h, --help
|
|
615
|
-
print(' -
|
|
616
|
-
print(' -
|
|
617
|
-
print('
|
|
618
|
-
print('
|
|
619
|
-
print(' -v, --verbose Be verbose (can be used multiple times)')
|
|
616
|
+
print(' -a, --algo=<string> Algorithm to use')
|
|
617
|
+
print(' Available values: enum, iter, compl, chunk, core, corechunk (default: iter)')
|
|
618
|
+
print(' -c, --chunk=<int,float> Chunk size for chunking algorithms')
|
|
619
|
+
print(' Available values: [1 .. INT_MAX] or (0 .. 1] (default: 100)')
|
|
620
|
+
print(' -h, --help Show this message')
|
|
621
|
+
print(' -r, --rotate Heuristically filter out rotatable literals')
|
|
622
|
+
print(' -s, --solver=<string> SAT solver to use')
|
|
623
|
+
print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default: g3)')
|
|
624
|
+
print(' -v, --verbose Be verbose (can be used multiple times)')
|
|
620
625
|
|
|
621
626
|
|
|
622
627
|
#
|
|
623
628
|
#==============================================================================
|
|
624
629
|
if __name__ == '__main__':
|
|
625
|
-
algo, chunk,
|
|
630
|
+
algo, chunk, rotate, solver, verbose, files = parse_options()
|
|
626
631
|
|
|
627
632
|
if files:
|
|
628
633
|
# read CNF from file
|
|
@@ -635,8 +640,7 @@ if __name__ == '__main__':
|
|
|
635
640
|
len(formula.clauses)))
|
|
636
641
|
|
|
637
642
|
# computing the backbone
|
|
638
|
-
with BBScan(formula, solver=solver,
|
|
639
|
-
verbose=verbose) as bbscan:
|
|
643
|
+
with BBScan(formula, solver=solver, rotate=rotate, verbose=verbose) as bbscan:
|
|
640
644
|
try:
|
|
641
645
|
bbone = bbscan.compute(algorithm=algo, chunk_size=chunk)
|
|
642
646
|
|
|
@@ -653,7 +657,7 @@ if __name__ == '__main__':
|
|
|
653
657
|
print('v 0')
|
|
654
658
|
|
|
655
659
|
if verbose > 1:
|
|
656
|
-
print('c filtered: {0}
|
|
660
|
+
print('c filtered: {0} ({1:.2f})'.format(bbscan.filtered, 100. * bbscan.filtered / formula.nv))
|
|
657
661
|
|
|
658
662
|
print('c oracle time: {0:.4f}'.format(bbscan.oracle_time()))
|
|
659
663
|
print('c oracle calls: {0}'.format(bbscan.calls))
|
pysolvers.cpython-314t-darwin.so
CHANGED
|
Binary file
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
>>> cnf = CNF(from_file='formula.wcnf.xz')
|
|
74
74
|
>>>
|
|
75
75
|
>>> # creating BBScan and running it
|
|
76
|
-
>>> with BBScan(cnf, solver='g3',
|
|
76
|
+
>>> with BBScan(cnf, solver='g3', rotate=True) as bbscan:
|
|
77
77
|
... bbone = bbscan.compute(algorithm='core')
|
|
78
78
|
...
|
|
79
79
|
... # outputting the results
|
|
@@ -81,10 +81,10 @@
|
|
|
81
81
|
... print(bbone)
|
|
82
82
|
[1, -3]
|
|
83
83
|
|
|
84
|
-
Each of the available algorithms can be augmented with
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
Each of the available algorithms can be augmented with a simple heuristic
|
|
85
|
+
aiming at reducing satisfying assignments and, thus, filtering out
|
|
86
|
+
unnecessary literals: namely, filtering rotatable literals. The heuristic
|
|
87
|
+
is described in the aforementioned paper.
|
|
88
88
|
|
|
89
89
|
Note that most of the methods of the class :class:`BBScan` are made
|
|
90
90
|
private. Therefore, the tool provides a minimalistic interface via which a
|
|
@@ -113,44 +113,42 @@ class BBScan:
|
|
|
113
113
|
"""
|
|
114
114
|
A solver for computing all backbone literals of a given Boolean
|
|
115
115
|
formula. It implements 6 algorithms for backbone computation described
|
|
116
|
-
in [1]_ augmented with
|
|
117
|
-
process.
|
|
116
|
+
in [1]_ augmented with a heuristic that can be speed up the process.
|
|
118
117
|
|
|
119
118
|
Note that the input formula can be a :class:`.CNF` object but also any
|
|
120
119
|
object of class :class:`.Formula`, thus the tool can used for
|
|
121
120
|
computing backbone literals of non-clausal formulas.
|
|
122
121
|
|
|
123
122
|
A user can select one of the SAT solvers available at hand
|
|
124
|
-
(``'glucose3'`` is used by default). The optional
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
(``'glucose3'`` is used by default). The optional heuristic can be
|
|
124
|
+
also specified as a Boolean input arguments ``rotate`` (turned off by
|
|
125
|
+
default).
|
|
127
126
|
|
|
128
127
|
The list of initialiser's arguments is as follows:
|
|
129
128
|
|
|
130
129
|
:param formula: input formula whose backbone is sought
|
|
131
130
|
:param solver: SAT oracle name
|
|
132
|
-
:param lift: apply literal lifting heuristic
|
|
133
131
|
:param rotate: apply rotatable literal filtering
|
|
134
132
|
:param verbose: verbosity level
|
|
135
133
|
|
|
136
134
|
:type formula: :class:`.Formula` or :class:`.CNF`
|
|
137
135
|
:type solver: str
|
|
138
|
-
:type lift: bool
|
|
139
136
|
:type rotate: bool
|
|
140
137
|
:type verbose: int
|
|
141
138
|
"""
|
|
142
139
|
|
|
143
|
-
def __init__(self, formula, solver='g3',
|
|
140
|
+
def __init__(self, formula, solver='g3', rotate=False, verbose=0):
|
|
144
141
|
"""
|
|
145
142
|
Constructor.
|
|
146
143
|
"""
|
|
147
144
|
|
|
148
145
|
self.formula = formula
|
|
146
|
+
self.focuscl = list(range(len(formula.clauses))) if rotate else []
|
|
149
147
|
self.verbose = verbose
|
|
150
148
|
self.oracle = None
|
|
151
149
|
|
|
152
150
|
# implicant reduction heuristics
|
|
153
|
-
self.
|
|
151
|
+
self.rotate = rotate
|
|
154
152
|
|
|
155
153
|
# basic stats
|
|
156
154
|
self.calls, self.filtered = 0, 0
|
|
@@ -204,13 +202,16 @@ class BBScan:
|
|
|
204
202
|
|
|
205
203
|
If the chunking algorithm is selected (either ``'chunk'`` or
|
|
206
204
|
``'corechunk'``), the user may specify the size of the chunk,
|
|
207
|
-
which is set to 100 by default.
|
|
205
|
+
which is set to 100 by default. Note that the chunk_size can be a
|
|
206
|
+
floating-point number in the interval (0, 1], which will set the
|
|
207
|
+
actual chunk_size relative to the total number of literals of
|
|
208
|
+
interest.
|
|
208
209
|
|
|
209
210
|
Finally, one may opt for computing backbone literals out of a
|
|
210
|
-
particular subset of literals
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
particular subset of literals, which may run faster than computing
|
|
212
|
+
the entire formula's backbone. To focus on a particular set of
|
|
213
|
+
literals, the user should use the parameter ``focus_on``, which is
|
|
214
|
+
set to ``None`` by default.
|
|
214
215
|
|
|
215
216
|
.. note::
|
|
216
217
|
|
|
@@ -222,20 +223,38 @@ class BBScan:
|
|
|
222
223
|
:param focus_on: a list of literals to focus on
|
|
223
224
|
|
|
224
225
|
:type algorithm: str
|
|
225
|
-
:type chunk_size: int
|
|
226
|
+
:type chunk_size: int or float
|
|
226
227
|
:type focus_on: iterable(int)
|
|
227
228
|
"""
|
|
228
229
|
|
|
229
230
|
self.calls += 1
|
|
231
|
+
trivial = []
|
|
230
232
|
|
|
231
233
|
if self.oracle.solve():
|
|
232
|
-
self.model = self.oracle.get_model()
|
|
234
|
+
self.model = set(self.oracle.get_model())
|
|
233
235
|
if focus_on is not None:
|
|
234
|
-
model
|
|
235
|
-
|
|
236
|
+
self.model &= set(focus_on)
|
|
237
|
+
|
|
238
|
+
# if filtering rotatable literals is requested then
|
|
239
|
+
# we should be clever about the clauses to traverse
|
|
240
|
+
if self.rotate:
|
|
241
|
+
self.focuscl = []
|
|
242
|
+
for i, cl in enumerate(self.formula):
|
|
243
|
+
for l in cl:
|
|
244
|
+
if l in self.model:
|
|
245
|
+
if len(cl) == 1:
|
|
246
|
+
trivial.append(l)
|
|
247
|
+
self.model.remove(l)
|
|
248
|
+
else:
|
|
249
|
+
self.focuscl.append(i)
|
|
250
|
+
break
|
|
236
251
|
else:
|
|
237
252
|
raise ValueError('Unsatisfiable formula')
|
|
238
253
|
|
|
254
|
+
if isinstance(chunk_size, float):
|
|
255
|
+
assert 0 < chunk_size <= 1, f'Wrong chunk proportion {chunk_size}'
|
|
256
|
+
chunk_size = int(min(self.formula.nv, len(self.model)) * chunk_size)
|
|
257
|
+
|
|
239
258
|
if algorithm == 'enum':
|
|
240
259
|
result = self._compute_enum()
|
|
241
260
|
elif algorithm == 'iter':
|
|
@@ -251,50 +270,36 @@ class BBScan:
|
|
|
251
270
|
else:
|
|
252
271
|
assert 0, f'Unknown algorithm: {algorithm}'
|
|
253
272
|
|
|
254
|
-
return sorted(result, key=lambda l: abs(l))
|
|
255
|
-
|
|
256
|
-
def _get_implicant(self, model):
|
|
257
|
-
"""
|
|
258
|
-
Simple literal lifting used to reduce a given model.
|
|
259
|
-
"""
|
|
260
|
-
|
|
261
|
-
res, model = set(), set(model)
|
|
262
|
-
|
|
263
|
-
# traversing the clauses and collecting all literals
|
|
264
|
-
# that satisfy at least one clause of the formula
|
|
265
|
-
for cl in self.formula:
|
|
266
|
-
res |= set([l for l in cl if l in model])
|
|
267
|
-
|
|
268
|
-
return list(res)
|
|
273
|
+
return sorted(trivial + result, key=lambda l: abs(l))
|
|
269
274
|
|
|
270
275
|
def _filter_rotatable(self, model):
|
|
271
276
|
"""
|
|
272
277
|
Filter out rotatable literals.
|
|
273
278
|
"""
|
|
274
279
|
|
|
275
|
-
|
|
280
|
+
def get_unit(cl):
|
|
281
|
+
# this is supposed to be a faster alternative to the
|
|
282
|
+
# complete clause traversal with a conditional inside
|
|
283
|
+
unit = None
|
|
284
|
+
for l in cl:
|
|
285
|
+
if l in model:
|
|
286
|
+
if unit:
|
|
287
|
+
return
|
|
288
|
+
else:
|
|
289
|
+
unit = l
|
|
290
|
+
return unit
|
|
291
|
+
|
|
292
|
+
# result of this procedure
|
|
293
|
+
units = set([])
|
|
276
294
|
|
|
277
295
|
# determining unit literals
|
|
278
|
-
for
|
|
279
|
-
|
|
280
|
-
if
|
|
281
|
-
units.add(
|
|
296
|
+
for i in self.focuscl:
|
|
297
|
+
unit = get_unit(self.formula.clauses[i])
|
|
298
|
+
if unit:
|
|
299
|
+
units.add(unit)
|
|
282
300
|
|
|
283
301
|
self.filtered += len(model) - len(units)
|
|
284
|
-
return
|
|
285
|
-
|
|
286
|
-
def _process_model(self, model):
|
|
287
|
-
"""
|
|
288
|
-
Heuristically reduce a model.
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
if self.lift:
|
|
292
|
-
model = self._get_implicant(model)
|
|
293
|
-
|
|
294
|
-
if self.rotate:
|
|
295
|
-
model = self._filter_rotatable(model)
|
|
296
|
-
|
|
297
|
-
return model
|
|
302
|
+
return units
|
|
298
303
|
|
|
299
304
|
def _compute_enum(self, focus_on=None):
|
|
300
305
|
"""
|
|
@@ -305,7 +310,7 @@ class BBScan:
|
|
|
305
310
|
print('c using enumeration-based algorithm')
|
|
306
311
|
|
|
307
312
|
# initial backbone estimate contains all literals
|
|
308
|
-
bbone =
|
|
313
|
+
bbone = self.model if focus_on is None else focus_on
|
|
309
314
|
|
|
310
315
|
while bbone:
|
|
311
316
|
# counting the number of calls
|
|
@@ -315,17 +320,14 @@ class BBScan:
|
|
|
315
320
|
if not self.oracle.solve():
|
|
316
321
|
break
|
|
317
322
|
|
|
318
|
-
self.model = self.oracle.get_model()
|
|
319
|
-
# if self.lift:
|
|
320
|
-
# self.model = self._get_implicant(self.model)
|
|
321
|
-
|
|
322
|
-
self.model = self._process_model(self.model)
|
|
323
|
-
|
|
324
323
|
# updating backbone estimate - intersection
|
|
325
|
-
bbone &= set(self.
|
|
324
|
+
bbone &= set(self.oracle.get_model())
|
|
325
|
+
|
|
326
|
+
if self.rotate:
|
|
327
|
+
bbone = self._filter_rotatable(bbone)
|
|
326
328
|
|
|
327
329
|
# blocking the previously found implicant
|
|
328
|
-
self.oracle.add_clause([-l for l in
|
|
330
|
+
self.oracle.add_clause([-l for l in bbone])
|
|
329
331
|
|
|
330
332
|
return list(bbone)
|
|
331
333
|
|
|
@@ -337,6 +339,8 @@ class BBScan:
|
|
|
337
339
|
if self.verbose:
|
|
338
340
|
print('c using iterative algorithm')
|
|
339
341
|
|
|
342
|
+
# initial estimate
|
|
343
|
+
# using sets for model and assumps to save filtering time
|
|
340
344
|
bbone, model = [], self.model if focus_on is None else focus_on
|
|
341
345
|
|
|
342
346
|
while model:
|
|
@@ -352,9 +356,10 @@ class BBScan:
|
|
|
352
356
|
else:
|
|
353
357
|
# it isn't and we've got a counterexample
|
|
354
358
|
# => using it to filter out more literals
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
359
|
+
model &= set(self.oracle.get_model())
|
|
360
|
+
|
|
361
|
+
if self.rotate:
|
|
362
|
+
model = self._filter_rotatable(model)
|
|
358
363
|
|
|
359
364
|
return bbone
|
|
360
365
|
|
|
@@ -367,7 +372,7 @@ class BBScan:
|
|
|
367
372
|
print('c using complement of backbone estimate algorithm')
|
|
368
373
|
|
|
369
374
|
# initial estimate
|
|
370
|
-
bbone =
|
|
375
|
+
bbone = self.model if focus_on is None else focus_on
|
|
371
376
|
|
|
372
377
|
# iterating until we find the backbone or determine there is none
|
|
373
378
|
while bbone:
|
|
@@ -380,22 +385,23 @@ class BBScan:
|
|
|
380
385
|
if self.oracle.solve() == False:
|
|
381
386
|
break
|
|
382
387
|
else:
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
388
|
+
bbone &= set(self.oracle.get_model())
|
|
389
|
+
|
|
390
|
+
if self.rotate:
|
|
391
|
+
bbone = self._filter_rotatable(bbone)
|
|
386
392
|
|
|
387
393
|
return list(bbone)
|
|
388
394
|
|
|
389
395
|
def _compute_chunking(self, chunk_size=100, focus_on=None):
|
|
390
396
|
"""
|
|
391
|
-
|
|
397
|
+
Chunking algorithm.
|
|
392
398
|
"""
|
|
393
399
|
|
|
394
400
|
if self.verbose:
|
|
395
401
|
print('c using chunking algorithm, with chunk size:', chunk_size)
|
|
396
402
|
|
|
397
403
|
# initial estimate
|
|
398
|
-
bbone, model = [], self.model if focus_on is None else focus_on
|
|
404
|
+
bbone, model = [], list(self.model if focus_on is None else focus_on)
|
|
399
405
|
|
|
400
406
|
# we are going to use clause selectors
|
|
401
407
|
vpool = IDPool(start_from=self.formula.nv + 1)
|
|
@@ -421,7 +427,9 @@ class BBScan:
|
|
|
421
427
|
else:
|
|
422
428
|
coex = set(self.oracle.get_model())
|
|
423
429
|
model = [l for l in model if l in coex]
|
|
424
|
-
|
|
430
|
+
|
|
431
|
+
if self.rotate:
|
|
432
|
+
model = list(self._filter_rotatable(set(model)))
|
|
425
433
|
|
|
426
434
|
return bbone
|
|
427
435
|
|
|
@@ -434,21 +442,24 @@ class BBScan:
|
|
|
434
442
|
print('c using core-based algorithm')
|
|
435
443
|
|
|
436
444
|
# initial estimate
|
|
445
|
+
# using sets for model and assumps to save filtering time
|
|
437
446
|
bbone, model = [], self.model if focus_on is None else focus_on
|
|
438
447
|
|
|
439
448
|
# iterating until we find the backbone or determine there is none
|
|
440
449
|
while model:
|
|
441
450
|
# flipping all the literals
|
|
442
|
-
assumps =
|
|
451
|
+
assumps = {-l for l in model}
|
|
443
452
|
|
|
444
453
|
# getting unsatisfiable cores with them
|
|
445
454
|
while True:
|
|
446
455
|
self.calls += 1
|
|
447
456
|
|
|
448
457
|
if self.oracle.solve(assumptions=assumps):
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
458
|
+
model &= set(self.oracle.get_model())
|
|
459
|
+
|
|
460
|
+
if self.rotate:
|
|
461
|
+
model = self._filter_rotatable(model)
|
|
462
|
+
|
|
452
463
|
break
|
|
453
464
|
|
|
454
465
|
else:
|
|
@@ -458,18 +469,12 @@ class BBScan:
|
|
|
458
469
|
self.oracle.add_clause([-core[0]])
|
|
459
470
|
|
|
460
471
|
# remove from the working model
|
|
461
|
-
|
|
462
|
-
if indx < len(model) - 1:
|
|
463
|
-
model[indx] = model.pop()
|
|
464
|
-
else:
|
|
465
|
-
model.pop()
|
|
472
|
+
model.remove(-core[0])
|
|
466
473
|
|
|
467
474
|
# filtering out unnecessary flipped literals
|
|
468
|
-
|
|
469
|
-
assumps = [l for l in assumps if l not in core]
|
|
475
|
+
assumps -= set(core)
|
|
470
476
|
|
|
471
477
|
if not assumps:
|
|
472
|
-
# resorting to the iterative traversal algorithm
|
|
473
478
|
self.model = model
|
|
474
479
|
return bbone + self._compute_iter()
|
|
475
480
|
|
|
@@ -484,7 +489,7 @@ class BBScan:
|
|
|
484
489
|
print('c using core-based chunking, with chunk size:', chunk_size)
|
|
485
490
|
|
|
486
491
|
# initial estimate
|
|
487
|
-
bbone, model = [], self.model if focus_on is None else focus_on
|
|
492
|
+
bbone, model = [], list(self.model if focus_on is None else focus_on)
|
|
488
493
|
|
|
489
494
|
# we are going to use clause selectors
|
|
490
495
|
vpool = IDPool(start_from=self.formula.nv + 1)
|
|
@@ -495,7 +500,7 @@ class BBScan:
|
|
|
495
500
|
size = min(chunk_size, len(model))
|
|
496
501
|
|
|
497
502
|
# flipping all the literals
|
|
498
|
-
assumps, skipped =
|
|
503
|
+
assumps, skipped = {-model.pop() for i in range(size)}, set()
|
|
499
504
|
|
|
500
505
|
# getting unsatisfiable cores with them
|
|
501
506
|
while True:
|
|
@@ -504,7 +509,9 @@ class BBScan:
|
|
|
504
509
|
if self.oracle.solve(assumptions=assumps):
|
|
505
510
|
coex = set(self.oracle.get_model())
|
|
506
511
|
model = [l for l in model if l in coex]
|
|
507
|
-
|
|
512
|
+
|
|
513
|
+
if self.rotate:
|
|
514
|
+
model = list(self._filter_rotatable(set(model)))
|
|
508
515
|
|
|
509
516
|
if skipped:
|
|
510
517
|
bbone += self._compute_iter(focus_on=skipped)
|
|
@@ -520,11 +527,10 @@ class BBScan:
|
|
|
520
527
|
self.oracle.add_clause([-core[0]])
|
|
521
528
|
else:
|
|
522
529
|
# all removed literals are going to be tested later
|
|
523
|
-
skipped
|
|
530
|
+
skipped |= {-l for l in core}
|
|
524
531
|
|
|
525
532
|
# filtering out unnecessary flipped literals
|
|
526
|
-
|
|
527
|
-
assumps = [l for l in assumps if l not in core]
|
|
533
|
+
assumps -= set(core)
|
|
528
534
|
|
|
529
535
|
if not assumps:
|
|
530
536
|
# resorting to the iterative traversal algorithm
|
|
@@ -555,11 +561,10 @@ def parse_options():
|
|
|
555
561
|
|
|
556
562
|
try:
|
|
557
563
|
opts, args = getopt.getopt(sys.argv[1:],
|
|
558
|
-
'a:c:
|
|
564
|
+
'a:c:hrs:v',
|
|
559
565
|
['algo=',
|
|
560
566
|
'chunk=',
|
|
561
567
|
'help',
|
|
562
|
-
'lift',
|
|
563
568
|
'rotate',
|
|
564
569
|
'solver=',
|
|
565
570
|
'verbose'])
|
|
@@ -570,7 +575,6 @@ def parse_options():
|
|
|
570
575
|
|
|
571
576
|
algo = 'iter'
|
|
572
577
|
chunk = 100
|
|
573
|
-
lift = False
|
|
574
578
|
rotate = False
|
|
575
579
|
solver = 'g3'
|
|
576
580
|
verbose = 0
|
|
@@ -580,12 +584,14 @@ def parse_options():
|
|
|
580
584
|
algo = str(arg)
|
|
581
585
|
assert algo in ('enum', 'iter', 'compl', 'chunk', 'core', 'corechunk'), 'Unknown algorithm'
|
|
582
586
|
elif opt in ('-c', '--chunk'):
|
|
583
|
-
chunk =
|
|
587
|
+
chunk = float(arg)
|
|
588
|
+
if chunk.is_integer():
|
|
589
|
+
chunk = int(chunk)
|
|
590
|
+
else:
|
|
591
|
+
assert 0 < chunk <= 1, f'Wrong chunk proportion {chunk_size}'
|
|
584
592
|
elif opt in ('-h', '--help'):
|
|
585
593
|
usage()
|
|
586
594
|
sys.exit(0)
|
|
587
|
-
elif opt in ('-l', '--lift'):
|
|
588
|
-
lift = True
|
|
589
595
|
elif opt in ('-r', '--rotate'):
|
|
590
596
|
rotate = True
|
|
591
597
|
elif opt in ('-s', '--solver'):
|
|
@@ -595,7 +601,7 @@ def parse_options():
|
|
|
595
601
|
else:
|
|
596
602
|
assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
|
|
597
603
|
|
|
598
|
-
return algo, chunk,
|
|
604
|
+
return algo, chunk, rotate, solver, verbose, args
|
|
599
605
|
|
|
600
606
|
|
|
601
607
|
#
|
|
@@ -607,22 +613,21 @@ def usage():
|
|
|
607
613
|
|
|
608
614
|
print('Usage:', os.path.basename(sys.argv[0]), '[options] file')
|
|
609
615
|
print('Options:')
|
|
610
|
-
print(' -a, --algo=<string>
|
|
611
|
-
print('
|
|
612
|
-
print(' -c, --chunk=<int>
|
|
613
|
-
print('
|
|
614
|
-
print(' -h, --help
|
|
615
|
-
print(' -
|
|
616
|
-
print(' -
|
|
617
|
-
print('
|
|
618
|
-
print('
|
|
619
|
-
print(' -v, --verbose Be verbose (can be used multiple times)')
|
|
616
|
+
print(' -a, --algo=<string> Algorithm to use')
|
|
617
|
+
print(' Available values: enum, iter, compl, chunk, core, corechunk (default: iter)')
|
|
618
|
+
print(' -c, --chunk=<int,float> Chunk size for chunking algorithms')
|
|
619
|
+
print(' Available values: [1 .. INT_MAX] or (0 .. 1] (default: 100)')
|
|
620
|
+
print(' -h, --help Show this message')
|
|
621
|
+
print(' -r, --rotate Heuristically filter out rotatable literals')
|
|
622
|
+
print(' -s, --solver=<string> SAT solver to use')
|
|
623
|
+
print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default: g3)')
|
|
624
|
+
print(' -v, --verbose Be verbose (can be used multiple times)')
|
|
620
625
|
|
|
621
626
|
|
|
622
627
|
#
|
|
623
628
|
#==============================================================================
|
|
624
629
|
if __name__ == '__main__':
|
|
625
|
-
algo, chunk,
|
|
630
|
+
algo, chunk, rotate, solver, verbose, files = parse_options()
|
|
626
631
|
|
|
627
632
|
if files:
|
|
628
633
|
# read CNF from file
|
|
@@ -635,8 +640,7 @@ if __name__ == '__main__':
|
|
|
635
640
|
len(formula.clauses)))
|
|
636
641
|
|
|
637
642
|
# computing the backbone
|
|
638
|
-
with BBScan(formula, solver=solver,
|
|
639
|
-
verbose=verbose) as bbscan:
|
|
643
|
+
with BBScan(formula, solver=solver, rotate=rotate, verbose=verbose) as bbscan:
|
|
640
644
|
try:
|
|
641
645
|
bbone = bbscan.compute(algorithm=algo, chunk_size=chunk)
|
|
642
646
|
|
|
@@ -653,7 +657,7 @@ if __name__ == '__main__':
|
|
|
653
657
|
print('v 0')
|
|
654
658
|
|
|
655
659
|
if verbose > 1:
|
|
656
|
-
print('c filtered: {0}
|
|
660
|
+
print('c filtered: {0} ({1:.2f})'.format(bbscan.filtered, 100. * bbscan.filtered / formula.nv))
|
|
657
661
|
|
|
658
662
|
print('c oracle time: {0:.4f}'.format(bbscan.oracle_time()))
|
|
659
663
|
print('c oracle calls: {0}'.format(bbscan.calls))
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
pysolvers.cpython-314t-darwin.so,sha256=
|
|
2
|
-
pycard.cpython-314t-darwin.so,sha256=
|
|
1
|
+
pysolvers.cpython-314t-darwin.so,sha256=jfFfq_4jCiG939efTy1OkmQ0x7EtI2MFWTwM0lGsxyc,3914016
|
|
2
|
+
pycard.cpython-314t-darwin.so,sha256=iv4v2w7csYZHRQlHbcouJd4FIcu0PdG0Vtf9_4lA2ho,97504
|
|
3
3
|
pysat/pb.py,sha256=fLmfzPPtmEFp-ufHlxyfL1YAVNmxWZdm_ARRdwxUib4,16686
|
|
4
|
-
pysat/_fileio.py,sha256=
|
|
4
|
+
pysat/_fileio.py,sha256=cmrqvhPXxC87lpvLlFDG7EWpusQOaqCGPMDfWhqT6dg,6373
|
|
5
5
|
pysat/formula.py,sha256=3ZhI1ePvlreXJ7nRKMIftRsllm9lDDfXWYMt7AA8nSc,202858
|
|
6
6
|
pysat/engines.py,sha256=PzcZJ4zFXDHfcSSHF7iPadtrwSVEt4t9B_cO5epwTZk,45182
|
|
7
|
-
pysat/__init__.py,sha256=
|
|
7
|
+
pysat/__init__.py,sha256=q4ytD1iDnO0YnCvacaurDV1OAzxUoOgDbus24YL7_88,670
|
|
8
8
|
pysat/solvers.py,sha256=00Y6yKNXvJOLA7l4aIDgueFbNM9lt3PkmT2OxB43aQ8,229975
|
|
9
9
|
pysat/card.py,sha256=Np6HK2LfD-7YuOP53jMAm6JIO1lPFu_jqJP18n03uvM,30066
|
|
10
10
|
pysat/process.py,sha256=8lNbpEKipFZjbQ9DCJPfdL8eZHx-ltNuu7h4geWWdJY,12739
|
|
@@ -20,29 +20,29 @@ pysat/examples/hitman.py,sha256=vHJEeSfoMQlDD3D3Poofaryv-k_dMH37XoqVXJUUuUg,2636
|
|
|
20
20
|
pysat/examples/bica.py,sha256=OGcGQGQnMepCCurwRbygokFgoDkI7eBhjmsYvr3FM1M,27098
|
|
21
21
|
pysat/examples/rc2.py,sha256=RnS_RLBryjTBk_jhYY1LV37DmOkeI0H-RV-OPC4d0fw,69981
|
|
22
22
|
pysat/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
pysat/examples/bbscan.py,sha256=
|
|
23
|
+
pysat/examples/bbscan.py,sha256=E4Y5j3Llr0-jIn-RDZIk-f02Sbujg4wX7uysoiFRnLg,22046
|
|
24
24
|
pysat/examples/fm.py,sha256=e_s6wNBstEeoMw9zYyyDRgkE-Hqa902Iy_CxCS961CE,18136
|
|
25
25
|
pysat/examples/mcsls.py,sha256=bP47P56OOP_uy0MuFA7TGxMdkAis7gX4E9AEEI0tSec,22093
|
|
26
26
|
pysat/examples/optux.py,sha256=8eYHO_uhL_4ugb-hRv2s6cnOwTurfqkpbdnK9T4iDtw,27697
|
|
27
27
|
pysat/examples/lsu.py,sha256=qyN2TvXG61EtMy-IrX5EYmLPM_iNOhtOkGTY5nHO_zo,16198
|
|
28
28
|
pysat/examples/genhard.py,sha256=DFXtSVQ4qn78y0-ZMr2D6gBCLizZSzgkvcbDsjqjnKI,19017
|
|
29
29
|
pysat/examples/usage.py,sha256=x9luw6pyKAAndlxAeUWFhS4aLSVmD2t_j_5uA9YddHg,2183
|
|
30
|
-
python_sat-1.8.
|
|
31
|
-
python_sat-1.8.
|
|
32
|
-
python_sat-1.8.
|
|
33
|
-
python_sat-1.8.
|
|
34
|
-
python_sat-1.8.
|
|
35
|
-
python_sat-1.8.
|
|
36
|
-
python_sat-1.8.
|
|
37
|
-
python_sat-1.8.
|
|
38
|
-
python_sat-1.8.
|
|
39
|
-
python_sat-1.8.
|
|
40
|
-
python_sat-1.8.
|
|
41
|
-
python_sat-1.8.
|
|
42
|
-
python_sat-1.8.
|
|
43
|
-
python_sat-1.8.
|
|
44
|
-
python_sat-1.8.
|
|
45
|
-
python_sat-1.8.
|
|
46
|
-
python_sat-1.8.
|
|
47
|
-
python_sat-1.8.
|
|
48
|
-
python_sat-1.8.
|
|
30
|
+
python_sat-1.8.dev24.dist-info/RECORD,,
|
|
31
|
+
python_sat-1.8.dev24.dist-info/WHEEL,sha256=UlOYVXZUoedBznR7TtcHLe8dLSyBBw50dixq5hWMaMI,139
|
|
32
|
+
python_sat-1.8.dev24.dist-info/top_level.txt,sha256=fgEEGhMLivlF1ExCX8Y3niWsr4pDPRb5HdaWjNtabFI,23
|
|
33
|
+
python_sat-1.8.dev24.dist-info/METADATA,sha256=R_7J8Y-D1s7n7ix1OlAGzu2MhAepsbob2QYQrl5vsQo,1739
|
|
34
|
+
python_sat-1.8.dev24.dist-info/licenses/LICENSE.txt,sha256=6QMvEzxqdPXEoiAZUBaZLeLF4hW2S81fjKz_THER0uQ,1109
|
|
35
|
+
python_sat-1.8.dev24.data/scripts/models.py,sha256=Ejojfty6n2REM2Di6aKoqQtQ_v_6MfHs7qkWLXruJuU,5728
|
|
36
|
+
python_sat-1.8.dev24.data/scripts/primer.py,sha256=HufdaErta1gd_gvvSOFyHdxTnPgIM0q6Z2PvV6u0mGM,23362
|
|
37
|
+
python_sat-1.8.dev24.data/scripts/lbx.py,sha256=PwvcU8eG2oaZpkNRLDsDOjTcOF0aS4CWg1MEuuK2_CA,23236
|
|
38
|
+
python_sat-1.8.dev24.data/scripts/musx.py,sha256=RO7eP6alQ8qDZTvI1co3xUoni1xRKb4sT9rm9Of0qGk,10933
|
|
39
|
+
python_sat-1.8.dev24.data/scripts/bica.py,sha256=Tc9RjYKO-Lj9GNQjXnM87PJ3Dx2f2bHd2V2Igb-B1FM,27085
|
|
40
|
+
python_sat-1.8.dev24.data/scripts/rc2.py,sha256=eVaXQCbFrOtM6SbGj1ivLHqFQHjxt8cjhoBicOD85Oo,69968
|
|
41
|
+
python_sat-1.8.dev24.data/scripts/bbscan.py,sha256=NpCCeJ0HAtQT41c06R2h_xYvkKgJtsCraCktUPmj7GQ,22033
|
|
42
|
+
python_sat-1.8.dev24.data/scripts/fm.py,sha256=nWL-TtQst6YMaHS_5amNZzeLkOrWeuv4HgU7xrBDu5o,18123
|
|
43
|
+
python_sat-1.8.dev24.data/scripts/mcsls.py,sha256=STkPj4-WY6PaNklluGbfciZREJ--gXNMKZHQXohu4p8,22080
|
|
44
|
+
python_sat-1.8.dev24.data/scripts/optux.py,sha256=kR3CD3h1Fve08eMt1aU4Vq5eTygWOHNIoZc008KgSdM,27684
|
|
45
|
+
python_sat-1.8.dev24.data/scripts/lsu.py,sha256=dBW9gqzXR6xTLRzjeIhMVREaaiSvGAnIuzLIZRKqEuE,16185
|
|
46
|
+
python_sat-1.8.dev24.data/scripts/genhard.py,sha256=o8qSa7sksQxyhbRahmRBws3F6emQu3pe_rkxY_aXEkw,19004
|
|
47
|
+
python_sat-1.8.dev24.data/scripts/unigen.py,sha256=_tk18u_4Jg8tOhZe1CmPOHzbEJ-mJqs-mFzBfcupXuQ,16705
|
|
48
|
+
python_sat-1.8.dev24.data/scripts/approxmc.py,sha256=Y_aUYOqQ-S1ABrkzFZx4sh4dP7Ua7J9Gn47XsHcAdM8,13573
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|