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
pysat/examples/fm.py CHANGED
@@ -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()
pysat/examples/genhard.py CHANGED
@@ -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
 
pysat/examples/hitman.py CHANGED
@@ -35,14 +35,14 @@
35
35
  by size) subset-minimal hitting enumeration.
36
36
 
37
37
  The minimal hitting set problem is trivially formulated as a MaxSAT formula
38
- in WCNF, as follows. Assume :math:`E=\{e_1,\ldots,e_n\}` to be a universe
38
+ in WCNF, as follows. Assume :math:`E=\\{e_1,\\ldots,e_n\\}` to be a universe
39
39
  of elements. Also assume there are :math:`k` sets to hit:
40
- :math:`s_i=\{e_{i,1},\ldots,e_{i,j_i}\}` s.t. :math:`e_{i,l}\in E`. Every
41
- set :math:`s_i=\{e_{i,1},\ldots,e_{i,j_i}\}` is translated into a hard
42
- clause :math:`(e_{i,1} \\vee \ldots \\vee e_{i,j_i})`. This results in the
40
+ :math:`s_i=\\{e_{i,1},\\ldots,e_{i,j_i}\\}` s.t. :math:`e_{i,l}\\in E`. Every
41
+ set :math:`s_i=\\{e_{i,1},\\ldots,e_{i,j_i}\\}` is translated into a hard
42
+ clause :math:`(e_{i,1} \\vee \\ldots \\vee e_{i,j_i})`. This results in the
43
43
  set of hard clauses having size :math:`k`. The set of soft clauses
44
44
  comprises unit clauses of the form :math:`(\\neg{e_{j}})` s.t.
45
- :math:`e_{j}\in E`, each having weight 1.
45
+ :math:`e_{j}\\in E`, each having weight 1.
46
46
 
47
47
  Taking into account this problem formulation as MaxSAT, ordered hitting
48
48
  enumeration is done with the use of the state-of-the-art MaxSAT solver
@@ -387,21 +387,27 @@ class Hitman(object):
387
387
  nohard=True, trim=self.trim)
388
388
  elif self.htype == 'lbx':
389
389
  self.oracle = LBX(formula, solver_name=self.solver,
390
- use_cld=self.usecld)
390
+ use_cld=self.usecld, use_timer=True)
391
391
  elif self.htype == 'mcsls':
392
392
  self.oracle = MCSls(formula, solver_name=self.solver,
393
- use_cld=self.usecld)
393
+ use_cld=self.usecld, use_timer=True)
394
394
  else: # 'sat'
395
395
  assert self.solver in SolverNames.minisatgh + \
396
- SolverNames.lingeling + SolverNames.cadical153, \
397
- 'Hard polarity setting is unsupported by {0}'.format(self.solver)
398
-
399
- assert formula.atms == [], 'Native AtMostK constraints aren\'t' \
400
- 'supported by MinisatGH, Lingeling, or CaDiCaL 153'
396
+ SolverNames.lingeling + SolverNames.cadical153 + \
397
+ SolverNames.cadical195, 'Hard polarity setting is unsupported by {0}'.format(self.solver)
401
398
 
402
399
  # setting up a SAT solver, so that it supports the same interface
403
- self.oracle = Solver(name=self.solver, bootstrap_with=formula.hard,
404
- use_timer=True)
400
+ if formula.atms == []:
401
+ self.oracle = Solver(name=self.solver, bootstrap_with=formula.hard,
402
+ use_timer=True)
403
+ elif self.solver in SolverNames.cadical195:
404
+ self.oracle = Solver(name=self.solver, bootstrap_with=formula.hard,
405
+ use_timer=True, native_card=True)
406
+
407
+ for atm in formula.atms:
408
+ self.oracle.add_atmost(*atm)
409
+ else:
410
+ assert 0, 'Native AtMostK constraints aren\'t supported by MinisatGH, Lingeling, or CaDiCaL 153'
405
411
 
406
412
  # MinisatGH supports warm start mode
407
413
  if self.solver in SolverNames.minisatgh:
@@ -440,7 +446,7 @@ class Hitman(object):
440
446
  # updating the preferences
441
447
  self.oracle.set_phases(literals=[self.phase * (-v) for v in self.idpool.id2obj])
442
448
 
443
- def add_hard(self, clause, weights=None):
449
+ def add_hard(self, clause, weights=None, no_obj=False):
444
450
  """
445
451
  Add a hard constraint, which can be either a pure clause or an
446
452
  AtMostK constraint.
@@ -450,47 +456,57 @@ class Hitman(object):
450
456
  question into weights. Also note that the weight of an object must
451
457
  not change from one call of :meth:`hit` to another.
452
458
 
459
+ Finally, note that if ``no_obj`` is set to True, no new objects
460
+ will be added to the hitting set enumerator. This is useful if the
461
+ variables in the clause are either already present or are
462
+ auxiliary. This is turned off by default.
463
+
453
464
  :param clause: hard constraint (either a clause or a native AtMostK constraint)
454
465
  :param weights: a mapping from objects to weights
466
+ :param no_obj: if True, no new objects are added to the oracle
455
467
 
456
468
  :type clause: iterable(obj)
457
469
  :type weights: dict(obj)
470
+ :type no_obj: bool
458
471
  """
459
472
 
460
473
  if not len(clause) == 2 or not type(clause[0]) in (list, tuple, set):
461
474
  # this is a pure clause
462
475
  clause = list(map(lambda a: self.idpool.id(a.obj) * (2 * a.sign - 1), clause))
463
476
 
464
- # a soft clause should be added for each new object
465
- new_obj = filter(lambda vid: abs(vid) not in self.oracle.vmap.e2i, clause)
477
+ if no_obj is False:
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)
466
480
  else:
467
481
  # this is a native AtMostK constraint
468
482
  clause = [list(map(lambda a: self.idpool.id(a.obj) * (2 * a.sign - 1), clause[0])), clause[1]]
469
483
 
470
- # a soft clause should be added for each new object
471
- new_obj = filter(lambda vid: abs(vid) not in self.oracle.vmap.e2i, clause[0])
484
+ if no_obj is False:
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])
472
487
 
473
- # there may be duplicate literals if the constraint is weighted
474
- new_obj = list(set(new_obj))
475
-
476
- # some of the literals may also have the opposite polarity
477
- new_obj = [l if l in self.idpool.obj2id else -l for l in new_obj]
488
+ # there may be duplicate literals if the constraint is weighted
489
+ new_obj = list(set(new_obj))
478
490
 
479
491
  # adding the hard clause
480
492
  self.oracle.add_clause(clause)
481
493
 
482
- if self.htype != 'sat':
483
- # new soft clauses
484
- for vid in new_obj:
485
- self.oracle.add_clause([-vid], 1 if not weights else weights[self.idpool.obj(vid)])
486
- else:
487
- # dummy variable id mapping
488
- for vid in new_obj:
489
- self.oracle.vmap.e2i[vid] = vid
490
- self.oracle.vmap.i2e[vid] = vid
494
+ 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]
491
497
 
492
- # setting variable polarities
493
- self.oracle.set_phases(literals=[self.phase * (-vid) for vid in new_obj])
498
+ if self.htype != 'sat':
499
+ # new soft clauses
500
+ for vid in new_obj:
501
+ self.oracle.add_clause([-vid], 1 if not weights else weights[self.idpool.obj(vid)])
502
+ else:
503
+ # dummy variable id mapping
504
+ for vid in new_obj:
505
+ self.oracle.vmap.e2i[vid] = vid
506
+ self.oracle.vmap.i2e[vid] = vid
507
+
508
+ # setting variable polarities
509
+ self.oracle.set_phases(literals=[self.phase * (-vid) for vid in new_obj])
494
510
 
495
511
  def delete(self):
496
512
  """
@@ -545,7 +561,7 @@ class Hitman(object):
545
561
  to_hit = list(map(lambda obj: self.idpool.id(obj), to_hit))
546
562
 
547
563
  # a soft clause should be added for each new object
548
- new_obj = list(filter(lambda vid: vid not in self.oracle.vmap.e2i, to_hit))
564
+ new_obj = [vid for vid in to_hit if vid not in self.oracle.vmap.e2i]
549
565
 
550
566
  # new hard clause; phase multiplication is needed
551
567
  # for making phase switching possible (pure SAT only)
@@ -588,7 +604,7 @@ class Hitman(object):
588
604
  to_block = list(map(lambda obj: self.idpool.id(obj), to_block))
589
605
 
590
606
  # a soft clause should be added for each new object
591
- new_obj = list(filter(lambda vid: vid not in self.oracle.vmap.e2i, to_block))
607
+ new_obj = [vid for vid in to_block if vid not in self.oracle.vmap.e2i]
592
608
 
593
609
  # new hard clause; phase multiplication is needed
594
610
  # for making phase switching possible (pure SAT only)
pysat/examples/lbx.py CHANGED
@@ -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')
pysat/examples/lsu.py CHANGED
@@ -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: