python-sat 0.1.8.dev10__cp310-cp310-win_amd64.whl → 1.8.dev26__cp310-cp310-win_amd64.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 (45) hide show
  1. pycard.cp310-win_amd64.pyd +0 -0
  2. pysat/__init__.py +4 -4
  3. pysat/_fileio.py +30 -14
  4. pysat/allies/approxmc.py +22 -22
  5. pysat/allies/unigen.py +435 -0
  6. pysat/card.py +13 -12
  7. pysat/engines.py +1302 -0
  8. pysat/examples/bbscan.py +663 -0
  9. pysat/examples/bica.py +691 -0
  10. pysat/examples/fm.py +12 -8
  11. pysat/examples/genhard.py +24 -23
  12. pysat/examples/hitman.py +53 -37
  13. pysat/examples/lbx.py +56 -15
  14. pysat/examples/lsu.py +28 -14
  15. pysat/examples/mcsls.py +53 -15
  16. pysat/examples/models.py +6 -4
  17. pysat/examples/musx.py +15 -7
  18. pysat/examples/optux.py +71 -32
  19. pysat/examples/primer.py +620 -0
  20. pysat/examples/rc2.py +268 -69
  21. pysat/formula.py +3241 -229
  22. pysat/pb.py +85 -37
  23. pysat/process.py +16 -2
  24. pysat/solvers.py +2119 -724
  25. pysolvers.cp310-win_amd64.pyd +0 -0
  26. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/approxmc.py +22 -22
  27. python_sat-1.8.dev26.data/scripts/bbscan.py +663 -0
  28. python_sat-1.8.dev26.data/scripts/bica.py +691 -0
  29. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/fm.py +12 -8
  30. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/genhard.py +24 -23
  31. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/lbx.py +56 -15
  32. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/lsu.py +28 -14
  33. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/mcsls.py +53 -15
  34. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/models.py +6 -4
  35. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/musx.py +15 -7
  36. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/optux.py +71 -32
  37. python_sat-1.8.dev26.data/scripts/primer.py +620 -0
  38. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/rc2.py +268 -69
  39. python_sat-1.8.dev26.data/scripts/unigen.py +435 -0
  40. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/METADATA +19 -5
  41. python_sat-1.8.dev26.dist-info/RECORD +48 -0
  42. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/WHEEL +1 -1
  43. python_sat-0.1.8.dev10.dist-info/RECORD +0 -39
  44. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info/licenses}/LICENSE.txt +0 -0
  45. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/top_level.txt +0 -0
@@ -209,6 +209,10 @@ class FM(object):
209
209
  self.oracle = Solver(name=self.solver, bootstrap_with=self.hard, use_timer=True)
210
210
 
211
211
  if self.atm1: # this check is needed at the beggining (before iteration 1)
212
+ # we are using CaDiCaL195 and it can use external linear engine
213
+ if self.solver in SolverNames.cadical195:
214
+ self.oracle.activate_atmost()
215
+
212
216
  assert self.oracle.supports_atmost(), \
213
217
  '{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(solver_name)
214
218
 
@@ -288,8 +292,7 @@ class FM(object):
288
292
 
289
293
  while True:
290
294
  if self.oracle.solve(assumptions=self.sels):
291
- self.model = self.oracle.get_model()
292
- self.model = list(filter(lambda l: abs(l) <= self.orig_nv, self.model))
295
+ self.model = [l for l in self.oracle.get_model() if abs(l) <= self.orig_nv]
293
296
  return
294
297
  else:
295
298
  self.treat_core()
@@ -382,12 +385,12 @@ class FM(object):
382
385
 
383
386
  Otherwise (if the core contains more than one clause), every clause
384
387
  :math:`c` of the core is *relaxed*. This means a new *relaxation
385
- literal* is added to the clause, i.e. :math:`c\gets c\\vee r`,
388
+ literal* is added to the clause, i.e. :math:`c\\gets c\\vee r`,
386
389
  where :math:`r` is a fresh (unused) relaxation variable. After the
387
390
  clauses get relaxed, a new cardinality encoding is added to the
388
391
  formula enforcing the sum of the new relaxation variables to be not
389
- greater than 1, :math:`\sum_{c\in\phi}{r\leq 1}`, where
390
- :math:`\phi` denotes the unsatisfiable core.
392
+ greater than 1, :math:`\\sum_{c\\in\\phi}{r\\leq 1}`, where
393
+ :math:`\\phi` denotes the unsatisfiable core.
391
394
  """
392
395
 
393
396
  if len(self.core) > 1:
@@ -471,7 +474,8 @@ def parse_options():
471
474
  cardenc = encmap[cardenc]
472
475
 
473
476
  # using minicard's native implementation of AtMost1 constraints
474
- if solver in SolverNames.minicard or solver in SolverNames.gluecard3 or solver in SolverNames.gluecard4:
477
+ if solver in SolverNames.minicard + SolverNames.gluecard3 + \
478
+ SolverNames.gluecard4 + SolverNames.cadical195:
475
479
  cardenc = encmap['native']
476
480
  else:
477
481
  assert cardenc != encmap['native'], 'Only Minicard can handle cardinality constraints natively'
@@ -491,7 +495,7 @@ def usage():
491
495
  print(' Available values: bw, cardn, kmtot, ladder, mtot, pw, seqc, sortn, tot (default = seqc)')
492
496
  print(' -h, --help')
493
497
  print(' -s, --solver SAT solver to use')
494
- print(' Available values: g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
498
+ print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
495
499
  print(' -v, --verbose Be verbose')
496
500
 
497
501
 
@@ -502,7 +506,7 @@ if __name__ == '__main__':
502
506
 
503
507
  if files:
504
508
  # parsing the input formula
505
- if re.search('\.wcnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
509
+ if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
506
510
  formula = WCNFPlus(from_file=files[0])
507
511
  else: # expecting '*.cnf[,p,+].*'
508
512
  formula = CNFPlus(from_file=files[0]).weighted()
@@ -129,7 +129,7 @@ class PHP(CNF, object):
129
129
  """
130
130
  Generator of :math:`k` pigeonhole principle (:math:`k`-PHP) formulas.
131
131
  Given integer parameters :math:`m` and :math:`k`, the :math:`k`
132
- pigeonhole principle states that if :math:`k\cdot m+1` pigeons are
132
+ pigeonhole principle states that if :math:`k\\cdot m+1` pigeons are
133
133
  distributes by :math:`m` holes, then at least one hole contains more
134
134
  than :math:`k` pigeons.
135
135
 
@@ -139,9 +139,9 @@ class PHP(CNF, object):
139
139
 
140
140
  Assume that a Boolean variable :math:`x_{ij}` encodes that pigeon
141
141
  :math:`i` resides in hole :math:`j`. Then a PHP formula can be seen as
142
- a conjunction: :math:`\\bigwedge_{i=1}^{k\cdot
143
- m+1}{\\textsf{AtLeast1}(x_{i1},\ldots,x_{im})}\wedge
144
- \\bigwedge_{j=1}^{m}{\\textsf{AtMost}k(x_{1j},\ldots,x_{k\cdot
142
+ a conjunction: :math:`\\bigwedge_{i=1}^{k\\cdot
143
+ m+1}{\\textsf{AtLeast1}(x_{i1},\\ldots,x_{im})}\\wedge
144
+ \\bigwedge_{j=1}^{m}{\\textsf{AtMost}k(x_{1j},\\ldots,x_{k\\cdot
145
145
  m+1,j})}`. Here each :math:`\\textsf{AtLeast1}` constraint forces every
146
146
  pigeon to be placed into at least one hole while each
147
147
  :math:`\\textsf{AtMost}k` constraint allows the corresponding hole to
@@ -204,16 +204,17 @@ class GT(CNF, object):
204
204
  """
205
205
  Generator of ordering (or *greater than*, GT) principle formulas. Given
206
206
  an integer parameter :math:`n`, the principle states that any partial
207
- order on the set :math:`\{1,2,\ldots,n\}` must have a maximal element.
208
-
209
- Assume variable :math:`x_{ij}`, for :math:`i,j\in[n],i\\neq j`, denotes
210
- the fact that :math:`i \succ j`. Clauses :math:`(\\neg{x_{ij}} \\vee
211
- \\neg{x_{ji}})` and :math:`(\\neg{x_{ij}} \\vee \\neg{x_{jk}} \\vee
212
- x_{ik})` ensure that the relation :math:`\succ` is anti-symmetric and
213
- transitive. As a result, :math:`\succ` is a partial order on
214
- :math:`[n]`. The additional requirement that each element :math:`i` has
215
- a successor in :math:`[n]\setminus\{i\}` represented a clause
216
- :math:`(\\vee_{j \\neq i}{x_{ji}})` makes the formula unsatisfiable.
207
+ order on the set :math:`\\{1,2,\\ldots,n\\}` must have a maximal element.
208
+
209
+ Assume variable :math:`x_{ij}`, for :math:`i,j\\in[n],i\\neq j`,
210
+ denotes the fact that :math:`i \\succ j`. Clauses
211
+ :math:`(\\neg{x_{ij}} \\vee \\neg{x_{ji}})` and :math:`(\\neg{x_{ij}}
212
+ \\vee \\neg{x_{jk}} \\vee x_{ik})` ensure that the relation
213
+ :math:`\\succ` is anti-symmetric and transitive. As a result,
214
+ :math:`\\succ` is a partial order on :math:`[n]`. The additional
215
+ requirement that each element :math:`i` has a successor in
216
+ :math:`[n]\\setminus\\{i\\}` represented a clause :math:`(\\vee_{j
217
+ \\neq i}{x_{ji}})` makes the formula unsatisfiable.
217
218
 
218
219
  GT formulas were originally conjectured [2]_ to be hard for resolution.
219
220
  However, [5]_ proved the existence of a polynomial size resolution
@@ -276,18 +277,18 @@ class CB(CNF, object):
276
277
  """
277
278
  Mutilated chessboard principle (CB). Given an integer :math:`n`, the
278
279
  principle states that it is impossible to cover a chessboard of size
279
- :math:`2n\cdot 2n` by domino tiles if two diagonally opposite corners
280
+ :math:`2n\\cdot 2n` by domino tiles if two diagonally opposite corners
280
281
  of the chessboard are removed.
281
282
 
282
283
  Note that the chessboard has :math:`4n^2-2` cells. Introduce a Boolean
283
- variable :math:`x_{ij}` for :math:`i,j\in[4n^2-2]` s.t. cells :math:`i`
284
- and :math:`j` are adjacent (no variables are introduced for pairs of
285
- non-adjacent cells). CB formulas comprise clauses (1)
284
+ variable :math:`x_{ij}` for :math:`i,j\\in[4n^2-2]` s.t. cells
285
+ :math:`i` and :math:`j` are adjacent (no variables are introduced for
286
+ pairs of non-adjacent cells). CB formulas comprise clauses (1)
286
287
  :math:`(\\neg{x_{ji} \\vee \\neg{x_{ki}}})` for every :math:`i,j \\neq
287
288
  k` meaning that no more than one adjacent cell can be paired with the
288
- current one; and (2) :math:`(\\vee_{j \in \\text{Adj}(i)} {x_{ij}})\,\,
289
- \\forall i` enforcing that every cell :math:`i` should be paired with
290
- at least one adjacent cell.
289
+ current one; and (2) :math:`(\\vee_{j \\in \\text{Adj}(i)}
290
+ {x_{ij}})\\,\\, \\forall i` enforcing that every cell :math:`i` should
291
+ be paired with at least one adjacent cell.
291
292
 
292
293
  Clearly, since the two diagonal corners are removed, the formula is
293
294
  unsatisfiable. Also note the following. Assuming that the number of
@@ -389,8 +390,8 @@ class PAR(CNF, object):
389
390
  variables :math:`x_{ij},i \\neq j`. If variable :math:`x_{ij}` is
390
391
  *true*, then there is an edge between nodes :math:`i` and :math:`j`.
391
392
  The formula consists of the following clauses: :math:`(\\vee_{j \\neq
392
- i}{x_{ij}})` for every :math:`i\in[2n+1]`, and :math:`(\\neg{x_{ij}}
393
- \\vee \\neg{x_{kj}})` for all distinct :math:`i,j,k \in [2n+1]`.
393
+ i}{x_{ij}})` for every :math:`i\\in[2n+1]`, and :math:`(\\neg{x_{ij}}
394
+ \\vee \\neg{x_{kj}})` for all distinct :math:`i,j,k \\in [2n+1]`.
394
395
 
395
396
  The parity principle is known to be hard for resolution [4]_.
396
397
 
@@ -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,30 +113,46 @@ 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
137
150
  # this can be done only if the Minicard solver is in use
138
151
  if isinstance(formula, WCNFPlus) and formula.atms:
152
+ # we are using CaDiCaL195 and it can use external linear engine
153
+ if solver_name in SolverNames.cadical195:
154
+ self.oracle.activate_atmost()
155
+
139
156
  assert self.oracle.supports_atmost(), \
140
157
  '{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(solver_name)
141
158
 
@@ -162,10 +179,27 @@ class LBX(object):
162
179
  self.topv += 1
163
180
  sel = self.topv
164
181
 
165
- 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])
166
188
 
167
189
  self.sels.append(sel)
168
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
+
169
203
  def __del__(self):
170
204
  """
171
205
  Destructor.
@@ -510,10 +544,11 @@ def parse_options():
510
544
 
511
545
  try:
512
546
  opts, args = getopt.getopt(sys.argv[1:],
513
- 'de:hs:v',
547
+ 'de:hp:p:s:v',
514
548
  ['dcalls',
515
549
  'enum=',
516
550
  'help',
551
+ 'process=',
517
552
  'solver=',
518
553
  'verbose'])
519
554
  except getopt.GetoptError as err:
@@ -523,6 +558,7 @@ def parse_options():
523
558
 
524
559
  dcalls = False
525
560
  to_enum = 1
561
+ process = 0
526
562
  solver = 'm22'
527
563
  verbose = 0
528
564
 
@@ -536,6 +572,8 @@ def parse_options():
536
572
  elif opt in ('-h', '--help'):
537
573
  usage()
538
574
  sys.exit(0)
575
+ elif opt in ('-p', '--process'):
576
+ process = int(arg)
539
577
  elif opt in ('-s', '--solver'):
540
578
  solver = str(arg)
541
579
  elif opt in ('-v', '--verbose'):
@@ -543,7 +581,7 @@ def parse_options():
543
581
  else:
544
582
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
545
583
 
546
- return dcalls, to_enum, solver, verbose, args
584
+ return dcalls, to_enum, solver, process, verbose, args
547
585
 
548
586
 
549
587
  #
@@ -559,28 +597,31 @@ def usage():
559
597
  print(' -e, --enum=<string> How many solutions to compute')
560
598
  print(' Available values: [1 .. all] (default: 1)')
561
599
  print(' -h, --help')
600
+ print(' -p, --process=<int> Number of processing rounds')
601
+ print(' Available values: [0 .. INT_MAX] (default = 0)')
562
602
  print(' -s, --solver SAT solver to use')
563
- print(' Available values: g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
603
+ print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
564
604
  print(' -v, --verbose Be verbose')
565
605
 
566
606
 
567
607
  #
568
608
  #==============================================================================
569
609
  if __name__ == '__main__':
570
- dcalls, to_enum, solver, verbose, files = parse_options()
610
+ dcalls, to_enum, solver, process, verbose, files = parse_options()
571
611
 
572
612
  if type(to_enum) == str:
573
613
  to_enum = 0
574
614
 
575
615
  if files:
576
616
  # reading standard CNF, WCNF, or (W)CNF+
577
- if re.search('cnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
578
- if re.search('\.wcnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
579
- formula = WCNFPlus(from_file=files[0])
580
- else: # expecting '*.cnf[,p,+].*'
581
- formula = CNFPlus(from_file=files[0]).weighted()
582
-
583
- with LBX(formula, use_cld=dcalls, solver_name=solver, use_timer=True) as mcsls:
617
+ assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
618
+ if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
619
+ formula = WCNFPlus(from_file=files[0])
620
+ else: # expecting '*.cnf[,p,+].*'
621
+ formula = CNFPlus(from_file=files[0]).weighted()
622
+
623
+ with LBX(formula, use_cld=dcalls, solver_name=solver, process=process,
624
+ use_timer=True) as mcsls:
584
625
  for i, mcs in enumerate(mcsls.enumerate()):
585
626
  if verbose:
586
627
  print('c MCS:', ' '.join([str(cl_id) for cl_id in mcs]), '0')
@@ -118,22 +118,25 @@ class LSU:
118
118
 
119
119
  :param formula: input MaxSAT formula
120
120
  :param solver: name of SAT solver
121
+ :param incr: enable incremental mode of Glucose
121
122
  :param expect_interrupt: whether or not an :meth:`interrupt` call is expected
122
123
  :param verbose: verbosity level
123
124
 
124
125
  :type formula: :class:`.WCNF`
125
126
  :type solver: str
127
+ :type incr: bool
126
128
  :type expect_interrupt: bool
127
129
  :type verbose: int
128
130
  """
129
131
 
130
- def __init__(self, formula, solver='g4', expect_interrupt=False, verbose=0):
132
+ def __init__(self, formula, solver='g4', incr=False, expect_interrupt=False, verbose=0):
131
133
  """
132
134
  Constructor.
133
135
  """
134
136
 
135
137
  self.verbose = verbose
136
138
  self.solver = solver
139
+ self.incr = incr
137
140
  self.expect_interrupt = expect_interrupt
138
141
  self.formula = formula
139
142
  self.topv = formula.nv # largest variable index
@@ -154,7 +157,7 @@ class LSU:
154
157
  """
155
158
 
156
159
  self.oracle = Solver(name=self.solver, bootstrap_with=formula.hard,
157
- incr=True, use_timer=True)
160
+ incr=self.incr, use_timer=True)
158
161
 
159
162
  for i, cl in enumerate(formula.soft):
160
163
  # TODO: if clause is unit, use its literal as selector
@@ -353,18 +356,23 @@ class LSUPlus(LSU, object):
353
356
  :type verbose: int
354
357
  """
355
358
 
356
- def __init__(self, formula, solver, expect_interrupt=False, verbose=0):
359
+ def __init__(self, formula, solver='g4', incr=False, expect_interrupt=False, verbose=0):
357
360
  """
358
361
  Constructor.
359
362
  """
360
363
 
361
364
  assert solver in SolverNames.gluecard3 or \
362
365
  solver in SolverNames.gluecard4 or \
363
- solver in SolverNames.minicard, '{0} does not support native cardinality constraints'.format(solver)
366
+ solver in SolverNames.minicard or \
367
+ solver in SolverNames.cadical195, '{0} does not support native cardinality constraints'.format(solver)
364
368
 
365
- super(LSUPlus, self).__init__(formula, solver=solver,
369
+ super(LSUPlus, self).__init__(formula, solver=solver, incr=incr,
366
370
  expect_interrupt=expect_interrupt, verbose=verbose)
367
371
 
372
+ # we are using CaDiCaL195 and it can use external linear engine
373
+ if solver in SolverNames.cadical195:
374
+ self.oracle.activate_atmost()
375
+
368
376
  # adding atmost constraints
369
377
  for am in formula.atms:
370
378
  self.oracle.add_atmost(*am)
@@ -391,13 +399,14 @@ def parse_options():
391
399
  """
392
400
 
393
401
  try:
394
- opts, args = getopt.getopt(sys.argv[1:], 'hms:t:v', ['help', 'model', 'solver=', 'timeout=', 'verbose'])
402
+ opts, args = getopt.getopt(sys.argv[1:], 'hims:t:v', ['help', 'incr', 'model', 'solver=', 'timeout=', 'verbose'])
395
403
  except getopt.GetoptError as err:
396
404
  sys.stderr.write(str(err).capitalize())
397
405
  print_usage()
398
406
  sys.exit(1)
399
407
 
400
408
  solver = 'g4'
409
+ incr = False
401
410
  verbose = 1
402
411
  print_model = False
403
412
  timeout = None
@@ -406,6 +415,8 @@ def parse_options():
406
415
  if opt in ('-h', '--help'):
407
416
  print_usage()
408
417
  sys.exit(0)
418
+ elif opt in ('-i', '--incr'):
419
+ incr = True
409
420
  elif opt in ('-m', '--model'):
410
421
  print_model = True
411
422
  elif opt in ('-s', '--solver'):
@@ -418,7 +429,7 @@ def parse_options():
418
429
  else:
419
430
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
420
431
 
421
- return print_model, solver, timeout, verbose, args
432
+ return incr, print_model, solver, timeout, verbose, args
422
433
 
423
434
 
424
435
  #
@@ -431,9 +442,10 @@ def print_usage():
431
442
  print('Usage: ' + os.path.basename(sys.argv[0]) + ' [options] dimacs-file')
432
443
  print('Options:')
433
444
  print(' -h, --help Show this message')
445
+ print(' -i, --incr Enable incremental model (for Glucose only)')
434
446
  print(' -m, --model Print model')
435
447
  print(' -s, --solver=<string> SAT solver to use')
436
- print(' Available values: g3, g4, mc, m22, mgh (default = g4)')
448
+ print(' Available values: cd15, cd19, g3, g4, mc, m22, mgh (default = g4)')
437
449
  print(' -t, --timeout=<float> Set time limit for MaxSAT solver')
438
450
  print(' Available values: [0 .. FLOAT_MAX], none (default: none)')
439
451
  print(' -v, --verbose Be verbose')
@@ -442,24 +454,26 @@ def print_usage():
442
454
  #
443
455
  #==============================================================================
444
456
  if __name__ == '__main__':
445
- print_model, solver, timeout, verbose, files = parse_options()
457
+ incr, print_model, solver, timeout, verbose, files = parse_options()
446
458
 
447
459
  if files:
448
460
  # reading standard CNF or WCNF
449
- if re.search('cnf(\.(gz|bz2|lzma|xz))?$', files[0]):
450
- if re.search('\.wcnf(\.(gz|bz2|lzma|xz))?$', files[0]):
461
+ if re.search(r'cnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
462
+ if re.search(r'\.wcnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
451
463
  formula = WCNF(from_file=files[0])
452
464
  else: # expecting '*.cnf'
453
465
  formula = CNF(from_file=files[0]).weighted()
454
466
 
455
- lsu = LSU(formula, solver=solver,
467
+ lsu = LSU(formula, solver=solver, incr=incr,
456
468
  expect_interrupt=(timeout != None), verbose=verbose)
457
469
 
458
470
  # reading WCNF+
459
- elif re.search('\.wcnf[p,+](\.(gz|bz2|lzma|xz))?$', files[0]):
471
+ elif re.search(r'\.wcnf[p,+](\.(gz|bz2|lzma|xz|zst))?$', files[0]):
460
472
  formula = WCNFPlus(from_file=files[0])
461
- lsu = LSUPlus(formula, solver=solver,
473
+ lsu = LSUPlus(formula, solver=solver, incr=incr,
462
474
  expect_interrupt=(timeout != None), verbose=verbose)
475
+ else:
476
+ assert False, 'Unknown input file extension'
463
477
 
464
478
  # setting a timer if necessary
465
479
  if timeout is not None:
@@ -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,30 +113,46 @@ 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
137
150
  # this can be done only if the Minicard solver is in use
138
151
  if isinstance(formula, WCNFPlus) and formula.atms:
152
+ # we are using CaDiCaL195 and it can use external linear engine
153
+ if solver_name in SolverNames.cadical195:
154
+ self.oracle.activate_atmost()
155
+
139
156
  assert self.oracle.supports_atmost(), \
140
157
  '{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(solver_name)
141
158
 
@@ -163,13 +180,27 @@ class MCSls(object):
163
180
  sel = self.topv
164
181
 
165
182
  new_cl.append(-sel) # creating a new selector
166
- 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)
167
190
  else:
168
191
  sel = cl[0]
169
192
 
170
193
  self.sels.append(sel)
171
194
  self.smap[sel] = len(self.sels)
172
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
+
173
204
  def __del__(self):
174
205
  """
175
206
  Destructor.
@@ -485,10 +516,11 @@ def parse_options():
485
516
 
486
517
  try:
487
518
  opts, args = getopt.getopt(sys.argv[1:],
488
- 'de:hs:v',
519
+ 'de:hp:s:v',
489
520
  ['dcalls',
490
521
  'enum=',
491
522
  'help',
523
+ 'process=',
492
524
  'solver=',
493
525
  'verbose'])
494
526
  except getopt.GetoptError as err:
@@ -498,6 +530,7 @@ def parse_options():
498
530
 
499
531
  dcalls = False
500
532
  to_enum = 1
533
+ process = 0
501
534
  solver = 'm22'
502
535
  verbose = 0
503
536
 
@@ -511,6 +544,8 @@ def parse_options():
511
544
  elif opt in ('-h', '--help'):
512
545
  usage()
513
546
  sys.exit(0)
547
+ elif opt in ('-p', '--process'):
548
+ process = int(arg)
514
549
  elif opt in ('-s', '--solver'):
515
550
  solver = str(arg)
516
551
  elif opt in ('-v', '--verbose'):
@@ -518,7 +553,7 @@ def parse_options():
518
553
  else:
519
554
  assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
520
555
 
521
- return dcalls, to_enum, solver, verbose, args
556
+ return dcalls, to_enum, solver, process, verbose, args
522
557
 
523
558
 
524
559
  #
@@ -534,28 +569,31 @@ def usage():
534
569
  print(' -e, --enum=<string> How many solutions to compute')
535
570
  print(' Available values: [1 .. all] (default: 1)')
536
571
  print(' -h, --help')
572
+ print(' -p, --process=<int> Number of processing rounds')
573
+ print(' Available values: [0 .. INT_MAX] (default = 0)')
537
574
  print(' -s, --solver SAT solver to use')
538
- print(' Available values: g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
575
+ print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
539
576
  print(' -v, --verbose Be verbose')
540
577
 
541
578
 
542
579
  #
543
580
  #==============================================================================
544
581
  if __name__ == '__main__':
545
- dcalls, to_enum, solver, verbose, files = parse_options()
582
+ dcalls, to_enum, solver, process, verbose, files = parse_options()
546
583
 
547
584
  if type(to_enum) == str:
548
585
  to_enum = 0
549
586
 
550
587
  if files:
551
588
  # reading standard CNF, WCNF, or (W)CNF+
552
- if re.search('cnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
553
- if re.search('\.wcnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
554
- formula = WCNFPlus(from_file=files[0])
555
- else: # expecting '*.cnf[,p,+].*'
556
- formula = CNFPlus(from_file=files[0]).weighted()
557
-
558
- with MCSls(formula, use_cld=dcalls, solver_name=solver, use_timer=True) as mcsls:
589
+ assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
590
+ if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
591
+ formula = WCNFPlus(from_file=files[0])
592
+ else: # expecting '*.cnf[,p,+].*'
593
+ formula = CNFPlus(from_file=files[0]).weighted()
594
+
595
+ with MCSls(formula, use_cld=dcalls, solver_name=solver,
596
+ process=process, use_timer=True) as mcsls:
559
597
  for i, mcs in enumerate(mcsls.enumerate()):
560
598
  if verbose:
561
599
  print('c MCS:', ' '.join([str(cl_id) for cl_id in mcs]), '0')
@@ -87,10 +87,12 @@ def enumerate_models(formula, to_enum, solver, warm=False):
87
87
  use_timer=True, warm_start=warm) as s:
88
88
  # adding native cardinality constraints if needed
89
89
  if formula.atmosts:
90
- assert solver_name in SolverNames.minicard or \
91
- solver_name in SolverNames.gluecard3 or \
92
- solver_name in SolverNames.gluecard4, \
93
- '{0} does not support native cardinality constraints'.format(solver_name)
90
+ # we are using CaDiCaL195 and it can use external linear engine
91
+ if solver in SolverNames.cadical195:
92
+ s.activate_atmost()
93
+
94
+ assert s.supports_atmost(), \
95
+ '{0} does not support native cardinality constraints'.format(solver)
94
96
 
95
97
  for atm in formula.atmosts:
96
98
  s.add_atmost(*atm)