python-sat 1.8.dev25__tar.gz → 1.8.dev26__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_sat-1.8.dev25/python_sat.egg-info → python_sat-1.8.dev26}/PKG-INFO +1 -1
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/rc2.py +160 -19
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/__init__.py +1 -1
- {python_sat-1.8.dev25 → python_sat-1.8.dev26/python_sat.egg-info}/PKG-INFO +1 -1
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/LICENSE.txt +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/MANIFEST.in +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/README.rst +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/allies/__init__.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/allies/approxmc.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/allies/unigen.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/bitwise.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/card.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/clset.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/common.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/itot.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/ladder.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/mto.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/pairwise.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/ptypes.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/pycard.cc +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/seqcounter.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/sortcard.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/cardenc/utils.hh +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/__init__.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/bbscan.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/bica.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/fm.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/genhard.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/hitman.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/lbx.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/lsu.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/mcsls.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/models.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/musx.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/optux.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/primer.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/examples/usage.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/_fileio.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/_utils.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/card.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/engines.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/formula.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/pb.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/process.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/pysat/solvers.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/python_sat.egg-info/SOURCES.txt +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/python_sat.egg-info/dependency_links.txt +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/python_sat.egg-info/requires.txt +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/python_sat.egg-info/top_level.txt +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/requirements.txt +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/setup.cfg +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/setup.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/cadical103.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/cadical153.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/cadical195.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/glucose30.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/glucose41.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/glucose421.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/lingeling.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/maplechrono.zip +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/maplecm.zip +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/maplesat.zip +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/mergesat3.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/minicard.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/minisat22.tar.gz +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/minisatgh.zip +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/cadical103.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/cadical153.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/cadical195.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/glucose30.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/glucose41.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/glucose421.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/gluecard30.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/gluecard41.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/lingeling.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/maplechrono.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/maplecm.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/maplesat.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/mergesat3.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/minicard.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/minisat22.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/patches/minisatgh.patch +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/prepare.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/solvers/pysolvers.cc +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_accum_stats.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_atmost.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_atmost1.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_atmostk.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_boolengine.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_clausification.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_cnf.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_encode_pb_conditional.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_equals1.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_formula_unique.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_idpool.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_process.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_propagate.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_unique_model.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_unique_mus.py +0 -0
- {python_sat-1.8.dev25 → python_sat-1.8.dev26}/tests/test_warmstart.py +0 -0
|
@@ -140,6 +140,7 @@ from pysat.formula import CNFPlus, WCNFPlus, IDPool
|
|
|
140
140
|
from pysat.card import ITotalizer
|
|
141
141
|
from pysat.process import Processor
|
|
142
142
|
from pysat.solvers import Solver, SolverNames
|
|
143
|
+
from threading import Timer
|
|
143
144
|
import re
|
|
144
145
|
import six
|
|
145
146
|
from six.moves import range
|
|
@@ -222,6 +223,9 @@ class RC2(object):
|
|
|
222
223
|
# oracles are initialised to be None
|
|
223
224
|
self.oracle, self.processor = None, None
|
|
224
225
|
|
|
226
|
+
# parameters related to asynchronous interruption
|
|
227
|
+
self.expect_interrupt, self.interrupted = False, False
|
|
228
|
+
|
|
225
229
|
# clause selectors and mapping from selectors to clause ids
|
|
226
230
|
# .sall, .s2cl, and .sneg are required only for model enumeration
|
|
227
231
|
self.sels, self.smap, self.sall, self.s2cl, self.sneg = [], {}, [], {}, set([])
|
|
@@ -248,6 +252,23 @@ class RC2(object):
|
|
|
248
252
|
if not formula.hard and len(self.sels) > 100000 and min(wght) == max(wght):
|
|
249
253
|
self.minz = False
|
|
250
254
|
|
|
255
|
+
def _call_oracle(self, assumptions=[], expect_interrupt=False):
|
|
256
|
+
"""
|
|
257
|
+
Makes a call to the internal SAT solver by means of invoking
|
|
258
|
+
`oracle.solve_limited()`. The two arguments are the list of
|
|
259
|
+
assumption literals and the Boolean flag indicating whether the
|
|
260
|
+
call can be interrupted.
|
|
261
|
+
|
|
262
|
+
:param assumptions: a list of assumption literals.
|
|
263
|
+
:param expect_interrupt: whether :meth:`interrupt` may be called
|
|
264
|
+
|
|
265
|
+
:type assumptions: iterable(int)
|
|
266
|
+
:type expect_interrupt: bool
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
return self.oracle.solve_limited(assumptions=assumptions,
|
|
270
|
+
expect_interrupt=expect_interrupt)
|
|
271
|
+
|
|
251
272
|
def __del__(self):
|
|
252
273
|
"""
|
|
253
274
|
Destructor.
|
|
@@ -469,7 +490,7 @@ class RC2(object):
|
|
|
469
490
|
self.processor.delete()
|
|
470
491
|
self.processor = None
|
|
471
492
|
|
|
472
|
-
def compute(self):
|
|
493
|
+
def compute(self, expect_interrupt=False):
|
|
473
494
|
"""
|
|
474
495
|
This method can be used for computing one MaxSAT solution,
|
|
475
496
|
i.e. for computing an assignment satisfying all hard
|
|
@@ -484,6 +505,12 @@ class RC2(object):
|
|
|
484
505
|
can enumerate top-:math:`k` MaxSAT solutions (this can
|
|
485
506
|
also be done by calling :meth:`enumerate()`).
|
|
486
507
|
|
|
508
|
+
This MaxSAT call can be asynchronously interrupted, in which case
|
|
509
|
+
the value of ``expect_interrupt`` must be set to ``True``.
|
|
510
|
+
|
|
511
|
+
:param expect_interrupt: whether :meth:`interrupt` may be called
|
|
512
|
+
:type expect_interrupt: bool
|
|
513
|
+
|
|
487
514
|
:return: a MaxSAT model
|
|
488
515
|
:rtype: list(int)
|
|
489
516
|
|
|
@@ -509,6 +536,9 @@ class RC2(object):
|
|
|
509
536
|
>>> rc2.delete()
|
|
510
537
|
"""
|
|
511
538
|
|
|
539
|
+
# keeping the current interruption preference
|
|
540
|
+
self.expect_interrupt = expect_interrupt
|
|
541
|
+
|
|
512
542
|
# simply apply MaxSAT only once
|
|
513
543
|
res = self.compute_()
|
|
514
544
|
|
|
@@ -532,7 +562,7 @@ class RC2(object):
|
|
|
532
562
|
|
|
533
563
|
return self.model
|
|
534
564
|
|
|
535
|
-
def enumerate(self, block=0):
|
|
565
|
+
def enumerate(self, block=0, expect_interrupt=False):
|
|
536
566
|
"""
|
|
537
567
|
Enumerate top MaxSAT solutions (from best to worst). The
|
|
538
568
|
method works as a generator, which iteratively calls
|
|
@@ -546,8 +576,15 @@ class RC2(object):
|
|
|
546
576
|
it to ``-1``. By the default (for blocking MaxSAT models),
|
|
547
577
|
``block`` is set to ``0``.
|
|
548
578
|
|
|
579
|
+
This MaxSAT enumeration call can be asynchronously interrupted, in
|
|
580
|
+
which case the value of ``expect_interrupt`` must be set to
|
|
581
|
+
``True``.
|
|
582
|
+
|
|
549
583
|
:param block: preferred way to block solutions when enumerating
|
|
584
|
+
:param expect_interrupt: whether :meth:`interrupt` may be called
|
|
585
|
+
|
|
550
586
|
:type block: int
|
|
587
|
+
:type expect_interrupt: bool
|
|
551
588
|
|
|
552
589
|
:return: a MaxSAT model
|
|
553
590
|
:rtype: list(int)
|
|
@@ -577,7 +614,7 @@ class RC2(object):
|
|
|
577
614
|
|
|
578
615
|
done = False
|
|
579
616
|
while not done:
|
|
580
|
-
model = self.compute()
|
|
617
|
+
model = self.compute(expect_interrupt=expect_interrupt)
|
|
581
618
|
|
|
582
619
|
if model != None:
|
|
583
620
|
if block == 1:
|
|
@@ -631,8 +668,21 @@ class RC2(object):
|
|
|
631
668
|
if self.adapt:
|
|
632
669
|
self.adapt_am1()
|
|
633
670
|
|
|
671
|
+
# at the beginning, the solver is not interrupted
|
|
672
|
+
self.interrupted = False
|
|
673
|
+
|
|
634
674
|
# main solving loop
|
|
635
|
-
while not self.
|
|
675
|
+
while not self._call_oracle(assumptions=self.sels + self.sums,
|
|
676
|
+
expect_interrupt=self.expect_interrupt):
|
|
677
|
+
|
|
678
|
+
# even if the call has been interrupted, we
|
|
679
|
+
# still need to finish the current iteration
|
|
680
|
+
if self.oracle.get_status() is None:
|
|
681
|
+
self.clear_interrupt()
|
|
682
|
+
|
|
683
|
+
if self.verbose > 1:
|
|
684
|
+
print('c interrupted; processing the last found core')
|
|
685
|
+
|
|
636
686
|
self.get_core()
|
|
637
687
|
|
|
638
688
|
if not self.core:
|
|
@@ -645,6 +695,10 @@ class RC2(object):
|
|
|
645
695
|
print('c cost: {0}; core sz: {1}; soft sz: {2}'.format(self.cost,
|
|
646
696
|
len(self.core), len(self.sels) + len(self.sums)))
|
|
647
697
|
|
|
698
|
+
# the solver got interrupted => returning None
|
|
699
|
+
if self.interrupted:
|
|
700
|
+
return # None
|
|
701
|
+
|
|
648
702
|
return True
|
|
649
703
|
|
|
650
704
|
def get_core(self):
|
|
@@ -892,7 +946,7 @@ class RC2(object):
|
|
|
892
946
|
for i in range(self.trim):
|
|
893
947
|
# call solver with core assumption only
|
|
894
948
|
# it must return 'unsatisfiable'
|
|
895
|
-
self.
|
|
949
|
+
self._call_oracle(assumptions=self.core)
|
|
896
950
|
|
|
897
951
|
# extract a new core
|
|
898
952
|
new_core = self.oracle.get_core()
|
|
@@ -930,13 +984,16 @@ class RC2(object):
|
|
|
930
984
|
while i < len(self.core):
|
|
931
985
|
to_test = self.core[:i] + self.core[(i + 1):]
|
|
932
986
|
|
|
933
|
-
if self.
|
|
987
|
+
if self._call_oracle(assumptions=to_test) == False:
|
|
934
988
|
self.core = to_test
|
|
935
989
|
elif self.oracle.get_status() == True:
|
|
936
990
|
i += 1
|
|
937
991
|
else:
|
|
938
992
|
break
|
|
939
993
|
|
|
994
|
+
# disabling the budget
|
|
995
|
+
self.oracle.conf_budget(budget=-1)
|
|
996
|
+
|
|
940
997
|
def exhaust_core(self, tobj):
|
|
941
998
|
"""
|
|
942
999
|
Exhaust core by increasing its bound as much as possible.
|
|
@@ -962,7 +1019,7 @@ class RC2(object):
|
|
|
962
1019
|
"""
|
|
963
1020
|
|
|
964
1021
|
# the first case is simpler
|
|
965
|
-
if self.
|
|
1022
|
+
if self._call_oracle(assumptions=[-tobj.rhs[1]]):
|
|
966
1023
|
return 1
|
|
967
1024
|
else:
|
|
968
1025
|
self.cost += self.minw
|
|
@@ -975,7 +1032,7 @@ class RC2(object):
|
|
|
975
1032
|
# increasing the bound
|
|
976
1033
|
self.update_sum(-tobj.rhs[i - 1])
|
|
977
1034
|
|
|
978
|
-
if self.
|
|
1035
|
+
if self._call_oracle(assumptions=[-tobj.rhs[i]]):
|
|
979
1036
|
# the bound should be equal to i
|
|
980
1037
|
return i
|
|
981
1038
|
|
|
@@ -1259,6 +1316,65 @@ class RC2(object):
|
|
|
1259
1316
|
|
|
1260
1317
|
return int(copysign(i, l))
|
|
1261
1318
|
|
|
1319
|
+
def interrupt(self):
|
|
1320
|
+
"""
|
|
1321
|
+
Interrupt the execution of the current *limited* SAT call in the
|
|
1322
|
+
RC2 algorithm. Can be used to enforce time limits using timer
|
|
1323
|
+
objects. The interrupt must be cleared before performing another
|
|
1324
|
+
`compute` call (see :meth:`clear_interrupt`).
|
|
1325
|
+
|
|
1326
|
+
Importantly, interruption is implemented such that it can work
|
|
1327
|
+
incrementally with multiple MaxSAT calls, i.e. upon an interrupted
|
|
1328
|
+
invocation a user may extend the resources / time and call the
|
|
1329
|
+
solver again. To make this work, none of the SAT calls used by the
|
|
1330
|
+
heuristics are interrupted. For this reason, the solver may take
|
|
1331
|
+
slightly more time than assumed (spent to properly finish the
|
|
1332
|
+
processing of the last unsatisfiable core).
|
|
1333
|
+
|
|
1334
|
+
**Note** that this method can be called if the `compute` call was
|
|
1335
|
+
made with the option ``expect_interrupt`` set to ``True``.
|
|
1336
|
+
Behaviour is **undefined** if used to ``expect_interrupt`` was set
|
|
1337
|
+
to ``False``.
|
|
1338
|
+
|
|
1339
|
+
Example:
|
|
1340
|
+
|
|
1341
|
+
.. code-block:: python
|
|
1342
|
+
|
|
1343
|
+
>>> from pysat.examples.rc2 import RC2
|
|
1344
|
+
>>> from pysat.formula import WCNF
|
|
1345
|
+
>>> from threading import Timer
|
|
1346
|
+
>>>
|
|
1347
|
+
>>> wcnf = WCNF(from_file='somefile.wcnf')
|
|
1348
|
+
>>>
|
|
1349
|
+
>>> with RC2(wcnf) as rc2:
|
|
1350
|
+
>>>
|
|
1351
|
+
>>> def interrupt(s):
|
|
1352
|
+
>>> print('interrupted!')
|
|
1353
|
+
>>> s.interrupt()
|
|
1354
|
+
>>>
|
|
1355
|
+
>>> timer = Timer(1, interrupt, [rc2])
|
|
1356
|
+
>>> timer.start()
|
|
1357
|
+
>>> print('computing...')
|
|
1358
|
+
>>> rc2.compute(expect_interrupt=True)
|
|
1359
|
+
>>> print('done')
|
|
1360
|
+
"""
|
|
1361
|
+
|
|
1362
|
+
if self.oracle:
|
|
1363
|
+
self.oracle.interrupt()
|
|
1364
|
+
|
|
1365
|
+
# recording the interruption request
|
|
1366
|
+
self.interrupted = True
|
|
1367
|
+
|
|
1368
|
+
def clear_interrupt(self):
|
|
1369
|
+
"""
|
|
1370
|
+
Clears a previous interrupt. Technically, this method should be
|
|
1371
|
+
used every time after the previous MaxSAT call gets interrupted.
|
|
1372
|
+
**However**, the current implementation handles this on its own.
|
|
1373
|
+
"""
|
|
1374
|
+
|
|
1375
|
+
if self.oracle:
|
|
1376
|
+
self.oracle.clear_interrupt()
|
|
1377
|
+
|
|
1262
1378
|
|
|
1263
1379
|
#
|
|
1264
1380
|
#==============================================================================
|
|
@@ -1343,7 +1459,7 @@ class RC2Stratified(RC2, object):
|
|
|
1343
1459
|
# number of finished levels
|
|
1344
1460
|
self.done = 0
|
|
1345
1461
|
|
|
1346
|
-
def compute(self):
|
|
1462
|
+
def compute(self, expect_interrupt=False):
|
|
1347
1463
|
"""
|
|
1348
1464
|
This method solves the MaxSAT problem iteratively. Each
|
|
1349
1465
|
optimization level is tackled the standard way, i.e. by
|
|
@@ -1354,6 +1470,9 @@ class RC2Stratified(RC2, object):
|
|
|
1354
1470
|
:func:`activate_clauses`.
|
|
1355
1471
|
"""
|
|
1356
1472
|
|
|
1473
|
+
# keeping the current interruption preference
|
|
1474
|
+
self.expect_interrupt = expect_interrupt
|
|
1475
|
+
|
|
1357
1476
|
if self.done == 0 and self.levl != None:
|
|
1358
1477
|
# it is a fresh start of the solver
|
|
1359
1478
|
# i.e. no optimization level is finished yet
|
|
@@ -1369,7 +1488,7 @@ class RC2Stratified(RC2, object):
|
|
|
1369
1488
|
print('c wght str:', self.blop[self.levl])
|
|
1370
1489
|
|
|
1371
1490
|
# call RC2
|
|
1372
|
-
if self.compute_()
|
|
1491
|
+
if self.compute_() != True: # can be either False or None
|
|
1373
1492
|
return
|
|
1374
1493
|
|
|
1375
1494
|
# updating the list of distinct weight levels
|
|
@@ -1397,7 +1516,7 @@ class RC2Stratified(RC2, object):
|
|
|
1397
1516
|
# i.e. all levels are finished and so all clauses are present
|
|
1398
1517
|
# thus, we need to simply call RC2 for the next model
|
|
1399
1518
|
self.done = -1 # we are done with stratification, disabling it
|
|
1400
|
-
if self.compute_()
|
|
1519
|
+
if self.compute_() != True:
|
|
1401
1520
|
return
|
|
1402
1521
|
|
|
1403
1522
|
# extracting a model
|
|
@@ -1679,10 +1798,10 @@ def parse_options():
|
|
|
1679
1798
|
"""
|
|
1680
1799
|
|
|
1681
1800
|
try:
|
|
1682
|
-
opts, args = getopt.getopt(sys.argv[1:], 'ab:c:e:hil:mp:s:t:vx',
|
|
1801
|
+
opts, args = getopt.getopt(sys.argv[1:], 'ab:c:e:hil:mp:s:t:T:vx',
|
|
1683
1802
|
['adapt', 'block=', 'comp=', 'enum=', 'exhaust', 'help',
|
|
1684
1803
|
'incr', 'blo=', 'minimize', 'process=', 'solver=',
|
|
1685
|
-
'trim=', 'verbose', 'vnew'])
|
|
1804
|
+
'trim=', 'timeout=', 'verbose', 'vnew'])
|
|
1686
1805
|
except getopt.GetoptError as err:
|
|
1687
1806
|
sys.stderr.write(str(err).capitalize())
|
|
1688
1807
|
usage()
|
|
@@ -1699,6 +1818,7 @@ def parse_options():
|
|
|
1699
1818
|
process = 0
|
|
1700
1819
|
solver = 'g3'
|
|
1701
1820
|
trim = 0
|
|
1821
|
+
timeout = None
|
|
1702
1822
|
verbose = 1
|
|
1703
1823
|
vnew = False
|
|
1704
1824
|
|
|
@@ -1730,6 +1850,9 @@ def parse_options():
|
|
|
1730
1850
|
solver = str(arg)
|
|
1731
1851
|
elif opt in ('-t', '--trim'):
|
|
1732
1852
|
trim = int(arg)
|
|
1853
|
+
elif opt in ('-T', '--timeout'):
|
|
1854
|
+
if str(arg) != 'none':
|
|
1855
|
+
timeout = float(arg)
|
|
1733
1856
|
elif opt in ('-v', '--verbose'):
|
|
1734
1857
|
verbose += 1
|
|
1735
1858
|
elif opt == '--vnew':
|
|
@@ -1745,7 +1868,7 @@ def parse_options():
|
|
|
1745
1868
|
block = bmap[block]
|
|
1746
1869
|
|
|
1747
1870
|
return adapt, blo, block, cmode, to_enum, exhaust, incr, minz, \
|
|
1748
|
-
process, solver, trim, verbose, vnew, args
|
|
1871
|
+
process, solver, trim, timeout, verbose, vnew, args
|
|
1749
1872
|
|
|
1750
1873
|
|
|
1751
1874
|
#
|
|
@@ -1775,6 +1898,8 @@ def usage():
|
|
|
1775
1898
|
print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = g3)')
|
|
1776
1899
|
print(' -t, --trim=<int> How many times to trim unsatisfiable cores')
|
|
1777
1900
|
print(' Available values: [0 .. INT_MAX] (default = 0)')
|
|
1901
|
+
print(' -T, --timeout=<float> Set time limit for MaxSAT solver')
|
|
1902
|
+
print(' Available values: [0 .. FLOAT_MAX], none (default: none)')
|
|
1778
1903
|
print(' -v, --verbose Be verbose')
|
|
1779
1904
|
print(' --vnew Print v-line in the new format')
|
|
1780
1905
|
print(' -x, --exhaust Exhaust new unsatisfiable cores')
|
|
@@ -1784,7 +1909,7 @@ def usage():
|
|
|
1784
1909
|
#==============================================================================
|
|
1785
1910
|
if __name__ == '__main__':
|
|
1786
1911
|
adapt, blo, block, cmode, to_enum, exhaust, incr, minz, process, solver, \
|
|
1787
|
-
trim, verbose, vnew, files = parse_options()
|
|
1912
|
+
trim, timeout, verbose, vnew, files = parse_options()
|
|
1788
1913
|
|
|
1789
1914
|
if files:
|
|
1790
1915
|
# parsing the input formula
|
|
@@ -1826,14 +1951,26 @@ if __name__ == '__main__':
|
|
|
1826
1951
|
print('c hardening is disabled for model enumeration')
|
|
1827
1952
|
rc2.hard = False
|
|
1828
1953
|
|
|
1954
|
+
# setting a timer if necessary
|
|
1955
|
+
if timeout is not None:
|
|
1956
|
+
if verbose > 1:
|
|
1957
|
+
print('c timeout: {0}'.format(timeout))
|
|
1958
|
+
|
|
1959
|
+
timer = Timer(timeout, lambda s: s.interrupt(), [rc2])
|
|
1960
|
+
timer.start()
|
|
1961
|
+
else:
|
|
1962
|
+
timer = None
|
|
1963
|
+
|
|
1829
1964
|
optimum_found = False
|
|
1830
|
-
for i, model in enumerate(rc2.enumerate(block=block
|
|
1965
|
+
for i, model in enumerate(rc2.enumerate(block=block,
|
|
1966
|
+
expect_interrupt=timeout is not None), 1):
|
|
1831
1967
|
optimum_found = True
|
|
1832
1968
|
|
|
1833
1969
|
if verbose:
|
|
1834
1970
|
if i == 1:
|
|
1835
|
-
|
|
1836
|
-
|
|
1971
|
+
if not rc2.interrupted:
|
|
1972
|
+
print('s OPTIMUM FOUND')
|
|
1973
|
+
print('o {0}'.format(rc2.cost))
|
|
1837
1974
|
|
|
1838
1975
|
if verbose > 2:
|
|
1839
1976
|
if vnew: # new format of the v-line
|
|
@@ -1849,10 +1986,14 @@ if __name__ == '__main__':
|
|
|
1849
1986
|
print('v')
|
|
1850
1987
|
|
|
1851
1988
|
if verbose:
|
|
1852
|
-
if not optimum_found:
|
|
1989
|
+
if not optimum_found and not rc2.interrupted:
|
|
1853
1990
|
print('s UNSATISFIABLE')
|
|
1854
1991
|
elif to_enum != 1:
|
|
1855
1992
|
print('c models found:', i)
|
|
1856
1993
|
|
|
1857
1994
|
if verbose > 1:
|
|
1858
1995
|
print('c oracle time: {0:.4f}'.format(rc2.oracle_time()))
|
|
1996
|
+
|
|
1997
|
+
# cancelling the timer (if any) because we are done
|
|
1998
|
+
if timer:
|
|
1999
|
+
timer.cancel()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|