python-sat 1.8.dev25__tar.gz → 1.8.dev27__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. {python_sat-1.8.dev25/python_sat.egg-info → python_sat-1.8.dev27}/PKG-INFO +1 -1
  2. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/rc2.py +160 -19
  3. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/__init__.py +1 -1
  4. {python_sat-1.8.dev25 → python_sat-1.8.dev27/python_sat.egg-info}/PKG-INFO +1 -1
  5. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/LICENSE.txt +0 -0
  6. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/MANIFEST.in +0 -0
  7. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/README.rst +0 -0
  8. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/allies/__init__.py +0 -0
  9. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/allies/approxmc.py +0 -0
  10. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/allies/unigen.py +0 -0
  11. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/bitwise.hh +0 -0
  12. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/card.hh +0 -0
  13. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/clset.hh +0 -0
  14. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/common.hh +0 -0
  15. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/itot.hh +0 -0
  16. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/ladder.hh +0 -0
  17. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/mto.hh +0 -0
  18. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/pairwise.hh +0 -0
  19. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/ptypes.hh +0 -0
  20. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/pycard.cc +0 -0
  21. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/seqcounter.hh +0 -0
  22. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/sortcard.hh +0 -0
  23. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/cardenc/utils.hh +0 -0
  24. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/__init__.py +0 -0
  25. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/bbscan.py +0 -0
  26. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/bica.py +0 -0
  27. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/fm.py +0 -0
  28. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/genhard.py +0 -0
  29. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/hitman.py +0 -0
  30. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/lbx.py +0 -0
  31. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/lsu.py +0 -0
  32. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/mcsls.py +0 -0
  33. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/models.py +0 -0
  34. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/musx.py +0 -0
  35. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/optux.py +0 -0
  36. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/primer.py +0 -0
  37. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/examples/usage.py +0 -0
  38. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/_fileio.py +0 -0
  39. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/_utils.py +0 -0
  40. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/card.py +0 -0
  41. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/engines.py +0 -0
  42. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/formula.py +0 -0
  43. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/pb.py +0 -0
  44. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/process.py +0 -0
  45. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/pysat/solvers.py +0 -0
  46. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/python_sat.egg-info/SOURCES.txt +0 -0
  47. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/python_sat.egg-info/dependency_links.txt +0 -0
  48. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/python_sat.egg-info/requires.txt +0 -0
  49. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/python_sat.egg-info/top_level.txt +0 -0
  50. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/requirements.txt +0 -0
  51. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/setup.cfg +0 -0
  52. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/setup.py +0 -0
  53. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/cadical103.tar.gz +0 -0
  54. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/cadical153.tar.gz +0 -0
  55. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/cadical195.tar.gz +0 -0
  56. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/glucose30.tar.gz +0 -0
  57. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/glucose41.tar.gz +0 -0
  58. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/glucose421.tar.gz +0 -0
  59. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/lingeling.tar.gz +0 -0
  60. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/maplechrono.zip +0 -0
  61. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/maplecm.zip +0 -0
  62. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/maplesat.zip +0 -0
  63. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/mergesat3.tar.gz +0 -0
  64. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/minicard.tar.gz +0 -0
  65. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/minisat22.tar.gz +0 -0
  66. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/minisatgh.zip +0 -0
  67. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/cadical103.patch +0 -0
  68. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/cadical153.patch +0 -0
  69. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/cadical195.patch +0 -0
  70. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/glucose30.patch +0 -0
  71. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/glucose41.patch +0 -0
  72. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/glucose421.patch +0 -0
  73. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/gluecard30.patch +0 -0
  74. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/gluecard41.patch +0 -0
  75. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/lingeling.patch +0 -0
  76. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/maplechrono.patch +0 -0
  77. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/maplecm.patch +0 -0
  78. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/maplesat.patch +0 -0
  79. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/mergesat3.patch +0 -0
  80. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/minicard.patch +0 -0
  81. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/minisat22.patch +0 -0
  82. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/patches/minisatgh.patch +0 -0
  83. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/prepare.py +0 -0
  84. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/solvers/pysolvers.cc +0 -0
  85. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_accum_stats.py +0 -0
  86. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_atmost.py +0 -0
  87. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_atmost1.py +0 -0
  88. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_atmostk.py +0 -0
  89. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_boolengine.py +0 -0
  90. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_clausification.py +0 -0
  91. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_cnf.py +0 -0
  92. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_encode_pb_conditional.py +0 -0
  93. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_equals1.py +0 -0
  94. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_formula_unique.py +0 -0
  95. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_idpool.py +0 -0
  96. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_process.py +0 -0
  97. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_propagate.py +0 -0
  98. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_unique_model.py +0 -0
  99. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_unique_mus.py +0 -0
  100. {python_sat-1.8.dev25 → python_sat-1.8.dev27}/tests/test_warmstart.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.8.dev25
3
+ Version: 1.8.dev27
4
4
  Summary: A Python library for prototyping with SAT oracles
5
5
  Home-page: https://github.com/pysathq/pysat
6
6
  Author: Alexey Ignatiev, Joao Marques-Silva, Antonio Morgado
@@ -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.oracle.solve(assumptions=self.sels + self.sums):
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.oracle.solve(assumptions=self.core)
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.oracle.solve_limited(assumptions=to_test) == False:
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.oracle.solve(assumptions=[-tobj.rhs[1]]):
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.oracle.solve(assumptions=[-tobj.rhs[i]]):
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_() == False:
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_() == False:
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), 1):
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
- print('s OPTIMUM FOUND')
1836
- print('o {0}'.format(rc2.cost))
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()
@@ -10,7 +10,7 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (1, 8, 'dev', 25)
13
+ VERSION = (1, 8, 'dev', 27)
14
14
 
15
15
 
16
16
  # PEP440 Format
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.8.dev25
3
+ Version: 1.8.dev27
4
4
  Summary: A Python library for prototyping with SAT oracles
5
5
  Home-page: https://github.com/pysathq/pysat
6
6
  Author: Alexey Ignatiev, Joao Marques-Silva, Antonio Morgado
File without changes
File without changes