python-sat 1.8.dev19__tar.gz → 1.8.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 (99) hide show
  1. {python_sat-1.8.dev19/python_sat.egg-info → python_sat-1.8.dev21}/PKG-INFO +12 -2
  2. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/lbx.py +45 -8
  3. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/mcsls.py +42 -8
  4. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/optux.py +30 -8
  5. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/rc2.py +82 -34
  6. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/__init__.py +1 -1
  7. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/_fileio.py +30 -14
  8. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/formula.py +123 -92
  9. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/process.py +16 -2
  10. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/solvers.py +4 -2
  11. {python_sat-1.8.dev19 → python_sat-1.8.dev21/python_sat.egg-info}/PKG-INFO +12 -2
  12. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/python_sat.egg-info/SOURCES.txt +0 -1
  13. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/glucose421.patch +366 -408
  14. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/pysolvers.cc +100 -34
  15. python_sat-1.8.dev21/tests/test_process.py +67 -0
  16. python_sat-1.8.dev19/solvers/cadical170.tar.gz +0 -0
  17. python_sat-1.8.dev19/tests/test_process.py +0 -26
  18. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/LICENSE.txt +0 -0
  19. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/MANIFEST.in +0 -0
  20. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/README.rst +0 -0
  21. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/allies/__init__.py +0 -0
  22. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/allies/approxmc.py +0 -0
  23. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/allies/unigen.py +0 -0
  24. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/bitwise.hh +0 -0
  25. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/card.hh +0 -0
  26. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/clset.hh +0 -0
  27. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/common.hh +0 -0
  28. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/itot.hh +0 -0
  29. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/ladder.hh +0 -0
  30. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/mto.hh +0 -0
  31. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/pairwise.hh +0 -0
  32. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/ptypes.hh +0 -0
  33. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/pycard.cc +0 -0
  34. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/seqcounter.hh +0 -0
  35. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/sortcard.hh +0 -0
  36. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/cardenc/utils.hh +0 -0
  37. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/__init__.py +0 -0
  38. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/bica.py +0 -0
  39. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/fm.py +0 -0
  40. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/genhard.py +0 -0
  41. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/hitman.py +0 -0
  42. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/lsu.py +0 -0
  43. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/models.py +0 -0
  44. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/musx.py +0 -0
  45. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/primer.py +0 -0
  46. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/examples/usage.py +0 -0
  47. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/_utils.py +0 -0
  48. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/card.py +0 -0
  49. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/engines.py +0 -0
  50. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/pysat/pb.py +0 -0
  51. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/python_sat.egg-info/dependency_links.txt +0 -0
  52. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/python_sat.egg-info/requires.txt +0 -0
  53. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/python_sat.egg-info/top_level.txt +0 -0
  54. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/requirements.txt +0 -0
  55. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/setup.cfg +0 -0
  56. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/setup.py +0 -0
  57. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/cadical103.tar.gz +0 -0
  58. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/cadical153.tar.gz +0 -0
  59. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/cadical195.tar.gz +0 -0
  60. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/glucose30.tar.gz +0 -0
  61. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/glucose41.tar.gz +0 -0
  62. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/glucose421.tar.gz +0 -0
  63. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/lingeling.tar.gz +0 -0
  64. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/maplechrono.zip +0 -0
  65. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/maplecm.zip +0 -0
  66. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/maplesat.zip +0 -0
  67. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/mergesat3.tar.gz +0 -0
  68. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/minicard.tar.gz +0 -0
  69. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/minisat22.tar.gz +0 -0
  70. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/minisatgh.zip +0 -0
  71. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/cadical103.patch +0 -0
  72. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/cadical153.patch +0 -0
  73. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/cadical195.patch +0 -0
  74. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/glucose30.patch +0 -0
  75. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/glucose41.patch +0 -0
  76. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/gluecard30.patch +0 -0
  77. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/gluecard41.patch +0 -0
  78. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/lingeling.patch +0 -0
  79. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/maplechrono.patch +0 -0
  80. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/maplecm.patch +0 -0
  81. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/maplesat.patch +0 -0
  82. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/mergesat3.patch +0 -0
  83. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/minicard.patch +0 -0
  84. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/minisat22.patch +0 -0
  85. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/patches/minisatgh.patch +0 -0
  86. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/solvers/prepare.py +0 -0
  87. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_accum_stats.py +0 -0
  88. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_atmost.py +0 -0
  89. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_atmost1.py +0 -0
  90. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_atmostk.py +0 -0
  91. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_boolengine.py +0 -0
  92. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_clausification.py +0 -0
  93. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_cnf.py +0 -0
  94. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_equals1.py +0 -0
  95. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_formula_unique.py +0 -0
  96. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_propagate.py +0 -0
  97. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_unique_model.py +0 -0
  98. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_unique_mus.py +0 -0
  99. {python_sat-1.8.dev19 → python_sat-1.8.dev21}/tests/test_warmstart.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.8.dev19
3
+ Version: 1.8.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
@@ -19,6 +19,16 @@ Provides-Extra: pblib
19
19
  Requires-Dist: pypblib>=0.0.3; extra == "pblib"
20
20
  Provides-Extra: unigen
21
21
  Requires-Dist: pyunigen>=4.1.20; extra == "unigen"
22
+ Dynamic: author
23
+ Dynamic: author-email
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: license
28
+ Dynamic: license-file
29
+ Dynamic: provides-extra
30
+ Dynamic: requires-dist
31
+ Dynamic: summary
22
32
 
23
33
 
24
34
  A Python library providing a simple interface to a number of state-of-art
@@ -90,6 +90,7 @@ import getopt
90
90
  from math import copysign
91
91
  import os
92
92
  from pysat.formula import CNFPlus, WCNFPlus
93
+ from pysat.process import Processor
93
94
  from pysat.solvers import Solver, SolverNames
94
95
  import re
95
96
  from six.moves import range
@@ -112,25 +113,37 @@ class LBX(object):
112
113
  ``use_cld`` is set to ``False``. Internal SAT solver's timer is also
113
114
  disabled by default, i.e. ``use_timer`` is ``False``.
114
115
 
116
+ Additionally, the input formula can be preprocessed before running MCS
117
+ enumeration. This is controlled by the input parameter ``process``
118
+ whose integer value signifies the number of processing rounds to be
119
+ applied. The number of rounds is set to 0 by default.
120
+
115
121
  :param formula: unsatisfiable partial CNF formula
116
122
  :param use_cld: whether or not to use "clause :math:`D`"
117
123
  :param solver_name: SAT oracle name
124
+ :param process: apply formula preprocessing this many times
118
125
  :param use_timer: whether or not to use SAT solver's timer
119
126
 
120
127
  :type formula: :class:`.WCNF`
121
128
  :type use_cld: bool
122
129
  :type solver_name: str
130
+ :type process: int
123
131
  :type use_timer: bool
124
132
  """
125
133
 
126
- def __init__(self, formula, use_cld=False, solver_name='m22', use_timer=False):
134
+ def __init__(self, formula, use_cld=False, solver_name='m22', process=0,
135
+ use_timer=False):
127
136
  """
128
137
  Constructor.
129
138
  """
130
139
 
140
+ # dealing with preprocessing, if required
141
+ hard = formula.hard if process > 0 else []
142
+
131
143
  # bootstrapping the solver with hard clauses
132
- self.oracle = Solver(name=solver_name, bootstrap_with=formula.hard,
133
- use_timer=use_timer)
144
+ self.oracle = Solver(name=solver_name,
145
+ bootstrap_with=formula.hard if process == 0 else [],
146
+ use_timer=use_timer)
134
147
  self.solver = solver_name
135
148
 
136
149
  # adding native cardinality constraints (if any) as hard clauses
@@ -166,10 +179,27 @@ class LBX(object):
166
179
  self.topv += 1
167
180
  sel = self.topv
168
181
 
169
- self.oracle.add_clause(cl + [-sel])
182
+ if process == 0:
183
+ self.oracle.add_clause(cl + [-sel])
184
+ else:
185
+ # adding to formula's hard clauses
186
+ # if any preprocessing is required
187
+ hard.append(cl + [-sel])
170
188
 
171
189
  self.sels.append(sel)
172
190
 
191
+ # finally, applying formula processing, if any
192
+ if process:
193
+ # the processor is immediately destroyed,
194
+ # as we do not need to restore the models
195
+ with Processor(bootstrap_with=hard) as processor:
196
+ proc = processor.process(rounds=process, freeze=self.sels)
197
+ self.oracle.append_formula(proc)
198
+
199
+ # we won't have access to the original soft
200
+ # clauses in the case of formula preprocessing
201
+ self.soft = [[l] for l in self.sels]
202
+
173
203
  def __del__(self):
174
204
  """
175
205
  Destructor.
@@ -514,10 +544,11 @@ def parse_options():
514
544
 
515
545
  try:
516
546
  opts, args = getopt.getopt(sys.argv[1:],
517
- 'de:hs:v',
547
+ 'de:hp:p:s:v',
518
548
  ['dcalls',
519
549
  'enum=',
520
550
  'help',
551
+ 'process=',
521
552
  'solver=',
522
553
  'verbose'])
523
554
  except getopt.GetoptError as err:
@@ -527,6 +558,7 @@ def parse_options():
527
558
 
528
559
  dcalls = False
529
560
  to_enum = 1
561
+ process = 0
530
562
  solver = 'm22'
531
563
  verbose = 0
532
564
 
@@ -540,6 +572,8 @@ def parse_options():
540
572
  elif opt in ('-h', '--help'):
541
573
  usage()
542
574
  sys.exit(0)
575
+ elif opt in ('-p', '--process'):
576
+ process = int(arg)
543
577
  elif opt in ('-s', '--solver'):
544
578
  solver = str(arg)
545
579
  elif opt in ('-v', '--verbose'):
@@ -547,7 +581,7 @@ def parse_options():
547
581
  else:
548
582
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
549
583
 
550
- return dcalls, to_enum, solver, verbose, args
584
+ return dcalls, to_enum, solver, process, verbose, args
551
585
 
552
586
 
553
587
  #
@@ -563,6 +597,8 @@ def usage():
563
597
  print(' -e, --enum=<string> How many solutions to compute')
564
598
  print(' Available values: [1 .. all] (default: 1)')
565
599
  print(' -h, --help')
600
+ print(' -p, --process=<int> Number of processing rounds')
601
+ print(' Available values: [0 .. INT_MAX] (default = 0)')
566
602
  print(' -s, --solver SAT solver to use')
567
603
  print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
568
604
  print(' -v, --verbose Be verbose')
@@ -571,7 +607,7 @@ def usage():
571
607
  #
572
608
  #==============================================================================
573
609
  if __name__ == '__main__':
574
- dcalls, to_enum, solver, verbose, files = parse_options()
610
+ dcalls, to_enum, solver, process, verbose, files = parse_options()
575
611
 
576
612
  if type(to_enum) == str:
577
613
  to_enum = 0
@@ -584,7 +620,8 @@ if __name__ == '__main__':
584
620
  else: # expecting '*.cnf[,p,+].*'
585
621
  formula = CNFPlus(from_file=files[0]).weighted()
586
622
 
587
- with LBX(formula, use_cld=dcalls, solver_name=solver, use_timer=True) as mcsls:
623
+ with LBX(formula, use_cld=dcalls, solver_name=solver, process=process,
624
+ use_timer=True) as mcsls:
588
625
  for i, mcs in enumerate(mcsls.enumerate()):
589
626
  if verbose:
590
627
  print('c MCS:', ' '.join([str(cl_id) for cl_id in mcs]), '0')
@@ -90,6 +90,7 @@ import getopt
90
90
  from math import copysign
91
91
  import os
92
92
  from pysat.formula import CNFPlus, WCNFPlus
93
+ from pysat.process import Processor
93
94
  from pysat.solvers import Solver, SolverNames
94
95
  import re
95
96
  import sys
@@ -112,25 +113,37 @@ class MCSls(object):
112
113
  ``use_cld`` is set to ``False``. Internal SAT solver's timer is also
113
114
  disabled by default, i.e. ``use_timer`` is ``False``.
114
115
 
116
+ Additionally, the input formula can be preprocessed before running MCS
117
+ enumeration. This is controlled by the input parameter ``process``
118
+ whose integer value signifies the number of processing rounds to be
119
+ applied. The number of rounds is set to 0 by default.
120
+
115
121
  :param formula: unsatisfiable partial CNF formula
116
122
  :param use_cld: whether or not to use "clause :math:`D`"
117
123
  :param solver_name: SAT oracle name
124
+ :param process: apply formula preprocessing this many times
118
125
  :param use_timer: whether or not to use SAT solver's timer
119
126
 
120
127
  :type formula: :class:`.WCNF`
121
128
  :type use_cld: bool
122
129
  :type solver_name: str
130
+ :type process: int
123
131
  :type use_timer: bool
124
132
  """
125
133
 
126
- def __init__(self, formula, use_cld=False, solver_name='m22', use_timer=False):
134
+ def __init__(self, formula, use_cld=False, solver_name='m22', process=0,
135
+ use_timer=False):
127
136
  """
128
137
  Constructor.
129
138
  """
130
139
 
140
+ # dealing with preprocessing, if required
141
+ hard = formula.hard if process > 0 else []
142
+
131
143
  # bootstrapping the solver with hard clauses
132
- self.oracle = Solver(name=solver_name, bootstrap_with=formula.hard,
133
- use_timer=use_timer)
144
+ self.oracle = Solver(name=solver_name,
145
+ bootstrap_with=formula.hard if process == 0 else [],
146
+ use_timer=use_timer)
134
147
  self.solver = solver_name
135
148
 
136
149
  # adding native cardinality constraints (if any) as hard clauses
@@ -167,13 +180,27 @@ class MCSls(object):
167
180
  sel = self.topv
168
181
 
169
182
  new_cl.append(-sel) # creating a new selector
170
- self.oracle.add_clause(new_cl)
183
+
184
+ if process == 0:
185
+ self.oracle.add_clause(new_cl)
186
+ else:
187
+ # adding to formula's hard clauses
188
+ # if any preprocessing is required
189
+ hard.append(new_cl)
171
190
  else:
172
191
  sel = cl[0]
173
192
 
174
193
  self.sels.append(sel)
175
194
  self.smap[sel] = len(self.sels)
176
195
 
196
+ # finally, applying formula processing, if any
197
+ if process:
198
+ # the processor is immediately destroyed,
199
+ # as we do not need to restore the models
200
+ with Processor(bootstrap_with=hard) as processor:
201
+ proc = processor.process(rounds=process, freeze=self.sels)
202
+ self.oracle.append_formula(proc)
203
+
177
204
  def __del__(self):
178
205
  """
179
206
  Destructor.
@@ -489,10 +516,11 @@ def parse_options():
489
516
 
490
517
  try:
491
518
  opts, args = getopt.getopt(sys.argv[1:],
492
- 'de:hs:v',
519
+ 'de:hp:s:v',
493
520
  ['dcalls',
494
521
  'enum=',
495
522
  'help',
523
+ 'process=',
496
524
  'solver=',
497
525
  'verbose'])
498
526
  except getopt.GetoptError as err:
@@ -502,6 +530,7 @@ def parse_options():
502
530
 
503
531
  dcalls = False
504
532
  to_enum = 1
533
+ process = 0
505
534
  solver = 'm22'
506
535
  verbose = 0
507
536
 
@@ -515,6 +544,8 @@ def parse_options():
515
544
  elif opt in ('-h', '--help'):
516
545
  usage()
517
546
  sys.exit(0)
547
+ elif opt in ('-p', '--process'):
548
+ process = int(arg)
518
549
  elif opt in ('-s', '--solver'):
519
550
  solver = str(arg)
520
551
  elif opt in ('-v', '--verbose'):
@@ -522,7 +553,7 @@ def parse_options():
522
553
  else:
523
554
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
524
555
 
525
- return dcalls, to_enum, solver, verbose, args
556
+ return dcalls, to_enum, solver, process, verbose, args
526
557
 
527
558
 
528
559
  #
@@ -538,6 +569,8 @@ def usage():
538
569
  print(' -e, --enum=<string> How many solutions to compute')
539
570
  print(' Available values: [1 .. all] (default: 1)')
540
571
  print(' -h, --help')
572
+ print(' -p, --process=<int> Number of processing rounds')
573
+ print(' Available values: [0 .. INT_MAX] (default = 0)')
541
574
  print(' -s, --solver SAT solver to use')
542
575
  print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
543
576
  print(' -v, --verbose Be verbose')
@@ -546,7 +579,7 @@ def usage():
546
579
  #
547
580
  #==============================================================================
548
581
  if __name__ == '__main__':
549
- dcalls, to_enum, solver, verbose, files = parse_options()
582
+ dcalls, to_enum, solver, process, verbose, files = parse_options()
550
583
 
551
584
  if type(to_enum) == str:
552
585
  to_enum = 0
@@ -559,7 +592,8 @@ if __name__ == '__main__':
559
592
  else: # expecting '*.cnf[,p,+].*'
560
593
  formula = CNFPlus(from_file=files[0]).weighted()
561
594
 
562
- with MCSls(formula, use_cld=dcalls, solver_name=solver, use_timer=True) as mcsls:
595
+ with MCSls(formula, use_cld=dcalls, solver_name=solver,
596
+ process=process, use_timer=True) as mcsls:
563
597
  for i, mcs in enumerate(mcsls.enumerate()):
564
598
  if verbose:
565
599
  print('c MCS:', ' '.join([str(cl_id) for cl_id in mcs]), '0')
@@ -111,6 +111,7 @@ import os
111
111
  from pysat.examples.hitman import Atom, Hitman
112
112
  from pysat.examples.rc2 import RC2
113
113
  from pysat.formula import CNFPlus, WCNFPlus
114
+ from pysat.process import Processor
114
115
  from pysat.solvers import Solver, SolverNames
115
116
  import re
116
117
  import sys
@@ -168,6 +169,11 @@ class OptUx(object):
168
169
  support *hard* phase setting, i.e. user preferences will not be
169
170
  overwritten by the *phase saving* heuristic [8]_.
170
171
 
172
+ Additionally, the input formula can be preprocessed before running MUS
173
+ enumeration. This is controlled by the input parameter ``process``
174
+ whose integer value signifies the number of processing rounds to be
175
+ applied. The number of rounds is set to 0 by default.
176
+
171
177
  Finally, one more optional input parameter ``cover`` is to be used
172
178
  when exhaustive enumeration of MUSes is not necessary and the tool can
173
179
  stop as soon as a given formula is covered by the set of currently
@@ -198,6 +204,7 @@ class OptUx(object):
198
204
  :param exhaust: do core exhaustion
199
205
  :param minz: do heuristic core reduction
200
206
  :param nodisj: do not enumerate disjoint MCSes
207
+ :param process: apply formula preprocessing this many times
201
208
  :param puresat: use pure SAT-based hitting set enumeration
202
209
  :param unsorted: apply unsorted MUS enumeration
203
210
  :param trim: do core trimming at most this number of times
@@ -211,6 +218,7 @@ class OptUx(object):
211
218
  :type exhaust: bool
212
219
  :type minz: bool
213
220
  :type nodisj: bool
221
+ :type process: int
214
222
  :type puresat: str
215
223
  :type unsorted: bool
216
224
  :type trim: int
@@ -218,7 +226,7 @@ class OptUx(object):
218
226
  """
219
227
 
220
228
  def __init__(self, formula, solver='g3', adapt=False, cover=None,
221
- dcalls=False, exhaust=False, minz=False, nodisj=False,
229
+ dcalls=False, exhaust=False, minz=False, nodisj=False, process=0,
222
230
  puresat=False, unsorted=False, trim=False, verbose=0):
223
231
  """
224
232
  Constructor.
@@ -247,6 +255,15 @@ class OptUx(object):
247
255
  self._process_soft(formula)
248
256
  self.formula.nv = self.topv
249
257
 
258
+ # applying formula processing (if any)
259
+ if process:
260
+ # the processor is immediately destroyed,
261
+ # as we do not need to restore the models
262
+ with Processor(bootstrap_with=self.formula.hard) as processor:
263
+ proc = processor.process(rounds=process, freeze=self.sels)
264
+ self.formula.hard = proc.clauses
265
+ self.formula.nv = max(self.formula.nv, proc.nv)
266
+
250
267
  # creating an unweighted copy
251
268
  unweighted = self.formula.copy()
252
269
  unweighted.wght = [1 for w in unweighted.wght]
@@ -554,11 +571,11 @@ def parse_options():
554
571
  """
555
572
 
556
573
  try:
557
- opts, args = getopt.getopt(sys.argv[1:], 'ac:de:hmnp:s:t:uvx',
574
+ opts, args = getopt.getopt(sys.argv[1:], 'ac:de:hmnp:P:s:t:uvx',
558
575
  ['adapt', 'cover=', 'dcalls', 'enum=',
559
576
  'exhaust', 'help', 'minimize', 'no-disj',
560
- 'solver=', 'puresat=', 'unsorted',
561
- 'trim=', 'verbose'])
577
+ 'solver=', 'puresat=', 'process=',
578
+ 'unsorted', 'trim=', 'verbose'])
562
579
  except getopt.GetoptError as err:
563
580
  sys.stderr.write(str(err).capitalize() + '\n')
564
581
  usage()
@@ -572,6 +589,7 @@ def parse_options():
572
589
  no_disj = False
573
590
  to_enum = 1
574
591
  solver = 'g3'
592
+ process = 0
575
593
  puresat = False
576
594
  unsorted = False
577
595
  trim = 0
@@ -599,6 +617,8 @@ def parse_options():
599
617
  no_disj = True
600
618
  elif opt in ('-p', '--puresat'):
601
619
  puresat = str(arg)
620
+ elif opt in ('-P', '--process'):
621
+ process = int(arg)
602
622
  elif opt in ('-s', '--solver'):
603
623
  solver = str(arg)
604
624
  elif opt in ('-u', '--unsorted'):
@@ -613,7 +633,7 @@ def parse_options():
613
633
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
614
634
 
615
635
  return adapt, cover, dcalls, exhaust, minz, no_disj, trim, to_enum, \
616
- solver, puresat, unsorted, verbose, args
636
+ solver, process, puresat, unsorted, verbose, args
617
637
 
618
638
 
619
639
  #
@@ -637,6 +657,8 @@ def usage():
637
657
  print(' -p, --puresat=<string> Use a pure SAT-based hitting set enumerator')
638
658
  print(' Available values: cd15, cd19, lgl, mgh (default = mgh)')
639
659
  print(' Requires: unsorted mode, i.e. \'-u\'')
660
+ print(' -P, --process=<int> Number of processing rounds')
661
+ print(' Available values: [0 .. INT_MAX] (default = 0)')
640
662
  print(' -s, --solver SAT solver to use')
641
663
  print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = g3)')
642
664
  print(' -t, --trim=<int> How many times to trim unsatisfiable cores')
@@ -650,7 +672,7 @@ def usage():
650
672
  #==============================================================================
651
673
  if __name__ == '__main__':
652
674
  adapt, cover, dcalls, exhaust, minz, no_disj, trim, to_enum, solver, \
653
- puresat, unsorted, verbose, files = parse_options()
675
+ process, puresat, unsorted, verbose, files = parse_options()
654
676
 
655
677
  if files:
656
678
  # reading standard CNF, WCNF, or (W)CNF+
@@ -667,8 +689,8 @@ if __name__ == '__main__':
667
689
  # creating an object of OptUx
668
690
  with OptUx(formula, solver=solver, adapt=adapt, cover=cover,
669
691
  dcalls=dcalls, exhaust=exhaust, minz=minz, nodisj=no_disj,
670
- puresat=puresat, unsorted=unsorted, trim=trim,
671
- verbose=verbose) as optux:
692
+ process=process, puresat=puresat, unsorted=unsorted,
693
+ trim=trim, verbose=verbose) as optux:
672
694
 
673
695
  # iterating over the necessary number of optimal MUSes
674
696
  for i, mus in enumerate(optux.enumerate()):