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.
- pycard.cp310-win_amd64.pyd +0 -0
- pysat/__init__.py +4 -4
- pysat/_fileio.py +30 -14
- pysat/allies/approxmc.py +22 -22
- pysat/allies/unigen.py +435 -0
- pysat/card.py +13 -12
- pysat/engines.py +1302 -0
- pysat/examples/bbscan.py +663 -0
- pysat/examples/bica.py +691 -0
- pysat/examples/fm.py +12 -8
- pysat/examples/genhard.py +24 -23
- pysat/examples/hitman.py +53 -37
- pysat/examples/lbx.py +56 -15
- pysat/examples/lsu.py +28 -14
- pysat/examples/mcsls.py +53 -15
- pysat/examples/models.py +6 -4
- pysat/examples/musx.py +15 -7
- pysat/examples/optux.py +71 -32
- pysat/examples/primer.py +620 -0
- pysat/examples/rc2.py +268 -69
- pysat/formula.py +3241 -229
- pysat/pb.py +85 -37
- pysat/process.py +16 -2
- pysat/solvers.py +2119 -724
- pysolvers.cp310-win_amd64.pyd +0 -0
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/approxmc.py +22 -22
- python_sat-1.8.dev26.data/scripts/bbscan.py +663 -0
- python_sat-1.8.dev26.data/scripts/bica.py +691 -0
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/fm.py +12 -8
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/genhard.py +24 -23
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/lbx.py +56 -15
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/lsu.py +28 -14
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/mcsls.py +53 -15
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/models.py +6 -4
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/musx.py +15 -7
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/optux.py +71 -32
- python_sat-1.8.dev26.data/scripts/primer.py +620 -0
- {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/rc2.py +268 -69
- python_sat-1.8.dev26.data/scripts/unigen.py +435 -0
- {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/METADATA +19 -5
- python_sat-1.8.dev26.dist-info/RECORD +48 -0
- {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/WHEEL +1 -1
- python_sat-0.1.8.dev10.dist-info/RECORD +0 -39
- {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info/licenses}/LICENSE.txt +0 -0
- {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
|
|
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
|
|
390
|
-
:math
|
|
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
|
|
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
|
|
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
|
|
143
|
-
m+1}{\\textsf{AtLeast1}(x_{i1}
|
|
144
|
-
\\bigwedge_{j=1}^{m}{\\textsf{AtMost}k(x_{1j}
|
|
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
|
|
208
|
-
|
|
209
|
-
Assume variable :math:`x_{ij}`, for :math:`i,j
|
|
210
|
-
the fact that :math:`i
|
|
211
|
-
\\neg{x_{ji}})` and :math:`(\\neg{x_{ij}}
|
|
212
|
-
x_{ik})` ensure that the relation
|
|
213
|
-
|
|
214
|
-
:math
|
|
215
|
-
|
|
216
|
-
:math:`
|
|
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
|
|
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
|
|
284
|
-
and :math:`j` are adjacent (no variables are introduced for
|
|
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
|
|
289
|
-
\\forall i` enforcing that every cell :math:`i` should
|
|
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
|
|
393
|
-
\\vee \\neg{x_{kj}})` for all distinct :math:`i,j,k
|
|
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',
|
|
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,
|
|
133
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
with LBX(formula, use_cld=dcalls, solver_name=solver,
|
|
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=
|
|
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
|
|
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:], '
|
|
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',
|
|
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,
|
|
133
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
with MCSls(formula, use_cld=dcalls, solver_name=solver,
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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)
|