python-sat 1.9.dev16__tar.gz → 1.9.dev17__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 (125) hide show
  1. {python_sat-1.9.dev16/python_sat.egg-info → python_sat-1.9.dev17}/PKG-INFO +4 -2
  2. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/README.rst +37 -26
  3. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/__init__.py +1 -1
  4. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/card.py +25 -43
  5. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/integer.py +182 -20
  6. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/pb.py +122 -131
  7. {python_sat-1.9.dev16 → python_sat-1.9.dev17/python_sat.egg-info}/PKG-INFO +4 -2
  8. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/python_sat.egg-info/SOURCES.txt +1 -0
  9. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/python_sat.egg-info/requires.txt +4 -1
  10. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/requirements.txt +1 -1
  11. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/setup.py +2 -1
  12. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_atmostk.py +18 -0
  13. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_encode_pb_conditional.py +11 -3
  14. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_integer.py +92 -21
  15. python_sat-1.9.dev17/tests/test_pb.py +72 -0
  16. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/LICENSE.txt +0 -0
  17. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/MANIFEST.in +0 -0
  18. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/allies/__init__.py +0 -0
  19. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/allies/approxmc.py +0 -0
  20. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/allies/unigen.py +0 -0
  21. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/bitwise.hh +0 -0
  22. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/card.hh +0 -0
  23. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/clset.hh +0 -0
  24. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/common.hh +0 -0
  25. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/itot.hh +0 -0
  26. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/ladder.hh +0 -0
  27. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/mto.hh +0 -0
  28. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/pairwise.hh +0 -0
  29. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/ptypes.hh +0 -0
  30. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/pycard.cc +0 -0
  31. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/seqcounter.hh +0 -0
  32. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/sortcard.hh +0 -0
  33. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/cardenc/utils.hh +0 -0
  34. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/__init__.py +0 -0
  35. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/bbscan.py +0 -0
  36. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/bica.py +0 -0
  37. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/fm.py +0 -0
  38. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/genhard.py +0 -0
  39. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/hitman.py +0 -0
  40. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/lbx.py +0 -0
  41. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/lsu.py +0 -0
  42. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/mcsls.py +0 -0
  43. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/models.py +0 -0
  44. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/musx.py +0 -0
  45. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/optux.py +0 -0
  46. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/primer.py +0 -0
  47. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/rc2.py +0 -0
  48. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/examples/usage.py +0 -0
  49. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pf_parse.cc +0 -0
  50. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pf_plus.cc +0 -0
  51. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pf_plus.hh +0 -0
  52. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pf_py.hh +0 -0
  53. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pf_scan.hh +0 -0
  54. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pf_weight.cc +0 -0
  55. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pf_weight.hh +0 -0
  56. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pyformula.cc +0 -0
  57. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/formula/pyformula.hh +0 -0
  58. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/_fileio.py +0 -0
  59. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/_utils.py +0 -0
  60. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/engines.py +0 -0
  61. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/formula.py +0 -0
  62. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/process.py +0 -0
  63. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/pysat/solvers.py +0 -0
  64. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/python_sat.egg-info/dependency_links.txt +0 -0
  65. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/python_sat.egg-info/top_level.txt +0 -0
  66. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/setup.cfg +0 -0
  67. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/cadical103.tar.gz +0 -0
  68. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/cadical153.tar.gz +0 -0
  69. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/cadical195.tar.gz +0 -0
  70. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/cadical300.tar.gz +0 -0
  71. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/ergo.tar.gz +0 -0
  72. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/glucose30.tar.gz +0 -0
  73. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/glucose41.tar.gz +0 -0
  74. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/glucose421.tar.gz +0 -0
  75. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/lingeling.tar.gz +0 -0
  76. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/maplechrono.zip +0 -0
  77. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/maplecm.zip +0 -0
  78. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/maplesat.zip +0 -0
  79. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/mergesat3.tar.gz +0 -0
  80. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/minicard.tar.gz +0 -0
  81. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/minisat22.tar.gz +0 -0
  82. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/minisatgh.zip +0 -0
  83. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/cadical103.patch +0 -0
  84. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/cadical153.patch +0 -0
  85. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/cadical195.patch +0 -0
  86. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/cadical300.patch +0 -0
  87. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/ergo.patch +0 -0
  88. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/glucose30.patch +0 -0
  89. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/glucose41.patch +0 -0
  90. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/glucose421.patch +0 -0
  91. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/gluecard30.patch +0 -0
  92. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/gluecard41.patch +0 -0
  93. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/kissat404.patch +0 -0
  94. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/lingeling.patch +0 -0
  95. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/maplechrono.patch +0 -0
  96. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/maplecm.patch +0 -0
  97. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/maplesat.patch +0 -0
  98. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/mergesat3.patch +0 -0
  99. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/minicard.patch +0 -0
  100. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/minisat22.patch +0 -0
  101. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/minisatep.patch +0 -0
  102. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/patches/minisatgh.patch +0 -0
  103. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/prepare.py +0 -0
  104. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/solvers/pysolvers.cc +0 -0
  105. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_accum_stats.py +0 -0
  106. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_atmost.py +0 -0
  107. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_atmost1.py +0 -0
  108. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_boolengine.py +0 -0
  109. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_clausification.py +0 -0
  110. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_cnf.py +0 -0
  111. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_empty_clause.py +0 -0
  112. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_empty_extclause.py +0 -0
  113. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_equals1.py +0 -0
  114. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_formula_parsing.py +0 -0
  115. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_formula_unique.py +0 -0
  116. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_hitman.py +0 -0
  117. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_idpool.py +0 -0
  118. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_lsu.py +0 -0
  119. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_oom.py +0 -0
  120. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_process.py +0 -0
  121. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_proof.py +0 -0
  122. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_propagate.py +0 -0
  123. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_unique_model.py +0 -0
  124. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_unique_mus.py +0 -0
  125. {python_sat-1.9.dev16 → python_sat-1.9.dev17}/tests/test_warmstart.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.9.dev16
3
+ Version: 1.9.dev17
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
@@ -15,8 +15,10 @@ Provides-Extra: approxmc
15
15
  Requires-Dist: pyapproxmc>=4.1.8; extra == "approxmc"
16
16
  Provides-Extra: cryptosat
17
17
  Requires-Dist: pycryptosat>=5.11.18; extra == "cryptosat"
18
+ Provides-Extra: pb
19
+ Requires-Dist: pindakaas>=0.6.1; extra == "pb"
18
20
  Provides-Extra: pblib
19
- Requires-Dist: pypblib>=0.0.3; extra == "pblib"
21
+ Requires-Dist: pindakaas>=0.6.1; extra == "pblib"
20
22
  Provides-Extra: unigen
21
23
  Requires-Dist: pyunigen>=4.1.20; extra == "unigen"
22
24
  Dynamic: author
@@ -128,18 +128,19 @@ list of cardinality encodings included is the following:
128
128
 
129
129
  Furthermore, PySAT supports a number of encodings of pseudo-Boolean
130
130
  constraints listed below. This is done by exploiting a third-party library
131
- `PyPBLib <https://pypi.org/project/pypblib/>`__ developed by the `Logic
132
- Optimization Group <http://ulog.udl.cat/>`__ of the University of Lleida.
133
- (PyPBLib is a wrapper over the known PBLib library [10]_.)
131
+ `Pindakaas <https://pypi.org/project/pindakaas/>`__ [10]_.
134
132
 
135
133
  - binary decision diagrams (BDD) [11]_ [12]_
136
134
  - sequential weight counters [13]_
137
135
  - sorting networks [11]_
138
136
  - adder networks [11]_
139
- - and binary merge [14]_
137
+ - binary merge [14]_
138
+ - the watchdog encoding [15]_
139
+ - the generalized totalizer [16]_
140
+ - and the mixed radix encoding [17]_
140
141
 
141
- .. [10] Tobias Philipp, Peter Steinke. *PBLib - A Library for Encoding
142
- Pseudo-Boolean Constraints into CNF*. SAT 2015. pp. 9-16
142
+ .. [10] Hendrik Bierlee, Jip J. Dekker. *Pindakaas*. Software.
143
+ https://doi.org/10.5281/zenodo.10851855
143
144
 
144
145
  .. [11] Niklas Eén, Niklas Sörensson. *Translating Pseudo-Boolean
145
146
  Constraints into SAT*. JSAT. vol. 2(1-4). 2006. pp. 1-26
@@ -156,19 +157,29 @@ Optimization Group <http://ulog.udl.cat/>`__ of the University of Lleida.
156
157
  Translation of Pseudo-Boolean Constraints into CNF Such That
157
158
  Generalized Arc Consistency Is Maintained*. KI. 2014. pp. 123-134
158
159
 
160
+ .. [15] Olivier Bailleux, Yacine Boufkhad, Olivier Roussel. *New Encodings
161
+ of Pseudo-Boolean Constraints into CNF*. SAT 2009. pp. 181-194
162
+
163
+ .. [16] Saurabh Joshi, Ruben Martins, Vasco Manquinho. *Generalized Totalizer
164
+ Encoding for Pseudo-Boolean Constraints*. CP 2015. pp. 200-209
165
+
166
+ .. [17] Anpeng Zha, Miyuki Koshimura, Hiroshi Fujita. *N-level Modulo-based
167
+ CNF Encodings of Pseudo-Boolean Constraints for MaxSAT*. Constraints
168
+ 24(2). 2019. pp. 133-161
169
+
159
170
  Finally, PySAT now supports arbitrary Boolean formulas with on-the-fly
160
- clausification [21]_ and provides (pre-)processing functionality [22]_ by
171
+ clausification [24]_ and provides (pre-)processing functionality [25]_ by
161
172
  exposing an interface to CaDiCaL's (version 1.5.3) preprocessor as well as
162
- external user-defined engines following the IPASIR-UP interface [23]_.
173
+ external user-defined engines following the IPASIR-UP interface [26]_.
163
174
 
164
- .. [21] G. S. Tseitin. *On the complexity of derivations in the propositional
175
+ .. [24] G. S. Tseitin. *On the complexity of derivations in the propositional
165
176
  calculus*. Studies in Mathematics and Mathematical Logic, Part II. pp.
166
177
  115–125, 1968
167
178
 
168
- .. [22] Armin Biere, Matti Järvisalo, Benjamin Kiesl. *Preprocessing in SAT
179
+ .. [25] Armin Biere, Matti Järvisalo, Benjamin Kiesl. *Preprocessing in SAT
169
180
  Solving*. In *Handbook of Satisfiability - Second Edition*. pp. 391-435
170
181
 
171
- .. [23] Katalin Fazekas, Aina Niemetz, Mathias Preiner, Markus Kirchweger,
182
+ .. [26] Katalin Fazekas, Aina Niemetz, Mathias Preiner, Markus Kirchweger,
172
183
  Stefan Szeider, Armin Biere. *IPASIR-UP: User Propagators for CDCL*. SAT.
173
184
  2023. pp. 8:1-8:13
174
185
 
@@ -234,29 +245,29 @@ PySAT usage is detailed in the `provided examples
234
245
  <https://github.com/pysathq/pysat/tree/master/examples>`__. For instance, one
235
246
  can find simple PySAT-based implementations of
236
247
 
237
- - Fu&Malik algorithm for MaxSAT [15]_
238
- - RC2/OLLITI algorithm for MaxSAT [19]_ [20]_
239
- - CLD-like algorithm for MCS extraction and enumeration [17]_
240
- - LBX-like algorithm for MCS extraction and enumeration [18]_
241
- - Deletion-based MUS extraction [16]_
248
+ - Fu&Malik algorithm for MaxSAT [18]_
249
+ - RC2/OLLITI algorithm for MaxSAT [22]_ [23]_
250
+ - CLD-like algorithm for MCS extraction and enumeration [20]_
251
+ - LBX-like algorithm for MCS extraction and enumeration [21]_
252
+ - Deletion-based MUS extraction [19]_
242
253
 
243
- .. [15] Zhaohui Fu, Sharad Malik. *On Solving the Partial MAX-SAT Problem*.
254
+ .. [18] Zhaohui Fu, Sharad Malik. *On Solving the Partial MAX-SAT Problem*.
244
255
  SAT 2006. pp. 252-265
245
256
 
246
- .. [16] Joao Marques Silva. *Minimal Unsatisfiability: Models, Algorithms and
257
+ .. [19] Joao Marques Silva. *Minimal Unsatisfiability: Models, Algorithms and
247
258
  Applications*. ISMVL 2010. pp. 9-14
248
259
 
249
- .. [17] Joao Marques-Silva, Federico Heras, Mikolas Janota, Alessandro Previti,
260
+ .. [20] Joao Marques-Silva, Federico Heras, Mikolas Janota, Alessandro Previti,
250
261
  Anton Belov. *On Computing Minimal Correction Subsets*. IJCAI 2013. pp.
251
262
  615-622
252
263
 
253
- .. [18] Carlos Mencia, Alessandro Previti, Joao Marques-Silva. *Literal-Based
264
+ .. [21] Carlos Mencia, Alessandro Previti, Joao Marques-Silva. *Literal-Based
254
265
  MCS Extraction*. IJCAI 2015. pp. 1973-1979
255
266
 
256
- .. [19] António Morgado, Carmine Dodaro, Joao Marques-Silva. *Core-Guided
267
+ .. [22] António Morgado, Carmine Dodaro, Joao Marques-Silva. *Core-Guided
257
268
  MaxSAT with Soft Cardinality Constraints*. CP 2014. pp. 564-573
258
269
 
259
- .. [20] António Morgado, Alexey Ignatiev, Joao Marques-Silva. *MSCG: Robust
270
+ .. [23] António Morgado, Alexey Ignatiev, Joao Marques-Silva. *MSCG: Robust
260
271
  Core-Guided MaxSAT Solving. System Description*. JSAT 2015. vol. 9,
261
272
  pp. 129-134
262
273
 
@@ -313,12 +324,12 @@ using PySAT is to install the latest stable release of the toolkit from `PyPI
313
324
 
314
325
  ::
315
326
 
316
- $ pip install python-sat[aiger,approxmc,cryptosat,pblib]
327
+ $ pip install python-sat[aiger,approxmc,cryptosat,pb]
317
328
 
318
- We encourage you to install the *optional* dependencies `pblib`, `aiger`,
329
+ We encourage you to install the *optional* dependencies `pb`, `aiger`,
319
330
  `approxmc`, and `cryptosat` as in the previous command. However, if it cannot
320
331
  be done (e.g. if their installation fails), you can install PySAT with the
321
- functionality of `aiger`, `approxmc`, `cryptosat`, and `pblib` disabled:
332
+ functionality of `aiger`, `approxmc`, `cryptosat`, and `pb` disabled:
322
333
 
323
334
  ::
324
335
 
@@ -334,7 +345,7 @@ following way:
334
345
  .. note::
335
346
 
336
347
  For some shells, e.g. *zsh*, you may need to put the package names into
337
- single quotes, i.e. use ``pip install 'python-sat[aiger,approxmc,pblib]'``.
348
+ single quotes, i.e. use ``pip install 'python-sat[aiger,approxmc,pb]'``.
338
349
 
339
350
  Alternatively, one can clone `the repository
340
351
  <https://github.com/pysathq/pysat>`__ and execute the following command in the
@@ -10,7 +10,7 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (1, 9, 'dev', 16)
13
+ VERSION = (1, 9, 'dev', 17)
14
14
 
15
15
 
16
16
  # PEP440 Format
@@ -195,46 +195,38 @@ class CardEnc(object):
195
195
  """
196
196
 
197
197
  @classmethod
198
- def _update_vids(cls, cnf, inp, vpool):
198
+ def _update_vids(cls, cnf, top, vpool):
199
199
  """
200
- Update variable ids in the given formula and id pool. The literals
201
- serving as the input to the cardinality constraint are not
202
- updated.
200
+ Update the id pool to account for the auxiliary variables of the
201
+ given formula, i.e. all variables larger than ``top``. If these
202
+ overlap with the ranges marked as occupied in the pool, they are
203
+ renumbered to free ids. Variables up to ``top``, e.g. the input
204
+ literals of the constraint, are not updated.
203
205
 
204
- :param cnf: a list of literals in the sum.
205
- :param inp: the list of input literals
206
- :param vpool: the value of bound :math:`k`.
206
+ :param cnf: the encoding of the constraint
207
+ :param top: the largest variable id of the input
208
+ :param vpool: variable pool to update
207
209
 
208
210
  :type cnf: :class:`.formula.CNFPlus`
209
- :type inp: list(int)
211
+ :type top: int
210
212
  :type vpool: :class:`.formula.IDPool`
211
213
  """
212
214
 
213
- top, vmap = max(inp + [vpool.top]), {} # current top and variable mapping
215
+ # the pool must not hand out the input ids, nor the rest of an
216
+ # occupied range that the input ids reach into
217
+ vpool.top = top
218
+ while vpool._occupied and vpool._occupied[0][0] <= vpool.top:
219
+ vpool.top = max(vpool.top, vpool._occupied[0][1])
220
+ vpool._occupied.pop(0)
214
221
 
215
- # we are going to use this to check if literals are input
216
- inp = set([abs(l) for l in inp])
217
-
218
- # creating a new variable mapping, taking into
219
- # account variables marked as "occupied"
220
- while top < cnf.nv:
221
- top += 1
222
-
223
- # skipping the input literals
224
- if top in inp:
225
- vmap[top] = top
226
- continue
227
-
228
- vpool.top += 1
229
-
230
- while vpool._occupied and vpool.top >= vpool._occupied[0][0]:
231
- if vpool.top <= vpool._occupied[0][1] + 1:
232
- vpool.top = vpool._occupied[0][1] + 1
233
-
234
- vpool._occupied.pop(0)
222
+ if vpool.top == top and not (vpool._occupied and vpool._occupied[0][0] <= cnf.nv):
223
+ # the auxiliary variables already have free ids
224
+ vpool.top = max(vpool.top, cnf.nv)
225
+ return
235
226
 
236
- # mapping this literal to a free one
237
- vmap[top] = vpool.top
227
+ # mapping each auxiliary variable to a free id,
228
+ # taking into account variables marked as "occupied"
229
+ vmap = {var: vpool._next() for var in range(top + 1, cnf.nv + 1)}
238
230
 
239
231
  # updating the clauses
240
232
  for cl in cnf.clauses:
@@ -296,12 +288,7 @@ class CardEnc(object):
296
288
 
297
289
  # updating vpool if necessary
298
290
  if vpool:
299
- if vpool._occupied and vpool.top <= vpool._occupied[0][0] <= ret.nv:
300
- cls._update_vids(ret, lits, vpool)
301
- else:
302
- # here, ret.nv id is assumed to be larger than the top id
303
- vpool.top = ret.nv - 1
304
- vpool._next()
291
+ cls._update_vids(ret, top_id, vpool)
305
292
 
306
293
  return ret
307
294
 
@@ -397,12 +384,7 @@ class CardEnc(object):
397
384
 
398
385
  # updating vpool if necessary
399
386
  if vpool:
400
- if vpool._occupied and vpool.top <= vpool._occupied[0][0] <= ret.nv:
401
- cls._update_vids(ret, lits, vpool)
402
- else:
403
- # here, ret.nv id is assumed to be larger than the top id
404
- vpool.top = ret.nv - 1
405
- vpool._next()
387
+ cls._update_vids(ret, top_id, vpool)
406
388
 
407
389
  return ret
408
390
 
@@ -228,7 +228,9 @@ import numbers
228
228
  from pysat.card import CardEnc, EncType as CardEncType
229
229
  from pysat.engines import BooleanEngine
230
230
  from pysat.formula import CNF, Formula
231
- from pysat.pb import PBEnc, EncType as PBEncType
231
+ from pysat.pb import PBEnc, EncType as PBEncType, NoSuchEncodingError, \
232
+ _pindakaas_db, _pindakaas_cnf
233
+ import pindakaas # after pysat.pb, which checks that it is installed
232
234
 
233
235
 
234
236
  # importable components
@@ -577,7 +579,8 @@ class Integer:
577
579
  """
578
580
 
579
581
  def is_satisfied(lit):
580
- return model[abs(lit) - 1] == lit
582
+ # a literal beyond the model occurs in no clause, so it is free
583
+ return abs(lit) <= len(model) and model[abs(lit) - 1] == lit
581
584
 
582
585
  if self.encoding == 'direct':
583
586
  for value in self.domain:
@@ -752,6 +755,45 @@ class Integer:
752
755
 
753
756
  return wmap, shift
754
757
 
758
+ def _linear_term(self, kind, wmap):
759
+ """
760
+ Inverse of :meth:`linearize` for one kind of literal. Given a map
761
+ from domain values ``v`` to pairs ``(lit, weight)``, where ``lit``
762
+ is the literal of :math:`x \\geq v` (``kind == 'ge'``) or of
763
+ :math:`x = v` (``kind == 'eq'``), possibly negated, return
764
+ ``(coeff, const)`` such that the weighted sum of the literals
765
+ equals ``coeff * X + const``. Return ``None`` if the map does not
766
+ have this form.
767
+
768
+ :param kind: ``'ge'`` or ``'eq'``
769
+ :param wmap: literals and their weights by domain value
770
+ :type kind: str
771
+ :type wmap: dict
772
+
773
+ :rtype: tuple or None
774
+ """
775
+
776
+ if kind == 'ge':
777
+ # x = lb + sum_k [x >= k] and ub - x = sum_k -[x >= k]
778
+ signs = {lit > 0 for lit, _ in wmap.values()}
779
+ wvals = {wght for _, wght in wmap.values()}
780
+ if len(wmap) != self.ub - self.lb or len(signs) != 1 or len(wvals) != 1:
781
+ return None
782
+
783
+ wght = wvals.pop()
784
+ return (wght, -wght * self.lb) if signs.pop() else (-wght, wght * self.ub)
785
+
786
+ # direct literals, the weights must be affine in the values
787
+ if any(lit < 0 for lit, _ in wmap.values()):
788
+ return None
789
+
790
+ wvals = [wmap[v][1] if v in wmap else 0 for v in self.domain]
791
+ coeff = wvals[1] - wvals[0] if len(wvals) > 1 else 0
792
+ if any(w != wvals[0] + coeff * i for i, w in enumerate(wvals)):
793
+ return None
794
+
795
+ return coeff, wvals[0] - coeff * self.lb
796
+
755
797
  def _add_channeling(self):
756
798
  """
757
799
  Clauses linking direct and order encodings.
@@ -2142,26 +2184,45 @@ class IntegerEngine(BooleanEngine):
2142
2184
  self._add_clause([s1, s2], selv=selv)
2143
2185
  return selv
2144
2186
 
2145
- def clausify(self, cardenc=CardEncType.seqcounter, pbenc=PBEncType.best):
2187
+ def clausify(self, cardenc=CardEncType.seqcounter, pbenc=PBEncType.best,
2188
+ intenc=PBEncType.best):
2146
2189
  """
2147
- Clausify all constraints and return a CNF encoding. All linear
2148
- constraints are converted using the user-specified cardinality and
2149
- pseudo-Boolean encoding. The former are utilized for unweighted
2150
- constraints, while the latter are utilized for weighted
2151
- constraints.
2190
+ Clausify all constraints and return a CNF encoding.
2191
+
2192
+ Linear constraints over :class:`Integer` variables are encoded by
2193
+ Pindakaas as *integer* linear constraints, using the encoding
2194
+ ``intenc``. The literals of each :class:`Integer` are passed to
2195
+ Pindakaas as an integer variable, which lets it use integer
2196
+ encodings, e.g. the mixed radix encoding.
2197
+
2198
+ The remaining linear constraints, and all of them if ``intenc``
2199
+ is ``None``, are converted using the user-specified cardinality
2200
+ and pseudo-Boolean encoding. The former are utilized for
2201
+ unweighted constraints, while the latter are utilized for
2202
+ weighted constraints.
2152
2203
 
2153
- Note that parameters ``cardenc`` and ``pbenc`` default to
2154
- :attr:`.card.EncType.seqcounter` and
2155
- :class:`pysat.pb.EncType.best`, respectively.
2204
+ Note that parameters ``cardenc``, ``pbenc`` and ``intenc``
2205
+ default to :attr:`.card.EncType.seqcounter`,
2206
+ :class:`pysat.pb.EncType.best` and
2207
+ :class:`pysat.pb.EncType.best`, respectively. The *native*
2208
+ encodings produce no clauses and raise :class:`ValueError`.
2156
2209
 
2157
2210
  :param cardenc: cardinality encoding for unweighted constraints
2158
2211
  :param pbenc: PB encoding for weighted constraints
2212
+ :param intenc: encoding for integer linear constraints, or ``None``
2159
2213
  :type cardenc: int
2160
2214
  :type pbenc: int
2215
+ :type intenc: int or None
2161
2216
 
2162
2217
  :rtype: :class:`pysat.formula.CNF`
2163
2218
  """
2164
2219
 
2220
+ # native constraints are not clauses, and would be lost
2221
+ if cardenc == CardEncType.native or PBEncType.native in (pbenc, intenc):
2222
+ raise ValueError('Native encodings cannot be clausified')
2223
+ if intenc is not None and intenc not in PBEncType._to_encoder:
2224
+ raise NoSuchEncodingError(intenc)
2225
+
2165
2226
  # we are going to store the resulting formula here
2166
2227
  res = CNF()
2167
2228
 
@@ -2184,8 +2245,19 @@ class IntegerEngine(BooleanEngine):
2184
2245
  # (e.g. reified disjunctions) already cached in the engine
2185
2246
  res.extend(self.clauses)
2186
2247
 
2248
+ # separating integer linear constraints from Boolean ones
2249
+ lcons, iconss = self._lcons, []
2250
+ if intenc is not None:
2251
+ lcons = []
2252
+ for cs in self._lcons:
2253
+ icons = self._integer_terms(cs, vpool)
2254
+ if icons:
2255
+ iconss.append(icons)
2256
+ else:
2257
+ lcons.append(cs)
2258
+
2187
2259
  # the main part is to encode all linear constraints
2188
- for cs in self._lcons:
2260
+ for cs in lcons:
2189
2261
  lits, bound = cs[1][0], cs[1][1]
2190
2262
  weights = {} if len(cs[1]) == 2 else cs[1][2]
2191
2263
 
@@ -2200,13 +2272,11 @@ class IntegerEngine(BooleanEngine):
2200
2272
 
2201
2273
  # we have a common weight wght => cardinality constraint
2202
2274
  if wght is not None:
2203
- new_bound = bound // wght
2204
- if isinstance(new_bound, Decimal):
2205
- new_bound = int(new_bound)
2206
- elif isinstance(new_bound, float):
2207
- new_bound = int(math.floor(new_bound))
2275
+ if isinstance(bound, Decimal):
2276
+ # Decimal's // rounds towards zero, not down
2277
+ new_bound = math.floor(bound / wght)
2208
2278
  else:
2209
- new_bound = int(new_bound)
2279
+ new_bound = int(math.floor(bound // wght))
2210
2280
 
2211
2281
  if new_bound < 0:
2212
2282
  # the bound is negative => constraint is unsatisfiable
@@ -2235,20 +2305,112 @@ class IntegerEngine(BooleanEngine):
2235
2305
  wlist = [int(w) if not isinstance(w, Decimal) else int(w)
2236
2306
  for w in wlist]
2237
2307
 
2308
+ if bound_int < 0:
2309
+ # the weights are non-negative => unsatisfiable
2310
+ res.append([])
2311
+ continue
2312
+
2238
2313
  enc = PBEnc.atmost(lits=lits, weights=wlist, bound=bound_int,
2239
2314
  vpool=vpool, encoding=pbenc)
2240
2315
  res.extend(enc.clauses)
2241
2316
 
2317
+ # integer linear constraints go last, as they share one encoding
2318
+ if iconss:
2319
+ res.extend(self._clausify_integer(iconss, vpool, intenc))
2320
+
2242
2321
  return res
2243
2322
 
2323
+ def _integer_terms(self, cs, vpool):
2324
+ """
2325
+ Recover the integer structure of a linear constraint. The
2326
+ literals of each :class:`Integer` recognized by
2327
+ :meth:`Integer._linear_term` become a single integer term.
2328
+ Return a triple ``(terms, rest, bound)``, where ``terms`` is a
2329
+ list of ``(var, kind, coeff)``, ``rest`` is a list of the
2330
+ remaining weighted literals, and the constraint is ``terms +
2331
+ rest <= bound``. Return ``None`` if there are no integer terms or
2332
+ if the numbers are not integers.
2333
+ """
2334
+
2335
+ _, _, bound, lits, weights = self._linear_key(cs)
2336
+
2337
+ if not self._intlike(bound) or not all(self._intlike(weights[l]) for l in lits):
2338
+ return None
2339
+
2340
+ # grouping the literals by their integer variable and kind
2341
+ groups, rest = defaultdict(dict), []
2342
+ for l in lits:
2343
+ obj, wght = vpool.obj(abs(l)), int(weights[l])
2344
+ if isinstance(obj, tuple) and len(obj) == 3 and isinstance(obj[0], Integer):
2345
+ group = groups[obj[0], obj[1]]
2346
+ if obj[2] not in group:
2347
+ group[obj[2]] = (l, wght)
2348
+ continue
2349
+
2350
+ # a Boolean literal, or the negation of one already in its group
2351
+ rest.append((l, wght))
2352
+
2353
+ terms, bound = [], int(bound)
2354
+ for (var, kind), wmap in groups.items():
2355
+ term = var._linear_term(kind, wmap)
2356
+ if term is None:
2357
+ rest.extend(wmap.values())
2358
+ else:
2359
+ coeff, const = term
2360
+ if coeff:
2361
+ terms.append((var, kind, coeff))
2362
+ bound -= const
2363
+
2364
+ return (terms, rest, bound) if terms else None
2365
+
2366
+ def _clausify_integer(self, iconss, vpool, intenc):
2367
+ """
2368
+ Encode integer linear constraints, as returned by
2369
+ :meth:`_integer_terms`, with Pindakaas. Return the list of
2370
+ clauses.
2371
+ """
2372
+
2373
+ encoder, options = PBEncType._to_encoder[intenc]
2374
+ Lit = pindakaas.Lit.from_raw
2375
+
2376
+ # new variables must also avoid literals that are not in the pool
2377
+ top_id = max([vpool.top] + [abs(l) for _, rest, _ in iconss for l, _ in rest])
2378
+ db = _pindakaas_db(top_id)
2379
+ ivars = {} # integer variables, created lazily
2380
+
2381
+ def ivar(var, kind):
2382
+ if (var, kind) not in ivars:
2383
+ if kind == 'ge':
2384
+ lits = [var.ge(k) for k in range(var.lb + 1, var.ub + 1)]
2385
+ make = db.int_var_from_order_literals
2386
+ else:
2387
+ lits = [var.equals(v) for v in var.domain]
2388
+ make = db.int_var_from_direct_literals
2389
+ ivars[var, kind] = make(var.domain, [Lit(l) for l in lits])
2390
+ return ivars[var, kind]
2391
+
2392
+ try:
2393
+ for terms, rest, bound in iconss:
2394
+ lhs = 0
2395
+ for var, kind, coeff in terms:
2396
+ lhs += coeff * ivar(var, kind)
2397
+ for l, w in rest:
2398
+ lhs += w * Lit(l)
2399
+
2400
+ db.add_encoding(lhs <= bound, encoder, **options)
2401
+ except pindakaas.Unsatisfiable:
2402
+ return [[]]
2403
+
2404
+ return _pindakaas_cnf(db, top_id, vpool=vpool).clauses
2405
+
2244
2406
  @staticmethod
2245
2407
  def _intlike(value):
2246
2408
  """
2247
2409
  Checking if the value can be converted to integer.
2248
2410
  """
2249
2411
 
2250
- # yes, nothing to do
2251
- if isinstance(value, numbers.Integral):
2412
+ # yes, nothing to do (plain ints first, as the ABC check is slow)
2413
+ if type(value) is int or isinstance(value, numbers.Integral):
2252
2414
  return True
2253
2415
 
2254
2416
  # yes, it's a convertable decimal