python-sat 1.8.dev23__cp39-cp39-macosx_11_0_arm64.whl → 1.8.dev24__cp39-cp39-macosx_11_0_arm64.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 (24) hide show
  1. pycard.cpython-39-darwin.so +0 -0
  2. pysat/__init__.py +1 -1
  3. pysat/_fileio.py +1 -1
  4. pysat/examples/bbscan.py +91 -97
  5. pysolvers.cpython-39-darwin.so +0 -0
  6. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/bbscan.py +91 -97
  7. {python_sat-1.8.dev23.dist-info → python_sat-1.8.dev24.dist-info}/METADATA +1 -1
  8. {python_sat-1.8.dev23.dist-info → python_sat-1.8.dev24.dist-info}/RECORD +24 -24
  9. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/approxmc.py +0 -0
  10. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/bica.py +0 -0
  11. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/fm.py +0 -0
  12. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/genhard.py +0 -0
  13. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/lbx.py +0 -0
  14. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/lsu.py +0 -0
  15. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/mcsls.py +0 -0
  16. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/models.py +0 -0
  17. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/musx.py +0 -0
  18. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/optux.py +0 -0
  19. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/primer.py +0 -0
  20. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/rc2.py +0 -0
  21. {python_sat-1.8.dev23.data → python_sat-1.8.dev24.data}/scripts/unigen.py +0 -0
  22. {python_sat-1.8.dev23.dist-info → python_sat-1.8.dev24.dist-info}/WHEEL +0 -0
  23. {python_sat-1.8.dev23.dist-info → python_sat-1.8.dev24.dist-info}/licenses/LICENSE.txt +0 -0
  24. {python_sat-1.8.dev23.dist-info → python_sat-1.8.dev24.dist-info}/top_level.txt +0 -0
Binary file
pysat/__init__.py CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (1, 8, 'dev', 23)
13
+ VERSION = (1, 8, 'dev', 24)
14
14
 
15
15
 
16
16
  # PEP440 Format
pysat/_fileio.py CHANGED
@@ -189,7 +189,7 @@ class FileObject(object):
189
189
  self.ctype = 'bzip2'
190
190
  elif ext in ('.xz', '.lzma'):
191
191
  self.ctype = 'lzma'
192
- elif ext == 'zst':
192
+ elif ext == '.zst':
193
193
  self.ctype = 'zstd'
194
194
  else:
195
195
  self.ctype = None
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', lift=False, rotate=True) as bbscan:
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 two simple
85
- heuristics aiming at reducing satisfying assignments and, thus, filtering
86
- out unnecessary literals: literal lifting and filtering rotatable
87
- literals. Both are described in the aforementioned paper.
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 two simple heuristics that can be speed up the
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 two heuristics can
125
- be also specified as Boolean input arguments ``lift``, and ``rotate``
126
- (turned off by default).
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', lift=False, rotate=False, verbose=0):
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.lift, self.rotate = lift, rotate
151
+ self.rotate = rotate
154
152
 
155
153
  # basic stats
156
154
  self.calls, self.filtered = 0, 0
@@ -230,12 +228,26 @@ class BBScan:
230
228
  """
231
229
 
232
230
  self.calls += 1
231
+ trivial = []
233
232
 
234
233
  if self.oracle.solve():
235
- self.model = self.oracle.get_model()
234
+ self.model = set(self.oracle.get_model())
236
235
  if focus_on is not None:
237
- model = set(self.model)
238
- self.model = [l for l in focus_on if l in model]
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
239
251
  else:
240
252
  raise ValueError('Unsatisfiable formula')
241
253
 
@@ -258,50 +270,36 @@ class BBScan:
258
270
  else:
259
271
  assert 0, f'Unknown algorithm: {algorithm}'
260
272
 
261
- return sorted(result, key=lambda l: abs(l))
262
-
263
- def _get_implicant(self, model):
264
- """
265
- Simple literal lifting used to reduce a given model.
266
- """
267
-
268
- res, model = set(), set(model)
269
-
270
- # traversing the clauses and collecting all literals
271
- # that satisfy at least one clause of the formula
272
- for cl in self.formula:
273
- res |= set([l for l in cl if l in model])
274
-
275
- return list(res)
273
+ return sorted(trivial + result, key=lambda l: abs(l))
276
274
 
277
275
  def _filter_rotatable(self, model):
278
276
  """
279
277
  Filter out rotatable literals.
280
278
  """
281
279
 
282
- units, model = set([]), set(model)
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([])
283
294
 
284
295
  # determining unit literals
285
- for cl in self.formula:
286
- satisfied = [l for l in cl if l in model]
287
- if len(satisfied) == 1:
288
- units.add(satisfied[0])
296
+ for i in self.focuscl:
297
+ unit = get_unit(self.formula.clauses[i])
298
+ if unit:
299
+ units.add(unit)
289
300
 
290
301
  self.filtered += len(model) - len(units)
291
- return list(units)
292
-
293
- def _process_model(self, model):
294
- """
295
- Heuristically reduce a model.
296
- """
297
-
298
- if self.lift:
299
- model = self._get_implicant(model)
300
-
301
- if self.rotate:
302
- model = self._filter_rotatable(model)
303
-
304
- return model
302
+ return units
305
303
 
306
304
  def _compute_enum(self, focus_on=None):
307
305
  """
@@ -312,7 +310,7 @@ class BBScan:
312
310
  print('c using enumeration-based algorithm')
313
311
 
314
312
  # initial backbone estimate contains all literals
315
- bbone = set(self.model) if focus_on is None else set(focus_on)
313
+ bbone = self.model if focus_on is None else focus_on
316
314
 
317
315
  while bbone:
318
316
  # counting the number of calls
@@ -322,15 +320,14 @@ class BBScan:
322
320
  if not self.oracle.solve():
323
321
  break
324
322
 
325
- coex = set(self.oracle.get_model())
326
- self.model = [l for l in bbone if l in coex]
327
- self.model = self._process_model(self.model)
328
-
329
323
  # updating backbone estimate - intersection
330
- bbone &= set(self.model)
324
+ bbone &= set(self.oracle.get_model())
325
+
326
+ if self.rotate:
327
+ bbone = self._filter_rotatable(bbone)
331
328
 
332
329
  # blocking the previously found implicant
333
- self.oracle.add_clause([-l for l in self.model])
330
+ self.oracle.add_clause([-l for l in bbone])
334
331
 
335
332
  return list(bbone)
336
333
 
@@ -342,6 +339,8 @@ class BBScan:
342
339
  if self.verbose:
343
340
  print('c using iterative algorithm')
344
341
 
342
+ # initial estimate
343
+ # using sets for model and assumps to save filtering time
345
344
  bbone, model = [], self.model if focus_on is None else focus_on
346
345
 
347
346
  while model:
@@ -357,9 +356,10 @@ class BBScan:
357
356
  else:
358
357
  # it isn't and we've got a counterexample
359
358
  # => using it to filter out more literals
360
- coex = set(self.oracle.get_model())
361
- model = [l for l in model if l in coex]
362
- model = self._process_model(model)
359
+ model &= set(self.oracle.get_model())
360
+
361
+ if self.rotate:
362
+ model = self._filter_rotatable(model)
363
363
 
364
364
  return bbone
365
365
 
@@ -372,7 +372,7 @@ class BBScan:
372
372
  print('c using complement of backbone estimate algorithm')
373
373
 
374
374
  # initial estimate
375
- bbone = set(self.model) if focus_on is None else set(focus_on)
375
+ bbone = self.model if focus_on is None else focus_on
376
376
 
377
377
  # iterating until we find the backbone or determine there is none
378
378
  while bbone:
@@ -385,23 +385,23 @@ class BBScan:
385
385
  if self.oracle.solve() == False:
386
386
  break
387
387
  else:
388
- coex = set(self.oracle.get_model())
389
- model = [l for l in bbone if l in coex]
390
- model = self._process_model(model)
391
- bbone &= set(model)
388
+ bbone &= set(self.oracle.get_model())
389
+
390
+ if self.rotate:
391
+ bbone = self._filter_rotatable(bbone)
392
392
 
393
393
  return list(bbone)
394
394
 
395
395
  def _compute_chunking(self, chunk_size=100, focus_on=None):
396
396
  """
397
- Algorithm 5: Chunking algorithm.
397
+ Chunking algorithm.
398
398
  """
399
399
 
400
400
  if self.verbose:
401
401
  print('c using chunking algorithm, with chunk size:', chunk_size)
402
402
 
403
403
  # initial estimate
404
- 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)
405
405
 
406
406
  # we are going to use clause selectors
407
407
  vpool = IDPool(start_from=self.formula.nv + 1)
@@ -427,7 +427,9 @@ class BBScan:
427
427
  else:
428
428
  coex = set(self.oracle.get_model())
429
429
  model = [l for l in model if l in coex]
430
- model = self._process_model(model)
430
+
431
+ if self.rotate:
432
+ model = list(self._filter_rotatable(set(model)))
431
433
 
432
434
  return bbone
433
435
 
@@ -440,21 +442,24 @@ class BBScan:
440
442
  print('c using core-based algorithm')
441
443
 
442
444
  # initial estimate
445
+ # using sets for model and assumps to save filtering time
443
446
  bbone, model = [], self.model if focus_on is None else focus_on
444
447
 
445
448
  # iterating until we find the backbone or determine there is none
446
449
  while model:
447
450
  # flipping all the literals
448
- assumps = [-l for l in model]
451
+ assumps = {-l for l in model}
449
452
 
450
453
  # getting unsatisfiable cores with them
451
454
  while True:
452
455
  self.calls += 1
453
456
 
454
457
  if self.oracle.solve(assumptions=assumps):
455
- coex = set(self.oracle.get_model())
456
- model = [l for l in model if l in coex]
457
- model = self._process_model(model)
458
+ model &= set(self.oracle.get_model())
459
+
460
+ if self.rotate:
461
+ model = self._filter_rotatable(model)
462
+
458
463
  break
459
464
 
460
465
  else:
@@ -464,18 +469,12 @@ class BBScan:
464
469
  self.oracle.add_clause([-core[0]])
465
470
 
466
471
  # remove from the working model
467
- indx = model.index(-core[0]) # may be slow
468
- if indx < len(model) - 1:
469
- model[indx] = model.pop()
470
- else:
471
- model.pop()
472
+ model.remove(-core[0])
472
473
 
473
474
  # filtering out unnecessary flipped literals
474
- core = set(core)
475
- assumps = [l for l in assumps if l not in core]
475
+ assumps -= set(core)
476
476
 
477
477
  if not assumps:
478
- # resorting to the iterative traversal algorithm
479
478
  self.model = model
480
479
  return bbone + self._compute_iter()
481
480
 
@@ -490,7 +489,7 @@ class BBScan:
490
489
  print('c using core-based chunking, with chunk size:', chunk_size)
491
490
 
492
491
  # initial estimate
493
- 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)
494
493
 
495
494
  # we are going to use clause selectors
496
495
  vpool = IDPool(start_from=self.formula.nv + 1)
@@ -501,7 +500,7 @@ class BBScan:
501
500
  size = min(chunk_size, len(model))
502
501
 
503
502
  # flipping all the literals
504
- assumps, skipped = [-model.pop() for i in range(size)], []
503
+ assumps, skipped = {-model.pop() for i in range(size)}, set()
505
504
 
506
505
  # getting unsatisfiable cores with them
507
506
  while True:
@@ -510,7 +509,9 @@ class BBScan:
510
509
  if self.oracle.solve(assumptions=assumps):
511
510
  coex = set(self.oracle.get_model())
512
511
  model = [l for l in model if l in coex]
513
- model = self._process_model(model)
512
+
513
+ if self.rotate:
514
+ model = list(self._filter_rotatable(set(model)))
514
515
 
515
516
  if skipped:
516
517
  bbone += self._compute_iter(focus_on=skipped)
@@ -526,11 +527,10 @@ class BBScan:
526
527
  self.oracle.add_clause([-core[0]])
527
528
  else:
528
529
  # all removed literals are going to be tested later
529
- skipped += [-l for l in core]
530
+ skipped |= {-l for l in core}
530
531
 
531
532
  # filtering out unnecessary flipped literals
532
- core = set(core)
533
- assumps = [l for l in assumps if l not in core]
533
+ assumps -= set(core)
534
534
 
535
535
  if not assumps:
536
536
  # resorting to the iterative traversal algorithm
@@ -561,11 +561,10 @@ def parse_options():
561
561
 
562
562
  try:
563
563
  opts, args = getopt.getopt(sys.argv[1:],
564
- 'a:c:hlrs:v',
564
+ 'a:c:hrs:v',
565
565
  ['algo=',
566
566
  'chunk=',
567
567
  'help',
568
- 'lift',
569
568
  'rotate',
570
569
  'solver=',
571
570
  'verbose'])
@@ -576,7 +575,6 @@ def parse_options():
576
575
 
577
576
  algo = 'iter'
578
577
  chunk = 100
579
- lift = False
580
578
  rotate = False
581
579
  solver = 'g3'
582
580
  verbose = 0
@@ -594,8 +592,6 @@ def parse_options():
594
592
  elif opt in ('-h', '--help'):
595
593
  usage()
596
594
  sys.exit(0)
597
- elif opt in ('-l', '--lift'):
598
- lift = True
599
595
  elif opt in ('-r', '--rotate'):
600
596
  rotate = True
601
597
  elif opt in ('-s', '--solver'):
@@ -605,7 +601,7 @@ def parse_options():
605
601
  else:
606
602
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
607
603
 
608
- return algo, chunk, lift, rotate, solver, verbose, args
604
+ return algo, chunk, rotate, solver, verbose, args
609
605
 
610
606
 
611
607
  #
@@ -622,7 +618,6 @@ def usage():
622
618
  print(' -c, --chunk=<int,float> Chunk size for chunking algorithms')
623
619
  print(' Available values: [1 .. INT_MAX] or (0 .. 1] (default: 100)')
624
620
  print(' -h, --help Show this message')
625
- print(' -l, --lift Apply literal lifting for heuristic model reduction')
626
621
  print(' -r, --rotate Heuristically filter out rotatable literals')
627
622
  print(' -s, --solver=<string> SAT solver to use')
628
623
  print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default: g3)')
@@ -632,7 +627,7 @@ def usage():
632
627
  #
633
628
  #==============================================================================
634
629
  if __name__ == '__main__':
635
- algo, chunk, lift, rotate, solver, verbose, files = parse_options()
630
+ algo, chunk, rotate, solver, verbose, files = parse_options()
636
631
 
637
632
  if files:
638
633
  # read CNF from file
@@ -645,8 +640,7 @@ if __name__ == '__main__':
645
640
  len(formula.clauses)))
646
641
 
647
642
  # computing the backbone
648
- with BBScan(formula, solver=solver, lift=lift, rotate=rotate,
649
- verbose=verbose) as bbscan:
643
+ with BBScan(formula, solver=solver, rotate=rotate, verbose=verbose) as bbscan:
650
644
  try:
651
645
  bbone = bbscan.compute(algorithm=algo, chunk_size=chunk)
652
646
 
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', lift=False, rotate=True) as bbscan:
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 two simple
85
- heuristics aiming at reducing satisfying assignments and, thus, filtering
86
- out unnecessary literals: literal lifting and filtering rotatable
87
- literals. Both are described in the aforementioned paper.
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 two simple heuristics that can be speed up the
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 two heuristics can
125
- be also specified as Boolean input arguments ``lift``, and ``rotate``
126
- (turned off by default).
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', lift=False, rotate=False, verbose=0):
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.lift, self.rotate = lift, rotate
151
+ self.rotate = rotate
154
152
 
155
153
  # basic stats
156
154
  self.calls, self.filtered = 0, 0
@@ -230,12 +228,26 @@ class BBScan:
230
228
  """
231
229
 
232
230
  self.calls += 1
231
+ trivial = []
233
232
 
234
233
  if self.oracle.solve():
235
- self.model = self.oracle.get_model()
234
+ self.model = set(self.oracle.get_model())
236
235
  if focus_on is not None:
237
- model = set(self.model)
238
- self.model = [l for l in focus_on if l in model]
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
239
251
  else:
240
252
  raise ValueError('Unsatisfiable formula')
241
253
 
@@ -258,50 +270,36 @@ class BBScan:
258
270
  else:
259
271
  assert 0, f'Unknown algorithm: {algorithm}'
260
272
 
261
- return sorted(result, key=lambda l: abs(l))
262
-
263
- def _get_implicant(self, model):
264
- """
265
- Simple literal lifting used to reduce a given model.
266
- """
267
-
268
- res, model = set(), set(model)
269
-
270
- # traversing the clauses and collecting all literals
271
- # that satisfy at least one clause of the formula
272
- for cl in self.formula:
273
- res |= set([l for l in cl if l in model])
274
-
275
- return list(res)
273
+ return sorted(trivial + result, key=lambda l: abs(l))
276
274
 
277
275
  def _filter_rotatable(self, model):
278
276
  """
279
277
  Filter out rotatable literals.
280
278
  """
281
279
 
282
- units, model = set([]), set(model)
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([])
283
294
 
284
295
  # determining unit literals
285
- for cl in self.formula:
286
- satisfied = [l for l in cl if l in model]
287
- if len(satisfied) == 1:
288
- units.add(satisfied[0])
296
+ for i in self.focuscl:
297
+ unit = get_unit(self.formula.clauses[i])
298
+ if unit:
299
+ units.add(unit)
289
300
 
290
301
  self.filtered += len(model) - len(units)
291
- return list(units)
292
-
293
- def _process_model(self, model):
294
- """
295
- Heuristically reduce a model.
296
- """
297
-
298
- if self.lift:
299
- model = self._get_implicant(model)
300
-
301
- if self.rotate:
302
- model = self._filter_rotatable(model)
303
-
304
- return model
302
+ return units
305
303
 
306
304
  def _compute_enum(self, focus_on=None):
307
305
  """
@@ -312,7 +310,7 @@ class BBScan:
312
310
  print('c using enumeration-based algorithm')
313
311
 
314
312
  # initial backbone estimate contains all literals
315
- bbone = set(self.model) if focus_on is None else set(focus_on)
313
+ bbone = self.model if focus_on is None else focus_on
316
314
 
317
315
  while bbone:
318
316
  # counting the number of calls
@@ -322,15 +320,14 @@ class BBScan:
322
320
  if not self.oracle.solve():
323
321
  break
324
322
 
325
- coex = set(self.oracle.get_model())
326
- self.model = [l for l in bbone if l in coex]
327
- self.model = self._process_model(self.model)
328
-
329
323
  # updating backbone estimate - intersection
330
- bbone &= set(self.model)
324
+ bbone &= set(self.oracle.get_model())
325
+
326
+ if self.rotate:
327
+ bbone = self._filter_rotatable(bbone)
331
328
 
332
329
  # blocking the previously found implicant
333
- self.oracle.add_clause([-l for l in self.model])
330
+ self.oracle.add_clause([-l for l in bbone])
334
331
 
335
332
  return list(bbone)
336
333
 
@@ -342,6 +339,8 @@ class BBScan:
342
339
  if self.verbose:
343
340
  print('c using iterative algorithm')
344
341
 
342
+ # initial estimate
343
+ # using sets for model and assumps to save filtering time
345
344
  bbone, model = [], self.model if focus_on is None else focus_on
346
345
 
347
346
  while model:
@@ -357,9 +356,10 @@ class BBScan:
357
356
  else:
358
357
  # it isn't and we've got a counterexample
359
358
  # => using it to filter out more literals
360
- coex = set(self.oracle.get_model())
361
- model = [l for l in model if l in coex]
362
- model = self._process_model(model)
359
+ model &= set(self.oracle.get_model())
360
+
361
+ if self.rotate:
362
+ model = self._filter_rotatable(model)
363
363
 
364
364
  return bbone
365
365
 
@@ -372,7 +372,7 @@ class BBScan:
372
372
  print('c using complement of backbone estimate algorithm')
373
373
 
374
374
  # initial estimate
375
- bbone = set(self.model) if focus_on is None else set(focus_on)
375
+ bbone = self.model if focus_on is None else focus_on
376
376
 
377
377
  # iterating until we find the backbone or determine there is none
378
378
  while bbone:
@@ -385,23 +385,23 @@ class BBScan:
385
385
  if self.oracle.solve() == False:
386
386
  break
387
387
  else:
388
- coex = set(self.oracle.get_model())
389
- model = [l for l in bbone if l in coex]
390
- model = self._process_model(model)
391
- bbone &= set(model)
388
+ bbone &= set(self.oracle.get_model())
389
+
390
+ if self.rotate:
391
+ bbone = self._filter_rotatable(bbone)
392
392
 
393
393
  return list(bbone)
394
394
 
395
395
  def _compute_chunking(self, chunk_size=100, focus_on=None):
396
396
  """
397
- Algorithm 5: Chunking algorithm.
397
+ Chunking algorithm.
398
398
  """
399
399
 
400
400
  if self.verbose:
401
401
  print('c using chunking algorithm, with chunk size:', chunk_size)
402
402
 
403
403
  # initial estimate
404
- 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)
405
405
 
406
406
  # we are going to use clause selectors
407
407
  vpool = IDPool(start_from=self.formula.nv + 1)
@@ -427,7 +427,9 @@ class BBScan:
427
427
  else:
428
428
  coex = set(self.oracle.get_model())
429
429
  model = [l for l in model if l in coex]
430
- model = self._process_model(model)
430
+
431
+ if self.rotate:
432
+ model = list(self._filter_rotatable(set(model)))
431
433
 
432
434
  return bbone
433
435
 
@@ -440,21 +442,24 @@ class BBScan:
440
442
  print('c using core-based algorithm')
441
443
 
442
444
  # initial estimate
445
+ # using sets for model and assumps to save filtering time
443
446
  bbone, model = [], self.model if focus_on is None else focus_on
444
447
 
445
448
  # iterating until we find the backbone or determine there is none
446
449
  while model:
447
450
  # flipping all the literals
448
- assumps = [-l for l in model]
451
+ assumps = {-l for l in model}
449
452
 
450
453
  # getting unsatisfiable cores with them
451
454
  while True:
452
455
  self.calls += 1
453
456
 
454
457
  if self.oracle.solve(assumptions=assumps):
455
- coex = set(self.oracle.get_model())
456
- model = [l for l in model if l in coex]
457
- model = self._process_model(model)
458
+ model &= set(self.oracle.get_model())
459
+
460
+ if self.rotate:
461
+ model = self._filter_rotatable(model)
462
+
458
463
  break
459
464
 
460
465
  else:
@@ -464,18 +469,12 @@ class BBScan:
464
469
  self.oracle.add_clause([-core[0]])
465
470
 
466
471
  # remove from the working model
467
- indx = model.index(-core[0]) # may be slow
468
- if indx < len(model) - 1:
469
- model[indx] = model.pop()
470
- else:
471
- model.pop()
472
+ model.remove(-core[0])
472
473
 
473
474
  # filtering out unnecessary flipped literals
474
- core = set(core)
475
- assumps = [l for l in assumps if l not in core]
475
+ assumps -= set(core)
476
476
 
477
477
  if not assumps:
478
- # resorting to the iterative traversal algorithm
479
478
  self.model = model
480
479
  return bbone + self._compute_iter()
481
480
 
@@ -490,7 +489,7 @@ class BBScan:
490
489
  print('c using core-based chunking, with chunk size:', chunk_size)
491
490
 
492
491
  # initial estimate
493
- 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)
494
493
 
495
494
  # we are going to use clause selectors
496
495
  vpool = IDPool(start_from=self.formula.nv + 1)
@@ -501,7 +500,7 @@ class BBScan:
501
500
  size = min(chunk_size, len(model))
502
501
 
503
502
  # flipping all the literals
504
- assumps, skipped = [-model.pop() for i in range(size)], []
503
+ assumps, skipped = {-model.pop() for i in range(size)}, set()
505
504
 
506
505
  # getting unsatisfiable cores with them
507
506
  while True:
@@ -510,7 +509,9 @@ class BBScan:
510
509
  if self.oracle.solve(assumptions=assumps):
511
510
  coex = set(self.oracle.get_model())
512
511
  model = [l for l in model if l in coex]
513
- model = self._process_model(model)
512
+
513
+ if self.rotate:
514
+ model = list(self._filter_rotatable(set(model)))
514
515
 
515
516
  if skipped:
516
517
  bbone += self._compute_iter(focus_on=skipped)
@@ -526,11 +527,10 @@ class BBScan:
526
527
  self.oracle.add_clause([-core[0]])
527
528
  else:
528
529
  # all removed literals are going to be tested later
529
- skipped += [-l for l in core]
530
+ skipped |= {-l for l in core}
530
531
 
531
532
  # filtering out unnecessary flipped literals
532
- core = set(core)
533
- assumps = [l for l in assumps if l not in core]
533
+ assumps -= set(core)
534
534
 
535
535
  if not assumps:
536
536
  # resorting to the iterative traversal algorithm
@@ -561,11 +561,10 @@ def parse_options():
561
561
 
562
562
  try:
563
563
  opts, args = getopt.getopt(sys.argv[1:],
564
- 'a:c:hlrs:v',
564
+ 'a:c:hrs:v',
565
565
  ['algo=',
566
566
  'chunk=',
567
567
  'help',
568
- 'lift',
569
568
  'rotate',
570
569
  'solver=',
571
570
  'verbose'])
@@ -576,7 +575,6 @@ def parse_options():
576
575
 
577
576
  algo = 'iter'
578
577
  chunk = 100
579
- lift = False
580
578
  rotate = False
581
579
  solver = 'g3'
582
580
  verbose = 0
@@ -594,8 +592,6 @@ def parse_options():
594
592
  elif opt in ('-h', '--help'):
595
593
  usage()
596
594
  sys.exit(0)
597
- elif opt in ('-l', '--lift'):
598
- lift = True
599
595
  elif opt in ('-r', '--rotate'):
600
596
  rotate = True
601
597
  elif opt in ('-s', '--solver'):
@@ -605,7 +601,7 @@ def parse_options():
605
601
  else:
606
602
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
607
603
 
608
- return algo, chunk, lift, rotate, solver, verbose, args
604
+ return algo, chunk, rotate, solver, verbose, args
609
605
 
610
606
 
611
607
  #
@@ -622,7 +618,6 @@ def usage():
622
618
  print(' -c, --chunk=<int,float> Chunk size for chunking algorithms')
623
619
  print(' Available values: [1 .. INT_MAX] or (0 .. 1] (default: 100)')
624
620
  print(' -h, --help Show this message')
625
- print(' -l, --lift Apply literal lifting for heuristic model reduction')
626
621
  print(' -r, --rotate Heuristically filter out rotatable literals')
627
622
  print(' -s, --solver=<string> SAT solver to use')
628
623
  print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default: g3)')
@@ -632,7 +627,7 @@ def usage():
632
627
  #
633
628
  #==============================================================================
634
629
  if __name__ == '__main__':
635
- algo, chunk, lift, rotate, solver, verbose, files = parse_options()
630
+ algo, chunk, rotate, solver, verbose, files = parse_options()
636
631
 
637
632
  if files:
638
633
  # read CNF from file
@@ -645,8 +640,7 @@ if __name__ == '__main__':
645
640
  len(formula.clauses)))
646
641
 
647
642
  # computing the backbone
648
- with BBScan(formula, solver=solver, lift=lift, rotate=rotate,
649
- verbose=verbose) as bbscan:
643
+ with BBScan(formula, solver=solver, rotate=rotate, verbose=verbose) as bbscan:
650
644
  try:
651
645
  bbone = bbscan.compute(algorithm=algo, chunk_size=chunk)
652
646
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.8.dev23
3
+ Version: 1.8.dev24
4
4
  Summary: A Python library for prototyping with SAT oracles
5
5
  Home-page: https://github.com/pysathq/pysat
6
6
  Author: Alexey Ignatiev, Joao Marques-Silva, Antonio Morgado
@@ -1,29 +1,10 @@
1
- pycard.cpython-39-darwin.so,sha256=EgVGITkAF9LetCP4b0e52b1Ns3PR5m_zn_L4kYRQMdE,97848
2
- pysolvers.cpython-39-darwin.so,sha256=A3460NonRrJMHFKnpxLUKlOCXGNDgfJOCR68-QU9oo0,3369072
3
- python_sat-1.8.dev23.data/scripts/models.py,sha256=Ejojfty6n2REM2Di6aKoqQtQ_v_6MfHs7qkWLXruJuU,5728
4
- python_sat-1.8.dev23.data/scripts/primer.py,sha256=HufdaErta1gd_gvvSOFyHdxTnPgIM0q6Z2PvV6u0mGM,23362
5
- python_sat-1.8.dev23.data/scripts/lbx.py,sha256=PwvcU8eG2oaZpkNRLDsDOjTcOF0aS4CWg1MEuuK2_CA,23236
6
- python_sat-1.8.dev23.data/scripts/musx.py,sha256=RO7eP6alQ8qDZTvI1co3xUoni1xRKb4sT9rm9Of0qGk,10933
7
- python_sat-1.8.dev23.data/scripts/bica.py,sha256=Tc9RjYKO-Lj9GNQjXnM87PJ3Dx2f2bHd2V2Igb-B1FM,27085
8
- python_sat-1.8.dev23.data/scripts/rc2.py,sha256=eVaXQCbFrOtM6SbGj1ivLHqFQHjxt8cjhoBicOD85Oo,69968
9
- python_sat-1.8.dev23.data/scripts/bbscan.py,sha256=HlOkdZMGHaBiz4Rz37nyJPj-MasC3hpNnnROU12b3V8,22367
10
- python_sat-1.8.dev23.data/scripts/fm.py,sha256=nWL-TtQst6YMaHS_5amNZzeLkOrWeuv4HgU7xrBDu5o,18123
11
- python_sat-1.8.dev23.data/scripts/mcsls.py,sha256=STkPj4-WY6PaNklluGbfciZREJ--gXNMKZHQXohu4p8,22080
12
- python_sat-1.8.dev23.data/scripts/optux.py,sha256=kR3CD3h1Fve08eMt1aU4Vq5eTygWOHNIoZc008KgSdM,27684
13
- python_sat-1.8.dev23.data/scripts/lsu.py,sha256=dBW9gqzXR6xTLRzjeIhMVREaaiSvGAnIuzLIZRKqEuE,16185
14
- python_sat-1.8.dev23.data/scripts/genhard.py,sha256=o8qSa7sksQxyhbRahmRBws3F6emQu3pe_rkxY_aXEkw,19004
15
- python_sat-1.8.dev23.data/scripts/unigen.py,sha256=_tk18u_4Jg8tOhZe1CmPOHzbEJ-mJqs-mFzBfcupXuQ,16705
16
- python_sat-1.8.dev23.data/scripts/approxmc.py,sha256=Y_aUYOqQ-S1ABrkzFZx4sh4dP7Ua7J9Gn47XsHcAdM8,13573
17
- python_sat-1.8.dev23.dist-info/RECORD,,
18
- python_sat-1.8.dev23.dist-info/WHEEL,sha256=bDWaFWigpG5bEpqw9IoRiyYs8MvmSFh0OhUAOoi_-KA,134
19
- python_sat-1.8.dev23.dist-info/top_level.txt,sha256=fgEEGhMLivlF1ExCX8Y3niWsr4pDPRb5HdaWjNtabFI,23
20
- python_sat-1.8.dev23.dist-info/METADATA,sha256=VzlHWKec_KsWy4N2EgDaLstTj-d-4CbDNjU_jT3F7Gk,1739
21
- python_sat-1.8.dev23.dist-info/licenses/LICENSE.txt,sha256=6QMvEzxqdPXEoiAZUBaZLeLF4hW2S81fjKz_THER0uQ,1109
1
+ pycard.cpython-39-darwin.so,sha256=5AxUxWXxUnFRT_8fgozJg_pc0YzO22zvWrqB6w_5dZY,97848
2
+ pysolvers.cpython-39-darwin.so,sha256=ASfremBAsymdHDF4TV4zM4f6Y8RY87C41PFeC0lLWOk,3369072
22
3
  pysat/pb.py,sha256=fLmfzPPtmEFp-ufHlxyfL1YAVNmxWZdm_ARRdwxUib4,16686
23
- pysat/_fileio.py,sha256=GawbH3lYAMxYM5KWw9Nx6Gg9UkwOeLZel335Z24Un9Y,6372
4
+ pysat/_fileio.py,sha256=cmrqvhPXxC87lpvLlFDG7EWpusQOaqCGPMDfWhqT6dg,6373
24
5
  pysat/formula.py,sha256=3ZhI1ePvlreXJ7nRKMIftRsllm9lDDfXWYMt7AA8nSc,202858
25
6
  pysat/engines.py,sha256=PzcZJ4zFXDHfcSSHF7iPadtrwSVEt4t9B_cO5epwTZk,45182
26
- pysat/__init__.py,sha256=jTyPWWk0l_tTnjL98vmfnR_-PY2qKOg9blx1FJNoTiI,670
7
+ pysat/__init__.py,sha256=q4ytD1iDnO0YnCvacaurDV1OAzxUoOgDbus24YL7_88,670
27
8
  pysat/solvers.py,sha256=00Y6yKNXvJOLA7l4aIDgueFbNM9lt3PkmT2OxB43aQ8,229975
28
9
  pysat/card.py,sha256=Np6HK2LfD-7YuOP53jMAm6JIO1lPFu_jqJP18n03uvM,30066
29
10
  pysat/process.py,sha256=8lNbpEKipFZjbQ9DCJPfdL8eZHx-ltNuu7h4geWWdJY,12739
@@ -39,10 +20,29 @@ pysat/examples/hitman.py,sha256=vHJEeSfoMQlDD3D3Poofaryv-k_dMH37XoqVXJUUuUg,2636
39
20
  pysat/examples/bica.py,sha256=OGcGQGQnMepCCurwRbygokFgoDkI7eBhjmsYvr3FM1M,27098
40
21
  pysat/examples/rc2.py,sha256=RnS_RLBryjTBk_jhYY1LV37DmOkeI0H-RV-OPC4d0fw,69981
41
22
  pysat/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
- pysat/examples/bbscan.py,sha256=XnOCsaG3VdS9-nF2qaL9AGsQFz1-n-hzTWQqQkiVSPc,22380
23
+ pysat/examples/bbscan.py,sha256=E4Y5j3Llr0-jIn-RDZIk-f02Sbujg4wX7uysoiFRnLg,22046
43
24
  pysat/examples/fm.py,sha256=e_s6wNBstEeoMw9zYyyDRgkE-Hqa902Iy_CxCS961CE,18136
44
25
  pysat/examples/mcsls.py,sha256=bP47P56OOP_uy0MuFA7TGxMdkAis7gX4E9AEEI0tSec,22093
45
26
  pysat/examples/optux.py,sha256=8eYHO_uhL_4ugb-hRv2s6cnOwTurfqkpbdnK9T4iDtw,27697
46
27
  pysat/examples/lsu.py,sha256=qyN2TvXG61EtMy-IrX5EYmLPM_iNOhtOkGTY5nHO_zo,16198
47
28
  pysat/examples/genhard.py,sha256=DFXtSVQ4qn78y0-ZMr2D6gBCLizZSzgkvcbDsjqjnKI,19017
48
29
  pysat/examples/usage.py,sha256=x9luw6pyKAAndlxAeUWFhS4aLSVmD2t_j_5uA9YddHg,2183
30
+ python_sat-1.8.dev24.dist-info/RECORD,,
31
+ python_sat-1.8.dev24.dist-info/WHEEL,sha256=bDWaFWigpG5bEpqw9IoRiyYs8MvmSFh0OhUAOoi_-KA,134
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