python-sat 1.9.dev15__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.
- {python_sat-1.9.dev15/python_sat.egg-info → python_sat-1.9.dev17}/PKG-INFO +4 -2
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/README.rst +37 -26
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/rc2.py +25 -17
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/__init__.py +1 -1
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/card.py +25 -43
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/integer.py +182 -20
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/pb.py +122 -131
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/solvers.py +1004 -58
- {python_sat-1.9.dev15 → python_sat-1.9.dev17/python_sat.egg-info}/PKG-INFO +4 -2
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/python_sat.egg-info/SOURCES.txt +5 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/python_sat.egg-info/requires.txt +4 -1
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/requirements.txt +1 -1
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/setup.py +6 -2
- python_sat-1.9.dev17/solvers/ergo.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/cadical153.patch +3 -3
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/cadical195.patch +3 -3
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/cadical300.patch +3 -3
- python_sat-1.9.dev17/solvers/patches/ergo.patch +646 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/prepare.py +17 -2
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/pysolvers.cc +1609 -183
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_accum_stats.py +5 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_atmostk.py +18 -0
- python_sat-1.9.dev17/tests/test_boolengine.py +97 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_cnf.py +5 -0
- python_sat-1.9.dev17/tests/test_empty_clause.py +32 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_encode_pb_conditional.py +11 -3
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_integer.py +92 -21
- python_sat-1.9.dev17/tests/test_pb.py +72 -0
- python_sat-1.9.dev17/tests/test_proof.py +40 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_propagate.py +50 -4
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_unique_model.py +5 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_warmstart.py +2 -1
- python_sat-1.9.dev15/tests/test_boolengine.py +0 -34
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/LICENSE.txt +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/MANIFEST.in +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/allies/__init__.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/allies/approxmc.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/allies/unigen.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/bitwise.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/card.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/clset.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/common.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/itot.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/ladder.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/mto.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/pairwise.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/ptypes.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/pycard.cc +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/seqcounter.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/sortcard.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/cardenc/utils.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/__init__.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/bbscan.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/bica.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/fm.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/genhard.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/hitman.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/lbx.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/lsu.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/mcsls.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/models.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/musx.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/optux.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/primer.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/examples/usage.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pf_parse.cc +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pf_plus.cc +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pf_plus.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pf_py.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pf_scan.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pf_weight.cc +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pf_weight.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pyformula.cc +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/formula/pyformula.hh +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/_fileio.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/_utils.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/engines.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/formula.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/pysat/process.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/python_sat.egg-info/dependency_links.txt +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/python_sat.egg-info/top_level.txt +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/setup.cfg +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/cadical103.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/cadical153.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/cadical195.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/cadical300.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/glucose30.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/glucose41.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/glucose421.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/lingeling.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/maplechrono.zip +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/maplecm.zip +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/maplesat.zip +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/mergesat3.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/minicard.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/minisat22.tar.gz +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/minisatgh.zip +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/cadical103.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/glucose30.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/glucose41.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/glucose421.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/gluecard30.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/gluecard41.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/kissat404.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/lingeling.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/maplechrono.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/maplecm.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/maplesat.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/mergesat3.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/minicard.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/minisat22.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/minisatep.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/solvers/patches/minisatgh.patch +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_atmost.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_atmost1.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_clausification.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_empty_extclause.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_equals1.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_formula_parsing.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_formula_unique.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_hitman.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_idpool.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_lsu.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_oom.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_process.py +0 -0
- {python_sat-1.9.dev15 → python_sat-1.9.dev17}/tests/test_unique_mus.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-sat
|
|
3
|
-
Version: 1.9.
|
|
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:
|
|
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
|
-
`
|
|
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
|
-
-
|
|
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]
|
|
142
|
-
|
|
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 [
|
|
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 [
|
|
173
|
+
external user-defined engines following the IPASIR-UP interface [26]_.
|
|
163
174
|
|
|
164
|
-
.. [
|
|
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
|
-
.. [
|
|
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
|
-
.. [
|
|
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 [
|
|
238
|
-
- RC2/OLLITI algorithm for MaxSAT [
|
|
239
|
-
- CLD-like algorithm for MCS extraction and enumeration [
|
|
240
|
-
- LBX-like algorithm for MCS extraction and enumeration [
|
|
241
|
-
- Deletion-based MUS extraction [
|
|
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
|
-
.. [
|
|
254
|
+
.. [18] Zhaohui Fu, Sharad Malik. *On Solving the Partial MAX-SAT Problem*.
|
|
244
255
|
SAT 2006. pp. 252-265
|
|
245
256
|
|
|
246
|
-
.. [
|
|
257
|
+
.. [19] Joao Marques Silva. *Minimal Unsatisfiability: Models, Algorithms and
|
|
247
258
|
Applications*. ISMVL 2010. pp. 9-14
|
|
248
259
|
|
|
249
|
-
.. [
|
|
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
|
-
.. [
|
|
264
|
+
.. [21] Carlos Mencia, Alessandro Previti, Joao Marques-Silva. *Literal-Based
|
|
254
265
|
MCS Extraction*. IJCAI 2015. pp. 1973-1979
|
|
255
266
|
|
|
256
|
-
.. [
|
|
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
|
-
.. [
|
|
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,
|
|
327
|
+
$ pip install python-sat[aiger,approxmc,cryptosat,pb]
|
|
317
328
|
|
|
318
|
-
We encourage you to install the *optional* dependencies `
|
|
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 `
|
|
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,
|
|
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
|
|
@@ -237,6 +237,7 @@ class RC2(object):
|
|
|
237
237
|
self.bnds = {} # a mapping from sum assumptions to totalizer bounds
|
|
238
238
|
self.tobj = {} # a mapping from sum assumptions to totalizer objects
|
|
239
239
|
self.swgt = {} # a mapping from sum assumptions to their core weights
|
|
240
|
+
self.filt = [] # contextual assumptions (useless in RC2)
|
|
240
241
|
self.cost = 0
|
|
241
242
|
|
|
242
243
|
# mappings between internal and external variables
|
|
@@ -440,7 +441,7 @@ class RC2(object):
|
|
|
440
441
|
# introduce new variables if necessary
|
|
441
442
|
cl = list(map(lambda l: self._map_extlit(l), clause if not len(clause) == 2 or not type(clause[0]) in (list, tuple, set) else clause[0]))
|
|
442
443
|
|
|
443
|
-
if
|
|
444
|
+
if weight is None:
|
|
444
445
|
if not len(clause) == 2 or not type(clause[0]) in (list, tuple, set):
|
|
445
446
|
# the clause is hard, and so we simply add it to the SAT oracle
|
|
446
447
|
self.oracle.add_clause(cl)
|
|
@@ -464,17 +465,18 @@ class RC2(object):
|
|
|
464
465
|
cl.append(-selv)
|
|
465
466
|
self.oracle.add_clause(cl)
|
|
466
467
|
|
|
467
|
-
if
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
468
|
+
if weight:
|
|
469
|
+
if selv not in self.wght:
|
|
470
|
+
# record selector and its weight
|
|
471
|
+
self.sels.append(selv)
|
|
472
|
+
self.wght[selv] = weight
|
|
473
|
+
self.smap[selv] = len(self.sels) - 1
|
|
474
|
+
else:
|
|
475
|
+
# selector is not new; increment its weight
|
|
476
|
+
self.wght[selv] += weight
|
|
475
477
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
+
self.sall.append(selv)
|
|
479
|
+
self.sels_set.add(selv)
|
|
478
480
|
|
|
479
481
|
return selv
|
|
480
482
|
|
|
@@ -658,6 +660,13 @@ class RC2(object):
|
|
|
658
660
|
else:
|
|
659
661
|
done = True
|
|
660
662
|
|
|
663
|
+
def active_assumps(self):
|
|
664
|
+
"""
|
|
665
|
+
Get the assumptions for the main core-guided loop.
|
|
666
|
+
"""
|
|
667
|
+
|
|
668
|
+
return self.sels + self.sums
|
|
669
|
+
|
|
661
670
|
def compute_(self):
|
|
662
671
|
"""
|
|
663
672
|
Main core-guided loop, which iteratively calls a SAT
|
|
@@ -679,7 +688,7 @@ class RC2(object):
|
|
|
679
688
|
self.interrupted = False
|
|
680
689
|
|
|
681
690
|
# main solving loop
|
|
682
|
-
while not self._call_oracle(assumptions=self.
|
|
691
|
+
while not self._call_oracle(assumptions=self.active_assumps(),
|
|
683
692
|
expect_interrupt=self.expect_interrupt):
|
|
684
693
|
|
|
685
694
|
# even if the call has been interrupted, we
|
|
@@ -1055,7 +1064,7 @@ class RC2(object):
|
|
|
1055
1064
|
"""
|
|
1056
1065
|
|
|
1057
1066
|
# the first case is simpler
|
|
1058
|
-
if self._call_oracle(assumptions=[-tobj.rhs[1]]):
|
|
1067
|
+
if self._call_oracle(assumptions=self.filt + [-tobj.rhs[1]]):
|
|
1059
1068
|
return 1
|
|
1060
1069
|
else:
|
|
1061
1070
|
self.cost += self.minw
|
|
@@ -1068,7 +1077,7 @@ class RC2(object):
|
|
|
1068
1077
|
# increasing the bound
|
|
1069
1078
|
self.update_sum(-tobj.rhs[i - 1])
|
|
1070
1079
|
|
|
1071
|
-
if self._call_oracle(assumptions=[-tobj.rhs[i]]):
|
|
1080
|
+
if self._call_oracle(assumptions=self.filt + [-tobj.rhs[i]]):
|
|
1072
1081
|
# the bound should be equal to i
|
|
1073
1082
|
return i
|
|
1074
1083
|
|
|
@@ -1244,10 +1253,9 @@ class RC2(object):
|
|
|
1244
1253
|
# right-hand side is always equal to the number of input literals
|
|
1245
1254
|
rhs = len(t.lits)
|
|
1246
1255
|
|
|
1247
|
-
if b < rhs:
|
|
1256
|
+
if b < rhs and not t.rhs[b]:
|
|
1248
1257
|
# creating an additional bound
|
|
1249
|
-
|
|
1250
|
-
t.rhs[b] = self.pool.id()
|
|
1258
|
+
t.rhs[b] = self.pool.id()
|
|
1251
1259
|
|
|
1252
1260
|
# a new at-most-b constraint
|
|
1253
1261
|
amb = [[-t.rhs[b]] * (rhs - b) + t.lits, rhs]
|
|
@@ -195,46 +195,38 @@ class CardEnc(object):
|
|
|
195
195
|
"""
|
|
196
196
|
|
|
197
197
|
@classmethod
|
|
198
|
-
def _update_vids(cls, cnf,
|
|
198
|
+
def _update_vids(cls, cnf, top, vpool):
|
|
199
199
|
"""
|
|
200
|
-
Update
|
|
201
|
-
|
|
202
|
-
|
|
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:
|
|
205
|
-
:param
|
|
206
|
-
:param vpool:
|
|
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
|
|
211
|
+
:type top: int
|
|
210
212
|
:type vpool: :class:`.formula.IDPool`
|
|
211
213
|
"""
|
|
212
214
|
|
|
213
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
237
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
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 ``
|
|
2154
|
-
:attr:`.card.EncType.seqcounter
|
|
2155
|
-
:class:`pysat.pb.EncType.best
|
|
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
|
|
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
|
-
|
|
2204
|
-
|
|
2205
|
-
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(
|
|
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
|