python-sat 1.8.dev25__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_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.

Files changed (48) hide show
  1. pycard.cpython-314-x86_64-linux-gnu.so +0 -0
  2. pysat/__init__.py +24 -0
  3. pysat/_fileio.py +209 -0
  4. pysat/_utils.py +58 -0
  5. pysat/allies/__init__.py +0 -0
  6. pysat/allies/approxmc.py +385 -0
  7. pysat/allies/unigen.py +435 -0
  8. pysat/card.py +802 -0
  9. pysat/engines.py +1302 -0
  10. pysat/examples/__init__.py +0 -0
  11. pysat/examples/bbscan.py +663 -0
  12. pysat/examples/bica.py +691 -0
  13. pysat/examples/fm.py +527 -0
  14. pysat/examples/genhard.py +516 -0
  15. pysat/examples/hitman.py +653 -0
  16. pysat/examples/lbx.py +638 -0
  17. pysat/examples/lsu.py +496 -0
  18. pysat/examples/mcsls.py +610 -0
  19. pysat/examples/models.py +189 -0
  20. pysat/examples/musx.py +344 -0
  21. pysat/examples/optux.py +710 -0
  22. pysat/examples/primer.py +620 -0
  23. pysat/examples/rc2.py +1858 -0
  24. pysat/examples/usage.py +63 -0
  25. pysat/formula.py +5619 -0
  26. pysat/pb.py +463 -0
  27. pysat/process.py +363 -0
  28. pysat/solvers.py +7591 -0
  29. pysolvers.cpython-314-x86_64-linux-gnu.so +0 -0
  30. python_sat-1.8.dev25.data/scripts/approxmc.py +385 -0
  31. python_sat-1.8.dev25.data/scripts/bbscan.py +663 -0
  32. python_sat-1.8.dev25.data/scripts/bica.py +691 -0
  33. python_sat-1.8.dev25.data/scripts/fm.py +527 -0
  34. python_sat-1.8.dev25.data/scripts/genhard.py +516 -0
  35. python_sat-1.8.dev25.data/scripts/lbx.py +638 -0
  36. python_sat-1.8.dev25.data/scripts/lsu.py +496 -0
  37. python_sat-1.8.dev25.data/scripts/mcsls.py +610 -0
  38. python_sat-1.8.dev25.data/scripts/models.py +189 -0
  39. python_sat-1.8.dev25.data/scripts/musx.py +344 -0
  40. python_sat-1.8.dev25.data/scripts/optux.py +710 -0
  41. python_sat-1.8.dev25.data/scripts/primer.py +620 -0
  42. python_sat-1.8.dev25.data/scripts/rc2.py +1858 -0
  43. python_sat-1.8.dev25.data/scripts/unigen.py +435 -0
  44. python_sat-1.8.dev25.dist-info/METADATA +45 -0
  45. python_sat-1.8.dev25.dist-info/RECORD +48 -0
  46. python_sat-1.8.dev25.dist-info/WHEEL +6 -0
  47. python_sat-1.8.dev25.dist-info/licenses/LICENSE.txt +21 -0
  48. python_sat-1.8.dev25.dist-info/top_level.txt +3 -0
@@ -0,0 +1,663 @@
1
+ #!python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## bbscan.py
5
+ ##
6
+ ## Created on: Sep 21, 2025
7
+ ## Author: Alexey Ignatiev
8
+ ## E-mail: alexey.ignatiev@monash.edu
9
+ ##
10
+
11
+ """
12
+ ===============
13
+ List of classes
14
+ ===============
15
+
16
+ .. autosummary::
17
+ :nosignatures:
18
+
19
+ BBScan
20
+
21
+ ==================
22
+ Module description
23
+ ==================
24
+
25
+ A simple implementation of a few backbone extraction algorithms described
26
+ in [1]_. Given a Boolean formula :math:`\\varphi`, a backbone literal is a
27
+ literal that holds true in every satisfying assignment of
28
+ :math:`\\varphi`. The backbone of formula :math:`\\varphi` is the set of
29
+ all backbone literals. This implementation includes the following
30
+ algorithms from [1]_:
31
+
32
+ * implicant enumeration algorithm,
33
+ * iterative algorithm (with one test per variable),
34
+ * complement of backbone estimate algorithm,
35
+ * chunking algorithm,
36
+ * core-based algorithm,
37
+ * core-based algorithm with chunking.
38
+
39
+ .. [1] Mikolás Janota, Inês Lynce, Joao Marques-Silva. *Algorithms for
40
+ computing backbones of propositional formulae*. AI Commun. 28(2).
41
+ 2015. pp. 161-177
42
+
43
+ The implementation can be used as an executable (the list of
44
+ available command-line options can be shown using ``bbscan.py -h``)
45
+ in the following way:
46
+
47
+ ::
48
+
49
+ $ xzcat formula.cnf.xz
50
+ p cnf 3 4
51
+ 1 2 0
52
+ 1 -2 0
53
+ 2 -3 0
54
+ -2 -3 0
55
+
56
+ $ bbscan.py -v formula.cnf.xz
57
+ c formula: 3 vars, 4 clauses
58
+ c using iterative algorithm
59
+ v +1 -3 0
60
+ c backbone size: 2 (66.67% of all variables)
61
+ c oracle time: 0.0000
62
+ c oracle calls: 4
63
+
64
+ Alternatively, the algorithms can be accessed and invoked through the
65
+ standard ``import`` interface of Python, e.g.
66
+
67
+ .. code-block:: python
68
+
69
+ >>> from pysat.examples.bbscan import BBScan
70
+ >>> from pysat.formula import CNF
71
+ >>>
72
+ >>> # reading a formula from file
73
+ >>> cnf = CNF(from_file='formula.wcnf.xz')
74
+ >>>
75
+ >>> # creating BBScan and running it
76
+ >>> with BBScan(cnf, solver='g3', rotate=True) as bbscan:
77
+ ... bbone = bbscan.compute(algorithm='core')
78
+ ...
79
+ ... # outputting the results
80
+ ... if bbone:
81
+ ... print(bbone)
82
+ [1, -3]
83
+
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
+
89
+ Note that most of the methods of the class :class:`BBScan` are made
90
+ private. Therefore, the tool provides a minimalistic interface via which a
91
+ user can specify all the necessary parameters.
92
+
93
+ ==============
94
+ Module details
95
+ ==============
96
+ """
97
+
98
+ #
99
+ #==============================================================================
100
+ import getopt
101
+ import itertools
102
+ import os
103
+ from pysat.formula import CNF, IDPool
104
+ from pysat.solvers import Solver, SolverNames
105
+ import sys
106
+ import re
107
+ import time
108
+
109
+
110
+ #
111
+ #==============================================================================
112
+ class BBScan:
113
+ """
114
+ A solver for computing all backbone literals of a given Boolean
115
+ formula. It implements 6 algorithms for backbone computation described
116
+ in [1]_ augmented with a heuristic that can be speed up the process.
117
+
118
+ Note that the input formula can be a :class:`.CNF` object but also any
119
+ object of class :class:`.Formula`, thus the tool can used for
120
+ computing backbone literals of non-clausal formulas.
121
+
122
+ A user can select one of the SAT solvers available at hand
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).
126
+
127
+ The list of initialiser's arguments is as follows:
128
+
129
+ :param formula: input formula whose backbone is sought
130
+ :param solver: SAT oracle name
131
+ :param rotate: apply rotatable literal filtering
132
+ :param verbose: verbosity level
133
+
134
+ :type formula: :class:`.Formula` or :class:`.CNF`
135
+ :type solver: str
136
+ :type rotate: bool
137
+ :type verbose: int
138
+ """
139
+
140
+ def __init__(self, formula, solver='g3', rotate=False, verbose=0):
141
+ """
142
+ Constructor.
143
+ """
144
+
145
+ self.formula = formula
146
+ self.focuscl = list(range(len(formula.clauses))) if rotate else []
147
+ self.verbose = verbose
148
+ self.oracle = None
149
+
150
+ # implicant reduction heuristics
151
+ self.rotate = rotate
152
+
153
+ # basic stats
154
+ self.calls, self.filtered = 0, 0
155
+
156
+ # this is going to be our workhorse
157
+ self.oracle = Solver(name=solver, bootstrap_with=formula, use_timer=True)
158
+
159
+ def __del__(self):
160
+ """
161
+ Destructor.
162
+ """
163
+
164
+ self.delete()
165
+
166
+ def __enter__(self):
167
+ """
168
+ 'with' constructor.
169
+ """
170
+
171
+ return self
172
+
173
+ def __exit__(self, exc_type, exc_value, traceback):
174
+ """
175
+ 'with' destructor.
176
+ """
177
+
178
+ self.delete()
179
+
180
+ def delete(self):
181
+ """
182
+ Explicit destructor of the internal SAT oracle.
183
+ """
184
+
185
+ if self.oracle:
186
+ self.oracle.delete()
187
+ self.oracle = None
188
+
189
+ def compute(self, algorithm='iter', chunk_size=100, focus_on=None):
190
+ """
191
+ Main solving method. A user is supposed to specify which backbone
192
+ computation algorithm they want to use:
193
+
194
+ * ``'enum'`` (implicant enumeration algorithm),
195
+ * ``'iter'`` (iterative algorithm, with one test per variable),
196
+ * ``'compl'`` (complement of backbone estimate algorithm),
197
+ * ``'chunk'`` (chunking algorithm),
198
+ * ``'core'`` (core-based algorithm),
199
+ * ``'corechunk'`` (core-based algorithm with chunking).
200
+
201
+ By default, :class:`.BBScan` opts for ``'iter'``.
202
+
203
+ If the chunking algorithm is selected (either ``'chunk'`` or
204
+ ``'corechunk'``), the user may specify the size of the chunk,
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.
209
+
210
+ Finally, one may opt for computing backbone literals out of a
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.
215
+
216
+ .. note::
217
+
218
+ The method raises ``ValueError`` if the input formula is
219
+ unsatisfiable.
220
+
221
+ :param algorithm: backbone computation algorithm
222
+ :param chunk_size: number of literals in the chunk (for chunking algorithms)
223
+ :param focus_on: a list of literals to focus on
224
+
225
+ :type algorithm: str
226
+ :type chunk_size: int or float
227
+ :type focus_on: iterable(int)
228
+ """
229
+
230
+ self.calls += 1
231
+ trivial = []
232
+
233
+ if self.oracle.solve():
234
+ self.model = set(self.oracle.get_model())
235
+ if focus_on is not None:
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
251
+ else:
252
+ raise ValueError('Unsatisfiable formula')
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
+
258
+ if algorithm == 'enum':
259
+ result = self._compute_enum()
260
+ elif algorithm == 'iter':
261
+ result = self._compute_iter()
262
+ elif algorithm == 'compl':
263
+ result = self._compute_compl()
264
+ elif algorithm == 'chunk':
265
+ result = self._compute_chunking(chunk_size)
266
+ elif algorithm == 'core':
267
+ result = self._compute_core_based()
268
+ elif algorithm == 'corechunk':
269
+ result = self._compute_core_chunking(chunk_size)
270
+ else:
271
+ assert 0, f'Unknown algorithm: {algorithm}'
272
+
273
+ return sorted(trivial + result, key=lambda l: abs(l))
274
+
275
+ def _filter_rotatable(self, model):
276
+ """
277
+ Filter out rotatable literals.
278
+ """
279
+
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([])
294
+
295
+ # determining unit literals
296
+ for i in self.focuscl:
297
+ unit = get_unit(self.formula.clauses[i])
298
+ if unit:
299
+ units.add(unit)
300
+
301
+ self.filtered += len(model) - len(units)
302
+ return units
303
+
304
+ def _compute_enum(self, focus_on=None):
305
+ """
306
+ Enumeration-based backbone computation.
307
+ """
308
+
309
+ if self.verbose:
310
+ print('c using enumeration-based algorithm')
311
+
312
+ # initial backbone estimate contains all literals
313
+ bbone = self.model if focus_on is None else focus_on
314
+
315
+ while bbone:
316
+ # counting the number of calls
317
+ self.calls += 1
318
+
319
+ # stop if there are no more models
320
+ if not self.oracle.solve():
321
+ break
322
+
323
+ # updating backbone estimate - intersection
324
+ bbone &= set(self.oracle.get_model())
325
+
326
+ if self.rotate:
327
+ bbone = self._filter_rotatable(bbone)
328
+
329
+ # blocking the previously found implicant
330
+ self.oracle.add_clause([-l for l in bbone])
331
+
332
+ return list(bbone)
333
+
334
+ def _compute_iter(self, focus_on=None):
335
+ """
336
+ Iterative algorithm with one test per variable.
337
+ """
338
+
339
+ if self.verbose:
340
+ print('c using iterative algorithm')
341
+
342
+ # initial estimate
343
+ # using sets for model and assumps to save filtering time
344
+ bbone, model = [], self.model if focus_on is None else focus_on
345
+
346
+ while model:
347
+ # counting the number of oracle calls
348
+ self.calls += 1
349
+
350
+ # checking this literal
351
+ lit = model.pop()
352
+
353
+ if self.oracle.solve(assumptions=[-lit]) == False:
354
+ # it is a backbone literal
355
+ bbone.append(lit)
356
+ else:
357
+ # it isn't and we've got a counterexample
358
+ # => using it to filter out more literals
359
+ model &= set(self.oracle.get_model())
360
+
361
+ if self.rotate:
362
+ model = self._filter_rotatable(model)
363
+
364
+ return bbone
365
+
366
+ def _compute_compl(self, focus_on=None):
367
+ """
368
+ Iterative algorithm with complement of backbone estimate.
369
+ """
370
+
371
+ if self.verbose:
372
+ print('c using complement of backbone estimate algorithm')
373
+
374
+ # initial estimate
375
+ bbone = self.model if focus_on is None else focus_on
376
+
377
+ # iterating until we find the backbone or determine there is none
378
+ while bbone:
379
+ self.calls += 1
380
+
381
+ # first, adding a new D-clause
382
+ self.oracle.add_clause([-l for l in bbone])
383
+
384
+ # testing for unsatisfiability
385
+ if self.oracle.solve() == False:
386
+ break
387
+ else:
388
+ bbone &= set(self.oracle.get_model())
389
+
390
+ if self.rotate:
391
+ bbone = self._filter_rotatable(bbone)
392
+
393
+ return list(bbone)
394
+
395
+ def _compute_chunking(self, chunk_size=100, focus_on=None):
396
+ """
397
+ Chunking algorithm.
398
+ """
399
+
400
+ if self.verbose:
401
+ print('c using chunking algorithm, with chunk size:', chunk_size)
402
+
403
+ # initial estimate
404
+ bbone, model = [], list(self.model if focus_on is None else focus_on)
405
+
406
+ # we are going to use clause selectors
407
+ vpool = IDPool(start_from=self.formula.nv + 1)
408
+
409
+ # iterating until we find the backbone or determine there is none
410
+ while model:
411
+ self.calls += 1
412
+
413
+ # preparing the call
414
+ size = min(chunk_size, len(model))
415
+ selv = vpool.id()
416
+
417
+ # first, adding a new D-clause for the selected chunk
418
+ self.oracle.add_clause([-model[-i] for i in range(1, size + 1)] + [-selv])
419
+
420
+ # testing for unsatisfiability
421
+ if self.oracle.solve(assumptions=[selv]) == False:
422
+ # all literals in the chunk are in the backbone
423
+ for _ in range(size):
424
+ lit = model.pop()
425
+ bbone.append(lit)
426
+ self.oracle.add_clause([lit])
427
+ else:
428
+ coex = set(self.oracle.get_model())
429
+ model = [l for l in model if l in coex]
430
+
431
+ if self.rotate:
432
+ model = list(self._filter_rotatable(set(model)))
433
+
434
+ return bbone
435
+
436
+ def _compute_core_based(self, focus_on=None):
437
+ """
438
+ Core-based algorithm.
439
+ """
440
+
441
+ if self.verbose:
442
+ print('c using core-based algorithm')
443
+
444
+ # initial estimate
445
+ # using sets for model and assumps to save filtering time
446
+ bbone, model = [], self.model if focus_on is None else focus_on
447
+
448
+ # iterating until we find the backbone or determine there is none
449
+ while model:
450
+ # flipping all the literals
451
+ assumps = {-l for l in model}
452
+
453
+ # getting unsatisfiable cores with them
454
+ while True:
455
+ self.calls += 1
456
+
457
+ if self.oracle.solve(assumptions=assumps):
458
+ model &= set(self.oracle.get_model())
459
+
460
+ if self.rotate:
461
+ model = self._filter_rotatable(model)
462
+
463
+ break
464
+
465
+ else:
466
+ core = self.oracle.get_core()
467
+ if len(core) == 1:
468
+ bbone.append(-core[0])
469
+ self.oracle.add_clause([-core[0]])
470
+
471
+ # remove from the working model
472
+ model.remove(-core[0])
473
+
474
+ # filtering out unnecessary flipped literals
475
+ assumps -= set(core)
476
+
477
+ if not assumps:
478
+ self.model = model
479
+ return bbone + self._compute_iter()
480
+
481
+ return bbone
482
+
483
+ def _compute_core_chunking(self, chunk_size=100, focus_on=None):
484
+ """
485
+ Core-based algorithm with chunking.
486
+ """
487
+
488
+ if self.verbose:
489
+ print('c using core-based chunking, with chunk size:', chunk_size)
490
+
491
+ # initial estimate
492
+ bbone, model = [], list(self.model if focus_on is None else focus_on)
493
+
494
+ # we are going to use clause selectors
495
+ vpool = IDPool(start_from=self.formula.nv + 1)
496
+
497
+ # iterating until we find the backbone or determine there is none
498
+ while model:
499
+ # preparing the chunking
500
+ size = min(chunk_size, len(model))
501
+
502
+ # flipping all the literals
503
+ assumps, skipped = {-model.pop() for i in range(size)}, set()
504
+
505
+ # getting unsatisfiable cores with them
506
+ while True:
507
+ self.calls += 1
508
+
509
+ if self.oracle.solve(assumptions=assumps):
510
+ coex = set(self.oracle.get_model())
511
+ model = [l for l in model if l in coex]
512
+
513
+ if self.rotate:
514
+ model = list(self._filter_rotatable(set(model)))
515
+
516
+ if skipped:
517
+ bbone += self._compute_iter(focus_on=skipped)
518
+
519
+ break
520
+
521
+ else:
522
+ core = self.oracle.get_core()
523
+
524
+ if len(core) == 1:
525
+ # a unit-size core must contain a backbone literal
526
+ bbone.append(-core[0])
527
+ self.oracle.add_clause([-core[0]])
528
+ else:
529
+ # all removed literals are going to be tested later
530
+ skipped |= {-l for l in core}
531
+
532
+ # filtering out unnecessary flipped literals
533
+ assumps -= set(core)
534
+
535
+ if not assumps:
536
+ # resorting to the iterative traversal algorithm
537
+ # in order to test all the removed literals
538
+ bbone += self._compute_iter(focus_on=skipped)
539
+ break
540
+
541
+ return bbone
542
+
543
+ def oracle_time(self):
544
+ """
545
+ This method computes and returns the total SAT solving time
546
+ involved, including the time spent by the hitting set enumerator
547
+ and the two SAT oracles.
548
+
549
+ :rtype: float
550
+ """
551
+
552
+ return self.oracle.time_accum()
553
+
554
+
555
+ #
556
+ #==============================================================================
557
+ def parse_options():
558
+ """
559
+ Parses command-line options.
560
+ """
561
+
562
+ try:
563
+ opts, args = getopt.getopt(sys.argv[1:],
564
+ 'a:c:hrs:v',
565
+ ['algo=',
566
+ 'chunk=',
567
+ 'help',
568
+ 'rotate',
569
+ 'solver=',
570
+ 'verbose'])
571
+ except getopt.GetoptError as err:
572
+ sys.stderr.write(str(err).capitalize() + '\n')
573
+ usage()
574
+ sys.exit(1)
575
+
576
+ algo = 'iter'
577
+ chunk = 100
578
+ rotate = False
579
+ solver = 'g3'
580
+ verbose = 0
581
+
582
+ for opt, arg in opts:
583
+ if opt in ('-a', '--algo'):
584
+ algo = str(arg)
585
+ assert algo in ('enum', 'iter', 'compl', 'chunk', 'core', 'corechunk'), 'Unknown algorithm'
586
+ elif opt in ('-c', '--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}'
592
+ elif opt in ('-h', '--help'):
593
+ usage()
594
+ sys.exit(0)
595
+ elif opt in ('-r', '--rotate'):
596
+ rotate = True
597
+ elif opt in ('-s', '--solver'):
598
+ solver = str(arg)
599
+ elif opt in ('-v', '--verbose'):
600
+ verbose += 1
601
+ else:
602
+ assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
603
+
604
+ return algo, chunk, rotate, solver, verbose, args
605
+
606
+
607
+ #
608
+ #==============================================================================
609
+ def usage():
610
+ """
611
+ Prints usage message.
612
+ """
613
+
614
+ print('Usage:', os.path.basename(sys.argv[0]), '[options] file')
615
+ print('Options:')
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)')
625
+
626
+
627
+ #
628
+ #==============================================================================
629
+ if __name__ == '__main__':
630
+ algo, chunk, rotate, solver, verbose, files = parse_options()
631
+
632
+ if files:
633
+ # read CNF from file
634
+ assert re.search(r'cnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]), \
635
+ 'Unknown input file extension'
636
+ formula = CNF(from_file=files[0])
637
+
638
+ if verbose:
639
+ print('c formula: {0} vars, {1} clauses'.format(formula.nv,
640
+ len(formula.clauses)))
641
+
642
+ # computing the backbone
643
+ with BBScan(formula, solver=solver, rotate=rotate, verbose=verbose) as bbscan:
644
+ try:
645
+ bbone = bbscan.compute(algorithm=algo, chunk_size=chunk)
646
+
647
+ except ValueError as e:
648
+ print('s UNSATISFIABLE')
649
+ print('c', str(e))
650
+ sys.exit(1)
651
+
652
+ # outputting the results
653
+ if bbone:
654
+ print('v {0} 0'.format(' '.join(['{0}{1}'.format('+' if v > 0 else '', v) for v in bbone])))
655
+ print('c backbone size: {0} ({1:.2f}% of all variables)'.format(len(bbone), 100. * len(bbone) / formula.nv))
656
+ else:
657
+ print('v 0')
658
+
659
+ if verbose > 1:
660
+ print('c filtered: {0} ({1:.2f})'.format(bbscan.filtered, 100. * bbscan.filtered / formula.nv))
661
+
662
+ print('c oracle time: {0:.4f}'.format(bbscan.oracle_time()))
663
+ print('c oracle calls: {0}'.format(bbscan.calls))