python-sat 1.9.dev13__tar.gz → 1.9.dev15__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 (120) hide show
  1. {python_sat-1.9.dev13/python_sat.egg-info → python_sat-1.9.dev15}/PKG-INFO +1 -1
  2. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/bica.py +26 -16
  3. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/hitman.py +2 -7
  4. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/lsu.py +1 -1
  5. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/optux.py +47 -7
  6. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/__init__.py +1 -1
  7. {python_sat-1.9.dev13 → python_sat-1.9.dev15/python_sat.egg-info}/PKG-INFO +1 -1
  8. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/python_sat.egg-info/SOURCES.txt +2 -0
  9. python_sat-1.9.dev15/tests/test_hitman.py +31 -0
  10. python_sat-1.9.dev15/tests/test_lsu.py +15 -0
  11. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/LICENSE.txt +0 -0
  12. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/MANIFEST.in +0 -0
  13. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/README.rst +0 -0
  14. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/allies/__init__.py +0 -0
  15. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/allies/approxmc.py +0 -0
  16. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/allies/unigen.py +0 -0
  17. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/bitwise.hh +0 -0
  18. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/card.hh +0 -0
  19. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/clset.hh +0 -0
  20. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/common.hh +0 -0
  21. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/itot.hh +0 -0
  22. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/ladder.hh +0 -0
  23. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/mto.hh +0 -0
  24. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/pairwise.hh +0 -0
  25. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/ptypes.hh +0 -0
  26. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/pycard.cc +0 -0
  27. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/seqcounter.hh +0 -0
  28. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/sortcard.hh +0 -0
  29. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/cardenc/utils.hh +0 -0
  30. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/__init__.py +0 -0
  31. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/bbscan.py +0 -0
  32. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/fm.py +0 -0
  33. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/genhard.py +0 -0
  34. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/lbx.py +0 -0
  35. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/mcsls.py +0 -0
  36. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/models.py +0 -0
  37. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/musx.py +0 -0
  38. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/primer.py +0 -0
  39. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/rc2.py +0 -0
  40. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/examples/usage.py +0 -0
  41. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pf_parse.cc +0 -0
  42. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pf_plus.cc +0 -0
  43. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pf_plus.hh +0 -0
  44. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pf_py.hh +0 -0
  45. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pf_scan.hh +0 -0
  46. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pf_weight.cc +0 -0
  47. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pf_weight.hh +0 -0
  48. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pyformula.cc +0 -0
  49. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/formula/pyformula.hh +0 -0
  50. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/_fileio.py +0 -0
  51. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/_utils.py +0 -0
  52. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/card.py +0 -0
  53. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/engines.py +0 -0
  54. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/formula.py +0 -0
  55. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/integer.py +0 -0
  56. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/pb.py +0 -0
  57. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/process.py +0 -0
  58. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/pysat/solvers.py +0 -0
  59. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/python_sat.egg-info/dependency_links.txt +0 -0
  60. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/python_sat.egg-info/requires.txt +0 -0
  61. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/python_sat.egg-info/top_level.txt +0 -0
  62. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/requirements.txt +0 -0
  63. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/setup.cfg +0 -0
  64. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/setup.py +0 -0
  65. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/cadical103.tar.gz +0 -0
  66. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/cadical153.tar.gz +0 -0
  67. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/cadical195.tar.gz +0 -0
  68. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/cadical300.tar.gz +0 -0
  69. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/glucose30.tar.gz +0 -0
  70. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/glucose41.tar.gz +0 -0
  71. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/glucose421.tar.gz +0 -0
  72. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/lingeling.tar.gz +0 -0
  73. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/maplechrono.zip +0 -0
  74. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/maplecm.zip +0 -0
  75. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/maplesat.zip +0 -0
  76. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/mergesat3.tar.gz +0 -0
  77. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/minicard.tar.gz +0 -0
  78. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/minisat22.tar.gz +0 -0
  79. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/minisatgh.zip +0 -0
  80. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/cadical103.patch +0 -0
  81. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/cadical153.patch +0 -0
  82. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/cadical195.patch +0 -0
  83. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/cadical300.patch +0 -0
  84. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/glucose30.patch +0 -0
  85. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/glucose41.patch +0 -0
  86. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/glucose421.patch +0 -0
  87. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/gluecard30.patch +0 -0
  88. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/gluecard41.patch +0 -0
  89. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/kissat404.patch +0 -0
  90. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/lingeling.patch +0 -0
  91. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/maplechrono.patch +0 -0
  92. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/maplecm.patch +0 -0
  93. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/maplesat.patch +0 -0
  94. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/mergesat3.patch +0 -0
  95. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/minicard.patch +0 -0
  96. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/minisat22.patch +0 -0
  97. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/minisatep.patch +0 -0
  98. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/patches/minisatgh.patch +0 -0
  99. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/prepare.py +0 -0
  100. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/solvers/pysolvers.cc +0 -0
  101. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_accum_stats.py +0 -0
  102. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_atmost.py +0 -0
  103. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_atmost1.py +0 -0
  104. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_atmostk.py +0 -0
  105. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_boolengine.py +0 -0
  106. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_clausification.py +0 -0
  107. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_cnf.py +0 -0
  108. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_empty_extclause.py +0 -0
  109. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_encode_pb_conditional.py +0 -0
  110. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_equals1.py +0 -0
  111. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_formula_parsing.py +0 -0
  112. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_formula_unique.py +0 -0
  113. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_idpool.py +0 -0
  114. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_integer.py +0 -0
  115. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_oom.py +0 -0
  116. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_process.py +0 -0
  117. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_propagate.py +0 -0
  118. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_unique_model.py +0 -0
  119. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/tests/test_unique_mus.py +0 -0
  120. {python_sat-1.9.dev13 → python_sat-1.9.dev15}/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.dev13
3
+ Version: 1.9.dev15
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
@@ -231,6 +231,7 @@ class Bica:
231
231
  :param ominz: do heuristic core reduction
232
232
  :param onodisj: do not enumerate disjoint MCSes with OptUx
233
233
  :param opuresat: use pure SAT-based hitting set enumeration
234
+ :param oreduce: minimize correction sets with this budget on the conflict count
234
235
  :param ounsorted: apply unsorted MUS enumeration
235
236
  :param otrim: do core trimming at most this number of times
236
237
  :param weighted: get a minimal cover wrt. the total number of literals
@@ -255,6 +256,7 @@ class Bica:
255
256
  :type ominz: bool
256
257
  :type onodisj: bool
257
258
  :type opuresat: str
259
+ :type oreduce: int
258
260
  :type ounsorted: bool
259
261
  :type otrim: int
260
262
  :type weighted: bool
@@ -265,8 +267,8 @@ class Bica:
265
267
  padapt=False, pdcalls=False, pexhaust=False, pminz=False,
266
268
  ppuresat=False, psearch='lin', punsorted=False, ptrim=False,
267
269
  osolver='mgh', oadapt=False, odcalls=False, oexhaust=False,
268
- ominz=False, onodisj=False, opuresat=False, ounsorted=False,
269
- otrim=False, weighted=False, verbose=0):
270
+ ominz=False, onodisj=False, opuresat=False, oreduce=0,
271
+ ounsorted=False, otrim=False, weighted=False, verbose=0):
270
272
  """
271
273
  Initialiser.
272
274
  """
@@ -297,6 +299,7 @@ class Bica:
297
299
  self.ominz = ominz
298
300
  self.onodisj = onodisj
299
301
  self.opuresat = opuresat
302
+ self.oreduce = oreduce
300
303
  self.ounsorted = ounsorted
301
304
  self.otrim = otrim
302
305
 
@@ -425,8 +428,9 @@ class Bica:
425
428
  adapt=self.oadapt, cover=None,
426
429
  dcalls=self.odcalls, exhaust=self.oexhaust,
427
430
  minz=self.ominz, nodisj=self.onodisj,
428
- puresat=self.opuresat, unsorted=self.ounsorted,
429
- trim=self.otrim, verbose=self.verbose)
431
+ puresat=self.opuresat, reduce=self.oreduce,
432
+ unsorted=self.ounsorted, trim=self.otrim,
433
+ verbose=self.verbose)
430
434
 
431
435
  return self.optux.compute()
432
436
 
@@ -510,12 +514,13 @@ def parse_options():
510
514
  opts, args = getopt.getopt(sys.argv[1:],
511
515
  'aAdDe:hmMnp:P:r:R:s:S:t:T:uUvwxX',
512
516
  ['padapt', 'oadapt', 'pdcalls', 'odcalls',
513
- 'enum=', 'help', 'pminimize', 'ominimize',
514
- 'no-disj', 'ppuresat=', 'opuresat=',
515
- 'reduce=', 'repr=', 'psolver=',
516
- 'osolver=', 'ptrim=', 'otrim=',
517
- 'punsorted', 'ounsorted', 'verbose',
518
- 'weighted', 'pexhaust', 'oexhaust'])
517
+ 'enum=', 'help', 'mincs=', 'pminimize',
518
+ 'ominimize', 'no-disj', 'ppuresat=',
519
+ 'opuresat=', 'reduce=', 'repr=',
520
+ 'psolver=', 'osolver=', 'ptrim=',
521
+ 'otrim=', 'punsorted', 'ounsorted',
522
+ 'verbose', 'weighted', 'pexhaust',
523
+ 'oexhaust'])
519
524
  except getopt.GetoptError as err:
520
525
  sys.stderr.write(str(err).capitalize())
521
526
  usage()
@@ -540,6 +545,7 @@ def parse_options():
540
545
  osolver = 'mgh'
541
546
  ppuresat = False
542
547
  opuresat = False
548
+ oreduce = 0
543
549
  punsorted = False
544
550
  ounsorted = False
545
551
  ptrim = 0
@@ -561,6 +567,8 @@ def parse_options():
561
567
  elif opt in ('-h', '--help'):
562
568
  usage()
563
569
  sys.exit(0)
570
+ elif opt == '--mincs':
571
+ oreduce = int(arg)
564
572
  elif opt in ('-m', '--pminimize'):
565
573
  pminz = True
566
574
  elif opt in ('-M', '--ominimize'):
@@ -601,8 +609,8 @@ def parse_options():
601
609
 
602
610
  return to_enum, mode, padapt, oadapt, pdcalls, odcalls, pexhaust, \
603
611
  oexhaust, pminz, ominz, nodisj, ptrim, otrim, search, psolver, \
604
- osolver, ppuresat, opuresat, punsorted, ounsorted, verbose, \
605
- weighted, args
612
+ osolver, ppuresat, opuresat, oreduce, punsorted, ounsorted, \
613
+ verbose, weighted, args
606
614
 
607
615
 
608
616
  #
@@ -621,6 +629,8 @@ def usage():
621
629
  print(' -e, --enum=<int> Enumerate this many "top-k" solutions')
622
630
  print(' Available values: [1 .. INT_MAX], all (default = 1)')
623
631
  print(' -h, --help Print this help message')
632
+ print(' --mincs=<int> Budget on conflicts number when applying heuristic correction set minimization [OptUx]')
633
+ print(' Available values: [0 .. INT_MAX], -1 (default = 0, meaning no minimization)')
624
634
  print(' -m, --pminimize Use a heuristic unsatisfiable core minimizer [Primer]')
625
635
  print(' -M, --ominimize Use a heuristic unsatisfiable core minimizer [OptUx]')
626
636
  print(' -n, --no-disj Do not enumerate disjoint MCSes [OptUx]')
@@ -656,8 +666,8 @@ if __name__ == '__main__':
656
666
  # parse command-line options
657
667
  to_enum, mode, padapt, oadapt, pdcalls, odcalls, pexhaust, oexhaust, \
658
668
  pminz, ominz, nodisj, ptrim, otrim, search, psolver, osolver, \
659
- ppuresat, opuresat, punsorted, ounsorted, verbose, weighted, \
660
- files = parse_options()
669
+ ppuresat, opuresat, oreduce, punsorted, ounsorted, verbose, \
670
+ weighted, files = parse_options()
661
671
 
662
672
  if files:
663
673
  # read CNF from file
@@ -671,8 +681,8 @@ if __name__ == '__main__':
671
681
  punsorted=punsorted, ptrim=ptrim, osolver=osolver,
672
682
  oadapt=oadapt, odcalls=odcalls, oexhaust=oexhaust,
673
683
  ominz=ominz, onodisj=nodisj, opuresat=opuresat,
674
- ounsorted=ounsorted, otrim=otrim, weighted=weighted,
675
- verbose=verbose) as bica:
684
+ oreduce=oreduce, ounsorted=ounsorted, otrim=otrim,
685
+ weighted=weighted, verbose=verbose) as bica:
676
686
 
677
687
  for i, minf in enumerate(bica.enumerate()):
678
688
  if verbose > 1:
@@ -476,24 +476,19 @@ class Hitman(object):
476
476
 
477
477
  if no_obj is False:
478
478
  # a soft clause should be added for each new object
479
- new_obj = filter(lambda vid: abs(vid) not in self.oracle.vmap.e2i, clause)
479
+ new_obj = sorted(set(abs(vid) for vid in clause if abs(vid) not in self.oracle.vmap.e2i))
480
480
  else:
481
481
  # this is a native AtMostK constraint
482
482
  clause = [list(map(lambda a: self.idpool.id(a.obj) * (2 * a.sign - 1), clause[0])), clause[1]]
483
483
 
484
484
  if no_obj is False:
485
485
  # a soft clause should be added for each new object
486
- new_obj = filter(lambda vid: abs(vid) not in self.oracle.vmap.e2i, clause[0])
487
-
488
- # there may be duplicate literals if the constraint is weighted
489
- new_obj = list(set(new_obj))
486
+ new_obj = sorted(set(abs(vid) for vid in clause[0] if abs(vid) not in self.oracle.vmap.e2i))
490
487
 
491
488
  # adding the hard clause
492
489
  self.oracle.add_clause(clause)
493
490
 
494
491
  if no_obj is False:
495
- # some of the literals may also have the opposite polarity
496
- new_obj = [l if l in self.idpool.obj2id else -l for l in new_obj]
497
492
 
498
493
  if self.htype != 'sat':
499
494
  # new soft clauses
@@ -235,7 +235,7 @@ class LSU:
235
235
  self._assert_lt(self.cost)
236
236
 
237
237
  if is_sat:
238
- self.model = filter(lambda l: abs(l) <= self.formula.nv, self.model)
238
+ self.model = list(filter(lambda l: abs(l) <= self.formula.nv, self.model))
239
239
  if self.verbose:
240
240
  if self.found_optimum():
241
241
  print('s OPTIMUM FOUND')
@@ -174,6 +174,16 @@ class OptUx(object):
174
174
  whose integer value signifies the number of processing rounds to be
175
175
  applied. The number of rounds is set to 0 by default.
176
176
 
177
+ Since the algorithm builds on implicit hitting set dualization,
178
+ correction sets computed along the way can be minimized
179
+ *heuristically*, by limiting the budget on the number of conflicts a
180
+ SAT solver can collect during the minimization process. Parameter
181
+ ``reduce`` is responsible for this behaviour. By default, the budget
182
+ is set to ``0``, meaning that no reduction of correction sets is
183
+ performed. Setting it to ``-1`` forces the solver to compute an exact
184
+ MCS. Otherwise, any positive value of this parameter will indicate the
185
+ upper bound on the number of conflicts.
186
+
177
187
  Finally, one more optional input parameter ``cover`` is to be used
178
188
  when exhaustive enumeration of MUSes is not necessary and the tool can
179
189
  stop as soon as a given formula is covered by the set of currently
@@ -206,6 +216,7 @@ class OptUx(object):
206
216
  :param nodisj: do not enumerate disjoint MCSes
207
217
  :param process: apply formula preprocessing this many times
208
218
  :param puresat: use pure SAT-based hitting set enumeration
219
+ :param reduce: minimize correction sets using this budget on the number of conflicts
209
220
  :param unsorted: apply unsorted MUS enumeration
210
221
  :param trim: do core trimming at most this number of times
211
222
  :param verbose: verbosity level
@@ -220,6 +231,7 @@ class OptUx(object):
220
231
  :type nodisj: bool
221
232
  :type process: int
222
233
  :type puresat: str
234
+ :type reduce: int
223
235
  :type unsorted: bool
224
236
  :type trim: int
225
237
  :type verbose: int
@@ -227,7 +239,7 @@ class OptUx(object):
227
239
 
228
240
  def __init__(self, formula, solver='g3', adapt=False, cover=None,
229
241
  dcalls=False, exhaust=False, minz=False, nodisj=False, process=0,
230
- puresat=False, unsorted=False, trim=False, verbose=0):
242
+ puresat=False, reduce=0, unsorted=False, trim=False, verbose=0):
231
243
  """
232
244
  Constructor.
233
245
  """
@@ -237,6 +249,9 @@ class OptUx(object):
237
249
  # verbosity level
238
250
  self.verbose = verbose
239
251
 
252
+ # budget on the number of conflicts when reducing corrections sets
253
+ self.reduce_budget = reduce
254
+
240
255
  # constructing a local copy of the formula
241
256
  self.formula = WCNFPlus()
242
257
  self.formula.hard = formula.hard[:]
@@ -497,9 +512,12 @@ class OptUx(object):
497
512
  # correctly computed cost of the unit-mcs component
498
513
  units_cost = sum(map(lambda l: self.weights[l], self.units))
499
514
 
515
+ it = 0
500
516
  while True:
501
517
  # computing a new optimal hitting set
502
518
  hs = self.hitman.get()
519
+ if self.verbose > 3:
520
+ self.cost = sum(map(lambda l: self.weights[l], hs)) + units_cost
503
521
 
504
522
  if hs is None:
505
523
  # no more hitting sets exist
@@ -530,9 +548,26 @@ class OptUx(object):
530
548
  model = self.oracle.get_model()
531
549
  cs = [l for l in self.sels if model[abs(l) - 1] == -l]
532
550
 
551
+ # naive MCS extraction with a budget on the conflict count
552
+ if self.reduce_budget:
553
+ ss = [l for l in self.sels if model[abs(l) - 1] == +l]
554
+ self.oracle.conf_budget(self.reduce_budget)
555
+
556
+ # probing all the literals of the correction set
557
+ for l in cs:
558
+ if self.oracle.solve_limited(assumptions=[l] + ss) is True:
559
+ ss.append(l)
560
+
561
+ cs = sorted(set(cs) - set(ss))
562
+ self.oracle.conf_budget(budget=-1)
563
+
533
564
  # hitting the new correction subset
534
565
  self.hitman.hit(cs, weights=self.weights)
535
566
 
567
+ it += 1
568
+ if self.verbose > 3:
569
+ print(f'c iter: {it}, cost: {self.cost}; cs sz: {len(cs)}')
570
+
536
571
  def enumerate(self):
537
572
  """
538
573
  This is generator method iterating through MUSes and enumerating
@@ -571,11 +606,11 @@ def parse_options():
571
606
  """
572
607
 
573
608
  try:
574
- opts, args = getopt.getopt(sys.argv[1:], 'ac:de:hmnp:P:s:t:uvx',
609
+ opts, args = getopt.getopt(sys.argv[1:], 'ac:de:hmnp:P:r:s:t:uvx',
575
610
  ['adapt', 'cover=', 'dcalls', 'enum=',
576
611
  'exhaust', 'help', 'minimize', 'no-disj',
577
612
  'solver=', 'puresat=', 'process=',
578
- 'unsorted', 'trim=', 'verbose'])
613
+ 'reduce=', 'unsorted', 'trim=', 'verbose'])
579
614
  except getopt.GetoptError as err:
580
615
  sys.stderr.write(str(err).capitalize() + '\n')
581
616
  usage()
@@ -591,6 +626,7 @@ def parse_options():
591
626
  solver = 'g3'
592
627
  process = 0
593
628
  puresat = False
629
+ reduce = 0
594
630
  unsorted = False
595
631
  trim = 0
596
632
  verbose = 1
@@ -619,6 +655,8 @@ def parse_options():
619
655
  puresat = str(arg)
620
656
  elif opt in ('-P', '--process'):
621
657
  process = int(arg)
658
+ elif opt in ('-r', '--reduce'):
659
+ reduce = int(arg)
622
660
  elif opt in ('-s', '--solver'):
623
661
  solver = str(arg)
624
662
  elif opt in ('-u', '--unsorted'):
@@ -633,7 +671,7 @@ def parse_options():
633
671
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
634
672
 
635
673
  return adapt, cover, dcalls, exhaust, minz, no_disj, trim, to_enum, \
636
- solver, process, puresat, unsorted, verbose, args
674
+ solver, process, puresat, reduce, unsorted, verbose, args
637
675
 
638
676
 
639
677
  #
@@ -659,6 +697,8 @@ def usage():
659
697
  print(' Requires: unsorted mode, i.e. \'-u\'')
660
698
  print(' -P, --process=<int> Number of processing rounds')
661
699
  print(' Available values: [0 .. INT_MAX] (default = 0)')
700
+ print(' -r, --reduce=<int> Conflict budget when reducing correction sets')
701
+ print(' Available values: [0 .. INT_MAX], -1 (default = 0, meaning no reduction)')
662
702
  print(' -s, --solver SAT solver to use')
663
703
  print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = g3)')
664
704
  print(' -t, --trim=<int> How many times to trim unsatisfiable cores')
@@ -672,7 +712,7 @@ def usage():
672
712
  #==============================================================================
673
713
  if __name__ == '__main__':
674
714
  adapt, cover, dcalls, exhaust, minz, no_disj, trim, to_enum, solver, \
675
- process, puresat, unsorted, verbose, files = parse_options()
715
+ process, puresat, reduce, unsorted, verbose, files = parse_options()
676
716
 
677
717
  if files:
678
718
  # reading standard CNF, WCNF, or (W)CNF+
@@ -689,8 +729,8 @@ if __name__ == '__main__':
689
729
  # creating an object of OptUx
690
730
  with OptUx(formula, solver=solver, adapt=adapt, cover=cover,
691
731
  dcalls=dcalls, exhaust=exhaust, minz=minz, nodisj=no_disj,
692
- process=process, puresat=puresat, unsorted=unsorted,
693
- trim=trim, verbose=verbose) as optux:
732
+ process=process, puresat=puresat, reduce=reduce,
733
+ unsorted=unsorted, trim=trim, verbose=verbose) as optux:
694
734
 
695
735
  # iterating over the necessary number of optimal MUSes
696
736
  for i, mus in enumerate(optux.enumerate()):
@@ -10,7 +10,7 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (1, 9, 'dev', 13)
13
+ VERSION = (1, 9, 'dev', 15)
14
14
 
15
15
 
16
16
  # PEP440 Format
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.9.dev13
3
+ Version: 1.9.dev15
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
@@ -107,8 +107,10 @@ tests/test_encode_pb_conditional.py
107
107
  tests/test_equals1.py
108
108
  tests/test_formula_parsing.py
109
109
  tests/test_formula_unique.py
110
+ tests/test_hitman.py
110
111
  tests/test_idpool.py
111
112
  tests/test_integer.py
113
+ tests/test_lsu.py
112
114
  tests/test_oom.py
113
115
  tests/test_process.py
114
116
  tests/test_propagate.py
@@ -0,0 +1,31 @@
1
+ from pysat.examples.hitman import Atom, Hitman
2
+
3
+
4
+ def test_add_hard_registers_new_object_with_mcs_enumerators():
5
+ for htype in ('lbx', 'mcsls'):
6
+ with Hitman(bootstrap_with=[['first']], htype=htype) as hitman:
7
+ hitman.add_hard([Atom('second')])
8
+
9
+ assert sorted(hitman.get()) == ['first', 'second']
10
+
11
+
12
+ def test_add_hard_registers_weighted_new_objects_with_rc2():
13
+ weights = {'base': 1, 'expensive': 10, 'cheap': 1}
14
+
15
+ with Hitman(bootstrap_with=[['base']], weights=weights,
16
+ htype='sorted') as hitman:
17
+ hitman.add_hard([Atom('expensive'), Atom('cheap')], weights=weights)
18
+
19
+ assert sorted(hitman.get()) == ['base', 'cheap']
20
+
21
+
22
+ def test_add_hard_registers_new_objects_with_sat():
23
+ with Hitman(bootstrap_with=[['base']], htype='sat',
24
+ solver='mgh') as hitman:
25
+ hitman.add_hard([Atom('left'), Atom('right')])
26
+
27
+ result = hitman.get()
28
+
29
+ assert 'base' in result
30
+ assert len(set(result) & {'left', 'right'}) == 1
31
+ assert set(hitman.oracle.vmap.e2i) == {1, 2, 3}
@@ -0,0 +1,15 @@
1
+ from pysat.examples.lsu import LSU
2
+ from pysat.formula import WCNF
3
+
4
+
5
+ def test_get_model_returns_reusable_list():
6
+ formula = WCNF()
7
+ formula.append([1], weight=1)
8
+
9
+ with LSU(formula, solver='g3') as lsu:
10
+ assert lsu.solve() is True
11
+
12
+ model = lsu.get_model()
13
+
14
+ assert isinstance(model, list)
15
+ assert lsu.get_model() == model
File without changes
File without changes