python-sat 1.9.dev19__tar.gz → 1.9.dev21__tar.gz

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.
Files changed (128) hide show
  1. {python_sat-1.9.dev19/python_sat.egg-info → python_sat-1.9.dev21}/PKG-INFO +1 -1
  2. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/lbx.py +4 -0
  3. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/mcsls.py +4 -0
  4. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/rc2.py +46 -15
  5. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/__init__.py +1 -1
  6. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/process.py +78 -21
  7. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/solvers.py +104 -16
  8. {python_sat-1.9.dev19 → python_sat-1.9.dev21/python_sat.egg-info}/PKG-INFO +1 -1
  9. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/python_sat.egg-info/SOURCES.txt +1 -0
  10. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/cadical153.patch +11 -11
  11. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/cadical195.patch +11 -11
  12. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/cadical300.patch +349 -30
  13. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/pysolvers.cc +157 -19
  14. python_sat-1.9.dev21/tests/test_flip_bits.py +85 -0
  15. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/LICENSE.txt +0 -0
  16. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/MANIFEST.in +0 -0
  17. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/README.rst +0 -0
  18. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/allies/__init__.py +0 -0
  19. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/allies/approxmc.py +0 -0
  20. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/allies/unigen.py +0 -0
  21. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/bitwise.hh +0 -0
  22. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/card.hh +0 -0
  23. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/clset.hh +0 -0
  24. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/common.hh +0 -0
  25. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/iseq.hh +0 -0
  26. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/itot.hh +0 -0
  27. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/ladder.hh +0 -0
  28. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/mto.hh +0 -0
  29. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/pairwise.hh +0 -0
  30. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/ptypes.hh +0 -0
  31. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/pycard.cc +0 -0
  32. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/seqcounter.hh +0 -0
  33. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/sortcard.hh +0 -0
  34. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/cardenc/utils.hh +0 -0
  35. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/__init__.py +0 -0
  36. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/bbscan.py +0 -0
  37. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/bica.py +0 -0
  38. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/fm.py +0 -0
  39. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/genhard.py +0 -0
  40. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/hitman.py +0 -0
  41. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/lsu.py +0 -0
  42. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/models.py +0 -0
  43. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/musx.py +0 -0
  44. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/optux.py +0 -0
  45. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/primer.py +0 -0
  46. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/examples/usage.py +0 -0
  47. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pf_parse.cc +0 -0
  48. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pf_plus.cc +0 -0
  49. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pf_plus.hh +0 -0
  50. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pf_py.hh +0 -0
  51. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pf_scan.hh +0 -0
  52. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pf_weight.cc +0 -0
  53. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pf_weight.hh +0 -0
  54. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pyformula.cc +0 -0
  55. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/formula/pyformula.hh +0 -0
  56. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/_fileio.py +0 -0
  57. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/_utils.py +0 -0
  58. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/card.py +0 -0
  59. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/engines.py +0 -0
  60. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/formula.py +0 -0
  61. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/integer.py +0 -0
  62. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/pysat/pb.py +0 -0
  63. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/python_sat.egg-info/dependency_links.txt +0 -0
  64. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/python_sat.egg-info/requires.txt +0 -0
  65. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/python_sat.egg-info/top_level.txt +0 -0
  66. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/requirements.txt +0 -0
  67. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/setup.cfg +0 -0
  68. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/setup.py +0 -0
  69. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/cadical103.tar.gz +0 -0
  70. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/cadical153.tar.gz +0 -0
  71. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/cadical195.tar.gz +0 -0
  72. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/cadical300.tar.gz +0 -0
  73. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/ergo.tar.gz +0 -0
  74. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/glucose30.tar.gz +0 -0
  75. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/glucose41.tar.gz +0 -0
  76. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/glucose421.tar.gz +0 -0
  77. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/lingeling.tar.gz +0 -0
  78. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/maplechrono.zip +0 -0
  79. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/maplecm.zip +0 -0
  80. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/maplesat.zip +0 -0
  81. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/mergesat3.tar.gz +0 -0
  82. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/minicard.tar.gz +0 -0
  83. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/minisat22.tar.gz +0 -0
  84. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/minisatgh.zip +0 -0
  85. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/cadical103.patch +0 -0
  86. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/ergo.patch +0 -0
  87. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/glucose30.patch +0 -0
  88. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/glucose41.patch +0 -0
  89. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/glucose421.patch +0 -0
  90. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/gluecard30.patch +0 -0
  91. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/gluecard41.patch +0 -0
  92. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/kissat404.patch +0 -0
  93. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/lingeling.patch +0 -0
  94. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/maplechrono.patch +0 -0
  95. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/maplecm.patch +0 -0
  96. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/maplesat.patch +0 -0
  97. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/mergesat3.patch +0 -0
  98. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/minicard.patch +0 -0
  99. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/minisat22.patch +0 -0
  100. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/minisatep.patch +0 -0
  101. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/patches/minisatgh.patch +0 -0
  102. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/solvers/prepare.py +0 -0
  103. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_accum_stats.py +0 -0
  104. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_atmost.py +0 -0
  105. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_atmost1.py +0 -0
  106. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_atmostk.py +0 -0
  107. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_boolengine.py +0 -0
  108. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_clausification.py +0 -0
  109. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_cnf.py +0 -0
  110. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_empty_clause.py +0 -0
  111. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_empty_extclause.py +0 -0
  112. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_encode_pb_conditional.py +0 -0
  113. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_equals1.py +0 -0
  114. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_formula_parsing.py +0 -0
  115. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_formula_unique.py +0 -0
  116. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_hitman.py +0 -0
  117. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_idpool.py +0 -0
  118. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_integer.py +0 -0
  119. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_iseqcounter.py +0 -0
  120. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_lsu.py +0 -0
  121. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_oom.py +0 -0
  122. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_pb.py +0 -0
  123. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_process.py +0 -0
  124. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_proof.py +0 -0
  125. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_propagate.py +0 -0
  126. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_unique_model.py +0 -0
  127. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_unique_mus.py +0 -0
  128. {python_sat-1.9.dev19 → python_sat-1.9.dev21}/tests/test_warmstart.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.9.dev19
3
+ Version: 1.9.dev21
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
@@ -247,6 +247,10 @@ class LBX(object):
247
247
  native cardinality constraints represented as a pair ``(lits,
248
248
  bound)``. Only hard cardinality constraints can be added.
249
249
 
250
+ Finally, note that clauses cannot be safely added if formula
251
+ processing is used (see parameter ``process``) as they may
252
+ refer to variables eliminated by the processor.
253
+
250
254
  :param clause: a clause to add
251
255
  :param soft: whether or not the clause is soft
252
256
 
@@ -248,6 +248,10 @@ class MCSls(object):
248
248
  native cardinality constraints represented as a pair ``(lits,
249
249
  bound)``. Only hard cardinality constraints can be added.
250
250
 
251
+ Finally, note that clauses cannot be safely added if formula
252
+ processing is used (see parameter ``process``) as they may
253
+ refer to variables eliminated by the processor.
254
+
251
255
  :param clause: a clause to add
252
256
  :param soft: whether or not the clause is soft
253
257
 
@@ -362,6 +362,9 @@ class RC2(object):
362
362
  self.sels_set = set(self.sels)
363
363
  self.sall = self.sels[:]
364
364
 
365
+ # original weights (as opposed to those after splitting)
366
+ self.owgt = dict(self.wght)
367
+
365
368
  # we may end up having zero-weighed soft clauses
366
369
  self.garbage = set([l for l in self.sels if self.wght[l] == 0])
367
370
  if self.garbage:
@@ -403,6 +406,10 @@ class RC2(object):
403
406
  native cardinality constraints represented as a pair ``(lits,
404
407
  bound)``. Only hard cardinality constraints can be added.
405
408
 
409
+ Finally, note that clauses cannot be safely added if formula
410
+ processing is used (see parameter ``process``) as they may
411
+ refer to variables eliminated by the processor.
412
+
406
413
  :param clause: a clause to add
407
414
  :param weight: weight of the clause (if any)
408
415
 
@@ -475,6 +482,7 @@ class RC2(object):
475
482
  # selector is not new; increment its weight
476
483
  self.wght[selv] += weight
477
484
 
485
+ self.owgt[selv] = self.owgt.get(selv, 0) + weight
478
486
  self.sall.append(selv)
479
487
  self.sels_set.add(selv)
480
488
 
@@ -560,15 +568,15 @@ class RC2(object):
560
568
  # so let's transform the None model returned to []
561
569
  self.model = []
562
570
 
571
+ # if formula processing was used, we should restore the model
572
+ # for the original formula (before selectors are filtered out)
573
+ if self.processor:
574
+ self.model = self.processor.restore(self.model)
575
+
563
576
  self.model = filter(lambda l: abs(l) in self.vmap.i2e, self.model)
564
577
  self.model = map(lambda l: int(copysign(self.vmap.i2e[abs(l)], l)), self.model)
565
578
  self.model = sorted(self.model, key=lambda l: abs(l))
566
579
 
567
- # if formula processing was used, we should
568
- # restore the model for the original formula
569
- if self.processor:
570
- self.model = self.processor.restore(self.model)
571
-
572
580
  return self.model
573
581
 
574
582
  def enumerate(self, block=0, expect_interrupt=False):
@@ -583,7 +591,8 @@ class RC2(object):
583
591
  (MSSes) or minimal correction subsets (MCSes). To block MSSes, one
584
592
  should set the ``block`` parameter to ``1``. To block MCSes, set
585
593
  it to ``-1``. By the default (for blocking MaxSAT models),
586
- ``block`` is set to ``0``.
594
+ ``block`` is set to ``0``. Note that if formula processing is
595
+ used (see parameter ``process``), only MCSes can be blocked.
587
596
 
588
597
  This MaxSAT enumeration call can be asynchronously interrupted, in
589
598
  which case the value of ``expect_interrupt`` must be set to
@@ -621,6 +630,9 @@ class RC2(object):
621
630
  >>> rc2.delete()
622
631
  """
623
632
 
633
+ assert not self.processor or block == -1, \
634
+ 'Only MCSes can be blocked if formula processing is used'
635
+
624
636
  done = False
625
637
  while not done:
626
638
  model = self.compute(expect_interrupt=expect_interrupt)
@@ -1471,6 +1483,9 @@ class RC2Stratified(RC2, object):
1471
1483
  # do clause hardening
1472
1484
  self.hard = nohard == False
1473
1485
 
1486
+ # upper bound on the cost (trivial at first)
1487
+ self.ubnd = sum(self.owgt.values())
1488
+
1474
1489
  # backing up selectors
1475
1490
  self.bckp, self.bckp_set = self.sels, self.sels_set
1476
1491
  self.sels = []
@@ -1571,15 +1586,15 @@ class RC2Stratified(RC2, object):
1571
1586
  # so let's transform the None model returned to []
1572
1587
  self.model = []
1573
1588
 
1589
+ # if formula processing was used, we should restore the model
1590
+ # for the original formula (before selectors are filtered out)
1591
+ if self.processor:
1592
+ self.model = self.processor.restore(self.model)
1593
+
1574
1594
  self.model = filter(lambda l: abs(l) in self.vmap.i2e, self.model)
1575
1595
  self.model = map(lambda l: int(copysign(self.vmap.i2e[abs(l)], l)), self.model)
1576
1596
  self.model = sorted(self.model, key=lambda l: abs(l))
1577
1597
 
1578
- # if formula processing was used, we should
1579
- # restore the model for the original formula
1580
- if self.processor:
1581
- self.model = self.processor.restore(self.model)
1582
-
1583
1598
  return self.model
1584
1599
 
1585
1600
  def next_level(self):
@@ -1648,14 +1663,17 @@ class RC2Stratified(RC2, object):
1648
1663
  to optimization levels up to the newly computed level. It
1649
1664
  also reactivates previously deactivated clauses (see
1650
1665
  :func:`process_sels` and :func:`process_sums` for
1651
- details).
1666
+ details). Clauses whose weight exceeds the gap between
1667
+ the upper and lower bounds are hardened instead.
1652
1668
  """
1653
1669
 
1654
1670
  end = min(self.levl + 1, len(self.blop))
1655
1671
 
1656
1672
  for l in range(beg, end):
1657
1673
  for sel in self.wstr[self.blop[l]]:
1658
- if sel in self.bckp_set:
1674
+ if self.hard and self.blop[l] > self.ubnd - self.cost:
1675
+ self.oracle.add_clause([sel])
1676
+ elif sel in self.bckp_set:
1659
1677
  self.sels.append(sel)
1660
1678
  else:
1661
1679
  self.sums.append(sel)
@@ -1670,7 +1688,8 @@ class RC2Stratified(RC2, object):
1670
1688
  This method does postprocessing of the current
1671
1689
  optimization level after it is solved. This includes
1672
1690
  *hardening* some of the soft clauses (depending on their
1673
- remaining weights) and also garbage collection.
1691
+ remaining weights) and also garbage collection. The
1692
+ level's model is used to tighten the upper bound.
1674
1693
  """
1675
1694
 
1676
1695
  # assumptions to remove
@@ -1679,9 +1698,15 @@ class RC2Stratified(RC2, object):
1679
1698
  # sum of weights of the remaining levels
1680
1699
  sumw = sum([w * len(self.wstr[w]) for w in self.blop[(self.levl + 1):]])
1681
1700
 
1701
+ # upper bound given by the current model;
1702
+ # selectors unknown to the oracle are deemed to be falsified
1703
+ model = self.oracle.get_model()
1704
+ cost = sum([w for s, w in six.iteritems(self.owgt) if abs(s) > len(model) or model[abs(s) - 1] != s])
1705
+ self.ubnd = min(self.ubnd, cost)
1706
+
1682
1707
  # trying to harden selectors and sums
1683
1708
  for s in self.sels + self.sums:
1684
- if self.wght[s] > sumw:
1709
+ if self.wght[s] > min(sumw, self.ubnd - self.cost):
1685
1710
  self.oracle.add_clause([s])
1686
1711
  self.garbage.add(s)
1687
1712
 
@@ -1983,6 +2008,12 @@ if __name__ == '__main__':
1983
2008
  else:
1984
2009
  MXS = RC2
1985
2010
 
2011
+ if process and to_enum != 1 and block != -1:
2012
+ # no formula processing in case we enumerate multiple models or
2013
+ # MSSes, as blocking them refers to variables it may eliminate
2014
+ print('c formula processing is disabled for model enumeration')
2015
+ process = 0
2016
+
1986
2017
  # starting the solver
1987
2018
  with MXS(formula, solver=solver, adapt=adapt, exhaust=exhaust,
1988
2019
  incr=incr, minz=minz, process=process, trim=trim,
@@ -10,7 +10,7 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (1, 9, 'dev', 19)
13
+ VERSION = (1, 9, 'dev', 21)
14
14
 
15
15
 
16
16
  # PEP440 Format
@@ -23,7 +23,8 @@
23
23
  ==================
24
24
 
25
25
  This module provides access to the preprocessor functionality of `CaDiCaL
26
- 1.5.3 <https://github.com/arminbiere/cadical>`__. It can be used to
26
+ <https://github.com/arminbiere/cadical>`__, version 1.5.3 by default
27
+ (versions 1.9.5 and 3.0.0 can be selected instead). It can be used to
27
28
  process [1]_ (also see references therein) a given CNF formula and output
28
29
  a another formula, which is guaranteed to be *equisatisfiable* with the
29
30
  original formula. The processor can be invoked for a user-provided number
@@ -85,20 +86,30 @@
85
86
  #
86
87
  #==============================================================================
87
88
  from pysat.formula import CNF
88
- from pysat.solvers import Cadical153
89
+ from pysat.solvers import Cadical153, Cadical195, Cadical300, NoSuchSolverError, SolverNames
89
90
 
90
91
 
91
92
  #
92
93
  #==============================================================================
93
94
  class Processor(object):
94
95
  """
95
- This class provides interface to CaDiCaL's preprocessor. The only
96
- input parameter is ``bootstrap_with``, which is expected to be a
97
- :class:`.CNF` formula or a list (or iterable) of clauses.
96
+ This class provides interface to CaDiCaL's preprocessor. The input
97
+ parameters are ``bootstrap_with``, which is expected to be a
98
+ :class:`.CNF` formula or a list (or iterable) of clauses, and
99
+ ``engine``, which is the name of a CaDiCaL version to use, i.e. one of
100
+ the names of :class:`.Cadical153` (default), :class:`.Cadical195`, and
101
+ :class:`.Cadical300`. Note that preprocessing in CaDiCaL 1.9.5 should
102
+ not differ from that of CaDiCaL 1.5.3, as the two versions apply the
103
+ same techniques. In contrast, CaDiCaL 3.0.0 additionally applies
104
+ congruence closure, SAT sweeping, and binary clause backbone
105
+ computation.
98
106
 
99
107
  :param bootstrap_with: a list of clauses for processor initialization.
100
108
  :type bootstrap_with: :class:`.CNF` or iterable(iterable(int))
101
109
 
110
+ :param engine: name of the CaDiCaL version to use
111
+ :type engine: str
112
+
102
113
  Once created and used, a processor must be deleted with the
103
114
  :meth:`delete` method. Alternatively, if created using the ``with``
104
115
  statement, deletion is done automatically when the end of the ``with``
@@ -119,7 +130,7 @@ class Processor(object):
119
130
  >>> from pysat.process import Processor
120
131
  >>> from pysat.solvers import Solver
121
132
  >>>
122
- >>> processor = Processor(bootstrap_with=[[-1, 2], [1, -2]])
133
+ >>> processor = Processor(bootstrap_with=[[-1, 2], [1, -2]], engine='cd195')
123
134
  >>> processor.append_formula([[-2, 3], [1]])
124
135
  >>> processor.add_clause([-3, 4])
125
136
  >>>
@@ -140,13 +151,23 @@ class Processor(object):
140
151
  >>> processor.delete()
141
152
  """
142
153
 
143
- def __init__(self, bootstrap_with=None):
154
+ def __init__(self, bootstrap_with=None, engine='cd15'):
144
155
  """
145
156
  Basic constructor.
146
157
  """
147
158
 
159
+ self.cadical = None
160
+
148
161
  # immediately creating a CaDiCaL object
149
- self.cadical = Cadical153()
162
+ engine_ = engine.lower()
163
+ if engine_ in SolverNames.cadical153:
164
+ self.cadical = Cadical153()
165
+ elif engine_ in SolverNames.cadical195:
166
+ self.cadical = Cadical195()
167
+ elif engine_ in SolverNames.cadical300:
168
+ self.cadical = Cadical300()
169
+ else:
170
+ raise(NoSuchSolverError(engine))
150
171
 
151
172
  # status of processor is True by default meaning
152
173
  # that the input formula *is not* unsatisfiable
@@ -219,14 +240,18 @@ class Processor(object):
219
240
  if self.cadical:
220
241
  self.cadical.append_formula(formula)
221
242
 
222
- def process(self, rounds=1, block=False, cover=False, condition=False,
223
- decompose=True, elim=True, probe=True, probehbr=True,
224
- subsume=True, vivify=True, freeze=[]):
243
+ def process(self, rounds=1, backbone=True, block=False, condition=False,
244
+ congruence=True, cover=False, decompose=True, elim=True,
245
+ probe=True, probehbr=True, subsume=True, sweep=True,
246
+ ternary=True, transred=True, vivify=True, freeze=[]):
225
247
  """
226
248
  Runs CaDiCaL's preprocessor for the internal formula for a given
227
249
  number of rounds and using the techniques specified in the
228
250
  arguments. Note that the default values of all the arguments used
229
- are set as in the default configuration of CaDiCaL 1.5.3.
251
+ are set as in the default configuration of CaDiCaL 1.5.3 and
252
+ 3.0.0. Arguments ``backbone``, ``congruence``, and ``sweep`` are
253
+ used by CaDiCaL 3.0.0 only, as the older versions do not support
254
+ these techniques.
230
255
 
231
256
  As the result, the method returns a :class:`.CNF` object
232
257
  containing the processed formula. Additionally to the clauses, the
@@ -241,7 +266,11 @@ class Processor(object):
241
266
  and covered clause elimination is invoked only if bounded variable
242
267
  elimination is active. Subsumption elimination in turn may trigger
243
268
  vivification and transitive reduction if the corresponding flags
244
- are set.
269
+ are set. Also, in CaDiCaL 3.0.0, flag ``probe`` controls only
270
+ failed literal probing while the other techniques applied along
271
+ with probing are controlled by their own flags. Flag ``elim``
272
+ there controls both bounded variable elimination and its fast
273
+ variant applied at the beginning of preprocessing.
245
274
 
246
275
  Finally, note that the ``freeze`` argument can be used to keep
247
276
  some of the variables of the original formula unchanged during
@@ -251,15 +280,21 @@ class Processor(object):
251
280
  :param rounds: number of preprocessing rounds
252
281
  :type rounds: int
253
282
 
283
+ :param backbone: compute backbones of the binary clauses (CaDiCaL 3.0.0 only)
284
+ :type backbone: bool
285
+
254
286
  :param block: apply blocked clause elimination
255
287
  :type block: bool
256
288
 
257
- :param cover: apply covered clause elimination
258
- :type cover: bool
259
-
260
289
  :param condition: detect conditional autarkies and apply globally-blocked clause elimination
261
290
  :type condition: bool
262
291
 
292
+ :param congruence: detect AND, XOR, and ITE gates and merge equivalent gate outputs (CaDiCaL 3.0.0 only)
293
+ :type congruence: bool
294
+
295
+ :param cover: apply covered clause elimination
296
+ :type cover: bool
297
+
263
298
  :param decompose: detect strongly connected components (SCCs) in the binary implication graph (BIG) and apply equivalent literal substitution (ELS)
264
299
  :type decompose: bool
265
300
 
@@ -275,6 +310,15 @@ class Processor(object):
275
310
  :param subsume: apply global forward clause subsumption
276
311
  :type subsume: bool
277
312
 
313
+ :param sweep: apply SAT sweeping to detect equivalent literals and backbones (CaDiCaL 3.0.0 only)
314
+ :type sweep: bool
315
+
316
+ :param ternary: apply hyper ternary resolution
317
+ :type ternary: bool
318
+
319
+ :param transred: apply transitive reduction of the binary implication graph (BIG)
320
+ :type transred: bool
321
+
278
322
  :param vivify: apply clause vivification
279
323
  :type vivify: bool
280
324
 
@@ -304,11 +348,24 @@ class Processor(object):
304
348
  """
305
349
 
306
350
  if self.cadical:
307
- self.status, result = self.cadical.process(rounds, block, cover,
308
- condition, decompose,
309
- elim, probe, probehbr,
310
- subsume, vivify,
311
- freeze)
351
+ # techniques unavailable in the older versions of CaDiCaL
352
+ extra = {}
353
+ if isinstance(self.cadical, Cadical300):
354
+ extra = dict(backbone=backbone, congruence=congruence,
355
+ sweep=sweep)
356
+
357
+ self.status, result = self.cadical.process(rounds=rounds,
358
+ block=block,
359
+ condition=condition,
360
+ cover=cover,
361
+ decompose=decompose,
362
+ elim=elim, probe=probe,
363
+ probehbr=probehbr,
364
+ subsume=subsume,
365
+ ternary=ternary,
366
+ transred=transred,
367
+ vivify=vivify,
368
+ freeze=freeze, **extra)
312
369
 
313
370
  # making the status Boolean
314
371
  self.status = False if self.status == 20 else True
@@ -1359,6 +1359,64 @@ class Solver(object):
1359
1359
  if self.solver:
1360
1360
  return self.solver.propagate(assumptions, phase_saving)
1361
1361
 
1362
+ def flip_bits(self, assumptions=[], preferences=None, budget=100000):
1363
+ """
1364
+ Run a "bit flipper" a.k.a. local search (based on CaDiCaL's
1365
+ ProbSAT-like walker) aiming at a model of the formula that
1366
+ minimizes its *cost* with respect to a set of weighted literal
1367
+ preferences, i.e. the sum of the weights of the preferred literals
1368
+ falsified by the model. The method returns a pair of a model and
1369
+ its cost, or ``(None, None)`` if no model is found.
1370
+
1371
+ Preferences are given as a dictionary mapping literals to their
1372
+ non-negative integer weights (both polarities of a variable can
1373
+ be preferred). The preferences are kept between the calls unless
1374
+ new ones are provided. The search is limited by a ``budget``
1375
+ given in CaDiCaL's local search *ticks* (a measure of memory
1376
+ accesses).
1377
+
1378
+ **Note** that all the clauses removed by preprocessing (see
1379
+ :meth:`Cadical300.process`) are restored by the method.
1380
+
1381
+ **Note** that only :class:`Cadical300` supports this
1382
+ functionality, without external propagators (and so without
1383
+ native cardinality constraints).
1384
+
1385
+ .. warning::
1386
+
1387
+ This prototype implementation has been written by an LLM. So
1388
+ use at your own risk!
1389
+
1390
+ :param assumptions: a list of assumption literals.
1391
+ :param preferences: weighted preferred literals.
1392
+ :param budget: limit on the number of ticks.
1393
+
1394
+ :type assumptions: iterable(int)
1395
+ :type preferences: dict(int, int)
1396
+ :type budget: int
1397
+
1398
+ :rtype: tuple(list(int), int) or tuple(None, None).
1399
+
1400
+ Usage example:
1401
+
1402
+ .. code-block:: python
1403
+
1404
+ >>> from pysat.solvers import Solver
1405
+ >>>
1406
+ >>> with Solver(name='cd30', bootstrap_with=[[1, 2], [-1, -2]]) as s:
1407
+ ... print(s.solve())
1408
+ ... print(s.flip_bits(preferences={1: 5, 2: 3}))
1409
+ ...
1410
+ True
1411
+ ([1, -2], 3)
1412
+ """
1413
+
1414
+ if self.solver:
1415
+ if isinstance(self.solver, Cadical300):
1416
+ return self.solver.flip_bits(assumptions, preferences, budget)
1417
+ else:
1418
+ raise NotImplementedError('Local search is supported only by CaDiCaL 3.0.0')
1419
+
1362
1420
  def apply_jw(self):
1363
1421
  """
1364
1422
  Recompute and apply JW scores to the current formula.
@@ -1416,9 +1474,7 @@ class Solver(object):
1416
1474
  """
1417
1475
 
1418
1476
  if self.solver:
1419
- if hard:
1420
- if not isinstance(self.solver, Ergo):
1421
- raise NotImplementedError('Setting persistent phases is unsupported by this solver')
1477
+ if hard and isinstance(self.solver, Ergo):
1422
1478
  return self.solver.set_phases(literals, hard=True)
1423
1479
 
1424
1480
  return self.solver.set_phases(literals)
@@ -2365,9 +2421,10 @@ class Cadical153(object):
2365
2421
  if self.cadical:
2366
2422
  pysolvers.cadical153_dbudget(self.cadical, budget)
2367
2423
 
2368
- def process(self, rounds=1, block=False, cover=False, condition=False,
2424
+ def process(self, rounds=1, block=False, condition=False, cover=False,
2369
2425
  decompose=True, elim=True, probe=True, probehbr=True,
2370
- subsume=True, vivify=True, freeze=[]):
2426
+ subsume=True, ternary=True, transred=True, vivify=True,
2427
+ freeze=[]):
2371
2428
  """
2372
2429
  Apply the preprocessor for the internal formula. See the
2373
2430
  documentation for the ``process`` module for details.
@@ -2379,7 +2436,8 @@ class Cadical153(object):
2379
2436
  int(condition),
2380
2437
  int(decompose), int(elim),
2381
2438
  int(probe), int(probehbr),
2382
- int(subsume), int(vivify),
2439
+ int(subsume), int(ternary),
2440
+ int(transred), int(vivify),
2383
2441
  freeze,
2384
2442
  int(MainThread.check()))
2385
2443
 
@@ -2799,9 +2857,10 @@ class Cadical195(object):
2799
2857
  if self.cadical:
2800
2858
  pysolvers.cadical195_dbudget(self.cadical, budget)
2801
2859
 
2802
- def process(self, rounds=1, block=False, cover=False, condition=False,
2860
+ def process(self, rounds=1, block=False, condition=False, cover=False,
2803
2861
  decompose=True, elim=True, probe=True, probehbr=True,
2804
- subsume=True, vivify=True, freeze=[]):
2862
+ subsume=True, ternary=True, transred=True, vivify=True,
2863
+ freeze=[]):
2805
2864
  """
2806
2865
  Apply the preprocessor for the internal formula. See the
2807
2866
  documentation for the ``process`` module for details.
@@ -2813,7 +2872,8 @@ class Cadical195(object):
2813
2872
  int(condition),
2814
2873
  int(decompose), int(elim),
2815
2874
  int(probe), int(probehbr),
2816
- int(subsume), int(vivify),
2875
+ int(subsume), int(ternary),
2876
+ int(transred), int(vivify),
2817
2877
  freeze,
2818
2878
  int(MainThread.check()))
2819
2879
 
@@ -3373,6 +3433,31 @@ class Cadical300(object):
3373
3433
 
3374
3434
  return self.status
3375
3435
 
3436
+ def flip_bits(self, assumptions=[], preferences=None, budget=100000):
3437
+ """
3438
+ Run local search guided by weighted literal preferences.
3439
+ """
3440
+
3441
+ if self.cadical:
3442
+ self.sync_vars()
3443
+
3444
+ if self.use_timer:
3445
+ start_time = process_time()
3446
+
3447
+ res = pysolvers.cadical300_flipbits(self.cadical, assumptions,
3448
+ preferences, budget, int(MainThread.check()))
3449
+
3450
+ if self.use_timer:
3451
+ self.call_time = process_time() - start_time
3452
+ self.accu_time += self.call_time
3453
+
3454
+ # the solver is in the inconclusive state now
3455
+ self.status = None
3456
+ self.prev_assumps = assumptions
3457
+ self.sync_vpool()
3458
+
3459
+ return res
3460
+
3376
3461
  def conf_budget(self, budget):
3377
3462
  """
3378
3463
  Set limit on the number of conflicts.
@@ -3389,9 +3474,10 @@ class Cadical300(object):
3389
3474
  if self.cadical:
3390
3475
  pysolvers.cadical300_dbudget(self.cadical, budget)
3391
3476
 
3392
- def process(self, rounds=1, block=False, cover=False, condition=False,
3393
- decompose=True, elim=True, probe=True, probehbr=True,
3394
- subsume=True, vivify=True, freeze=[]):
3477
+ def process(self, rounds=1, backbone=True, block=False, condition=False,
3478
+ congruence=True, cover=False, decompose=True, elim=True,
3479
+ probe=True, probehbr=True, subsume=True, sweep=True,
3480
+ ternary=True, transred=True, vivify=True, freeze=[]):
3395
3481
  """
3396
3482
  Apply the preprocessor for the internal formula. See the
3397
3483
  documentation for the ``process`` module for details.
@@ -3401,12 +3487,14 @@ class Cadical300(object):
3401
3487
  self.sync_vars()
3402
3488
 
3403
3489
  result = pysolvers.cadical300_process(self.cadical, rounds,
3404
- int(block), int(cover),
3405
- int(condition),
3490
+ int(backbone), int(block),
3491
+ int(cover), int(condition),
3492
+ int(congruence),
3406
3493
  int(decompose), int(elim),
3407
3494
  int(probe), int(probehbr),
3408
- int(subsume), int(vivify),
3409
- freeze,
3495
+ int(subsume), int(sweep),
3496
+ int(ternary), int(transred),
3497
+ int(vivify), freeze,
3410
3498
  int(MainThread.check()))
3411
3499
 
3412
3500
  self.sync_vpool()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.9.dev19
3
+ Version: 1.9.dev21
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
@@ -109,6 +109,7 @@ tests/test_empty_clause.py
109
109
  tests/test_empty_extclause.py
110
110
  tests/test_encode_pb_conditional.py
111
111
  tests/test_equals1.py
112
+ tests/test_flip_bits.py
112
113
  tests/test_formula_parsing.py
113
114
  tests/test_formula_unique.py
114
115
  tests/test_hitman.py
@@ -1873,20 +1873,20 @@ diff -Naur solvers/cadical153/solver.cpp solvers/cdc153/solver.cpp
1873
1873
  +
1874
1874
  +vector<bool> Solver::extend(vector<int>& solution)
1875
1875
  +{
1876
- + for (int i = 1; i <= solution.size(); i++) {
1877
- + if (internal->vals[i]) continue; //if already set
1878
- + int val = solution[i - 1];
1879
- + if (val > 0) {
1880
- + internal->vals[i] = 1;
1881
- + internal->vals[-i] = -1;
1882
- + }
1883
- + else {
1884
- + internal->vals[i] = -1;
1885
- + internal->vals[-i] = 1;
1886
- + }
1876
+ + vector<int> assigned; // values set here, to be undone after extension
1877
+ + for (int lit : solution) {
1878
+ + const int idx = abs(lit);
1879
+ + if (idx > external->max_var) continue; // unknown variable
1880
+ + const int ilit = external->e2i[idx];
1881
+ + if (!ilit || internal->vals[ilit]) continue; // unmapped or fixed
1882
+ + internal->vals[ilit] = lit > 0 ? 1 : -1;
1883
+ + internal->vals[-ilit] = -internal->vals[ilit];
1884
+ + assigned.push_back(ilit);
1887
1885
  + }
1888
1886
  + external->extend();
1889
1887
  +
1888
+ + for (int ilit : assigned)
1889
+ + internal->vals[ilit] = internal->vals[-ilit] = 0;
1890
1890
  + return external->vals;
1891
1891
  +}
1892
1892
  +
@@ -2855,20 +2855,20 @@ diff -Naur solvers/cadical195/solver.cpp solvers/cdc195/solver.cpp
2855
2855
  +
2856
2856
  +vector<bool> Solver::extend(vector<int>& solution)
2857
2857
  +{
2858
- + for (int i = 1; i <= solution.size(); i++) {
2859
- + if (internal->vals[i]) continue; //if already set
2860
- + int val = solution[i - 1];
2861
- + if (val > 0) {
2862
- + internal->vals[i] = 1;
2863
- + internal->vals[-i] = -1;
2864
- + }
2865
- + else {
2866
- + internal->vals[i] = -1;
2867
- + internal->vals[-i] = 1;
2868
- + }
2858
+ + vector<int> assigned; // values set here, to be undone after extension
2859
+ + for (int lit : solution) {
2860
+ + const int idx = abs(lit);
2861
+ + if (idx > external->max_var) continue; // unknown variable
2862
+ + const int ilit = external->e2i[idx];
2863
+ + if (!ilit || internal->vals[ilit]) continue; // unmapped or fixed
2864
+ + internal->vals[ilit] = lit > 0 ? 1 : -1;
2865
+ + internal->vals[-ilit] = -internal->vals[ilit];
2866
+ + assigned.push_back(ilit);
2869
2867
  + }
2870
2868
  + external->extend();
2871
2869
  +
2870
+ + for (int ilit : assigned)
2871
+ + internal->vals[ilit] = internal->vals[-ilit] = 0;
2872
2872
  + return external->vals;
2873
2873
  +}
2874
2874
  +