python-sat 1.8.dev22__cp313-cp313-macosx_11_0_arm64.whl → 1.8.dev24__cp313-cp313-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-313-darwin.so +0 -0
  2. pysat/__init__.py +1 -1
  3. pysat/_fileio.py +1 -1
  4. pysat/examples/bbscan.py +119 -115
  5. pysolvers.cpython-313-darwin.so +0 -0
  6. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/bbscan.py +119 -115
  7. {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/METADATA +1 -1
  8. {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/RECORD +24 -24
  9. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/approxmc.py +0 -0
  10. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/bica.py +0 -0
  11. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/fm.py +0 -0
  12. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/genhard.py +0 -0
  13. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/lbx.py +0 -0
  14. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/lsu.py +0 -0
  15. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/mcsls.py +0 -0
  16. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/models.py +0 -0
  17. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/musx.py +0 -0
  18. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/optux.py +0 -0
  19. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/primer.py +0 -0
  20. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/rc2.py +0 -0
  21. {python_sat-1.8.dev22.data → python_sat-1.8.dev24.data}/scripts/unigen.py +0 -0
  22. {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/WHEEL +0 -0
  23. {python_sat-1.8.dev22.dist-info → python_sat-1.8.dev24.dist-info}/licenses/LICENSE.txt +0 -0
  24. {python_sat-1.8.dev22.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', 22)
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
@@ -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 / variables, which may run faster
211
- than computing the entire formula's backbone. To focus on a
212
- particular set of variables / literals, the user should use the
213
- parameter ``focus_on``, which is set to ``None`` by default.
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 = set(self.model)
235
- 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
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
- 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([])
276
294
 
277
295
  # determining unit literals
278
- for cl in self.formula:
279
- satisfied = [l for l in cl if l in model]
280
- if len(satisfied) == 1:
281
- 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)
282
300
 
283
301
  self.filtered += len(model) - len(units)
284
- return list(units)
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 = set(self.model) if focus_on is None else set(focus_on)
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.model)
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 self.model])
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
- coex = set(self.oracle.get_model())
356
- model = [l for l in model if l in coex]
357
- model = self._process_model(model)
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 = set(self.model) if focus_on is None else set(focus_on)
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
- model = self._process_model(self.oracle.get_model())
384
- model = self._process_model(model)
385
- bbone &= set(model)
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
- Algorithm 5: Chunking algorithm.
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
- model = self._process_model(model)
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 = [-l for l in model]
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
- coex = set(self.oracle.get_model())
450
- model = [l for l in model if l in coex]
451
- model = self._process_model(model)
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
- indx = model.index(-core[0]) # may be slow
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
- core = set(core)
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 = [-model.pop() for i in range(size)], []
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
- model = self._process_model(model)
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 += [-l for l in core]
530
+ skipped |= {-l for l in core}
524
531
 
525
532
  # filtering out unnecessary flipped literals
526
- core = set(core)
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:hlrs:v',
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 = int(arg)
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, lift, rotate, solver, verbose, args
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> Algorithm to use')
611
- print(' Available values: enum, iter, compl, chunk, core, corechunk (default: iter)')
612
- print(' -c, --chunk=<int> Chunk size for chunking algorithms')
613
- print(' Available values: [1 .. INT_MAX] (default: 100)')
614
- print(' -h, --help Show this message')
615
- print(' -l, --lift Apply literal lifting for heuristic model reduction')
616
- print(' -r, --rotate Heuristically filter out rotatable literals')
617
- print(' -s, --solver=<string> SAT solver to use')
618
- print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default: g3)')
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, lift, rotate, solver, verbose, files = parse_options()
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, lift=lift, rotate=rotate,
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}; ({1:.2f})'.format(bbscan.filtered, 100. * bbscan.filtered / formula.nv))
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))
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
@@ -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 / variables, which may run faster
211
- than computing the entire formula's backbone. To focus on a
212
- particular set of variables / literals, the user should use the
213
- parameter ``focus_on``, which is set to ``None`` by default.
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 = set(self.model)
235
- 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
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
- 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([])
276
294
 
277
295
  # determining unit literals
278
- for cl in self.formula:
279
- satisfied = [l for l in cl if l in model]
280
- if len(satisfied) == 1:
281
- 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)
282
300
 
283
301
  self.filtered += len(model) - len(units)
284
- return list(units)
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 = set(self.model) if focus_on is None else set(focus_on)
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.model)
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 self.model])
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
- coex = set(self.oracle.get_model())
356
- model = [l for l in model if l in coex]
357
- model = self._process_model(model)
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 = set(self.model) if focus_on is None else set(focus_on)
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
- model = self._process_model(self.oracle.get_model())
384
- model = self._process_model(model)
385
- bbone &= set(model)
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
- Algorithm 5: Chunking algorithm.
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
- model = self._process_model(model)
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 = [-l for l in model]
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
- coex = set(self.oracle.get_model())
450
- model = [l for l in model if l in coex]
451
- model = self._process_model(model)
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
- indx = model.index(-core[0]) # may be slow
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
- core = set(core)
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 = [-model.pop() for i in range(size)], []
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
- model = self._process_model(model)
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 += [-l for l in core]
530
+ skipped |= {-l for l in core}
524
531
 
525
532
  # filtering out unnecessary flipped literals
526
- core = set(core)
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:hlrs:v',
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 = int(arg)
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, lift, rotate, solver, verbose, args
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> Algorithm to use')
611
- print(' Available values: enum, iter, compl, chunk, core, corechunk (default: iter)')
612
- print(' -c, --chunk=<int> Chunk size for chunking algorithms')
613
- print(' Available values: [1 .. INT_MAX] (default: 100)')
614
- print(' -h, --help Show this message')
615
- print(' -l, --lift Apply literal lifting for heuristic model reduction')
616
- print(' -r, --rotate Heuristically filter out rotatable literals')
617
- print(' -s, --solver=<string> SAT solver to use')
618
- print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default: g3)')
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, lift, rotate, solver, verbose, files = parse_options()
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, lift=lift, rotate=rotate,
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}; ({1:.2f})'.format(bbscan.filtered, 100. * bbscan.filtered / formula.nv))
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.8.dev22
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,10 +1,10 @@
1
- pycard.cpython-313-darwin.so,sha256=VzjcvdUkjVa5le1XjY7tW_E8nJS5IeFnTbwENBVJIpg,97824
2
- pysolvers.cpython-313-darwin.so,sha256=PF9et65dU0JhR_IuioMNmy1GtLX_HE-PlyipEWrkEkQ,3369008
1
+ pycard.cpython-313-darwin.so,sha256=NxQ6ahfx28sP06j03cpiNrrmkx912k6WU90pvaBQHDE,97824
2
+ pysolvers.cpython-313-darwin.so,sha256=kN-4aUBFG9lU4M88kFEeufoQ2Zz7r7ILNzWoZOJXzko,3369008
3
3
  pysat/pb.py,sha256=fLmfzPPtmEFp-ufHlxyfL1YAVNmxWZdm_ARRdwxUib4,16686
4
- pysat/_fileio.py,sha256=GawbH3lYAMxYM5KWw9Nx6Gg9UkwOeLZel335Z24Un9Y,6372
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=4b77R_gm1nOwqTsDYtZKtmar77O2mfr34EgWdLW6uVc,670
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=x0YyuhJ_LLcNvONGtw6b95F32sWi8O8P25AF-Nhmejk,21783
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.dev22.data/scripts/models.py,sha256=Ejojfty6n2REM2Di6aKoqQtQ_v_6MfHs7qkWLXruJuU,5728
31
- python_sat-1.8.dev22.data/scripts/primer.py,sha256=HufdaErta1gd_gvvSOFyHdxTnPgIM0q6Z2PvV6u0mGM,23362
32
- python_sat-1.8.dev22.data/scripts/lbx.py,sha256=PwvcU8eG2oaZpkNRLDsDOjTcOF0aS4CWg1MEuuK2_CA,23236
33
- python_sat-1.8.dev22.data/scripts/musx.py,sha256=RO7eP6alQ8qDZTvI1co3xUoni1xRKb4sT9rm9Of0qGk,10933
34
- python_sat-1.8.dev22.data/scripts/bica.py,sha256=Tc9RjYKO-Lj9GNQjXnM87PJ3Dx2f2bHd2V2Igb-B1FM,27085
35
- python_sat-1.8.dev22.data/scripts/rc2.py,sha256=eVaXQCbFrOtM6SbGj1ivLHqFQHjxt8cjhoBicOD85Oo,69968
36
- python_sat-1.8.dev22.data/scripts/bbscan.py,sha256=O749X1gvAQ0mZpSMxwibzMtE7qWql_-vU7ePyQMzF4c,21770
37
- python_sat-1.8.dev22.data/scripts/fm.py,sha256=nWL-TtQst6YMaHS_5amNZzeLkOrWeuv4HgU7xrBDu5o,18123
38
- python_sat-1.8.dev22.data/scripts/mcsls.py,sha256=STkPj4-WY6PaNklluGbfciZREJ--gXNMKZHQXohu4p8,22080
39
- python_sat-1.8.dev22.data/scripts/optux.py,sha256=kR3CD3h1Fve08eMt1aU4Vq5eTygWOHNIoZc008KgSdM,27684
40
- python_sat-1.8.dev22.data/scripts/lsu.py,sha256=dBW9gqzXR6xTLRzjeIhMVREaaiSvGAnIuzLIZRKqEuE,16185
41
- python_sat-1.8.dev22.data/scripts/genhard.py,sha256=o8qSa7sksQxyhbRahmRBws3F6emQu3pe_rkxY_aXEkw,19004
42
- python_sat-1.8.dev22.data/scripts/unigen.py,sha256=_tk18u_4Jg8tOhZe1CmPOHzbEJ-mJqs-mFzBfcupXuQ,16705
43
- python_sat-1.8.dev22.data/scripts/approxmc.py,sha256=Y_aUYOqQ-S1ABrkzFZx4sh4dP7Ua7J9Gn47XsHcAdM8,13573
44
- python_sat-1.8.dev22.dist-info/RECORD,,
45
- python_sat-1.8.dev22.dist-info/WHEEL,sha256=oqGJCpG61FZJmvyZ3C_0aCv-2mdfcY9e3fXvyUNmWfM,136
46
- python_sat-1.8.dev22.dist-info/top_level.txt,sha256=fgEEGhMLivlF1ExCX8Y3niWsr4pDPRb5HdaWjNtabFI,23
47
- python_sat-1.8.dev22.dist-info/METADATA,sha256=eUquMB6sHQTr5F3kCJ9Gm8v9HMBAWBhALdj57bAkBgA,1739
48
- python_sat-1.8.dev22.dist-info/licenses/LICENSE.txt,sha256=6QMvEzxqdPXEoiAZUBaZLeLF4hW2S81fjKz_THER0uQ,1109
30
+ python_sat-1.8.dev24.dist-info/RECORD,,
31
+ python_sat-1.8.dev24.dist-info/WHEEL,sha256=oqGJCpG61FZJmvyZ3C_0aCv-2mdfcY9e3fXvyUNmWfM,136
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