Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +4 -0
- phivenv/Lib/site-packages/numpy/random/_bounded_integers.cp39-win_amd64.pyd +3 -0
- phivenv/Lib/site-packages/numpy/random/_common.cp39-win_amd64.pyd +3 -0
- phivenv/Lib/site-packages/numpy/random/_generator.cp39-win_amd64.pyd +3 -0
- phivenv/Lib/site-packages/numpy/random/lib/npyrandom.lib +3 -0
- phivenv/Lib/site-packages/sympy/logic/__pycache__/__init__.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/__pycache__/inference.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__init__.py +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/__init__.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/dpll.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/dpll2.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/lra_theory.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/minisat22_wrapper.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/pycosat_wrapper.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/z3_wrapper.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/dpll2.py +688 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/lra_theory.py +912 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/minisat22_wrapper.py +46 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/pycosat_wrapper.py +41 -0
- phivenv/Lib/site-packages/sympy/logic/algorithms/z3_wrapper.py +115 -0
- phivenv/Lib/site-packages/sympy/logic/tests/__init__.py +0 -0
- phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/__init__.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_boolalg.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_dimacs.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_inference.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_lra_theory.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/tests/test_boolalg.py +1367 -0
- phivenv/Lib/site-packages/sympy/logic/tests/test_dimacs.py +234 -0
- phivenv/Lib/site-packages/sympy/logic/tests/test_inference.py +396 -0
- phivenv/Lib/site-packages/sympy/logic/tests/test_lra_theory.py +440 -0
- phivenv/Lib/site-packages/sympy/logic/utilities/__init__.py +3 -0
- phivenv/Lib/site-packages/sympy/logic/utilities/__pycache__/__init__.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/utilities/__pycache__/dimacs.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/logic/utilities/dimacs.py +69 -0
- phivenv/Lib/site-packages/sympy/matrices/__init__.py +72 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/__init__.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/decompositions.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/dense.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/determinant.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/eigen.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/exceptions.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/graph.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/immutable.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/inverse.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/kind.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/matrices.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/normalforms.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/reductions.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/repmatrix.cpython-39.pyc +0 -0
- phivenv/Lib/site-packages/sympy/matrices/__pycache__/solvers.cpython-39.pyc +0 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,7 @@ phivenv/Lib/site-packages/numpy/ma/tests/__pycache__/test_core.cpython-39.pyc fi
|
|
| 33 |
phivenv/Lib/site-packages/numpy/ma/__pycache__/core.cpython-39.pyc filter=lfs diff=lfs merge=lfs -text
|
| 34 |
phivenv/Lib/site-packages/numpy/random/bit_generator.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 35 |
phivenv/Lib/site-packages/numpy/random/mtrand.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
phivenv/Lib/site-packages/numpy/ma/__pycache__/core.cpython-39.pyc filter=lfs diff=lfs merge=lfs -text
|
| 34 |
phivenv/Lib/site-packages/numpy/random/bit_generator.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 35 |
phivenv/Lib/site-packages/numpy/random/mtrand.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
phivenv/Lib/site-packages/numpy/random/_common.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
phivenv/Lib/site-packages/numpy/random/_bounded_integers.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
phivenv/Lib/site-packages/numpy/random/lib/npyrandom.lib filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
phivenv/Lib/site-packages/numpy/random/_generator.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
phivenv/Lib/site-packages/numpy/random/_bounded_integers.cp39-win_amd64.pyd
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5e38cea16c127bbfb7291b60468e9500f45bbdc6b63f83d3240109d02fd00e44
|
| 3 |
+
size 252928
|
phivenv/Lib/site-packages/numpy/random/_common.cp39-win_amd64.pyd
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a4eceb8cd34b3ecf695c4252df85452c31f3e4e0a2cb43cfc62f70f21e35d46c
|
| 3 |
+
size 176640
|
phivenv/Lib/site-packages/numpy/random/_generator.cp39-win_amd64.pyd
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0831522b7e15b6b0888586a5bb296d093c4f771b314ec812caa261f96189d464
|
| 3 |
+
size 754688
|
phivenv/Lib/site-packages/numpy/random/lib/npyrandom.lib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6046451e469e3f9257f302b8a7b90e1fe9c20b4635113e0ab4497da3e36dc71f
|
| 3 |
+
size 147796
|
phivenv/Lib/site-packages/sympy/logic/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (666 Bytes). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/__pycache__/inference.cpython-39.pyc
ADDED
|
Binary file (9.19 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__init__.py
ADDED
|
File without changes
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (166 Bytes). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/dpll.cpython-39.pyc
ADDED
|
Binary file (7.96 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/dpll2.cpython-39.pyc
ADDED
|
Binary file (18 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/lra_theory.cpython-39.pyc
ADDED
|
Binary file (29.5 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/minisat22_wrapper.cpython-39.pyc
ADDED
|
Binary file (1.94 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/pycosat_wrapper.cpython-39.pyc
ADDED
|
Binary file (1.41 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/__pycache__/z3_wrapper.cpython-39.pyc
ADDED
|
Binary file (4.15 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/algorithms/dpll2.py
ADDED
|
@@ -0,0 +1,688 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of DPLL algorithm
|
| 2 |
+
|
| 3 |
+
Features:
|
| 4 |
+
- Clause learning
|
| 5 |
+
- Watch literal scheme
|
| 6 |
+
- VSIDS heuristic
|
| 7 |
+
|
| 8 |
+
References:
|
| 9 |
+
- https://en.wikipedia.org/wiki/DPLL_algorithm
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
from collections import defaultdict
|
| 13 |
+
from heapq import heappush, heappop
|
| 14 |
+
|
| 15 |
+
from sympy.core.sorting import ordered
|
| 16 |
+
from sympy.assumptions.cnf import EncodedCNF
|
| 17 |
+
|
| 18 |
+
from sympy.logic.algorithms.lra_theory import LRASolver
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def dpll_satisfiable(expr, all_models=False, use_lra_theory=False):
|
| 22 |
+
"""
|
| 23 |
+
Check satisfiability of a propositional sentence.
|
| 24 |
+
It returns a model rather than True when it succeeds.
|
| 25 |
+
Returns a generator of all models if all_models is True.
|
| 26 |
+
|
| 27 |
+
Examples
|
| 28 |
+
========
|
| 29 |
+
|
| 30 |
+
>>> from sympy.abc import A, B
|
| 31 |
+
>>> from sympy.logic.algorithms.dpll2 import dpll_satisfiable
|
| 32 |
+
>>> dpll_satisfiable(A & ~B)
|
| 33 |
+
{A: True, B: False}
|
| 34 |
+
>>> dpll_satisfiable(A & ~A)
|
| 35 |
+
False
|
| 36 |
+
|
| 37 |
+
"""
|
| 38 |
+
if not isinstance(expr, EncodedCNF):
|
| 39 |
+
exprs = EncodedCNF()
|
| 40 |
+
exprs.add_prop(expr)
|
| 41 |
+
expr = exprs
|
| 42 |
+
|
| 43 |
+
# Return UNSAT when False (encoded as 0) is present in the CNF
|
| 44 |
+
if {0} in expr.data:
|
| 45 |
+
if all_models:
|
| 46 |
+
return (f for f in [False])
|
| 47 |
+
return False
|
| 48 |
+
|
| 49 |
+
if use_lra_theory:
|
| 50 |
+
lra, immediate_conflicts = LRASolver.from_encoded_cnf(expr)
|
| 51 |
+
else:
|
| 52 |
+
lra = None
|
| 53 |
+
immediate_conflicts = []
|
| 54 |
+
solver = SATSolver(expr.data + immediate_conflicts, expr.variables, set(), expr.symbols, lra_theory=lra)
|
| 55 |
+
models = solver._find_model()
|
| 56 |
+
|
| 57 |
+
if all_models:
|
| 58 |
+
return _all_models(models)
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
return next(models)
|
| 62 |
+
except StopIteration:
|
| 63 |
+
return False
|
| 64 |
+
|
| 65 |
+
# Uncomment to confirm the solution is valid (hitting set for the clauses)
|
| 66 |
+
#else:
|
| 67 |
+
#for cls in clauses_int_repr:
|
| 68 |
+
#assert solver.var_settings.intersection(cls)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def _all_models(models):
|
| 72 |
+
satisfiable = False
|
| 73 |
+
try:
|
| 74 |
+
while True:
|
| 75 |
+
yield next(models)
|
| 76 |
+
satisfiable = True
|
| 77 |
+
except StopIteration:
|
| 78 |
+
if not satisfiable:
|
| 79 |
+
yield False
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class SATSolver:
|
| 83 |
+
"""
|
| 84 |
+
Class for representing a SAT solver capable of
|
| 85 |
+
finding a model to a boolean theory in conjunctive
|
| 86 |
+
normal form.
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
def __init__(self, clauses, variables, var_settings, symbols=None,
|
| 90 |
+
heuristic='vsids', clause_learning='none', INTERVAL=500,
|
| 91 |
+
lra_theory = None):
|
| 92 |
+
|
| 93 |
+
self.var_settings = var_settings
|
| 94 |
+
self.heuristic = heuristic
|
| 95 |
+
self.is_unsatisfied = False
|
| 96 |
+
self._unit_prop_queue = []
|
| 97 |
+
self.update_functions = []
|
| 98 |
+
self.INTERVAL = INTERVAL
|
| 99 |
+
|
| 100 |
+
if symbols is None:
|
| 101 |
+
self.symbols = list(ordered(variables))
|
| 102 |
+
else:
|
| 103 |
+
self.symbols = symbols
|
| 104 |
+
|
| 105 |
+
self._initialize_variables(variables)
|
| 106 |
+
self._initialize_clauses(clauses)
|
| 107 |
+
|
| 108 |
+
if 'vsids' == heuristic:
|
| 109 |
+
self._vsids_init()
|
| 110 |
+
self.heur_calculate = self._vsids_calculate
|
| 111 |
+
self.heur_lit_assigned = self._vsids_lit_assigned
|
| 112 |
+
self.heur_lit_unset = self._vsids_lit_unset
|
| 113 |
+
self.heur_clause_added = self._vsids_clause_added
|
| 114 |
+
|
| 115 |
+
# Note: Uncomment this if/when clause learning is enabled
|
| 116 |
+
#self.update_functions.append(self._vsids_decay)
|
| 117 |
+
|
| 118 |
+
else:
|
| 119 |
+
raise NotImplementedError
|
| 120 |
+
|
| 121 |
+
if 'simple' == clause_learning:
|
| 122 |
+
self.add_learned_clause = self._simple_add_learned_clause
|
| 123 |
+
self.compute_conflict = self._simple_compute_conflict
|
| 124 |
+
self.update_functions.append(self._simple_clean_clauses)
|
| 125 |
+
elif 'none' == clause_learning:
|
| 126 |
+
self.add_learned_clause = lambda x: None
|
| 127 |
+
self.compute_conflict = lambda: None
|
| 128 |
+
else:
|
| 129 |
+
raise NotImplementedError
|
| 130 |
+
|
| 131 |
+
# Create the base level
|
| 132 |
+
self.levels = [Level(0)]
|
| 133 |
+
self._current_level.varsettings = var_settings
|
| 134 |
+
|
| 135 |
+
# Keep stats
|
| 136 |
+
self.num_decisions = 0
|
| 137 |
+
self.num_learned_clauses = 0
|
| 138 |
+
self.original_num_clauses = len(self.clauses)
|
| 139 |
+
|
| 140 |
+
self.lra = lra_theory
|
| 141 |
+
|
| 142 |
+
def _initialize_variables(self, variables):
|
| 143 |
+
"""Set up the variable data structures needed."""
|
| 144 |
+
self.sentinels = defaultdict(set)
|
| 145 |
+
self.occurrence_count = defaultdict(int)
|
| 146 |
+
self.variable_set = [False] * (len(variables) + 1)
|
| 147 |
+
|
| 148 |
+
def _initialize_clauses(self, clauses):
|
| 149 |
+
"""Set up the clause data structures needed.
|
| 150 |
+
|
| 151 |
+
For each clause, the following changes are made:
|
| 152 |
+
- Unit clauses are queued for propagation right away.
|
| 153 |
+
- Non-unit clauses have their first and last literals set as sentinels.
|
| 154 |
+
- The number of clauses a literal appears in is computed.
|
| 155 |
+
"""
|
| 156 |
+
self.clauses = [list(clause) for clause in clauses]
|
| 157 |
+
|
| 158 |
+
for i, clause in enumerate(self.clauses):
|
| 159 |
+
|
| 160 |
+
# Handle the unit clauses
|
| 161 |
+
if 1 == len(clause):
|
| 162 |
+
self._unit_prop_queue.append(clause[0])
|
| 163 |
+
continue
|
| 164 |
+
|
| 165 |
+
self.sentinels[clause[0]].add(i)
|
| 166 |
+
self.sentinels[clause[-1]].add(i)
|
| 167 |
+
|
| 168 |
+
for lit in clause:
|
| 169 |
+
self.occurrence_count[lit] += 1
|
| 170 |
+
|
| 171 |
+
def _find_model(self):
|
| 172 |
+
"""
|
| 173 |
+
Main DPLL loop. Returns a generator of models.
|
| 174 |
+
|
| 175 |
+
Variables are chosen successively, and assigned to be either
|
| 176 |
+
True or False. If a solution is not found with this setting,
|
| 177 |
+
the opposite is chosen and the search continues. The solver
|
| 178 |
+
halts when every variable has a setting.
|
| 179 |
+
|
| 180 |
+
Examples
|
| 181 |
+
========
|
| 182 |
+
|
| 183 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 184 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 185 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 186 |
+
>>> list(l._find_model())
|
| 187 |
+
[{1: True, 2: False, 3: False}, {1: True, 2: True, 3: True}]
|
| 188 |
+
|
| 189 |
+
>>> from sympy.abc import A, B, C
|
| 190 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 191 |
+
... {3, -2}], {1, 2, 3}, set(), [A, B, C])
|
| 192 |
+
>>> list(l._find_model())
|
| 193 |
+
[{A: True, B: False, C: False}, {A: True, B: True, C: True}]
|
| 194 |
+
|
| 195 |
+
"""
|
| 196 |
+
|
| 197 |
+
# We use this variable to keep track of if we should flip a
|
| 198 |
+
# variable setting in successive rounds
|
| 199 |
+
flip_var = False
|
| 200 |
+
|
| 201 |
+
# Check if unit prop says the theory is unsat right off the bat
|
| 202 |
+
self._simplify()
|
| 203 |
+
if self.is_unsatisfied:
|
| 204 |
+
return
|
| 205 |
+
|
| 206 |
+
# While the theory still has clauses remaining
|
| 207 |
+
while True:
|
| 208 |
+
# Perform cleanup / fixup at regular intervals
|
| 209 |
+
if self.num_decisions % self.INTERVAL == 0:
|
| 210 |
+
for func in self.update_functions:
|
| 211 |
+
func()
|
| 212 |
+
|
| 213 |
+
if flip_var:
|
| 214 |
+
# We have just backtracked and we are trying to opposite literal
|
| 215 |
+
flip_var = False
|
| 216 |
+
lit = self._current_level.decision
|
| 217 |
+
|
| 218 |
+
else:
|
| 219 |
+
# Pick a literal to set
|
| 220 |
+
lit = self.heur_calculate()
|
| 221 |
+
self.num_decisions += 1
|
| 222 |
+
|
| 223 |
+
# Stopping condition for a satisfying theory
|
| 224 |
+
if 0 == lit:
|
| 225 |
+
|
| 226 |
+
# check if assignment satisfies lra theory
|
| 227 |
+
if self.lra:
|
| 228 |
+
for enc_var in self.var_settings:
|
| 229 |
+
res = self.lra.assert_lit(enc_var)
|
| 230 |
+
if res is not None:
|
| 231 |
+
break
|
| 232 |
+
res = self.lra.check()
|
| 233 |
+
self.lra.reset_bounds()
|
| 234 |
+
else:
|
| 235 |
+
res = None
|
| 236 |
+
if res is None or res[0]:
|
| 237 |
+
yield {self.symbols[abs(lit) - 1]:
|
| 238 |
+
lit > 0 for lit in self.var_settings}
|
| 239 |
+
else:
|
| 240 |
+
self._simple_add_learned_clause(res[1])
|
| 241 |
+
|
| 242 |
+
# backtrack until we unassign one of the literals causing the conflict
|
| 243 |
+
while not any(-lit in res[1] for lit in self._current_level.var_settings):
|
| 244 |
+
self._undo()
|
| 245 |
+
|
| 246 |
+
while self._current_level.flipped:
|
| 247 |
+
self._undo()
|
| 248 |
+
if len(self.levels) == 1:
|
| 249 |
+
return
|
| 250 |
+
flip_lit = -self._current_level.decision
|
| 251 |
+
self._undo()
|
| 252 |
+
self.levels.append(Level(flip_lit, flipped=True))
|
| 253 |
+
flip_var = True
|
| 254 |
+
continue
|
| 255 |
+
|
| 256 |
+
# Start the new decision level
|
| 257 |
+
self.levels.append(Level(lit))
|
| 258 |
+
|
| 259 |
+
# Assign the literal, updating the clauses it satisfies
|
| 260 |
+
self._assign_literal(lit)
|
| 261 |
+
|
| 262 |
+
# _simplify the theory
|
| 263 |
+
self._simplify()
|
| 264 |
+
|
| 265 |
+
# Check if we've made the theory unsat
|
| 266 |
+
if self.is_unsatisfied:
|
| 267 |
+
|
| 268 |
+
self.is_unsatisfied = False
|
| 269 |
+
|
| 270 |
+
# We unroll all of the decisions until we can flip a literal
|
| 271 |
+
while self._current_level.flipped:
|
| 272 |
+
self._undo()
|
| 273 |
+
|
| 274 |
+
# If we've unrolled all the way, the theory is unsat
|
| 275 |
+
if 1 == len(self.levels):
|
| 276 |
+
return
|
| 277 |
+
|
| 278 |
+
# Detect and add a learned clause
|
| 279 |
+
self.add_learned_clause(self.compute_conflict())
|
| 280 |
+
|
| 281 |
+
# Try the opposite setting of the most recent decision
|
| 282 |
+
flip_lit = -self._current_level.decision
|
| 283 |
+
self._undo()
|
| 284 |
+
self.levels.append(Level(flip_lit, flipped=True))
|
| 285 |
+
flip_var = True
|
| 286 |
+
|
| 287 |
+
########################
|
| 288 |
+
# Helper Methods #
|
| 289 |
+
########################
|
| 290 |
+
@property
|
| 291 |
+
def _current_level(self):
|
| 292 |
+
"""The current decision level data structure
|
| 293 |
+
|
| 294 |
+
Examples
|
| 295 |
+
========
|
| 296 |
+
|
| 297 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 298 |
+
>>> l = SATSolver([{1}, {2}], {1, 2}, set())
|
| 299 |
+
>>> next(l._find_model())
|
| 300 |
+
{1: True, 2: True}
|
| 301 |
+
>>> l._current_level.decision
|
| 302 |
+
0
|
| 303 |
+
>>> l._current_level.flipped
|
| 304 |
+
False
|
| 305 |
+
>>> l._current_level.var_settings
|
| 306 |
+
{1, 2}
|
| 307 |
+
|
| 308 |
+
"""
|
| 309 |
+
return self.levels[-1]
|
| 310 |
+
|
| 311 |
+
def _clause_sat(self, cls):
|
| 312 |
+
"""Check if a clause is satisfied by the current variable setting.
|
| 313 |
+
|
| 314 |
+
Examples
|
| 315 |
+
========
|
| 316 |
+
|
| 317 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 318 |
+
>>> l = SATSolver([{1}, {-1}], {1}, set())
|
| 319 |
+
>>> try:
|
| 320 |
+
... next(l._find_model())
|
| 321 |
+
... except StopIteration:
|
| 322 |
+
... pass
|
| 323 |
+
>>> l._clause_sat(0)
|
| 324 |
+
False
|
| 325 |
+
>>> l._clause_sat(1)
|
| 326 |
+
True
|
| 327 |
+
|
| 328 |
+
"""
|
| 329 |
+
for lit in self.clauses[cls]:
|
| 330 |
+
if lit in self.var_settings:
|
| 331 |
+
return True
|
| 332 |
+
return False
|
| 333 |
+
|
| 334 |
+
def _is_sentinel(self, lit, cls):
|
| 335 |
+
"""Check if a literal is a sentinel of a given clause.
|
| 336 |
+
|
| 337 |
+
Examples
|
| 338 |
+
========
|
| 339 |
+
|
| 340 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 341 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 342 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 343 |
+
>>> next(l._find_model())
|
| 344 |
+
{1: True, 2: False, 3: False}
|
| 345 |
+
>>> l._is_sentinel(2, 3)
|
| 346 |
+
True
|
| 347 |
+
>>> l._is_sentinel(-3, 1)
|
| 348 |
+
False
|
| 349 |
+
|
| 350 |
+
"""
|
| 351 |
+
return cls in self.sentinels[lit]
|
| 352 |
+
|
| 353 |
+
def _assign_literal(self, lit):
|
| 354 |
+
"""Make a literal assignment.
|
| 355 |
+
|
| 356 |
+
The literal assignment must be recorded as part of the current
|
| 357 |
+
decision level. Additionally, if the literal is marked as a
|
| 358 |
+
sentinel of any clause, then a new sentinel must be chosen. If
|
| 359 |
+
this is not possible, then unit propagation is triggered and
|
| 360 |
+
another literal is added to the queue to be set in the future.
|
| 361 |
+
|
| 362 |
+
Examples
|
| 363 |
+
========
|
| 364 |
+
|
| 365 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 366 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 367 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 368 |
+
>>> next(l._find_model())
|
| 369 |
+
{1: True, 2: False, 3: False}
|
| 370 |
+
>>> l.var_settings
|
| 371 |
+
{-3, -2, 1}
|
| 372 |
+
|
| 373 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 374 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 375 |
+
>>> l._assign_literal(-1)
|
| 376 |
+
>>> try:
|
| 377 |
+
... next(l._find_model())
|
| 378 |
+
... except StopIteration:
|
| 379 |
+
... pass
|
| 380 |
+
>>> l.var_settings
|
| 381 |
+
{-1}
|
| 382 |
+
|
| 383 |
+
"""
|
| 384 |
+
self.var_settings.add(lit)
|
| 385 |
+
self._current_level.var_settings.add(lit)
|
| 386 |
+
self.variable_set[abs(lit)] = True
|
| 387 |
+
self.heur_lit_assigned(lit)
|
| 388 |
+
|
| 389 |
+
sentinel_list = list(self.sentinels[-lit])
|
| 390 |
+
|
| 391 |
+
for cls in sentinel_list:
|
| 392 |
+
if not self._clause_sat(cls):
|
| 393 |
+
other_sentinel = None
|
| 394 |
+
for newlit in self.clauses[cls]:
|
| 395 |
+
if newlit != -lit:
|
| 396 |
+
if self._is_sentinel(newlit, cls):
|
| 397 |
+
other_sentinel = newlit
|
| 398 |
+
elif not self.variable_set[abs(newlit)]:
|
| 399 |
+
self.sentinels[-lit].remove(cls)
|
| 400 |
+
self.sentinels[newlit].add(cls)
|
| 401 |
+
other_sentinel = None
|
| 402 |
+
break
|
| 403 |
+
|
| 404 |
+
# Check if no sentinel update exists
|
| 405 |
+
if other_sentinel:
|
| 406 |
+
self._unit_prop_queue.append(other_sentinel)
|
| 407 |
+
|
| 408 |
+
def _undo(self):
|
| 409 |
+
"""
|
| 410 |
+
_undo the changes of the most recent decision level.
|
| 411 |
+
|
| 412 |
+
Examples
|
| 413 |
+
========
|
| 414 |
+
|
| 415 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 416 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 417 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 418 |
+
>>> next(l._find_model())
|
| 419 |
+
{1: True, 2: False, 3: False}
|
| 420 |
+
>>> level = l._current_level
|
| 421 |
+
>>> level.decision, level.var_settings, level.flipped
|
| 422 |
+
(-3, {-3, -2}, False)
|
| 423 |
+
>>> l._undo()
|
| 424 |
+
>>> level = l._current_level
|
| 425 |
+
>>> level.decision, level.var_settings, level.flipped
|
| 426 |
+
(0, {1}, False)
|
| 427 |
+
|
| 428 |
+
"""
|
| 429 |
+
# Undo the variable settings
|
| 430 |
+
for lit in self._current_level.var_settings:
|
| 431 |
+
self.var_settings.remove(lit)
|
| 432 |
+
self.heur_lit_unset(lit)
|
| 433 |
+
self.variable_set[abs(lit)] = False
|
| 434 |
+
|
| 435 |
+
# Pop the level off the stack
|
| 436 |
+
self.levels.pop()
|
| 437 |
+
|
| 438 |
+
#########################
|
| 439 |
+
# Propagation #
|
| 440 |
+
#########################
|
| 441 |
+
"""
|
| 442 |
+
Propagation methods should attempt to soundly simplify the boolean
|
| 443 |
+
theory, and return True if any simplification occurred and False
|
| 444 |
+
otherwise.
|
| 445 |
+
"""
|
| 446 |
+
def _simplify(self):
|
| 447 |
+
"""Iterate over the various forms of propagation to simplify the theory.
|
| 448 |
+
|
| 449 |
+
Examples
|
| 450 |
+
========
|
| 451 |
+
|
| 452 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 453 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 454 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 455 |
+
>>> l.variable_set
|
| 456 |
+
[False, False, False, False]
|
| 457 |
+
>>> l.sentinels
|
| 458 |
+
{-3: {0, 2}, -2: {3, 4}, 2: {0, 3}, 3: {2, 4}}
|
| 459 |
+
|
| 460 |
+
>>> l._simplify()
|
| 461 |
+
|
| 462 |
+
>>> l.variable_set
|
| 463 |
+
[False, True, False, False]
|
| 464 |
+
>>> l.sentinels
|
| 465 |
+
{-3: {0, 2}, -2: {3, 4}, -1: set(), 2: {0, 3},
|
| 466 |
+
...3: {2, 4}}
|
| 467 |
+
|
| 468 |
+
"""
|
| 469 |
+
changed = True
|
| 470 |
+
while changed:
|
| 471 |
+
changed = False
|
| 472 |
+
changed |= self._unit_prop()
|
| 473 |
+
changed |= self._pure_literal()
|
| 474 |
+
|
| 475 |
+
def _unit_prop(self):
|
| 476 |
+
"""Perform unit propagation on the current theory."""
|
| 477 |
+
result = len(self._unit_prop_queue) > 0
|
| 478 |
+
while self._unit_prop_queue:
|
| 479 |
+
next_lit = self._unit_prop_queue.pop()
|
| 480 |
+
if -next_lit in self.var_settings:
|
| 481 |
+
self.is_unsatisfied = True
|
| 482 |
+
self._unit_prop_queue = []
|
| 483 |
+
return False
|
| 484 |
+
else:
|
| 485 |
+
self._assign_literal(next_lit)
|
| 486 |
+
|
| 487 |
+
return result
|
| 488 |
+
|
| 489 |
+
def _pure_literal(self):
|
| 490 |
+
"""Look for pure literals and assign them when found."""
|
| 491 |
+
return False
|
| 492 |
+
|
| 493 |
+
#########################
|
| 494 |
+
# Heuristics #
|
| 495 |
+
#########################
|
| 496 |
+
def _vsids_init(self):
|
| 497 |
+
"""Initialize the data structures needed for the VSIDS heuristic."""
|
| 498 |
+
self.lit_heap = []
|
| 499 |
+
self.lit_scores = {}
|
| 500 |
+
|
| 501 |
+
for var in range(1, len(self.variable_set)):
|
| 502 |
+
self.lit_scores[var] = float(-self.occurrence_count[var])
|
| 503 |
+
self.lit_scores[-var] = float(-self.occurrence_count[-var])
|
| 504 |
+
heappush(self.lit_heap, (self.lit_scores[var], var))
|
| 505 |
+
heappush(self.lit_heap, (self.lit_scores[-var], -var))
|
| 506 |
+
|
| 507 |
+
def _vsids_decay(self):
|
| 508 |
+
"""Decay the VSIDS scores for every literal.
|
| 509 |
+
|
| 510 |
+
Examples
|
| 511 |
+
========
|
| 512 |
+
|
| 513 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 514 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 515 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 516 |
+
|
| 517 |
+
>>> l.lit_scores
|
| 518 |
+
{-3: -2.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -2.0, 3: -2.0}
|
| 519 |
+
|
| 520 |
+
>>> l._vsids_decay()
|
| 521 |
+
|
| 522 |
+
>>> l.lit_scores
|
| 523 |
+
{-3: -1.0, -2: -1.0, -1: 0.0, 1: 0.0, 2: -1.0, 3: -1.0}
|
| 524 |
+
|
| 525 |
+
"""
|
| 526 |
+
# We divide every literal score by 2 for a decay factor
|
| 527 |
+
# Note: This doesn't change the heap property
|
| 528 |
+
for lit in self.lit_scores.keys():
|
| 529 |
+
self.lit_scores[lit] /= 2.0
|
| 530 |
+
|
| 531 |
+
def _vsids_calculate(self):
|
| 532 |
+
"""
|
| 533 |
+
VSIDS Heuristic Calculation
|
| 534 |
+
|
| 535 |
+
Examples
|
| 536 |
+
========
|
| 537 |
+
|
| 538 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 539 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 540 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 541 |
+
|
| 542 |
+
>>> l.lit_heap
|
| 543 |
+
[(-2.0, -3), (-2.0, 2), (-2.0, -2), (0.0, 1), (-2.0, 3), (0.0, -1)]
|
| 544 |
+
|
| 545 |
+
>>> l._vsids_calculate()
|
| 546 |
+
-3
|
| 547 |
+
|
| 548 |
+
>>> l.lit_heap
|
| 549 |
+
[(-2.0, -2), (-2.0, 2), (0.0, -1), (0.0, 1), (-2.0, 3)]
|
| 550 |
+
|
| 551 |
+
"""
|
| 552 |
+
if len(self.lit_heap) == 0:
|
| 553 |
+
return 0
|
| 554 |
+
|
| 555 |
+
# Clean out the front of the heap as long the variables are set
|
| 556 |
+
while self.variable_set[abs(self.lit_heap[0][1])]:
|
| 557 |
+
heappop(self.lit_heap)
|
| 558 |
+
if len(self.lit_heap) == 0:
|
| 559 |
+
return 0
|
| 560 |
+
|
| 561 |
+
return heappop(self.lit_heap)[1]
|
| 562 |
+
|
| 563 |
+
def _vsids_lit_assigned(self, lit):
|
| 564 |
+
"""Handle the assignment of a literal for the VSIDS heuristic."""
|
| 565 |
+
pass
|
| 566 |
+
|
| 567 |
+
def _vsids_lit_unset(self, lit):
|
| 568 |
+
"""Handle the unsetting of a literal for the VSIDS heuristic.
|
| 569 |
+
|
| 570 |
+
Examples
|
| 571 |
+
========
|
| 572 |
+
|
| 573 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 574 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 575 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 576 |
+
>>> l.lit_heap
|
| 577 |
+
[(-2.0, -3), (-2.0, 2), (-2.0, -2), (0.0, 1), (-2.0, 3), (0.0, -1)]
|
| 578 |
+
|
| 579 |
+
>>> l._vsids_lit_unset(2)
|
| 580 |
+
|
| 581 |
+
>>> l.lit_heap
|
| 582 |
+
[(-2.0, -3), (-2.0, -2), (-2.0, -2), (-2.0, 2), (-2.0, 3), (0.0, -1),
|
| 583 |
+
...(-2.0, 2), (0.0, 1)]
|
| 584 |
+
|
| 585 |
+
"""
|
| 586 |
+
var = abs(lit)
|
| 587 |
+
heappush(self.lit_heap, (self.lit_scores[var], var))
|
| 588 |
+
heappush(self.lit_heap, (self.lit_scores[-var], -var))
|
| 589 |
+
|
| 590 |
+
def _vsids_clause_added(self, cls):
|
| 591 |
+
"""Handle the addition of a new clause for the VSIDS heuristic.
|
| 592 |
+
|
| 593 |
+
Examples
|
| 594 |
+
========
|
| 595 |
+
|
| 596 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 597 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 598 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 599 |
+
|
| 600 |
+
>>> l.num_learned_clauses
|
| 601 |
+
0
|
| 602 |
+
>>> l.lit_scores
|
| 603 |
+
{-3: -2.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -2.0, 3: -2.0}
|
| 604 |
+
|
| 605 |
+
>>> l._vsids_clause_added({2, -3})
|
| 606 |
+
|
| 607 |
+
>>> l.num_learned_clauses
|
| 608 |
+
1
|
| 609 |
+
>>> l.lit_scores
|
| 610 |
+
{-3: -1.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -1.0, 3: -2.0}
|
| 611 |
+
|
| 612 |
+
"""
|
| 613 |
+
self.num_learned_clauses += 1
|
| 614 |
+
for lit in cls:
|
| 615 |
+
self.lit_scores[lit] += 1
|
| 616 |
+
|
| 617 |
+
########################
|
| 618 |
+
# Clause Learning #
|
| 619 |
+
########################
|
| 620 |
+
def _simple_add_learned_clause(self, cls):
|
| 621 |
+
"""Add a new clause to the theory.
|
| 622 |
+
|
| 623 |
+
Examples
|
| 624 |
+
========
|
| 625 |
+
|
| 626 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 627 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 628 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 629 |
+
|
| 630 |
+
>>> l.num_learned_clauses
|
| 631 |
+
0
|
| 632 |
+
>>> l.clauses
|
| 633 |
+
[[2, -3], [1], [3, -3], [2, -2], [3, -2]]
|
| 634 |
+
>>> l.sentinels
|
| 635 |
+
{-3: {0, 2}, -2: {3, 4}, 2: {0, 3}, 3: {2, 4}}
|
| 636 |
+
|
| 637 |
+
>>> l._simple_add_learned_clause([3])
|
| 638 |
+
|
| 639 |
+
>>> l.clauses
|
| 640 |
+
[[2, -3], [1], [3, -3], [2, -2], [3, -2], [3]]
|
| 641 |
+
>>> l.sentinels
|
| 642 |
+
{-3: {0, 2}, -2: {3, 4}, 2: {0, 3}, 3: {2, 4, 5}}
|
| 643 |
+
|
| 644 |
+
"""
|
| 645 |
+
cls_num = len(self.clauses)
|
| 646 |
+
self.clauses.append(cls)
|
| 647 |
+
|
| 648 |
+
for lit in cls:
|
| 649 |
+
self.occurrence_count[lit] += 1
|
| 650 |
+
|
| 651 |
+
self.sentinels[cls[0]].add(cls_num)
|
| 652 |
+
self.sentinels[cls[-1]].add(cls_num)
|
| 653 |
+
|
| 654 |
+
self.heur_clause_added(cls)
|
| 655 |
+
|
| 656 |
+
def _simple_compute_conflict(self):
|
| 657 |
+
""" Build a clause representing the fact that at least one decision made
|
| 658 |
+
so far is wrong.
|
| 659 |
+
|
| 660 |
+
Examples
|
| 661 |
+
========
|
| 662 |
+
|
| 663 |
+
>>> from sympy.logic.algorithms.dpll2 import SATSolver
|
| 664 |
+
>>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
|
| 665 |
+
... {3, -2}], {1, 2, 3}, set())
|
| 666 |
+
>>> next(l._find_model())
|
| 667 |
+
{1: True, 2: False, 3: False}
|
| 668 |
+
>>> l._simple_compute_conflict()
|
| 669 |
+
[3]
|
| 670 |
+
|
| 671 |
+
"""
|
| 672 |
+
return [-(level.decision) for level in self.levels[1:]]
|
| 673 |
+
|
| 674 |
+
def _simple_clean_clauses(self):
|
| 675 |
+
"""Clean up learned clauses."""
|
| 676 |
+
pass
|
| 677 |
+
|
| 678 |
+
|
| 679 |
+
class Level:
|
| 680 |
+
"""
|
| 681 |
+
Represents a single level in the DPLL algorithm, and contains
|
| 682 |
+
enough information for a sound backtracking procedure.
|
| 683 |
+
"""
|
| 684 |
+
|
| 685 |
+
def __init__(self, decision, flipped=False):
|
| 686 |
+
self.decision = decision
|
| 687 |
+
self.var_settings = set()
|
| 688 |
+
self.flipped = flipped
|
phivenv/Lib/site-packages/sympy/logic/algorithms/lra_theory.py
ADDED
|
@@ -0,0 +1,912 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implements "A Fast Linear-Arithmetic Solver for DPLL(T)"
|
| 2 |
+
|
| 3 |
+
The LRASolver class defined in this file can be used
|
| 4 |
+
in conjunction with a SAT solver to check the
|
| 5 |
+
satisfiability of formulas involving inequalities.
|
| 6 |
+
|
| 7 |
+
Here's an example of how that would work:
|
| 8 |
+
|
| 9 |
+
Suppose you want to check the satisfiability of
|
| 10 |
+
the following formula:
|
| 11 |
+
|
| 12 |
+
>>> from sympy.core.relational import Eq
|
| 13 |
+
>>> from sympy.abc import x, y
|
| 14 |
+
>>> f = ((x > 0) | (x < 0)) & (Eq(x, 0) | Eq(y, 1)) & (~Eq(y, 1) | Eq(1, 2))
|
| 15 |
+
|
| 16 |
+
First a preprocessing step should be done on f. During preprocessing,
|
| 17 |
+
f should be checked for any predicates such as `Q.prime` that can't be
|
| 18 |
+
handled. Also unequality like `~Eq(y, 1)` should be split.
|
| 19 |
+
|
| 20 |
+
I should mention that the paper says to split both equalities and
|
| 21 |
+
unequality, but this implementation only requires that unequality
|
| 22 |
+
be split.
|
| 23 |
+
|
| 24 |
+
>>> f = ((x > 0) | (x < 0)) & (Eq(x, 0) | Eq(y, 1)) & ((y < 1) | (y > 1) | Eq(1, 2))
|
| 25 |
+
|
| 26 |
+
Then an LRASolver instance needs to be initialized with this formula.
|
| 27 |
+
|
| 28 |
+
>>> from sympy.assumptions.cnf import CNF, EncodedCNF
|
| 29 |
+
>>> from sympy.assumptions.ask import Q
|
| 30 |
+
>>> from sympy.logic.algorithms.lra_theory import LRASolver
|
| 31 |
+
>>> cnf = CNF.from_prop(f)
|
| 32 |
+
>>> enc = EncodedCNF()
|
| 33 |
+
>>> enc.add_from_cnf(cnf)
|
| 34 |
+
>>> lra, conflicts = LRASolver.from_encoded_cnf(enc)
|
| 35 |
+
|
| 36 |
+
Any immediate one-lital conflicts clauses will be detected here.
|
| 37 |
+
In this example, `~Eq(1, 2)` is one such conflict clause. We'll
|
| 38 |
+
want to add it to `f` so that the SAT solver is forced to
|
| 39 |
+
assign Eq(1, 2) to False.
|
| 40 |
+
|
| 41 |
+
>>> f = f & ~Eq(1, 2)
|
| 42 |
+
|
| 43 |
+
Now that the one-literal conflict clauses have been added
|
| 44 |
+
and an lra object has been initialized, we can pass `f`
|
| 45 |
+
to a SAT solver. The SAT solver will give us a satisfying
|
| 46 |
+
assignment such as:
|
| 47 |
+
|
| 48 |
+
(1 = 2): False
|
| 49 |
+
(y = 1): True
|
| 50 |
+
(y < 1): True
|
| 51 |
+
(y > 1): True
|
| 52 |
+
(x = 0): True
|
| 53 |
+
(x < 0): True
|
| 54 |
+
(x > 0): True
|
| 55 |
+
|
| 56 |
+
Next you would pass this assignment to the LRASolver
|
| 57 |
+
which will be able to determine that this particular
|
| 58 |
+
assignment is satisfiable or not.
|
| 59 |
+
|
| 60 |
+
Note that since EncodedCNF is inherently non-deterministic,
|
| 61 |
+
the int each predicate is encoded as is not consistent. As a
|
| 62 |
+
result, the code below likely does not reflect the assignment
|
| 63 |
+
given above.
|
| 64 |
+
|
| 65 |
+
>>> lra.assert_lit(-1) #doctest: +SKIP
|
| 66 |
+
>>> lra.assert_lit(2) #doctest: +SKIP
|
| 67 |
+
>>> lra.assert_lit(3) #doctest: +SKIP
|
| 68 |
+
>>> lra.assert_lit(4) #doctest: +SKIP
|
| 69 |
+
>>> lra.assert_lit(5) #doctest: +SKIP
|
| 70 |
+
>>> lra.assert_lit(6) #doctest: +SKIP
|
| 71 |
+
>>> lra.assert_lit(7) #doctest: +SKIP
|
| 72 |
+
>>> is_sat, conflict_or_assignment = lra.check()
|
| 73 |
+
|
| 74 |
+
As the particular assignment suggested is not satisfiable,
|
| 75 |
+
the LRASolver will return unsat and a conflict clause when
|
| 76 |
+
given that assignment. The conflict clause will always be
|
| 77 |
+
minimal, but there can be multiple minimal conflict clauses.
|
| 78 |
+
One possible conflict clause could be `~(x < 0) | ~(x > 0)`.
|
| 79 |
+
|
| 80 |
+
We would then add whatever conflict clause is given to
|
| 81 |
+
`f` to prevent the SAT solver from coming up with an
|
| 82 |
+
assignment with the same conflicting literals. In this case,
|
| 83 |
+
the conflict clause `~(x < 0) | ~(x > 0)` would prevent
|
| 84 |
+
any assignment where both (x < 0) and (x > 0) were both
|
| 85 |
+
true.
|
| 86 |
+
|
| 87 |
+
The SAT solver would then find another assignment
|
| 88 |
+
and we would check that assignment with the LRASolver
|
| 89 |
+
and so on. Eventually either a satisfying assignment
|
| 90 |
+
that the SAT solver and LRASolver agreed on would be found
|
| 91 |
+
or enough conflict clauses would be added so that the
|
| 92 |
+
boolean formula was unsatisfiable.
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
This implementation is based on [1]_, which includes a
|
| 96 |
+
detailed explanation of the algorithm and pseudocode
|
| 97 |
+
for the most important functions.
|
| 98 |
+
|
| 99 |
+
[1]_ also explains how backtracking and theory propagation
|
| 100 |
+
could be implemented to speed up the current implementation,
|
| 101 |
+
but these are not currently implemented.
|
| 102 |
+
|
| 103 |
+
TODO:
|
| 104 |
+
- Handle non-rational real numbers
|
| 105 |
+
- Handle positive and negative infinity
|
| 106 |
+
- Implement backtracking and theory proposition
|
| 107 |
+
- Simplify matrix by removing unused variables using Gaussian elimination
|
| 108 |
+
|
| 109 |
+
References
|
| 110 |
+
==========
|
| 111 |
+
|
| 112 |
+
.. [1] Dutertre, B., de Moura, L.:
|
| 113 |
+
A Fast Linear-Arithmetic Solver for DPLL(T)
|
| 114 |
+
https://link.springer.com/chapter/10.1007/11817963_11
|
| 115 |
+
"""
|
| 116 |
+
from sympy.solvers.solveset import linear_eq_to_matrix
|
| 117 |
+
from sympy.matrices.dense import eye
|
| 118 |
+
from sympy.assumptions import Predicate
|
| 119 |
+
from sympy.assumptions.assume import AppliedPredicate
|
| 120 |
+
from sympy.assumptions.ask import Q
|
| 121 |
+
from sympy.core import Dummy
|
| 122 |
+
from sympy.core.mul import Mul
|
| 123 |
+
from sympy.core.add import Add
|
| 124 |
+
from sympy.core.relational import Eq, Ne
|
| 125 |
+
from sympy.core.sympify import sympify
|
| 126 |
+
from sympy.core.singleton import S
|
| 127 |
+
from sympy.core.numbers import Rational, oo
|
| 128 |
+
from sympy.matrices.dense import Matrix
|
| 129 |
+
|
| 130 |
+
class UnhandledInput(Exception):
|
| 131 |
+
"""
|
| 132 |
+
Raised while creating an LRASolver if non-linearity
|
| 133 |
+
or non-rational numbers are present.
|
| 134 |
+
"""
|
| 135 |
+
|
| 136 |
+
# predicates that LRASolver understands and makes use of
|
| 137 |
+
ALLOWED_PRED = {Q.eq, Q.gt, Q.lt, Q.le, Q.ge}
|
| 138 |
+
|
| 139 |
+
# if true ~Q.gt(x, y) implies Q.le(x, y)
|
| 140 |
+
HANDLE_NEGATION = True
|
| 141 |
+
|
| 142 |
+
class LRASolver():
|
| 143 |
+
"""
|
| 144 |
+
Linear Arithmetic Solver for DPLL(T) implemented with an algorithm based on
|
| 145 |
+
the Dual Simplex method. Uses Bland's pivoting rule to avoid cycling.
|
| 146 |
+
|
| 147 |
+
References
|
| 148 |
+
==========
|
| 149 |
+
|
| 150 |
+
.. [1] Dutertre, B., de Moura, L.:
|
| 151 |
+
A Fast Linear-Arithmetic Solver for DPLL(T)
|
| 152 |
+
https://link.springer.com/chapter/10.1007/11817963_11
|
| 153 |
+
"""
|
| 154 |
+
|
| 155 |
+
def __init__(self, A, slack_variables, nonslack_variables, enc_to_boundary, s_subs, testing_mode):
|
| 156 |
+
"""
|
| 157 |
+
Use the "from_encoded_cnf" method to create a new LRASolver.
|
| 158 |
+
"""
|
| 159 |
+
self.run_checks = testing_mode
|
| 160 |
+
self.s_subs = s_subs # used only for test_lra_theory.test_random_problems
|
| 161 |
+
|
| 162 |
+
if any(not isinstance(a, Rational) for a in A):
|
| 163 |
+
raise UnhandledInput("Non-rational numbers are not handled")
|
| 164 |
+
if any(not isinstance(b.bound, Rational) for b in enc_to_boundary.values()):
|
| 165 |
+
raise UnhandledInput("Non-rational numbers are not handled")
|
| 166 |
+
m, n = len(slack_variables), len(slack_variables)+len(nonslack_variables)
|
| 167 |
+
if m != 0:
|
| 168 |
+
assert A.shape == (m, n)
|
| 169 |
+
if self.run_checks:
|
| 170 |
+
assert A[:, n-m:] == -eye(m)
|
| 171 |
+
|
| 172 |
+
self.enc_to_boundary = enc_to_boundary # mapping of int to Boundary objects
|
| 173 |
+
self.boundary_to_enc = {value: key for key, value in enc_to_boundary.items()}
|
| 174 |
+
self.A = A
|
| 175 |
+
self.slack = slack_variables
|
| 176 |
+
self.nonslack = nonslack_variables
|
| 177 |
+
self.all_var = nonslack_variables + slack_variables
|
| 178 |
+
|
| 179 |
+
self.slack_set = set(slack_variables)
|
| 180 |
+
|
| 181 |
+
self.is_sat = True # While True, all constraints asserted so far are satisfiable
|
| 182 |
+
self.result = None # always one of: (True, assignment), (False, conflict clause), None
|
| 183 |
+
|
| 184 |
+
@staticmethod
|
| 185 |
+
def from_encoded_cnf(encoded_cnf, testing_mode=False):
|
| 186 |
+
"""
|
| 187 |
+
Creates an LRASolver from an EncodedCNF object
|
| 188 |
+
and a list of conflict clauses for propositions
|
| 189 |
+
that can be simplified to True or False.
|
| 190 |
+
|
| 191 |
+
Parameters
|
| 192 |
+
==========
|
| 193 |
+
|
| 194 |
+
encoded_cnf : EncodedCNF
|
| 195 |
+
|
| 196 |
+
testing_mode : bool
|
| 197 |
+
Setting testing_mode to True enables some slow assert statements
|
| 198 |
+
and sorting to reduce nonterministic behavior.
|
| 199 |
+
|
| 200 |
+
Returns
|
| 201 |
+
=======
|
| 202 |
+
|
| 203 |
+
(lra, conflicts)
|
| 204 |
+
|
| 205 |
+
lra : LRASolver
|
| 206 |
+
|
| 207 |
+
conflicts : list
|
| 208 |
+
Contains a one-literal conflict clause for each proposition
|
| 209 |
+
that can be simplified to True or False.
|
| 210 |
+
|
| 211 |
+
Example
|
| 212 |
+
=======
|
| 213 |
+
|
| 214 |
+
>>> from sympy.core.relational import Eq
|
| 215 |
+
>>> from sympy.assumptions.cnf import CNF, EncodedCNF
|
| 216 |
+
>>> from sympy.assumptions.ask import Q
|
| 217 |
+
>>> from sympy.logic.algorithms.lra_theory import LRASolver
|
| 218 |
+
>>> from sympy.abc import x, y, z
|
| 219 |
+
>>> phi = (x >= 0) & ((x + y <= 2) | (x + 2 * y - z >= 6))
|
| 220 |
+
>>> phi = phi & (Eq(x + y, 2) | (x + 2 * y - z > 4))
|
| 221 |
+
>>> phi = phi & Q.gt(2, 1)
|
| 222 |
+
>>> cnf = CNF.from_prop(phi)
|
| 223 |
+
>>> enc = EncodedCNF()
|
| 224 |
+
>>> enc.from_cnf(cnf)
|
| 225 |
+
>>> lra, conflicts = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 226 |
+
>>> lra #doctest: +SKIP
|
| 227 |
+
<sympy.logic.algorithms.lra_theory.LRASolver object at 0x7fdcb0e15b70>
|
| 228 |
+
>>> conflicts #doctest: +SKIP
|
| 229 |
+
[[4]]
|
| 230 |
+
"""
|
| 231 |
+
# This function has three main jobs:
|
| 232 |
+
# - raise errors if the input formula is not handled
|
| 233 |
+
# - preprocesses the formula into a matrix and single variable constraints
|
| 234 |
+
# - create one-literal conflict clauses from predicates that are always True
|
| 235 |
+
# or always False such as Q.gt(3, 2)
|
| 236 |
+
#
|
| 237 |
+
# See the preprocessing section of "A Fast Linear-Arithmetic Solver for DPLL(T)"
|
| 238 |
+
# for an explanation of how the formula is converted into a matrix
|
| 239 |
+
# and a set of single variable constraints.
|
| 240 |
+
|
| 241 |
+
encoding = {} # maps int to boundary
|
| 242 |
+
A = []
|
| 243 |
+
|
| 244 |
+
basic = []
|
| 245 |
+
s_count = 0
|
| 246 |
+
s_subs = {}
|
| 247 |
+
nonbasic = []
|
| 248 |
+
|
| 249 |
+
if testing_mode:
|
| 250 |
+
# sort to reduce nondeterminism
|
| 251 |
+
encoded_cnf_items = sorted(encoded_cnf.encoding.items(), key=lambda x: str(x))
|
| 252 |
+
else:
|
| 253 |
+
encoded_cnf_items = encoded_cnf.encoding.items()
|
| 254 |
+
|
| 255 |
+
empty_var = Dummy()
|
| 256 |
+
var_to_lra_var = {}
|
| 257 |
+
conflicts = []
|
| 258 |
+
|
| 259 |
+
for prop, enc in encoded_cnf_items:
|
| 260 |
+
if isinstance(prop, Predicate):
|
| 261 |
+
prop = prop(empty_var)
|
| 262 |
+
if not isinstance(prop, AppliedPredicate):
|
| 263 |
+
if prop == True:
|
| 264 |
+
conflicts.append([enc])
|
| 265 |
+
continue
|
| 266 |
+
if prop == False:
|
| 267 |
+
conflicts.append([-enc])
|
| 268 |
+
continue
|
| 269 |
+
|
| 270 |
+
raise ValueError(f"Unhandled Predicate: {prop}")
|
| 271 |
+
|
| 272 |
+
assert prop.function in ALLOWED_PRED
|
| 273 |
+
if prop.lhs == S.NaN or prop.rhs == S.NaN:
|
| 274 |
+
raise ValueError(f"{prop} contains nan")
|
| 275 |
+
if prop.lhs.is_imaginary or prop.rhs.is_imaginary:
|
| 276 |
+
raise UnhandledInput(f"{prop} contains an imaginary component")
|
| 277 |
+
if prop.lhs == oo or prop.rhs == oo:
|
| 278 |
+
raise UnhandledInput(f"{prop} contains infinity")
|
| 279 |
+
|
| 280 |
+
prop = _eval_binrel(prop) # simplify variable-less quantities to True / False if possible
|
| 281 |
+
if prop == True:
|
| 282 |
+
conflicts.append([enc])
|
| 283 |
+
continue
|
| 284 |
+
elif prop == False:
|
| 285 |
+
conflicts.append([-enc])
|
| 286 |
+
continue
|
| 287 |
+
elif prop is None:
|
| 288 |
+
raise UnhandledInput(f"{prop} could not be simplified")
|
| 289 |
+
|
| 290 |
+
expr = prop.lhs - prop.rhs
|
| 291 |
+
if prop.function in [Q.ge, Q.gt]:
|
| 292 |
+
expr = -expr
|
| 293 |
+
|
| 294 |
+
# expr should be less than (or equal to) 0
|
| 295 |
+
# otherwise prop is False
|
| 296 |
+
if prop.function in [Q.le, Q.ge]:
|
| 297 |
+
bool = (expr <= 0)
|
| 298 |
+
elif prop.function in [Q.lt, Q.gt]:
|
| 299 |
+
bool = (expr < 0)
|
| 300 |
+
else:
|
| 301 |
+
assert prop.function == Q.eq
|
| 302 |
+
bool = Eq(expr, 0)
|
| 303 |
+
|
| 304 |
+
if bool == True:
|
| 305 |
+
conflicts.append([enc])
|
| 306 |
+
continue
|
| 307 |
+
elif bool == False:
|
| 308 |
+
conflicts.append([-enc])
|
| 309 |
+
continue
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
vars, const = _sep_const_terms(expr) # example: (2x + 3y + 2) --> (2x + 3y), (2)
|
| 313 |
+
vars, var_coeff = _sep_const_coeff(vars) # examples: (2x) --> (x, 2); (2x + 3y) --> (2x + 3y), (1)
|
| 314 |
+
const = const / var_coeff
|
| 315 |
+
|
| 316 |
+
terms = _list_terms(vars) # example: (2x + 3y) --> [2x, 3y]
|
| 317 |
+
for term in terms:
|
| 318 |
+
term, _ = _sep_const_coeff(term)
|
| 319 |
+
assert len(term.free_symbols) > 0
|
| 320 |
+
if term not in var_to_lra_var:
|
| 321 |
+
var_to_lra_var[term] = LRAVariable(term)
|
| 322 |
+
nonbasic.append(term)
|
| 323 |
+
|
| 324 |
+
if len(terms) > 1:
|
| 325 |
+
if vars not in s_subs:
|
| 326 |
+
s_count += 1
|
| 327 |
+
d = Dummy(f"s{s_count}")
|
| 328 |
+
var_to_lra_var[d] = LRAVariable(d)
|
| 329 |
+
basic.append(d)
|
| 330 |
+
s_subs[vars] = d
|
| 331 |
+
A.append(vars - d)
|
| 332 |
+
var = s_subs[vars]
|
| 333 |
+
else:
|
| 334 |
+
var = terms[0]
|
| 335 |
+
|
| 336 |
+
assert var_coeff != 0
|
| 337 |
+
|
| 338 |
+
equality = prop.function == Q.eq
|
| 339 |
+
upper = var_coeff > 0 if not equality else None
|
| 340 |
+
strict = prop.function in [Q.gt, Q.lt]
|
| 341 |
+
b = Boundary(var_to_lra_var[var], -const, upper, equality, strict)
|
| 342 |
+
encoding[enc] = b
|
| 343 |
+
|
| 344 |
+
fs = [v.free_symbols for v in nonbasic + basic]
|
| 345 |
+
assert all(len(syms) > 0 for syms in fs)
|
| 346 |
+
fs_count = sum(len(syms) for syms in fs)
|
| 347 |
+
if len(fs) > 0 and len(set.union(*fs)) < fs_count:
|
| 348 |
+
raise UnhandledInput("Nonlinearity is not handled")
|
| 349 |
+
|
| 350 |
+
A, _ = linear_eq_to_matrix(A, nonbasic + basic)
|
| 351 |
+
nonbasic = [var_to_lra_var[nb] for nb in nonbasic]
|
| 352 |
+
basic = [var_to_lra_var[b] for b in basic]
|
| 353 |
+
for idx, var in enumerate(nonbasic + basic):
|
| 354 |
+
var.col_idx = idx
|
| 355 |
+
|
| 356 |
+
return LRASolver(A, basic, nonbasic, encoding, s_subs, testing_mode), conflicts
|
| 357 |
+
|
| 358 |
+
def reset_bounds(self):
|
| 359 |
+
"""
|
| 360 |
+
Resets the state of the LRASolver to before
|
| 361 |
+
anything was asserted.
|
| 362 |
+
"""
|
| 363 |
+
self.result = None
|
| 364 |
+
for var in self.all_var:
|
| 365 |
+
var.lower = LRARational(-float("inf"), 0)
|
| 366 |
+
var.lower_from_eq = False
|
| 367 |
+
var.lower_from_neg = False
|
| 368 |
+
var.upper = LRARational(float("inf"), 0)
|
| 369 |
+
var.upper_from_eq= False
|
| 370 |
+
var.lower_from_neg = False
|
| 371 |
+
var.assign = LRARational(0, 0)
|
| 372 |
+
|
| 373 |
+
def assert_lit(self, enc_constraint):
|
| 374 |
+
"""
|
| 375 |
+
Assert a literal representing a constraint
|
| 376 |
+
and update the internal state accordingly.
|
| 377 |
+
|
| 378 |
+
Note that due to peculiarities of this implementation
|
| 379 |
+
asserting ~(x > 0) will assert (x <= 0) but asserting
|
| 380 |
+
~Eq(x, 0) will not do anything.
|
| 381 |
+
|
| 382 |
+
Parameters
|
| 383 |
+
==========
|
| 384 |
+
|
| 385 |
+
enc_constraint : int
|
| 386 |
+
A mapping of encodings to constraints
|
| 387 |
+
can be found in `self.enc_to_boundary`.
|
| 388 |
+
|
| 389 |
+
Returns
|
| 390 |
+
=======
|
| 391 |
+
|
| 392 |
+
None or (False, explanation)
|
| 393 |
+
|
| 394 |
+
explanation : set of ints
|
| 395 |
+
A conflict clause that "explains" why
|
| 396 |
+
the literals asserted so far are unsatisfiable.
|
| 397 |
+
"""
|
| 398 |
+
if abs(enc_constraint) not in self.enc_to_boundary:
|
| 399 |
+
return None
|
| 400 |
+
|
| 401 |
+
if not HANDLE_NEGATION and enc_constraint < 0:
|
| 402 |
+
return None
|
| 403 |
+
|
| 404 |
+
boundary = self.enc_to_boundary[abs(enc_constraint)]
|
| 405 |
+
sym, c, negated = boundary.var, boundary.bound, enc_constraint < 0
|
| 406 |
+
|
| 407 |
+
if boundary.equality and negated:
|
| 408 |
+
return None # negated equality is not handled and should only appear in conflict clauses
|
| 409 |
+
|
| 410 |
+
upper = boundary.upper != negated
|
| 411 |
+
if boundary.strict != negated:
|
| 412 |
+
delta = -1 if upper else 1
|
| 413 |
+
c = LRARational(c, delta)
|
| 414 |
+
else:
|
| 415 |
+
c = LRARational(c, 0)
|
| 416 |
+
|
| 417 |
+
if boundary.equality:
|
| 418 |
+
res1 = self._assert_lower(sym, c, from_equality=True, from_neg=negated)
|
| 419 |
+
if res1 and res1[0] == False:
|
| 420 |
+
res = res1
|
| 421 |
+
else:
|
| 422 |
+
res2 = self._assert_upper(sym, c, from_equality=True, from_neg=negated)
|
| 423 |
+
res = res2
|
| 424 |
+
elif upper:
|
| 425 |
+
res = self._assert_upper(sym, c, from_neg=negated)
|
| 426 |
+
else:
|
| 427 |
+
res = self._assert_lower(sym, c, from_neg=negated)
|
| 428 |
+
|
| 429 |
+
if self.is_sat and sym not in self.slack_set:
|
| 430 |
+
self.is_sat = res is None
|
| 431 |
+
else:
|
| 432 |
+
self.is_sat = False
|
| 433 |
+
|
| 434 |
+
return res
|
| 435 |
+
|
| 436 |
+
def _assert_upper(self, xi, ci, from_equality=False, from_neg=False):
|
| 437 |
+
"""
|
| 438 |
+
Adjusts the upper bound on variable xi if the new upper bound is
|
| 439 |
+
more limiting. The assignment of variable xi is adjusted to be
|
| 440 |
+
within the new bound if needed.
|
| 441 |
+
|
| 442 |
+
Also calls `self._update` to update the assignment for slack variables
|
| 443 |
+
to keep all equalities satisfied.
|
| 444 |
+
"""
|
| 445 |
+
if self.result:
|
| 446 |
+
assert self.result[0] != False
|
| 447 |
+
self.result = None
|
| 448 |
+
if ci >= xi.upper:
|
| 449 |
+
return None
|
| 450 |
+
if ci < xi.lower:
|
| 451 |
+
assert (xi.lower[1] >= 0) is True
|
| 452 |
+
assert (ci[1] <= 0) is True
|
| 453 |
+
|
| 454 |
+
lit1, neg1 = Boundary.from_lower(xi)
|
| 455 |
+
|
| 456 |
+
lit2 = Boundary(var=xi, const=ci[0], strict=ci[1] != 0, upper=True, equality=from_equality)
|
| 457 |
+
if from_neg:
|
| 458 |
+
lit2 = lit2.get_negated()
|
| 459 |
+
neg2 = -1 if from_neg else 1
|
| 460 |
+
|
| 461 |
+
conflict = [-neg1*self.boundary_to_enc[lit1], -neg2*self.boundary_to_enc[lit2]]
|
| 462 |
+
self.result = False, conflict
|
| 463 |
+
return self.result
|
| 464 |
+
xi.upper = ci
|
| 465 |
+
xi.upper_from_eq = from_equality
|
| 466 |
+
xi.upper_from_neg = from_neg
|
| 467 |
+
if xi in self.nonslack and xi.assign > ci:
|
| 468 |
+
self._update(xi, ci)
|
| 469 |
+
|
| 470 |
+
if self.run_checks and all(v.assign[0] != float("inf") and v.assign[0] != -float("inf")
|
| 471 |
+
for v in self.all_var):
|
| 472 |
+
M = self.A
|
| 473 |
+
X = Matrix([v.assign[0] for v in self.all_var])
|
| 474 |
+
assert all(abs(val) < 10 ** (-10) for val in M * X)
|
| 475 |
+
|
| 476 |
+
return None
|
| 477 |
+
|
| 478 |
+
def _assert_lower(self, xi, ci, from_equality=False, from_neg=False):
|
| 479 |
+
"""
|
| 480 |
+
Adjusts the lower bound on variable xi if the new lower bound is
|
| 481 |
+
more limiting. The assignment of variable xi is adjusted to be
|
| 482 |
+
within the new bound if needed.
|
| 483 |
+
|
| 484 |
+
Also calls `self._update` to update the assignment for slack variables
|
| 485 |
+
to keep all equalities satisfied.
|
| 486 |
+
"""
|
| 487 |
+
if self.result:
|
| 488 |
+
assert self.result[0] != False
|
| 489 |
+
self.result = None
|
| 490 |
+
if ci <= xi.lower:
|
| 491 |
+
return None
|
| 492 |
+
if ci > xi.upper:
|
| 493 |
+
assert (xi.upper[1] <= 0) is True
|
| 494 |
+
assert (ci[1] >= 0) is True
|
| 495 |
+
|
| 496 |
+
lit1, neg1 = Boundary.from_upper(xi)
|
| 497 |
+
|
| 498 |
+
lit2 = Boundary(var=xi, const=ci[0], strict=ci[1] != 0, upper=False, equality=from_equality)
|
| 499 |
+
if from_neg:
|
| 500 |
+
lit2 = lit2.get_negated()
|
| 501 |
+
neg2 = -1 if from_neg else 1
|
| 502 |
+
|
| 503 |
+
conflict = [-neg1*self.boundary_to_enc[lit1],-neg2*self.boundary_to_enc[lit2]]
|
| 504 |
+
self.result = False, conflict
|
| 505 |
+
return self.result
|
| 506 |
+
xi.lower = ci
|
| 507 |
+
xi.lower_from_eq = from_equality
|
| 508 |
+
xi.lower_from_neg = from_neg
|
| 509 |
+
if xi in self.nonslack and xi.assign < ci:
|
| 510 |
+
self._update(xi, ci)
|
| 511 |
+
|
| 512 |
+
if self.run_checks and all(v.assign[0] != float("inf") and v.assign[0] != -float("inf")
|
| 513 |
+
for v in self.all_var):
|
| 514 |
+
M = self.A
|
| 515 |
+
X = Matrix([v.assign[0] for v in self.all_var])
|
| 516 |
+
assert all(abs(val) < 10 ** (-10) for val in M * X)
|
| 517 |
+
|
| 518 |
+
return None
|
| 519 |
+
|
| 520 |
+
def _update(self, xi, v):
|
| 521 |
+
"""
|
| 522 |
+
Updates all slack variables that have equations that contain
|
| 523 |
+
variable xi so that they stay satisfied given xi is equal to v.
|
| 524 |
+
"""
|
| 525 |
+
i = xi.col_idx
|
| 526 |
+
for j, b in enumerate(self.slack):
|
| 527 |
+
aji = self.A[j, i]
|
| 528 |
+
b.assign = b.assign + (v - xi.assign)*aji
|
| 529 |
+
xi.assign = v
|
| 530 |
+
|
| 531 |
+
def check(self):
|
| 532 |
+
"""
|
| 533 |
+
Searches for an assignment that satisfies all constraints
|
| 534 |
+
or determines that no such assignment exists and gives
|
| 535 |
+
a minimal conflict clause that "explains" why the
|
| 536 |
+
constraints are unsatisfiable.
|
| 537 |
+
|
| 538 |
+
Returns
|
| 539 |
+
=======
|
| 540 |
+
|
| 541 |
+
(True, assignment) or (False, explanation)
|
| 542 |
+
|
| 543 |
+
assignment : dict of LRAVariables to values
|
| 544 |
+
Assigned values are tuples that represent a rational number
|
| 545 |
+
plus some infinatesimal delta.
|
| 546 |
+
|
| 547 |
+
explanation : set of ints
|
| 548 |
+
"""
|
| 549 |
+
if self.is_sat:
|
| 550 |
+
return True, {var: var.assign for var in self.all_var}
|
| 551 |
+
if self.result:
|
| 552 |
+
return self.result
|
| 553 |
+
|
| 554 |
+
from sympy.matrices.dense import Matrix
|
| 555 |
+
M = self.A.copy()
|
| 556 |
+
basic = {s: i for i, s in enumerate(self.slack)} # contains the row index associated with each basic variable
|
| 557 |
+
nonbasic = set(self.nonslack)
|
| 558 |
+
while True:
|
| 559 |
+
if self.run_checks:
|
| 560 |
+
# nonbasic variables must always be within bounds
|
| 561 |
+
assert all(((nb.assign >= nb.lower) == True) and ((nb.assign <= nb.upper) == True) for nb in nonbasic)
|
| 562 |
+
|
| 563 |
+
# assignments for x must always satisfy Ax = 0
|
| 564 |
+
# probably have to turn this off when dealing with strict ineq
|
| 565 |
+
if all(v.assign[0] != float("inf") and v.assign[0] != -float("inf")
|
| 566 |
+
for v in self.all_var):
|
| 567 |
+
X = Matrix([v.assign[0] for v in self.all_var])
|
| 568 |
+
assert all(abs(val) < 10**(-10) for val in M*X)
|
| 569 |
+
|
| 570 |
+
# check upper and lower match this format:
|
| 571 |
+
# x <= rat + delta iff x < rat
|
| 572 |
+
# x >= rat - delta iff x > rat
|
| 573 |
+
# this wouldn't make sense:
|
| 574 |
+
# x <= rat - delta
|
| 575 |
+
# x >= rat + delta
|
| 576 |
+
assert all(x.upper[1] <= 0 for x in self.all_var)
|
| 577 |
+
assert all(x.lower[1] >= 0 for x in self.all_var)
|
| 578 |
+
|
| 579 |
+
cand = [b for b in basic if b.assign < b.lower or b.assign > b.upper]
|
| 580 |
+
|
| 581 |
+
if len(cand) == 0:
|
| 582 |
+
return True, {var: var.assign for var in self.all_var}
|
| 583 |
+
|
| 584 |
+
xi = min(cand, key=lambda v: v.col_idx) # Bland's rule
|
| 585 |
+
i = basic[xi]
|
| 586 |
+
|
| 587 |
+
if xi.assign < xi.lower:
|
| 588 |
+
cand = [nb for nb in nonbasic
|
| 589 |
+
if (M[i, nb.col_idx] > 0 and nb.assign < nb.upper)
|
| 590 |
+
or (M[i, nb.col_idx] < 0 and nb.assign > nb.lower)]
|
| 591 |
+
if len(cand) == 0:
|
| 592 |
+
N_plus = [nb for nb in nonbasic if M[i, nb.col_idx] > 0]
|
| 593 |
+
N_minus = [nb for nb in nonbasic if M[i, nb.col_idx] < 0]
|
| 594 |
+
|
| 595 |
+
conflict = []
|
| 596 |
+
conflict += [Boundary.from_upper(nb) for nb in N_plus]
|
| 597 |
+
conflict += [Boundary.from_lower(nb) for nb in N_minus]
|
| 598 |
+
conflict.append(Boundary.from_lower(xi))
|
| 599 |
+
conflict = [-neg*self.boundary_to_enc[c] for c, neg in conflict]
|
| 600 |
+
return False, conflict
|
| 601 |
+
xj = min(cand, key=str)
|
| 602 |
+
M = self._pivot_and_update(M, basic, nonbasic, xi, xj, xi.lower)
|
| 603 |
+
|
| 604 |
+
if xi.assign > xi.upper:
|
| 605 |
+
cand = [nb for nb in nonbasic
|
| 606 |
+
if (M[i, nb.col_idx] < 0 and nb.assign < nb.upper)
|
| 607 |
+
or (M[i, nb.col_idx] > 0 and nb.assign > nb.lower)]
|
| 608 |
+
|
| 609 |
+
if len(cand) == 0:
|
| 610 |
+
N_plus = [nb for nb in nonbasic if M[i, nb.col_idx] > 0]
|
| 611 |
+
N_minus = [nb for nb in nonbasic if M[i, nb.col_idx] < 0]
|
| 612 |
+
|
| 613 |
+
conflict = []
|
| 614 |
+
conflict += [Boundary.from_upper(nb) for nb in N_minus]
|
| 615 |
+
conflict += [Boundary.from_lower(nb) for nb in N_plus]
|
| 616 |
+
conflict.append(Boundary.from_upper(xi))
|
| 617 |
+
|
| 618 |
+
conflict = [-neg*self.boundary_to_enc[c] for c, neg in conflict]
|
| 619 |
+
return False, conflict
|
| 620 |
+
xj = min(cand, key=lambda v: v.col_idx)
|
| 621 |
+
M = self._pivot_and_update(M, basic, nonbasic, xi, xj, xi.upper)
|
| 622 |
+
|
| 623 |
+
def _pivot_and_update(self, M, basic, nonbasic, xi, xj, v):
|
| 624 |
+
"""
|
| 625 |
+
Pivots basic variable xi with nonbasic variable xj,
|
| 626 |
+
and sets value of xi to v and adjusts the values of all basic variables
|
| 627 |
+
to keep equations satisfied.
|
| 628 |
+
"""
|
| 629 |
+
i, j = basic[xi], xj.col_idx
|
| 630 |
+
assert M[i, j] != 0
|
| 631 |
+
theta = (v - xi.assign)*(1/M[i, j])
|
| 632 |
+
xi.assign = v
|
| 633 |
+
xj.assign = xj.assign + theta
|
| 634 |
+
for xk in basic:
|
| 635 |
+
if xk != xi:
|
| 636 |
+
k = basic[xk]
|
| 637 |
+
akj = M[k, j]
|
| 638 |
+
xk.assign = xk.assign + theta*akj
|
| 639 |
+
# pivot
|
| 640 |
+
basic[xj] = basic[xi]
|
| 641 |
+
del basic[xi]
|
| 642 |
+
nonbasic.add(xi)
|
| 643 |
+
nonbasic.remove(xj)
|
| 644 |
+
return self._pivot(M, i, j)
|
| 645 |
+
|
| 646 |
+
@staticmethod
|
| 647 |
+
def _pivot(M, i, j):
|
| 648 |
+
"""
|
| 649 |
+
Performs a pivot operation about entry i, j of M by performing
|
| 650 |
+
a series of row operations on a copy of M and returning the result.
|
| 651 |
+
The original M is left unmodified.
|
| 652 |
+
|
| 653 |
+
Conceptually, M represents a system of equations and pivoting
|
| 654 |
+
can be thought of as rearranging equation i to be in terms of
|
| 655 |
+
variable j and then substituting in the rest of the equations
|
| 656 |
+
to get rid of other occurances of variable j.
|
| 657 |
+
|
| 658 |
+
Example
|
| 659 |
+
=======
|
| 660 |
+
|
| 661 |
+
>>> from sympy.matrices.dense import Matrix
|
| 662 |
+
>>> from sympy.logic.algorithms.lra_theory import LRASolver
|
| 663 |
+
>>> from sympy import var
|
| 664 |
+
>>> Matrix(3, 3, var('a:i'))
|
| 665 |
+
Matrix([
|
| 666 |
+
[a, b, c],
|
| 667 |
+
[d, e, f],
|
| 668 |
+
[g, h, i]])
|
| 669 |
+
|
| 670 |
+
This matrix is equivalent to:
|
| 671 |
+
0 = a*x + b*y + c*z
|
| 672 |
+
0 = d*x + e*y + f*z
|
| 673 |
+
0 = g*x + h*y + i*z
|
| 674 |
+
|
| 675 |
+
>>> LRASolver._pivot(_, 1, 0)
|
| 676 |
+
Matrix([
|
| 677 |
+
[ 0, -a*e/d + b, -a*f/d + c],
|
| 678 |
+
[-1, -e/d, -f/d],
|
| 679 |
+
[ 0, h - e*g/d, i - f*g/d]])
|
| 680 |
+
|
| 681 |
+
We rearrange equation 1 in terms of variable 0 (x)
|
| 682 |
+
and substitute to remove x from the other equations.
|
| 683 |
+
|
| 684 |
+
0 = 0 + (-a*e/d + b)*y + (-a*f/d + c)*z
|
| 685 |
+
0 = -x + (-e/d)*y + (-f/d)*z
|
| 686 |
+
0 = 0 + (h - e*g/d)*y + (i - f*g/d)*z
|
| 687 |
+
"""
|
| 688 |
+
_, _, Mij = M[i, :], M[:, j], M[i, j]
|
| 689 |
+
if Mij == 0:
|
| 690 |
+
raise ZeroDivisionError("Tried to pivot about zero-valued entry.")
|
| 691 |
+
A = M.copy()
|
| 692 |
+
A[i, :] = -A[i, :]/Mij
|
| 693 |
+
for row in range(M.shape[0]):
|
| 694 |
+
if row != i:
|
| 695 |
+
A[row, :] = A[row, :] + A[row, j] * A[i, :]
|
| 696 |
+
|
| 697 |
+
return A
|
| 698 |
+
|
| 699 |
+
|
| 700 |
+
def _sep_const_coeff(expr):
|
| 701 |
+
"""
|
| 702 |
+
Example
|
| 703 |
+
=======
|
| 704 |
+
|
| 705 |
+
>>> from sympy.logic.algorithms.lra_theory import _sep_const_coeff
|
| 706 |
+
>>> from sympy.abc import x, y
|
| 707 |
+
>>> _sep_const_coeff(2*x)
|
| 708 |
+
(x, 2)
|
| 709 |
+
>>> _sep_const_coeff(2*x + 3*y)
|
| 710 |
+
(2*x + 3*y, 1)
|
| 711 |
+
"""
|
| 712 |
+
if isinstance(expr, Add):
|
| 713 |
+
return expr, sympify(1)
|
| 714 |
+
|
| 715 |
+
if isinstance(expr, Mul):
|
| 716 |
+
coeffs = expr.args
|
| 717 |
+
else:
|
| 718 |
+
coeffs = [expr]
|
| 719 |
+
|
| 720 |
+
var, const = [], []
|
| 721 |
+
for c in coeffs:
|
| 722 |
+
c = sympify(c)
|
| 723 |
+
if len(c.free_symbols)==0:
|
| 724 |
+
const.append(c)
|
| 725 |
+
else:
|
| 726 |
+
var.append(c)
|
| 727 |
+
return Mul(*var), Mul(*const)
|
| 728 |
+
|
| 729 |
+
|
| 730 |
+
def _list_terms(expr):
|
| 731 |
+
if not isinstance(expr, Add):
|
| 732 |
+
return [expr]
|
| 733 |
+
|
| 734 |
+
return expr.args
|
| 735 |
+
|
| 736 |
+
|
| 737 |
+
def _sep_const_terms(expr):
|
| 738 |
+
"""
|
| 739 |
+
Example
|
| 740 |
+
=======
|
| 741 |
+
|
| 742 |
+
>>> from sympy.logic.algorithms.lra_theory import _sep_const_terms
|
| 743 |
+
>>> from sympy.abc import x, y
|
| 744 |
+
>>> _sep_const_terms(2*x + 3*y + 2)
|
| 745 |
+
(2*x + 3*y, 2)
|
| 746 |
+
"""
|
| 747 |
+
if isinstance(expr, Add):
|
| 748 |
+
terms = expr.args
|
| 749 |
+
else:
|
| 750 |
+
terms = [expr]
|
| 751 |
+
|
| 752 |
+
var, const = [], []
|
| 753 |
+
for t in terms:
|
| 754 |
+
if len(t.free_symbols) == 0:
|
| 755 |
+
const.append(t)
|
| 756 |
+
else:
|
| 757 |
+
var.append(t)
|
| 758 |
+
return sum(var), sum(const)
|
| 759 |
+
|
| 760 |
+
|
| 761 |
+
def _eval_binrel(binrel):
|
| 762 |
+
"""
|
| 763 |
+
Simplify binary relation to True / False if possible.
|
| 764 |
+
"""
|
| 765 |
+
if not (len(binrel.lhs.free_symbols) == 0 and len(binrel.rhs.free_symbols) == 0):
|
| 766 |
+
return binrel
|
| 767 |
+
if binrel.function == Q.lt:
|
| 768 |
+
res = binrel.lhs < binrel.rhs
|
| 769 |
+
elif binrel.function == Q.gt:
|
| 770 |
+
res = binrel.lhs > binrel.rhs
|
| 771 |
+
elif binrel.function == Q.le:
|
| 772 |
+
res = binrel.lhs <= binrel.rhs
|
| 773 |
+
elif binrel.function == Q.ge:
|
| 774 |
+
res = binrel.lhs >= binrel.rhs
|
| 775 |
+
elif binrel.function == Q.eq:
|
| 776 |
+
res = Eq(binrel.lhs, binrel.rhs)
|
| 777 |
+
elif binrel.function == Q.ne:
|
| 778 |
+
res = Ne(binrel.lhs, binrel.rhs)
|
| 779 |
+
|
| 780 |
+
if res == True or res == False:
|
| 781 |
+
return res
|
| 782 |
+
else:
|
| 783 |
+
return None
|
| 784 |
+
|
| 785 |
+
|
| 786 |
+
class Boundary:
|
| 787 |
+
"""
|
| 788 |
+
Represents an upper or lower bound or an equality between a symbol
|
| 789 |
+
and some constant.
|
| 790 |
+
"""
|
| 791 |
+
def __init__(self, var, const, upper, equality, strict=None):
|
| 792 |
+
if not equality in [True, False]:
|
| 793 |
+
assert equality in [True, False]
|
| 794 |
+
|
| 795 |
+
|
| 796 |
+
self.var = var
|
| 797 |
+
if isinstance(const, tuple):
|
| 798 |
+
s = const[1] != 0
|
| 799 |
+
if strict:
|
| 800 |
+
assert s == strict
|
| 801 |
+
self.bound = const[0]
|
| 802 |
+
self.strict = s
|
| 803 |
+
else:
|
| 804 |
+
self.bound = const
|
| 805 |
+
self.strict = strict
|
| 806 |
+
self.upper = upper if not equality else None
|
| 807 |
+
self.equality = equality
|
| 808 |
+
self.strict = strict
|
| 809 |
+
assert self.strict is not None
|
| 810 |
+
|
| 811 |
+
@staticmethod
|
| 812 |
+
def from_upper(var):
|
| 813 |
+
neg = -1 if var.upper_from_neg else 1
|
| 814 |
+
b = Boundary(var, var.upper[0], True, var.upper_from_eq, var.upper[1] != 0)
|
| 815 |
+
if neg < 0:
|
| 816 |
+
b = b.get_negated()
|
| 817 |
+
return b, neg
|
| 818 |
+
|
| 819 |
+
@staticmethod
|
| 820 |
+
def from_lower(var):
|
| 821 |
+
neg = -1 if var.lower_from_neg else 1
|
| 822 |
+
b = Boundary(var, var.lower[0], False, var.lower_from_eq, var.lower[1] != 0)
|
| 823 |
+
if neg < 0:
|
| 824 |
+
b = b.get_negated()
|
| 825 |
+
return b, neg
|
| 826 |
+
|
| 827 |
+
def get_negated(self):
|
| 828 |
+
return Boundary(self.var, self.bound, not self.upper, self.equality, not self.strict)
|
| 829 |
+
|
| 830 |
+
def get_inequality(self):
|
| 831 |
+
if self.equality:
|
| 832 |
+
return Eq(self.var.var, self.bound)
|
| 833 |
+
elif self.upper and self.strict:
|
| 834 |
+
return self.var.var < self.bound
|
| 835 |
+
elif not self.upper and self.strict:
|
| 836 |
+
return self.var.var > self.bound
|
| 837 |
+
elif self.upper:
|
| 838 |
+
return self.var.var <= self.bound
|
| 839 |
+
else:
|
| 840 |
+
return self.var.var >= self.bound
|
| 841 |
+
|
| 842 |
+
def __repr__(self):
|
| 843 |
+
return repr("Boundary(" + repr(self.get_inequality()) + ")")
|
| 844 |
+
|
| 845 |
+
def __eq__(self, other):
|
| 846 |
+
other = (other.var, other.bound, other.strict, other.upper, other.equality)
|
| 847 |
+
return (self.var, self.bound, self.strict, self.upper, self.equality) == other
|
| 848 |
+
|
| 849 |
+
def __hash__(self):
|
| 850 |
+
return hash((self.var, self.bound, self.strict, self.upper, self.equality))
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
class LRARational():
|
| 854 |
+
"""
|
| 855 |
+
Represents a rational plus or minus some amount
|
| 856 |
+
of arbitrary small deltas.
|
| 857 |
+
"""
|
| 858 |
+
def __init__(self, rational, delta):
|
| 859 |
+
self.value = (rational, delta)
|
| 860 |
+
|
| 861 |
+
def __lt__(self, other):
|
| 862 |
+
return self.value < other.value
|
| 863 |
+
|
| 864 |
+
def __le__(self, other):
|
| 865 |
+
return self.value <= other.value
|
| 866 |
+
|
| 867 |
+
def __eq__(self, other):
|
| 868 |
+
return self.value == other.value
|
| 869 |
+
|
| 870 |
+
def __add__(self, other):
|
| 871 |
+
return LRARational(self.value[0] + other.value[0], self.value[1] + other.value[1])
|
| 872 |
+
|
| 873 |
+
def __sub__(self, other):
|
| 874 |
+
return LRARational(self.value[0] - other.value[0], self.value[1] - other.value[1])
|
| 875 |
+
|
| 876 |
+
def __mul__(self, other):
|
| 877 |
+
assert not isinstance(other, LRARational)
|
| 878 |
+
return LRARational(self.value[0] * other, self.value[1] * other)
|
| 879 |
+
|
| 880 |
+
def __getitem__(self, index):
|
| 881 |
+
return self.value[index]
|
| 882 |
+
|
| 883 |
+
def __repr__(self):
|
| 884 |
+
return repr(self.value)
|
| 885 |
+
|
| 886 |
+
|
| 887 |
+
class LRAVariable():
|
| 888 |
+
"""
|
| 889 |
+
Object to keep track of upper and lower bounds
|
| 890 |
+
on `self.var`.
|
| 891 |
+
"""
|
| 892 |
+
def __init__(self, var):
|
| 893 |
+
self.upper = LRARational(float("inf"), 0)
|
| 894 |
+
self.upper_from_eq = False
|
| 895 |
+
self.upper_from_neg = False
|
| 896 |
+
self.lower = LRARational(-float("inf"), 0)
|
| 897 |
+
self.lower_from_eq = False
|
| 898 |
+
self.lower_from_neg = False
|
| 899 |
+
self.assign = LRARational(0,0)
|
| 900 |
+
self.var = var
|
| 901 |
+
self.col_idx = None
|
| 902 |
+
|
| 903 |
+
def __repr__(self):
|
| 904 |
+
return repr(self.var)
|
| 905 |
+
|
| 906 |
+
def __eq__(self, other):
|
| 907 |
+
if not isinstance(other, LRAVariable):
|
| 908 |
+
return False
|
| 909 |
+
return other.var == self.var
|
| 910 |
+
|
| 911 |
+
def __hash__(self):
|
| 912 |
+
return hash(self.var)
|
phivenv/Lib/site-packages/sympy/logic/algorithms/minisat22_wrapper.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.cnf import EncodedCNF
|
| 2 |
+
|
| 3 |
+
def minisat22_satisfiable(expr, all_models=False, minimal=False):
|
| 4 |
+
|
| 5 |
+
if not isinstance(expr, EncodedCNF):
|
| 6 |
+
exprs = EncodedCNF()
|
| 7 |
+
exprs.add_prop(expr)
|
| 8 |
+
expr = exprs
|
| 9 |
+
|
| 10 |
+
from pysat.solvers import Minisat22
|
| 11 |
+
|
| 12 |
+
# Return UNSAT when False (encoded as 0) is present in the CNF
|
| 13 |
+
if {0} in expr.data:
|
| 14 |
+
if all_models:
|
| 15 |
+
return (f for f in [False])
|
| 16 |
+
return False
|
| 17 |
+
|
| 18 |
+
r = Minisat22(expr.data)
|
| 19 |
+
|
| 20 |
+
if minimal:
|
| 21 |
+
r.set_phases([-(i+1) for i in range(r.nof_vars())])
|
| 22 |
+
|
| 23 |
+
if not r.solve():
|
| 24 |
+
return False
|
| 25 |
+
|
| 26 |
+
if not all_models:
|
| 27 |
+
return {expr.symbols[abs(lit) - 1]: lit > 0 for lit in r.get_model()}
|
| 28 |
+
|
| 29 |
+
else:
|
| 30 |
+
# Make solutions SymPy compatible by creating a generator
|
| 31 |
+
def _gen(results):
|
| 32 |
+
satisfiable = False
|
| 33 |
+
while results.solve():
|
| 34 |
+
sol = results.get_model()
|
| 35 |
+
yield {expr.symbols[abs(lit) - 1]: lit > 0 for lit in sol}
|
| 36 |
+
if minimal:
|
| 37 |
+
results.add_clause([-i for i in sol if i>0])
|
| 38 |
+
else:
|
| 39 |
+
results.add_clause([-i for i in sol])
|
| 40 |
+
satisfiable = True
|
| 41 |
+
if not satisfiable:
|
| 42 |
+
yield False
|
| 43 |
+
raise StopIteration
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
return _gen(r)
|
phivenv/Lib/site-packages/sympy/logic/algorithms/pycosat_wrapper.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.cnf import EncodedCNF
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def pycosat_satisfiable(expr, all_models=False):
|
| 5 |
+
import pycosat
|
| 6 |
+
if not isinstance(expr, EncodedCNF):
|
| 7 |
+
exprs = EncodedCNF()
|
| 8 |
+
exprs.add_prop(expr)
|
| 9 |
+
expr = exprs
|
| 10 |
+
|
| 11 |
+
# Return UNSAT when False (encoded as 0) is present in the CNF
|
| 12 |
+
if {0} in expr.data:
|
| 13 |
+
if all_models:
|
| 14 |
+
return (f for f in [False])
|
| 15 |
+
return False
|
| 16 |
+
|
| 17 |
+
if not all_models:
|
| 18 |
+
r = pycosat.solve(expr.data)
|
| 19 |
+
result = (r != "UNSAT")
|
| 20 |
+
if not result:
|
| 21 |
+
return result
|
| 22 |
+
return {expr.symbols[abs(lit) - 1]: lit > 0 for lit in r}
|
| 23 |
+
else:
|
| 24 |
+
r = pycosat.itersolve(expr.data)
|
| 25 |
+
result = (r != "UNSAT")
|
| 26 |
+
if not result:
|
| 27 |
+
return result
|
| 28 |
+
|
| 29 |
+
# Make solutions SymPy compatible by creating a generator
|
| 30 |
+
def _gen(results):
|
| 31 |
+
satisfiable = False
|
| 32 |
+
try:
|
| 33 |
+
while True:
|
| 34 |
+
sol = next(results)
|
| 35 |
+
yield {expr.symbols[abs(lit) - 1]: lit > 0 for lit in sol}
|
| 36 |
+
satisfiable = True
|
| 37 |
+
except StopIteration:
|
| 38 |
+
if not satisfiable:
|
| 39 |
+
yield False
|
| 40 |
+
|
| 41 |
+
return _gen(r)
|
phivenv/Lib/site-packages/sympy/logic/algorithms/z3_wrapper.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.printing.smtlib import smtlib_code
|
| 2 |
+
from sympy.assumptions.assume import AppliedPredicate
|
| 3 |
+
from sympy.assumptions.cnf import EncodedCNF
|
| 4 |
+
from sympy.assumptions.ask import Q
|
| 5 |
+
|
| 6 |
+
from sympy.core import Add, Mul
|
| 7 |
+
from sympy.core.relational import Equality, LessThan, GreaterThan, StrictLessThan, StrictGreaterThan
|
| 8 |
+
from sympy.functions.elementary.complexes import Abs
|
| 9 |
+
from sympy.functions.elementary.exponential import Pow
|
| 10 |
+
from sympy.functions.elementary.miscellaneous import Min, Max
|
| 11 |
+
from sympy.logic.boolalg import And, Or, Xor, Implies
|
| 12 |
+
from sympy.logic.boolalg import Not, ITE
|
| 13 |
+
from sympy.assumptions.relation.equality import StrictGreaterThanPredicate, StrictLessThanPredicate, GreaterThanPredicate, LessThanPredicate, EqualityPredicate
|
| 14 |
+
from sympy.external import import_module
|
| 15 |
+
|
| 16 |
+
def z3_satisfiable(expr, all_models=False):
|
| 17 |
+
if not isinstance(expr, EncodedCNF):
|
| 18 |
+
exprs = EncodedCNF()
|
| 19 |
+
exprs.add_prop(expr)
|
| 20 |
+
expr = exprs
|
| 21 |
+
|
| 22 |
+
z3 = import_module("z3")
|
| 23 |
+
if z3 is None:
|
| 24 |
+
raise ImportError("z3 is not installed")
|
| 25 |
+
|
| 26 |
+
s = encoded_cnf_to_z3_solver(expr, z3)
|
| 27 |
+
|
| 28 |
+
res = str(s.check())
|
| 29 |
+
if res == "unsat":
|
| 30 |
+
return False
|
| 31 |
+
elif res == "sat":
|
| 32 |
+
return z3_model_to_sympy_model(s.model(), expr)
|
| 33 |
+
else:
|
| 34 |
+
return None
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def z3_model_to_sympy_model(z3_model, enc_cnf):
|
| 38 |
+
rev_enc = {value : key for key, value in enc_cnf.encoding.items()}
|
| 39 |
+
return {rev_enc[int(var.name()[1:])] : bool(z3_model[var]) for var in z3_model}
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def clause_to_assertion(clause):
|
| 43 |
+
clause_strings = [f"d{abs(lit)}" if lit > 0 else f"(not d{abs(lit)})" for lit in clause]
|
| 44 |
+
return "(assert (or " + " ".join(clause_strings) + "))"
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def encoded_cnf_to_z3_solver(enc_cnf, z3):
|
| 48 |
+
def dummify_bool(pred):
|
| 49 |
+
return False
|
| 50 |
+
assert isinstance(pred, AppliedPredicate)
|
| 51 |
+
|
| 52 |
+
if pred.function in [Q.positive, Q.negative, Q.zero]:
|
| 53 |
+
return pred
|
| 54 |
+
else:
|
| 55 |
+
return False
|
| 56 |
+
|
| 57 |
+
s = z3.Solver()
|
| 58 |
+
|
| 59 |
+
declarations = [f"(declare-const d{var} Bool)" for var in enc_cnf.variables]
|
| 60 |
+
assertions = [clause_to_assertion(clause) for clause in enc_cnf.data]
|
| 61 |
+
|
| 62 |
+
symbols = set()
|
| 63 |
+
for pred, enc in enc_cnf.encoding.items():
|
| 64 |
+
if not isinstance(pred, AppliedPredicate):
|
| 65 |
+
continue
|
| 66 |
+
if pred.function not in (Q.gt, Q.lt, Q.ge, Q.le, Q.ne, Q.eq, Q.positive, Q.negative, Q.extended_negative, Q.extended_positive, Q.zero, Q.nonzero, Q.nonnegative, Q.nonpositive, Q.extended_nonzero, Q.extended_nonnegative, Q.extended_nonpositive):
|
| 67 |
+
continue
|
| 68 |
+
|
| 69 |
+
pred_str = smtlib_code(pred, auto_declare=False, auto_assert=False, known_functions=known_functions)
|
| 70 |
+
|
| 71 |
+
symbols |= pred.free_symbols
|
| 72 |
+
pred = pred_str
|
| 73 |
+
clause = f"(implies d{enc} {pred})"
|
| 74 |
+
assertion = "(assert " + clause + ")"
|
| 75 |
+
assertions.append(assertion)
|
| 76 |
+
|
| 77 |
+
for sym in symbols:
|
| 78 |
+
declarations.append(f"(declare-const {sym} Real)")
|
| 79 |
+
|
| 80 |
+
declarations = "\n".join(declarations)
|
| 81 |
+
assertions = "\n".join(assertions)
|
| 82 |
+
s.from_string(declarations)
|
| 83 |
+
s.from_string(assertions)
|
| 84 |
+
|
| 85 |
+
return s
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
known_functions = {
|
| 89 |
+
Add: '+',
|
| 90 |
+
Mul: '*',
|
| 91 |
+
|
| 92 |
+
Equality: '=',
|
| 93 |
+
LessThan: '<=',
|
| 94 |
+
GreaterThan: '>=',
|
| 95 |
+
StrictLessThan: '<',
|
| 96 |
+
StrictGreaterThan: '>',
|
| 97 |
+
|
| 98 |
+
EqualityPredicate(): '=',
|
| 99 |
+
LessThanPredicate(): '<=',
|
| 100 |
+
GreaterThanPredicate(): '>=',
|
| 101 |
+
StrictLessThanPredicate(): '<',
|
| 102 |
+
StrictGreaterThanPredicate(): '>',
|
| 103 |
+
|
| 104 |
+
Abs: 'abs',
|
| 105 |
+
Min: 'min',
|
| 106 |
+
Max: 'max',
|
| 107 |
+
Pow: '^',
|
| 108 |
+
|
| 109 |
+
And: 'and',
|
| 110 |
+
Or: 'or',
|
| 111 |
+
Xor: 'xor',
|
| 112 |
+
Not: 'not',
|
| 113 |
+
ITE: 'ite',
|
| 114 |
+
Implies: '=>',
|
| 115 |
+
}
|
phivenv/Lib/site-packages/sympy/logic/tests/__init__.py
ADDED
|
File without changes
|
phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (161 Bytes). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_boolalg.cpython-39.pyc
ADDED
|
Binary file (49.7 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_dimacs.cpython-39.pyc
ADDED
|
Binary file (4.48 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_inference.cpython-39.pyc
ADDED
|
Binary file (15.5 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/tests/__pycache__/test_lra_theory.cpython-39.pyc
ADDED
|
Binary file (14.9 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/tests/test_boolalg.py
ADDED
|
@@ -0,0 +1,1367 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.ask import Q
|
| 2 |
+
from sympy.assumptions.refine import refine
|
| 3 |
+
from sympy.core.numbers import oo
|
| 4 |
+
from sympy.core.relational import Equality, Eq, Ne
|
| 5 |
+
from sympy.core.singleton import S
|
| 6 |
+
from sympy.core.symbol import (Dummy, symbols)
|
| 7 |
+
from sympy.functions import Piecewise
|
| 8 |
+
from sympy.functions.elementary.trigonometric import cos, sin
|
| 9 |
+
from sympy.sets.sets import Interval, Union
|
| 10 |
+
from sympy.sets.contains import Contains
|
| 11 |
+
from sympy.simplify.simplify import simplify
|
| 12 |
+
from sympy.logic.boolalg import (
|
| 13 |
+
And, Boolean, Equivalent, ITE, Implies, Nand, Nor, Not, Or,
|
| 14 |
+
POSform, SOPform, Xor, Xnor, conjuncts, disjuncts,
|
| 15 |
+
distribute_or_over_and, distribute_and_over_or,
|
| 16 |
+
eliminate_implications, is_nnf, is_cnf, is_dnf, simplify_logic,
|
| 17 |
+
to_nnf, to_cnf, to_dnf, to_int_repr, bool_map, true, false,
|
| 18 |
+
BooleanAtom, is_literal, term_to_integer,
|
| 19 |
+
truth_table, as_Boolean, to_anf, is_anf, distribute_xor_over_and,
|
| 20 |
+
anf_coeffs, ANFform, bool_minterm, bool_maxterm, bool_monomial,
|
| 21 |
+
_check_pair, _convert_to_varsSOP, _convert_to_varsPOS, Exclusive,
|
| 22 |
+
gateinputcount)
|
| 23 |
+
from sympy.assumptions.cnf import CNF
|
| 24 |
+
|
| 25 |
+
from sympy.testing.pytest import raises, XFAIL, slow
|
| 26 |
+
|
| 27 |
+
from itertools import combinations, permutations, product
|
| 28 |
+
|
| 29 |
+
A, B, C, D = symbols('A:D')
|
| 30 |
+
a, b, c, d, e, w, x, y, z = symbols('a:e w:z')
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def test_overloading():
|
| 34 |
+
"""Test that |, & are overloaded as expected"""
|
| 35 |
+
|
| 36 |
+
assert A & B == And(A, B)
|
| 37 |
+
assert A | B == Or(A, B)
|
| 38 |
+
assert (A & B) | C == Or(And(A, B), C)
|
| 39 |
+
assert A >> B == Implies(A, B)
|
| 40 |
+
assert A << B == Implies(B, A)
|
| 41 |
+
assert ~A == Not(A)
|
| 42 |
+
assert A ^ B == Xor(A, B)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_And():
|
| 46 |
+
assert And() is true
|
| 47 |
+
assert And(A) == A
|
| 48 |
+
assert And(True) is true
|
| 49 |
+
assert And(False) is false
|
| 50 |
+
assert And(True, True) is true
|
| 51 |
+
assert And(True, False) is false
|
| 52 |
+
assert And(False, False) is false
|
| 53 |
+
assert And(True, A) == A
|
| 54 |
+
assert And(False, A) is false
|
| 55 |
+
assert And(True, True, True) is true
|
| 56 |
+
assert And(True, True, A) == A
|
| 57 |
+
assert And(True, False, A) is false
|
| 58 |
+
assert And(1, A) == A
|
| 59 |
+
raises(TypeError, lambda: And(2, A))
|
| 60 |
+
assert And(A < 1, A >= 1) is false
|
| 61 |
+
e = A > 1
|
| 62 |
+
assert And(e, e.canonical) == e.canonical
|
| 63 |
+
g, l, ge, le = A > B, B < A, A >= B, B <= A
|
| 64 |
+
assert And(g, l, ge, le) == And(ge, g)
|
| 65 |
+
assert {And(*i) for i in permutations((l, g, le, ge))} == {And(ge, g)}
|
| 66 |
+
assert And(And(Eq(a, 0), Eq(b, 0)), And(Ne(a, 0), Eq(c, 0))) is false
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def test_Or():
|
| 70 |
+
assert Or() is false
|
| 71 |
+
assert Or(A) == A
|
| 72 |
+
assert Or(True) is true
|
| 73 |
+
assert Or(False) is false
|
| 74 |
+
assert Or(True, True) is true
|
| 75 |
+
assert Or(True, False) is true
|
| 76 |
+
assert Or(False, False) is false
|
| 77 |
+
assert Or(True, A) is true
|
| 78 |
+
assert Or(False, A) == A
|
| 79 |
+
assert Or(True, False, False) is true
|
| 80 |
+
assert Or(True, False, A) is true
|
| 81 |
+
assert Or(False, False, A) == A
|
| 82 |
+
assert Or(1, A) is true
|
| 83 |
+
raises(TypeError, lambda: Or(2, A))
|
| 84 |
+
assert Or(A < 1, A >= 1) is true
|
| 85 |
+
e = A > 1
|
| 86 |
+
assert Or(e, e.canonical) == e
|
| 87 |
+
g, l, ge, le = A > B, B < A, A >= B, B <= A
|
| 88 |
+
assert Or(g, l, ge, le) == Or(g, ge)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def test_Xor():
|
| 92 |
+
assert Xor() is false
|
| 93 |
+
assert Xor(A) == A
|
| 94 |
+
assert Xor(A, A) is false
|
| 95 |
+
assert Xor(True, A, A) is true
|
| 96 |
+
assert Xor(A, A, A, A, A) == A
|
| 97 |
+
assert Xor(True, False, False, A, B) == ~Xor(A, B)
|
| 98 |
+
assert Xor(True) is true
|
| 99 |
+
assert Xor(False) is false
|
| 100 |
+
assert Xor(True, True) is false
|
| 101 |
+
assert Xor(True, False) is true
|
| 102 |
+
assert Xor(False, False) is false
|
| 103 |
+
assert Xor(True, A) == ~A
|
| 104 |
+
assert Xor(False, A) == A
|
| 105 |
+
assert Xor(True, False, False) is true
|
| 106 |
+
assert Xor(True, False, A) == ~A
|
| 107 |
+
assert Xor(False, False, A) == A
|
| 108 |
+
assert isinstance(Xor(A, B), Xor)
|
| 109 |
+
assert Xor(A, B, Xor(C, D)) == Xor(A, B, C, D)
|
| 110 |
+
assert Xor(A, B, Xor(B, C)) == Xor(A, C)
|
| 111 |
+
assert Xor(A < 1, A >= 1, B) == Xor(0, 1, B) == Xor(1, 0, B)
|
| 112 |
+
e = A > 1
|
| 113 |
+
assert Xor(e, e.canonical) == Xor(0, 0) == Xor(1, 1)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def test_rewrite_as_And():
|
| 117 |
+
expr = x ^ y
|
| 118 |
+
assert expr.rewrite(And) == (x | y) & (~x | ~y)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def test_rewrite_as_Or():
|
| 122 |
+
expr = x ^ y
|
| 123 |
+
assert expr.rewrite(Or) == (x & ~y) | (y & ~x)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def test_rewrite_as_Nand():
|
| 127 |
+
expr = (y & z) | (z & ~w)
|
| 128 |
+
assert expr.rewrite(Nand) == ~(~(y & z) & ~(z & ~w))
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def test_rewrite_as_Nor():
|
| 132 |
+
expr = z & (y | ~w)
|
| 133 |
+
assert expr.rewrite(Nor) == ~(~z | ~(y | ~w))
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def test_Not():
|
| 137 |
+
raises(TypeError, lambda: Not(True, False))
|
| 138 |
+
assert Not(True) is false
|
| 139 |
+
assert Not(False) is true
|
| 140 |
+
assert Not(0) is true
|
| 141 |
+
assert Not(1) is false
|
| 142 |
+
assert Not(2) is false
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def test_Nand():
|
| 146 |
+
assert Nand() is false
|
| 147 |
+
assert Nand(A) == ~A
|
| 148 |
+
assert Nand(True) is false
|
| 149 |
+
assert Nand(False) is true
|
| 150 |
+
assert Nand(True, True) is false
|
| 151 |
+
assert Nand(True, False) is true
|
| 152 |
+
assert Nand(False, False) is true
|
| 153 |
+
assert Nand(True, A) == ~A
|
| 154 |
+
assert Nand(False, A) is true
|
| 155 |
+
assert Nand(True, True, True) is false
|
| 156 |
+
assert Nand(True, True, A) == ~A
|
| 157 |
+
assert Nand(True, False, A) is true
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def test_Nor():
|
| 161 |
+
assert Nor() is true
|
| 162 |
+
assert Nor(A) == ~A
|
| 163 |
+
assert Nor(True) is false
|
| 164 |
+
assert Nor(False) is true
|
| 165 |
+
assert Nor(True, True) is false
|
| 166 |
+
assert Nor(True, False) is false
|
| 167 |
+
assert Nor(False, False) is true
|
| 168 |
+
assert Nor(True, A) is false
|
| 169 |
+
assert Nor(False, A) == ~A
|
| 170 |
+
assert Nor(True, True, True) is false
|
| 171 |
+
assert Nor(True, True, A) is false
|
| 172 |
+
assert Nor(True, False, A) is false
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def test_Xnor():
|
| 176 |
+
assert Xnor() is true
|
| 177 |
+
assert Xnor(A) == ~A
|
| 178 |
+
assert Xnor(A, A) is true
|
| 179 |
+
assert Xnor(True, A, A) is false
|
| 180 |
+
assert Xnor(A, A, A, A, A) == ~A
|
| 181 |
+
assert Xnor(True) is false
|
| 182 |
+
assert Xnor(False) is true
|
| 183 |
+
assert Xnor(True, True) is true
|
| 184 |
+
assert Xnor(True, False) is false
|
| 185 |
+
assert Xnor(False, False) is true
|
| 186 |
+
assert Xnor(True, A) == A
|
| 187 |
+
assert Xnor(False, A) == ~A
|
| 188 |
+
assert Xnor(True, False, False) is false
|
| 189 |
+
assert Xnor(True, False, A) == A
|
| 190 |
+
assert Xnor(False, False, A) == ~A
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def test_Implies():
|
| 194 |
+
raises(ValueError, lambda: Implies(A, B, C))
|
| 195 |
+
assert Implies(True, True) is true
|
| 196 |
+
assert Implies(True, False) is false
|
| 197 |
+
assert Implies(False, True) is true
|
| 198 |
+
assert Implies(False, False) is true
|
| 199 |
+
assert Implies(0, A) is true
|
| 200 |
+
assert Implies(1, 1) is true
|
| 201 |
+
assert Implies(1, 0) is false
|
| 202 |
+
assert A >> B == B << A
|
| 203 |
+
assert (A < 1) >> (A >= 1) == (A >= 1)
|
| 204 |
+
assert (A < 1) >> (S.One > A) is true
|
| 205 |
+
assert A >> A is true
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def test_Equivalent():
|
| 209 |
+
assert Equivalent(A, B) == Equivalent(B, A) == Equivalent(A, B, A)
|
| 210 |
+
assert Equivalent() is true
|
| 211 |
+
assert Equivalent(A, A) == Equivalent(A) is true
|
| 212 |
+
assert Equivalent(True, True) == Equivalent(False, False) is true
|
| 213 |
+
assert Equivalent(True, False) == Equivalent(False, True) is false
|
| 214 |
+
assert Equivalent(A, True) == A
|
| 215 |
+
assert Equivalent(A, False) == Not(A)
|
| 216 |
+
assert Equivalent(A, B, True) == A & B
|
| 217 |
+
assert Equivalent(A, B, False) == ~A & ~B
|
| 218 |
+
assert Equivalent(1, A) == A
|
| 219 |
+
assert Equivalent(0, A) == Not(A)
|
| 220 |
+
assert Equivalent(A, Equivalent(B, C)) != Equivalent(Equivalent(A, B), C)
|
| 221 |
+
assert Equivalent(A < 1, A >= 1) is false
|
| 222 |
+
assert Equivalent(A < 1, A >= 1, 0) is false
|
| 223 |
+
assert Equivalent(A < 1, A >= 1, 1) is false
|
| 224 |
+
assert Equivalent(A < 1, S.One > A) == Equivalent(1, 1) == Equivalent(0, 0)
|
| 225 |
+
assert Equivalent(Equality(A, B), Equality(B, A)) is true
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def test_Exclusive():
|
| 229 |
+
assert Exclusive(False, False, False) is true
|
| 230 |
+
assert Exclusive(True, False, False) is true
|
| 231 |
+
assert Exclusive(True, True, False) is false
|
| 232 |
+
assert Exclusive(True, True, True) is false
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def test_equals():
|
| 236 |
+
assert Not(Or(A, B)).equals(And(Not(A), Not(B))) is True
|
| 237 |
+
assert Equivalent(A, B).equals((A >> B) & (B >> A)) is True
|
| 238 |
+
assert ((A | ~B) & (~A | B)).equals((~A & ~B) | (A & B)) is True
|
| 239 |
+
assert (A >> B).equals(~A >> ~B) is False
|
| 240 |
+
assert (A >> (B >> A)).equals(A >> (C >> A)) is False
|
| 241 |
+
raises(NotImplementedError, lambda: (A & B).equals(A > B))
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
def test_simplification_boolalg():
|
| 245 |
+
"""
|
| 246 |
+
Test working of simplification methods.
|
| 247 |
+
"""
|
| 248 |
+
set1 = [[0, 0, 1], [0, 1, 1], [1, 0, 0], [1, 1, 0]]
|
| 249 |
+
set2 = [[0, 0, 0], [0, 1, 0], [1, 0, 1], [1, 1, 1]]
|
| 250 |
+
assert SOPform([x, y, z], set1) == Or(And(Not(x), z), And(Not(z), x))
|
| 251 |
+
assert Not(SOPform([x, y, z], set2)) == \
|
| 252 |
+
Not(Or(And(Not(x), Not(z)), And(x, z)))
|
| 253 |
+
assert POSform([x, y, z], set1 + set2) is true
|
| 254 |
+
assert SOPform([x, y, z], set1 + set2) is true
|
| 255 |
+
assert SOPform([Dummy(), Dummy(), Dummy()], set1 + set2) is true
|
| 256 |
+
|
| 257 |
+
minterms = [[0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1], [1, 0, 1, 1],
|
| 258 |
+
[1, 1, 1, 1]]
|
| 259 |
+
dontcares = [[0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 1]]
|
| 260 |
+
assert (
|
| 261 |
+
SOPform([w, x, y, z], minterms, dontcares) ==
|
| 262 |
+
Or(And(y, z), And(Not(w), Not(x))))
|
| 263 |
+
assert POSform([w, x, y, z], minterms, dontcares) == And(Or(Not(w), y), z)
|
| 264 |
+
|
| 265 |
+
minterms = [1, 3, 7, 11, 15]
|
| 266 |
+
dontcares = [0, 2, 5]
|
| 267 |
+
assert (
|
| 268 |
+
SOPform([w, x, y, z], minterms, dontcares) ==
|
| 269 |
+
Or(And(y, z), And(Not(w), Not(x))))
|
| 270 |
+
assert POSform([w, x, y, z], minterms, dontcares) == And(Or(Not(w), y), z)
|
| 271 |
+
|
| 272 |
+
minterms = [1, [0, 0, 1, 1], 7, [1, 0, 1, 1],
|
| 273 |
+
[1, 1, 1, 1]]
|
| 274 |
+
dontcares = [0, [0, 0, 1, 0], 5]
|
| 275 |
+
assert (
|
| 276 |
+
SOPform([w, x, y, z], minterms, dontcares) ==
|
| 277 |
+
Or(And(y, z), And(Not(w), Not(x))))
|
| 278 |
+
assert POSform([w, x, y, z], minterms, dontcares) == And(Or(Not(w), y), z)
|
| 279 |
+
|
| 280 |
+
minterms = [1, {y: 1, z: 1}]
|
| 281 |
+
dontcares = [0, [0, 0, 1, 0], 5]
|
| 282 |
+
assert (
|
| 283 |
+
SOPform([w, x, y, z], minterms, dontcares) ==
|
| 284 |
+
Or(And(y, z), And(Not(w), Not(x))))
|
| 285 |
+
assert POSform([w, x, y, z], minterms, dontcares) == And(Or(Not(w), y), z)
|
| 286 |
+
|
| 287 |
+
minterms = [{y: 1, z: 1}, 1]
|
| 288 |
+
dontcares = [[0, 0, 0, 0]]
|
| 289 |
+
|
| 290 |
+
minterms = [[0, 0, 0]]
|
| 291 |
+
raises(ValueError, lambda: SOPform([w, x, y, z], minterms))
|
| 292 |
+
raises(ValueError, lambda: POSform([w, x, y, z], minterms))
|
| 293 |
+
|
| 294 |
+
raises(TypeError, lambda: POSform([w, x, y, z], ["abcdefg"]))
|
| 295 |
+
|
| 296 |
+
# test simplification
|
| 297 |
+
ans = And(A, Or(B, C))
|
| 298 |
+
assert simplify_logic(A & (B | C)) == ans
|
| 299 |
+
assert simplify_logic((A & B) | (A & C)) == ans
|
| 300 |
+
assert simplify_logic(Implies(A, B)) == Or(Not(A), B)
|
| 301 |
+
assert simplify_logic(Equivalent(A, B)) == \
|
| 302 |
+
Or(And(A, B), And(Not(A), Not(B)))
|
| 303 |
+
assert simplify_logic(And(Equality(A, 2), C)) == And(Equality(A, 2), C)
|
| 304 |
+
assert simplify_logic(And(Equality(A, 2), A)) == And(Equality(A, 2), A)
|
| 305 |
+
assert simplify_logic(And(Equality(A, B), C)) == And(Equality(A, B), C)
|
| 306 |
+
assert simplify_logic(Or(And(Equality(A, 3), B), And(Equality(A, 3), C))) \
|
| 307 |
+
== And(Equality(A, 3), Or(B, C))
|
| 308 |
+
b = (~x & ~y & ~z) | (~x & ~y & z)
|
| 309 |
+
e = And(A, b)
|
| 310 |
+
assert simplify_logic(e) == A & ~x & ~y
|
| 311 |
+
raises(ValueError, lambda: simplify_logic(A & (B | C), form='blabla'))
|
| 312 |
+
assert simplify(Or(x <= y, And(x < y, z))) == (x <= y)
|
| 313 |
+
assert simplify(Or(x <= y, And(y > x, z))) == (x <= y)
|
| 314 |
+
assert simplify(Or(x >= y, And(y < x, z))) == (x >= y)
|
| 315 |
+
|
| 316 |
+
# Check that expressions with nine variables or more are not simplified
|
| 317 |
+
# (without the force-flag)
|
| 318 |
+
a, b, c, d, e, f, g, h, j = symbols('a b c d e f g h j')
|
| 319 |
+
expr = a & b & c & d & e & f & g & h & j | \
|
| 320 |
+
a & b & c & d & e & f & g & h & ~j
|
| 321 |
+
# This expression can be simplified to get rid of the j variables
|
| 322 |
+
assert simplify_logic(expr) == expr
|
| 323 |
+
|
| 324 |
+
# Test dontcare
|
| 325 |
+
assert simplify_logic((a & b) | c | d, dontcare=(a & b)) == c | d
|
| 326 |
+
|
| 327 |
+
# check input
|
| 328 |
+
ans = SOPform([x, y], [[1, 0]])
|
| 329 |
+
assert SOPform([x, y], [[1, 0]]) == ans
|
| 330 |
+
assert POSform([x, y], [[1, 0]]) == ans
|
| 331 |
+
|
| 332 |
+
raises(ValueError, lambda: SOPform([x], [[1]], [[1]]))
|
| 333 |
+
assert SOPform([x], [[1]], [[0]]) is true
|
| 334 |
+
assert SOPform([x], [[0]], [[1]]) is true
|
| 335 |
+
assert SOPform([x], [], []) is false
|
| 336 |
+
|
| 337 |
+
raises(ValueError, lambda: POSform([x], [[1]], [[1]]))
|
| 338 |
+
assert POSform([x], [[1]], [[0]]) is true
|
| 339 |
+
assert POSform([x], [[0]], [[1]]) is true
|
| 340 |
+
assert POSform([x], [], []) is false
|
| 341 |
+
|
| 342 |
+
# check working of simplify
|
| 343 |
+
assert simplify((A & B) | (A & C)) == And(A, Or(B, C))
|
| 344 |
+
assert simplify(And(x, Not(x))) == False
|
| 345 |
+
assert simplify(Or(x, Not(x))) == True
|
| 346 |
+
assert simplify(And(Eq(x, 0), Eq(x, y))) == And(Eq(x, 0), Eq(y, 0))
|
| 347 |
+
assert And(Eq(x - 1, 0), Eq(x, y)).simplify() == And(Eq(x, 1), Eq(y, 1))
|
| 348 |
+
assert And(Ne(x - 1, 0), Ne(x, y)).simplify() == And(Ne(x, 1), Ne(x, y))
|
| 349 |
+
assert And(Eq(x - 1, 0), Ne(x, y)).simplify() == And(Eq(x, 1), Ne(y, 1))
|
| 350 |
+
assert And(Eq(x - 1, 0), Eq(x, z + y), Eq(y + x, 0)).simplify(
|
| 351 |
+
) == And(Eq(x, 1), Eq(y, -1), Eq(z, 2))
|
| 352 |
+
assert And(Eq(x - 1, 0), Eq(x + 2, 3)).simplify() == Eq(x, 1)
|
| 353 |
+
assert And(Ne(x - 1, 0), Ne(x + 2, 3)).simplify() == Ne(x, 1)
|
| 354 |
+
assert And(Eq(x - 1, 0), Eq(x + 2, 2)).simplify() == False
|
| 355 |
+
assert And(Ne(x - 1, 0), Ne(x + 2, 2)).simplify(
|
| 356 |
+
) == And(Ne(x, 1), Ne(x, 0))
|
| 357 |
+
assert simplify(Xor(x, ~x)) == True
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def test_bool_map():
|
| 361 |
+
"""
|
| 362 |
+
Test working of bool_map function.
|
| 363 |
+
"""
|
| 364 |
+
|
| 365 |
+
minterms = [[0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1], [1, 0, 1, 1],
|
| 366 |
+
[1, 1, 1, 1]]
|
| 367 |
+
assert bool_map(Not(Not(a)), a) == (a, {a: a})
|
| 368 |
+
assert bool_map(SOPform([w, x, y, z], minterms),
|
| 369 |
+
POSform([w, x, y, z], minterms)) == \
|
| 370 |
+
(And(Or(Not(w), y), Or(Not(x), y), z), {x: x, w: w, z: z, y: y})
|
| 371 |
+
assert bool_map(SOPform([x, z, y], [[1, 0, 1]]),
|
| 372 |
+
SOPform([a, b, c], [[1, 0, 1]])) != False
|
| 373 |
+
function1 = SOPform([x, z, y], [[1, 0, 1], [0, 0, 1]])
|
| 374 |
+
function2 = SOPform([a, b, c], [[1, 0, 1], [1, 0, 0]])
|
| 375 |
+
assert bool_map(function1, function2) == \
|
| 376 |
+
(function1, {y: a, z: b})
|
| 377 |
+
assert bool_map(Xor(x, y), ~Xor(x, y)) == False
|
| 378 |
+
assert bool_map(And(x, y), Or(x, y)) is None
|
| 379 |
+
assert bool_map(And(x, y), And(x, y, z)) is None
|
| 380 |
+
# issue 16179
|
| 381 |
+
assert bool_map(Xor(x, y, z), ~Xor(x, y, z)) == False
|
| 382 |
+
assert bool_map(Xor(a, x, y, z), ~Xor(a, x, y, z)) == False
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
def test_bool_symbol():
|
| 386 |
+
"""Test that mixing symbols with boolean values
|
| 387 |
+
works as expected"""
|
| 388 |
+
|
| 389 |
+
assert And(A, True) == A
|
| 390 |
+
assert And(A, True, True) == A
|
| 391 |
+
assert And(A, False) is false
|
| 392 |
+
assert And(A, True, False) is false
|
| 393 |
+
assert Or(A, True) is true
|
| 394 |
+
assert Or(A, False) == A
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def test_is_boolean():
|
| 398 |
+
assert isinstance(True, Boolean) is False
|
| 399 |
+
assert isinstance(true, Boolean) is True
|
| 400 |
+
assert 1 == True
|
| 401 |
+
assert 1 != true
|
| 402 |
+
assert (1 == true) is False
|
| 403 |
+
assert 0 == False
|
| 404 |
+
assert 0 != false
|
| 405 |
+
assert (0 == false) is False
|
| 406 |
+
assert true.is_Boolean is True
|
| 407 |
+
assert (A & B).is_Boolean
|
| 408 |
+
assert (A | B).is_Boolean
|
| 409 |
+
assert (~A).is_Boolean
|
| 410 |
+
assert (A ^ B).is_Boolean
|
| 411 |
+
assert A.is_Boolean != isinstance(A, Boolean)
|
| 412 |
+
assert isinstance(A, Boolean)
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
def test_subs():
|
| 416 |
+
assert (A & B).subs(A, True) == B
|
| 417 |
+
assert (A & B).subs(A, False) is false
|
| 418 |
+
assert (A & B).subs(B, True) == A
|
| 419 |
+
assert (A & B).subs(B, False) is false
|
| 420 |
+
assert (A & B).subs({A: True, B: True}) is true
|
| 421 |
+
assert (A | B).subs(A, True) is true
|
| 422 |
+
assert (A | B).subs(A, False) == B
|
| 423 |
+
assert (A | B).subs(B, True) is true
|
| 424 |
+
assert (A | B).subs(B, False) == A
|
| 425 |
+
assert (A | B).subs({A: True, B: True}) is true
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
"""
|
| 429 |
+
we test for axioms of boolean algebra
|
| 430 |
+
see https://en.wikipedia.org/wiki/Boolean_algebra_(structure)
|
| 431 |
+
"""
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def test_commutative():
|
| 435 |
+
"""Test for commutativity of And and Or"""
|
| 436 |
+
A, B = map(Boolean, symbols('A,B'))
|
| 437 |
+
|
| 438 |
+
assert A & B == B & A
|
| 439 |
+
assert A | B == B | A
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def test_and_associativity():
|
| 443 |
+
"""Test for associativity of And"""
|
| 444 |
+
|
| 445 |
+
assert (A & B) & C == A & (B & C)
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def test_or_assicativity():
|
| 449 |
+
assert ((A | B) | C) == (A | (B | C))
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def test_double_negation():
|
| 453 |
+
a = Boolean()
|
| 454 |
+
assert ~(~a) == a
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
# test methods
|
| 458 |
+
|
| 459 |
+
def test_eliminate_implications():
|
| 460 |
+
assert eliminate_implications(Implies(A, B, evaluate=False)) == (~A) | B
|
| 461 |
+
assert eliminate_implications(
|
| 462 |
+
A >> (C >> Not(B))) == Or(Or(Not(B), Not(C)), Not(A))
|
| 463 |
+
assert eliminate_implications(Equivalent(A, B, C, D)) == \
|
| 464 |
+
(~A | B) & (~B | C) & (~C | D) & (~D | A)
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
def test_conjuncts():
|
| 468 |
+
assert conjuncts(A & B & C) == {A, B, C}
|
| 469 |
+
assert conjuncts((A | B) & C) == {A | B, C}
|
| 470 |
+
assert conjuncts(A) == {A}
|
| 471 |
+
assert conjuncts(True) == {True}
|
| 472 |
+
assert conjuncts(False) == {False}
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
def test_disjuncts():
|
| 476 |
+
assert disjuncts(A | B | C) == {A, B, C}
|
| 477 |
+
assert disjuncts((A | B) & C) == {(A | B) & C}
|
| 478 |
+
assert disjuncts(A) == {A}
|
| 479 |
+
assert disjuncts(True) == {True}
|
| 480 |
+
assert disjuncts(False) == {False}
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def test_distribute():
|
| 484 |
+
assert distribute_and_over_or(Or(And(A, B), C)) == And(Or(A, C), Or(B, C))
|
| 485 |
+
assert distribute_or_over_and(And(A, Or(B, C))) == Or(And(A, B), And(A, C))
|
| 486 |
+
assert distribute_xor_over_and(And(A, Xor(B, C))) == Xor(And(A, B), And(A, C))
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
def test_to_anf():
|
| 490 |
+
x, y, z = symbols('x,y,z')
|
| 491 |
+
assert to_anf(And(x, y)) == And(x, y)
|
| 492 |
+
assert to_anf(Or(x, y)) == Xor(x, y, And(x, y))
|
| 493 |
+
assert to_anf(Or(Implies(x, y), And(x, y), y)) == \
|
| 494 |
+
Xor(x, True, x & y, remove_true=False)
|
| 495 |
+
assert to_anf(Or(Nand(x, y), Nor(x, y), Xnor(x, y), Implies(x, y))) == True
|
| 496 |
+
assert to_anf(Or(x, Not(y), Nor(x, z), And(x, y), Nand(y, z))) == \
|
| 497 |
+
Xor(True, And(y, z), And(x, y, z), remove_true=False)
|
| 498 |
+
assert to_anf(Xor(x, y)) == Xor(x, y)
|
| 499 |
+
assert to_anf(Not(x)) == Xor(x, True, remove_true=False)
|
| 500 |
+
assert to_anf(Nand(x, y)) == Xor(True, And(x, y), remove_true=False)
|
| 501 |
+
assert to_anf(Nor(x, y)) == Xor(x, y, True, And(x, y), remove_true=False)
|
| 502 |
+
assert to_anf(Implies(x, y)) == Xor(x, True, And(x, y), remove_true=False)
|
| 503 |
+
assert to_anf(Equivalent(x, y)) == Xor(x, y, True, remove_true=False)
|
| 504 |
+
assert to_anf(Nand(x | y, x >> y), deep=False) == \
|
| 505 |
+
Xor(True, And(Or(x, y), Implies(x, y)), remove_true=False)
|
| 506 |
+
assert to_anf(Nor(x ^ y, x & y), deep=False) == \
|
| 507 |
+
Xor(True, Or(Xor(x, y), And(x, y)), remove_true=False)
|
| 508 |
+
# issue 25218
|
| 509 |
+
assert to_anf(x ^ ~(x ^ y ^ ~y)) == False
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
def test_to_nnf():
|
| 513 |
+
assert to_nnf(true) is true
|
| 514 |
+
assert to_nnf(false) is false
|
| 515 |
+
assert to_nnf(A) == A
|
| 516 |
+
assert to_nnf(A | ~A | B) is true
|
| 517 |
+
assert to_nnf(A & ~A & B) is false
|
| 518 |
+
assert to_nnf(A >> B) == ~A | B
|
| 519 |
+
assert to_nnf(Equivalent(A, B, C)) == (~A | B) & (~B | C) & (~C | A)
|
| 520 |
+
assert to_nnf(A ^ B ^ C) == \
|
| 521 |
+
(A | B | C) & (~A | ~B | C) & (A | ~B | ~C) & (~A | B | ~C)
|
| 522 |
+
assert to_nnf(ITE(A, B, C)) == (~A | B) & (A | C)
|
| 523 |
+
assert to_nnf(Not(A | B | C)) == ~A & ~B & ~C
|
| 524 |
+
assert to_nnf(Not(A & B & C)) == ~A | ~B | ~C
|
| 525 |
+
assert to_nnf(Not(A >> B)) == A & ~B
|
| 526 |
+
assert to_nnf(Not(Equivalent(A, B, C))) == And(Or(A, B, C), Or(~A, ~B, ~C))
|
| 527 |
+
assert to_nnf(Not(A ^ B ^ C)) == \
|
| 528 |
+
(~A | B | C) & (A | ~B | C) & (A | B | ~C) & (~A | ~B | ~C)
|
| 529 |
+
assert to_nnf(Not(ITE(A, B, C))) == (~A | ~B) & (A | ~C)
|
| 530 |
+
assert to_nnf((A >> B) ^ (B >> A)) == (A & ~B) | (~A & B)
|
| 531 |
+
assert to_nnf((A >> B) ^ (B >> A), False) == \
|
| 532 |
+
(~A | ~B | A | B) & ((A & ~B) | (~A & B))
|
| 533 |
+
assert ITE(A, 1, 0).to_nnf() == A
|
| 534 |
+
assert ITE(A, 0, 1).to_nnf() == ~A
|
| 535 |
+
# although ITE can hold non-Boolean, it will complain if
|
| 536 |
+
# an attempt is made to convert the ITE to Boolean nnf
|
| 537 |
+
raises(TypeError, lambda: ITE(A < 1, [1], B).to_nnf())
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
def test_to_cnf():
|
| 541 |
+
assert to_cnf(~(B | C)) == And(Not(B), Not(C))
|
| 542 |
+
assert to_cnf((A & B) | C) == And(Or(A, C), Or(B, C))
|
| 543 |
+
assert to_cnf(A >> B) == (~A) | B
|
| 544 |
+
assert to_cnf(A >> (B & C)) == (~A | B) & (~A | C)
|
| 545 |
+
assert to_cnf(A & (B | C) | ~A & (B | C), True) == B | C
|
| 546 |
+
assert to_cnf(A & B) == And(A, B)
|
| 547 |
+
|
| 548 |
+
assert to_cnf(Equivalent(A, B)) == And(Or(A, Not(B)), Or(B, Not(A)))
|
| 549 |
+
assert to_cnf(Equivalent(A, B & C)) == \
|
| 550 |
+
(~A | B) & (~A | C) & (~B | ~C | A)
|
| 551 |
+
assert to_cnf(Equivalent(A, B | C), True) == \
|
| 552 |
+
And(Or(Not(B), A), Or(Not(C), A), Or(B, C, Not(A)))
|
| 553 |
+
assert to_cnf(A + 1) == A + 1
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
def test_issue_18904():
|
| 557 |
+
x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 = symbols('x1:16')
|
| 558 |
+
eq = ((x1 & x2 & x3 & x4 & x5 & x6 & x7 & x8 & x9) |
|
| 559 |
+
(x1 & x2 & x3 & x4 & x5 & x6 & x7 & x10 & x9) |
|
| 560 |
+
(x1 & x11 & x3 & x12 & x5 & x13 & x14 & x15 & x9))
|
| 561 |
+
assert is_cnf(to_cnf(eq))
|
| 562 |
+
raises(ValueError, lambda: to_cnf(eq, simplify=True))
|
| 563 |
+
for f, t in zip((And, Or), (to_cnf, to_dnf)):
|
| 564 |
+
eq = f(x1, x2, x3, x4, x5, x6, x7, x8, x9)
|
| 565 |
+
raises(ValueError, lambda: to_cnf(eq, simplify=True))
|
| 566 |
+
assert t(eq, simplify=True, force=True) == eq
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
def test_issue_9949():
|
| 570 |
+
assert is_cnf(to_cnf((b > -5) | (a > 2) & (a < 4)))
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
def test_to_CNF():
|
| 574 |
+
assert CNF.CNF_to_cnf(CNF.to_CNF(~(B | C))) == to_cnf(~(B | C))
|
| 575 |
+
assert CNF.CNF_to_cnf(CNF.to_CNF((A & B) | C)) == to_cnf((A & B) | C)
|
| 576 |
+
assert CNF.CNF_to_cnf(CNF.to_CNF(A >> B)) == to_cnf(A >> B)
|
| 577 |
+
assert CNF.CNF_to_cnf(CNF.to_CNF(A >> (B & C))) == to_cnf(A >> (B & C))
|
| 578 |
+
assert CNF.CNF_to_cnf(CNF.to_CNF(A & (B | C) | ~A & (B | C))) == to_cnf(A & (B | C) | ~A & (B | C))
|
| 579 |
+
assert CNF.CNF_to_cnf(CNF.to_CNF(A & B)) == to_cnf(A & B)
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
def test_to_dnf():
|
| 583 |
+
assert to_dnf(~(B | C)) == And(Not(B), Not(C))
|
| 584 |
+
assert to_dnf(A & (B | C)) == Or(And(A, B), And(A, C))
|
| 585 |
+
assert to_dnf(A >> B) == (~A) | B
|
| 586 |
+
assert to_dnf(A >> (B & C)) == (~A) | (B & C)
|
| 587 |
+
assert to_dnf(A | B) == A | B
|
| 588 |
+
|
| 589 |
+
assert to_dnf(Equivalent(A, B), True) == \
|
| 590 |
+
Or(And(A, B), And(Not(A), Not(B)))
|
| 591 |
+
assert to_dnf(Equivalent(A, B & C), True) == \
|
| 592 |
+
Or(And(A, B, C), And(Not(A), Not(B)), And(Not(A), Not(C)))
|
| 593 |
+
assert to_dnf(A + 1) == A + 1
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
def test_to_int_repr():
|
| 597 |
+
x, y, z = map(Boolean, symbols('x,y,z'))
|
| 598 |
+
|
| 599 |
+
def sorted_recursive(arg):
|
| 600 |
+
try:
|
| 601 |
+
return sorted(sorted_recursive(x) for x in arg)
|
| 602 |
+
except TypeError: # arg is not a sequence
|
| 603 |
+
return arg
|
| 604 |
+
|
| 605 |
+
assert sorted_recursive(to_int_repr([x | y, z | x], [x, y, z])) == \
|
| 606 |
+
sorted_recursive([[1, 2], [1, 3]])
|
| 607 |
+
assert sorted_recursive(to_int_repr([x | y, z | ~x], [x, y, z])) == \
|
| 608 |
+
sorted_recursive([[1, 2], [3, -1]])
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
def test_is_anf():
|
| 612 |
+
x, y = symbols('x,y')
|
| 613 |
+
assert is_anf(true) is True
|
| 614 |
+
assert is_anf(false) is True
|
| 615 |
+
assert is_anf(x) is True
|
| 616 |
+
assert is_anf(And(x, y)) is True
|
| 617 |
+
assert is_anf(Xor(x, y, And(x, y))) is True
|
| 618 |
+
assert is_anf(Xor(x, y, Or(x, y))) is False
|
| 619 |
+
assert is_anf(Xor(Not(x), y)) is False
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
def test_is_nnf():
|
| 623 |
+
assert is_nnf(true) is True
|
| 624 |
+
assert is_nnf(A) is True
|
| 625 |
+
assert is_nnf(~A) is True
|
| 626 |
+
assert is_nnf(A & B) is True
|
| 627 |
+
assert is_nnf((A & B) | (~A & A) | (~B & B) | (~A & ~B), False) is True
|
| 628 |
+
assert is_nnf((A | B) & (~A | ~B)) is True
|
| 629 |
+
assert is_nnf(Not(Or(A, B))) is False
|
| 630 |
+
assert is_nnf(A ^ B) is False
|
| 631 |
+
assert is_nnf((A & B) | (~A & A) | (~B & B) | (~A & ~B), True) is False
|
| 632 |
+
|
| 633 |
+
|
| 634 |
+
def test_is_cnf():
|
| 635 |
+
assert is_cnf(x) is True
|
| 636 |
+
assert is_cnf(x | y | z) is True
|
| 637 |
+
assert is_cnf(x & y & z) is True
|
| 638 |
+
assert is_cnf((x | y) & z) is True
|
| 639 |
+
assert is_cnf((x & y) | z) is False
|
| 640 |
+
assert is_cnf(~(x & y) | z) is False
|
| 641 |
+
|
| 642 |
+
|
| 643 |
+
def test_is_dnf():
|
| 644 |
+
assert is_dnf(x) is True
|
| 645 |
+
assert is_dnf(x | y | z) is True
|
| 646 |
+
assert is_dnf(x & y & z) is True
|
| 647 |
+
assert is_dnf((x & y) | z) is True
|
| 648 |
+
assert is_dnf((x | y) & z) is False
|
| 649 |
+
assert is_dnf(~(x | y) & z) is False
|
| 650 |
+
|
| 651 |
+
|
| 652 |
+
def test_ITE():
|
| 653 |
+
A, B, C = symbols('A:C')
|
| 654 |
+
assert ITE(True, False, True) is false
|
| 655 |
+
assert ITE(True, True, False) is true
|
| 656 |
+
assert ITE(False, True, False) is false
|
| 657 |
+
assert ITE(False, False, True) is true
|
| 658 |
+
assert isinstance(ITE(A, B, C), ITE)
|
| 659 |
+
|
| 660 |
+
A = True
|
| 661 |
+
assert ITE(A, B, C) == B
|
| 662 |
+
A = False
|
| 663 |
+
assert ITE(A, B, C) == C
|
| 664 |
+
B = True
|
| 665 |
+
assert ITE(And(A, B), B, C) == C
|
| 666 |
+
assert ITE(Or(A, False), And(B, True), False) is false
|
| 667 |
+
assert ITE(x, A, B) == Not(x)
|
| 668 |
+
assert ITE(x, B, A) == x
|
| 669 |
+
assert ITE(1, x, y) == x
|
| 670 |
+
assert ITE(0, x, y) == y
|
| 671 |
+
raises(TypeError, lambda: ITE(2, x, y))
|
| 672 |
+
raises(TypeError, lambda: ITE(1, [], y))
|
| 673 |
+
raises(TypeError, lambda: ITE(1, (), y))
|
| 674 |
+
raises(TypeError, lambda: ITE(1, y, []))
|
| 675 |
+
assert ITE(1, 1, 1) is S.true
|
| 676 |
+
assert isinstance(ITE(1, 1, 1, evaluate=False), ITE)
|
| 677 |
+
|
| 678 |
+
assert ITE(Eq(x, True), y, x) == ITE(x, y, x)
|
| 679 |
+
assert ITE(Eq(x, False), y, x) == ITE(~x, y, x)
|
| 680 |
+
assert ITE(Ne(x, True), y, x) == ITE(~x, y, x)
|
| 681 |
+
assert ITE(Ne(x, False), y, x) == ITE(x, y, x)
|
| 682 |
+
assert ITE(Eq(S.true, x), y, x) == ITE(x, y, x)
|
| 683 |
+
assert ITE(Eq(S.false, x), y, x) == ITE(~x, y, x)
|
| 684 |
+
assert ITE(Ne(S.true, x), y, x) == ITE(~x, y, x)
|
| 685 |
+
assert ITE(Ne(S.false, x), y, x) == ITE(x, y, x)
|
| 686 |
+
# 0 and 1 in the context are not treated as True/False
|
| 687 |
+
# so the equality must always be False since dissimilar
|
| 688 |
+
# objects cannot be equal
|
| 689 |
+
assert ITE(Eq(x, 0), y, x) == x
|
| 690 |
+
assert ITE(Eq(x, 1), y, x) == x
|
| 691 |
+
assert ITE(Ne(x, 0), y, x) == y
|
| 692 |
+
assert ITE(Ne(x, 1), y, x) == y
|
| 693 |
+
assert ITE(Eq(x, 0), y, z).subs(x, 0) == y
|
| 694 |
+
assert ITE(Eq(x, 0), y, z).subs(x, 1) == z
|
| 695 |
+
raises(ValueError, lambda: ITE(x > 1, y, x, z))
|
| 696 |
+
|
| 697 |
+
|
| 698 |
+
def test_is_literal():
|
| 699 |
+
assert is_literal(True) is True
|
| 700 |
+
assert is_literal(False) is True
|
| 701 |
+
assert is_literal(A) is True
|
| 702 |
+
assert is_literal(~A) is True
|
| 703 |
+
assert is_literal(Or(A, B)) is False
|
| 704 |
+
assert is_literal(Q.zero(A)) is True
|
| 705 |
+
assert is_literal(Not(Q.zero(A))) is True
|
| 706 |
+
assert is_literal(Or(A, B)) is False
|
| 707 |
+
assert is_literal(And(Q.zero(A), Q.zero(B))) is False
|
| 708 |
+
assert is_literal(x < 3)
|
| 709 |
+
assert not is_literal(x + y < 3)
|
| 710 |
+
|
| 711 |
+
|
| 712 |
+
def test_operators():
|
| 713 |
+
# Mostly test __and__, __rand__, and so on
|
| 714 |
+
assert True & A == A & True == A
|
| 715 |
+
assert False & A == A & False == False
|
| 716 |
+
assert A & B == And(A, B)
|
| 717 |
+
assert True | A == A | True == True
|
| 718 |
+
assert False | A == A | False == A
|
| 719 |
+
assert A | B == Or(A, B)
|
| 720 |
+
assert ~A == Not(A)
|
| 721 |
+
assert True >> A == A << True == A
|
| 722 |
+
assert False >> A == A << False == True
|
| 723 |
+
assert A >> True == True << A == True
|
| 724 |
+
assert A >> False == False << A == ~A
|
| 725 |
+
assert A >> B == B << A == Implies(A, B)
|
| 726 |
+
assert True ^ A == A ^ True == ~A
|
| 727 |
+
assert False ^ A == A ^ False == A
|
| 728 |
+
assert A ^ B == Xor(A, B)
|
| 729 |
+
|
| 730 |
+
|
| 731 |
+
def test_true_false():
|
| 732 |
+
assert true is S.true
|
| 733 |
+
assert false is S.false
|
| 734 |
+
assert true is not True
|
| 735 |
+
assert false is not False
|
| 736 |
+
assert true
|
| 737 |
+
assert not false
|
| 738 |
+
assert true == True
|
| 739 |
+
assert false == False
|
| 740 |
+
assert not (true == False)
|
| 741 |
+
assert not (false == True)
|
| 742 |
+
assert not (true == false)
|
| 743 |
+
|
| 744 |
+
assert hash(true) == hash(True)
|
| 745 |
+
assert hash(false) == hash(False)
|
| 746 |
+
assert len({true, True}) == len({false, False}) == 1
|
| 747 |
+
|
| 748 |
+
assert isinstance(true, BooleanAtom)
|
| 749 |
+
assert isinstance(false, BooleanAtom)
|
| 750 |
+
# We don't want to subclass from bool, because bool subclasses from
|
| 751 |
+
# int. But operators like &, |, ^, <<, >>, and ~ act differently on 0 and
|
| 752 |
+
# 1 then we want them to on true and false. See the docstrings of the
|
| 753 |
+
# various And, Or, etc. functions for examples.
|
| 754 |
+
assert not isinstance(true, bool)
|
| 755 |
+
assert not isinstance(false, bool)
|
| 756 |
+
|
| 757 |
+
# Note: using 'is' comparison is important here. We want these to return
|
| 758 |
+
# true and false, not True and False
|
| 759 |
+
|
| 760 |
+
assert Not(true) is false
|
| 761 |
+
assert Not(True) is false
|
| 762 |
+
assert Not(false) is true
|
| 763 |
+
assert Not(False) is true
|
| 764 |
+
assert ~true is false
|
| 765 |
+
assert ~false is true
|
| 766 |
+
|
| 767 |
+
for T, F in product((True, true), (False, false)):
|
| 768 |
+
assert And(T, F) is false
|
| 769 |
+
assert And(F, T) is false
|
| 770 |
+
assert And(F, F) is false
|
| 771 |
+
assert And(T, T) is true
|
| 772 |
+
assert And(T, x) == x
|
| 773 |
+
assert And(F, x) is false
|
| 774 |
+
if not (T is True and F is False):
|
| 775 |
+
assert T & F is false
|
| 776 |
+
assert F & T is false
|
| 777 |
+
if F is not False:
|
| 778 |
+
assert F & F is false
|
| 779 |
+
if T is not True:
|
| 780 |
+
assert T & T is true
|
| 781 |
+
|
| 782 |
+
assert Or(T, F) is true
|
| 783 |
+
assert Or(F, T) is true
|
| 784 |
+
assert Or(F, F) is false
|
| 785 |
+
assert Or(T, T) is true
|
| 786 |
+
assert Or(T, x) is true
|
| 787 |
+
assert Or(F, x) == x
|
| 788 |
+
if not (T is True and F is False):
|
| 789 |
+
assert T | F is true
|
| 790 |
+
assert F | T is true
|
| 791 |
+
if F is not False:
|
| 792 |
+
assert F | F is false
|
| 793 |
+
if T is not True:
|
| 794 |
+
assert T | T is true
|
| 795 |
+
|
| 796 |
+
assert Xor(T, F) is true
|
| 797 |
+
assert Xor(F, T) is true
|
| 798 |
+
assert Xor(F, F) is false
|
| 799 |
+
assert Xor(T, T) is false
|
| 800 |
+
assert Xor(T, x) == ~x
|
| 801 |
+
assert Xor(F, x) == x
|
| 802 |
+
if not (T is True and F is False):
|
| 803 |
+
assert T ^ F is true
|
| 804 |
+
assert F ^ T is true
|
| 805 |
+
if F is not False:
|
| 806 |
+
assert F ^ F is false
|
| 807 |
+
if T is not True:
|
| 808 |
+
assert T ^ T is false
|
| 809 |
+
|
| 810 |
+
assert Nand(T, F) is true
|
| 811 |
+
assert Nand(F, T) is true
|
| 812 |
+
assert Nand(F, F) is true
|
| 813 |
+
assert Nand(T, T) is false
|
| 814 |
+
assert Nand(T, x) == ~x
|
| 815 |
+
assert Nand(F, x) is true
|
| 816 |
+
|
| 817 |
+
assert Nor(T, F) is false
|
| 818 |
+
assert Nor(F, T) is false
|
| 819 |
+
assert Nor(F, F) is true
|
| 820 |
+
assert Nor(T, T) is false
|
| 821 |
+
assert Nor(T, x) is false
|
| 822 |
+
assert Nor(F, x) == ~x
|
| 823 |
+
|
| 824 |
+
assert Implies(T, F) is false
|
| 825 |
+
assert Implies(F, T) is true
|
| 826 |
+
assert Implies(F, F) is true
|
| 827 |
+
assert Implies(T, T) is true
|
| 828 |
+
assert Implies(T, x) == x
|
| 829 |
+
assert Implies(F, x) is true
|
| 830 |
+
assert Implies(x, T) is true
|
| 831 |
+
assert Implies(x, F) == ~x
|
| 832 |
+
if not (T is True and F is False):
|
| 833 |
+
assert T >> F is false
|
| 834 |
+
assert F << T is false
|
| 835 |
+
assert F >> T is true
|
| 836 |
+
assert T << F is true
|
| 837 |
+
if F is not False:
|
| 838 |
+
assert F >> F is true
|
| 839 |
+
assert F << F is true
|
| 840 |
+
if T is not True:
|
| 841 |
+
assert T >> T is true
|
| 842 |
+
assert T << T is true
|
| 843 |
+
|
| 844 |
+
assert Equivalent(T, F) is false
|
| 845 |
+
assert Equivalent(F, T) is false
|
| 846 |
+
assert Equivalent(F, F) is true
|
| 847 |
+
assert Equivalent(T, T) is true
|
| 848 |
+
assert Equivalent(T, x) == x
|
| 849 |
+
assert Equivalent(F, x) == ~x
|
| 850 |
+
assert Equivalent(x, T) == x
|
| 851 |
+
assert Equivalent(x, F) == ~x
|
| 852 |
+
|
| 853 |
+
assert ITE(T, T, T) is true
|
| 854 |
+
assert ITE(T, T, F) is true
|
| 855 |
+
assert ITE(T, F, T) is false
|
| 856 |
+
assert ITE(T, F, F) is false
|
| 857 |
+
assert ITE(F, T, T) is true
|
| 858 |
+
assert ITE(F, T, F) is false
|
| 859 |
+
assert ITE(F, F, T) is true
|
| 860 |
+
assert ITE(F, F, F) is false
|
| 861 |
+
|
| 862 |
+
assert all(i.simplify(1, 2) is i for i in (S.true, S.false))
|
| 863 |
+
|
| 864 |
+
|
| 865 |
+
def test_bool_as_set():
|
| 866 |
+
assert ITE(y <= 0, False, y >= 1).as_set() == Interval(1, oo)
|
| 867 |
+
assert And(x <= 2, x >= -2).as_set() == Interval(-2, 2)
|
| 868 |
+
assert Or(x >= 2, x <= -2).as_set() == Interval(-oo, -2) + Interval(2, oo)
|
| 869 |
+
assert Not(x > 2).as_set() == Interval(-oo, 2)
|
| 870 |
+
# issue 10240
|
| 871 |
+
assert Not(And(x > 2, x < 3)).as_set() == \
|
| 872 |
+
Union(Interval(-oo, 2), Interval(3, oo))
|
| 873 |
+
assert true.as_set() == S.UniversalSet
|
| 874 |
+
assert false.as_set() is S.EmptySet
|
| 875 |
+
assert x.as_set() == S.UniversalSet
|
| 876 |
+
assert And(Or(x < 1, x > 3), x < 2).as_set() == Interval.open(-oo, 1)
|
| 877 |
+
assert And(x < 1, sin(x) < 3).as_set() == (x < 1).as_set()
|
| 878 |
+
raises(NotImplementedError, lambda: (sin(x) < 1).as_set())
|
| 879 |
+
# watch for object morph in as_set
|
| 880 |
+
assert Eq(-1, cos(2 * x) ** 2 / sin(2 * x) ** 2).as_set() is S.EmptySet
|
| 881 |
+
|
| 882 |
+
|
| 883 |
+
@XFAIL
|
| 884 |
+
def test_multivariate_bool_as_set():
|
| 885 |
+
x, y = symbols('x,y')
|
| 886 |
+
|
| 887 |
+
assert And(x >= 0, y >= 0).as_set() == Interval(0, oo) * Interval(0, oo)
|
| 888 |
+
assert Or(x >= 0, y >= 0).as_set() == S.Reals * S.Reals - \
|
| 889 |
+
Interval(-oo, 0, True, True) * Interval(-oo, 0, True, True)
|
| 890 |
+
|
| 891 |
+
|
| 892 |
+
def test_all_or_nothing():
|
| 893 |
+
x = symbols('x', extended_real=True)
|
| 894 |
+
args = x >= -oo, x <= oo
|
| 895 |
+
v = And(*args)
|
| 896 |
+
if v.func is And:
|
| 897 |
+
assert len(v.args) == len(args) - args.count(S.true)
|
| 898 |
+
else:
|
| 899 |
+
assert v == True
|
| 900 |
+
v = Or(*args)
|
| 901 |
+
if v.func is Or:
|
| 902 |
+
assert len(v.args) == 2
|
| 903 |
+
else:
|
| 904 |
+
assert v == True
|
| 905 |
+
|
| 906 |
+
|
| 907 |
+
def test_canonical_atoms():
|
| 908 |
+
assert true.canonical == true
|
| 909 |
+
assert false.canonical == false
|
| 910 |
+
|
| 911 |
+
|
| 912 |
+
def test_negated_atoms():
|
| 913 |
+
assert true.negated == false
|
| 914 |
+
assert false.negated == true
|
| 915 |
+
|
| 916 |
+
|
| 917 |
+
def test_issue_8777():
|
| 918 |
+
assert And(x > 2, x < oo).as_set() == Interval(2, oo, left_open=True)
|
| 919 |
+
assert And(x >= 1, x < oo).as_set() == Interval(1, oo)
|
| 920 |
+
assert (x < oo).as_set() == Interval(-oo, oo)
|
| 921 |
+
assert (x > -oo).as_set() == Interval(-oo, oo)
|
| 922 |
+
|
| 923 |
+
|
| 924 |
+
def test_issue_8975():
|
| 925 |
+
assert Or(And(-oo < x, x <= -2), And(2 <= x, x < oo)).as_set() == \
|
| 926 |
+
Interval(-oo, -2) + Interval(2, oo)
|
| 927 |
+
|
| 928 |
+
|
| 929 |
+
def test_term_to_integer():
|
| 930 |
+
assert term_to_integer([1, 0, 1, 0, 0, 1, 0]) == 82
|
| 931 |
+
assert term_to_integer('0010101000111001') == 10809
|
| 932 |
+
|
| 933 |
+
|
| 934 |
+
def test_issue_21971():
|
| 935 |
+
a, b, c, d = symbols('a b c d')
|
| 936 |
+
f = a & b & c | a & c
|
| 937 |
+
assert f.subs(a & c, d) == b & d | d
|
| 938 |
+
assert f.subs(a & b & c, d) == a & c | d
|
| 939 |
+
|
| 940 |
+
f = (a | b | c) & (a | c)
|
| 941 |
+
assert f.subs(a | c, d) == (b | d) & d
|
| 942 |
+
assert f.subs(a | b | c, d) == (a | c) & d
|
| 943 |
+
|
| 944 |
+
f = (a ^ b ^ c) & (a ^ c)
|
| 945 |
+
assert f.subs(a ^ c, d) == (b ^ d) & d
|
| 946 |
+
assert f.subs(a ^ b ^ c, d) == (a ^ c) & d
|
| 947 |
+
|
| 948 |
+
|
| 949 |
+
def test_truth_table():
|
| 950 |
+
assert list(truth_table(And(x, y), [x, y], input=False)) == \
|
| 951 |
+
[False, False, False, True]
|
| 952 |
+
assert list(truth_table(x | y, [x, y], input=False)) == \
|
| 953 |
+
[False, True, True, True]
|
| 954 |
+
assert list(truth_table(x >> y, [x, y], input=False)) == \
|
| 955 |
+
[True, True, False, True]
|
| 956 |
+
assert list(truth_table(And(x, y), [x, y])) == \
|
| 957 |
+
[([0, 0], False), ([0, 1], False), ([1, 0], False), ([1, 1], True)]
|
| 958 |
+
|
| 959 |
+
|
| 960 |
+
def test_issue_8571():
|
| 961 |
+
for t in (S.true, S.false):
|
| 962 |
+
raises(TypeError, lambda: +t)
|
| 963 |
+
raises(TypeError, lambda: -t)
|
| 964 |
+
raises(TypeError, lambda: abs(t))
|
| 965 |
+
# use int(bool(t)) to get 0 or 1
|
| 966 |
+
raises(TypeError, lambda: int(t))
|
| 967 |
+
|
| 968 |
+
for o in [S.Zero, S.One, x]:
|
| 969 |
+
for _ in range(2):
|
| 970 |
+
raises(TypeError, lambda: o + t)
|
| 971 |
+
raises(TypeError, lambda: o - t)
|
| 972 |
+
raises(TypeError, lambda: o % t)
|
| 973 |
+
raises(TypeError, lambda: o * t)
|
| 974 |
+
raises(TypeError, lambda: o / t)
|
| 975 |
+
raises(TypeError, lambda: o ** t)
|
| 976 |
+
o, t = t, o # do again in reversed order
|
| 977 |
+
|
| 978 |
+
|
| 979 |
+
def test_expand_relational():
|
| 980 |
+
n = symbols('n', negative=True)
|
| 981 |
+
p, q = symbols('p q', positive=True)
|
| 982 |
+
r = ((n + q * (-n / q + 1)) / (q * (-n / q + 1)) < 0)
|
| 983 |
+
assert r is not S.false
|
| 984 |
+
assert r.expand() is S.false
|
| 985 |
+
assert (q > 0).expand() is S.true
|
| 986 |
+
|
| 987 |
+
|
| 988 |
+
def test_issue_12717():
|
| 989 |
+
assert S.true.is_Atom == True
|
| 990 |
+
assert S.false.is_Atom == True
|
| 991 |
+
|
| 992 |
+
|
| 993 |
+
def test_as_Boolean():
|
| 994 |
+
nz = symbols('nz', nonzero=True)
|
| 995 |
+
assert all(as_Boolean(i) is S.true for i in (True, S.true, 1, nz))
|
| 996 |
+
z = symbols('z', zero=True)
|
| 997 |
+
assert all(as_Boolean(i) is S.false for i in (False, S.false, 0, z))
|
| 998 |
+
assert all(as_Boolean(i) == i for i in (x, x < 0))
|
| 999 |
+
for i in (2, S(2), x + 1, []):
|
| 1000 |
+
raises(TypeError, lambda: as_Boolean(i))
|
| 1001 |
+
|
| 1002 |
+
|
| 1003 |
+
def test_binary_symbols():
|
| 1004 |
+
assert ITE(x < 1, y, z).binary_symbols == {y, z}
|
| 1005 |
+
for f in (Eq, Ne):
|
| 1006 |
+
assert f(x, 1).binary_symbols == set()
|
| 1007 |
+
assert f(x, True).binary_symbols == {x}
|
| 1008 |
+
assert f(x, False).binary_symbols == {x}
|
| 1009 |
+
assert S.true.binary_symbols == set()
|
| 1010 |
+
assert S.false.binary_symbols == set()
|
| 1011 |
+
assert x.binary_symbols == {x}
|
| 1012 |
+
assert And(x, Eq(y, False), Eq(z, 1)).binary_symbols == {x, y}
|
| 1013 |
+
assert Q.prime(x).binary_symbols == set()
|
| 1014 |
+
assert Q.lt(x, 1).binary_symbols == set()
|
| 1015 |
+
assert Q.is_true(x).binary_symbols == {x}
|
| 1016 |
+
assert Q.eq(x, True).binary_symbols == {x}
|
| 1017 |
+
assert Q.prime(x).binary_symbols == set()
|
| 1018 |
+
|
| 1019 |
+
|
| 1020 |
+
def test_BooleanFunction_diff():
|
| 1021 |
+
assert And(x, y).diff(x) == Piecewise((0, Eq(y, False)), (1, True))
|
| 1022 |
+
|
| 1023 |
+
|
| 1024 |
+
def test_issue_14700():
|
| 1025 |
+
A, B, C, D, E, F, G, H = symbols('A B C D E F G H')
|
| 1026 |
+
q = ((B & D & H & ~F) | (B & H & ~C & ~D) | (B & H & ~C & ~F) |
|
| 1027 |
+
(B & H & ~D & ~G) | (B & H & ~F & ~G) | (C & G & ~B & ~D) |
|
| 1028 |
+
(C & G & ~D & ~H) | (C & G & ~F & ~H) | (D & F & H & ~B) |
|
| 1029 |
+
(D & F & ~G & ~H) | (B & D & F & ~C & ~H) | (D & E & F & ~B & ~C) |
|
| 1030 |
+
(D & F & ~A & ~B & ~C) | (D & F & ~A & ~C & ~H) |
|
| 1031 |
+
(A & B & D & F & ~E & ~H))
|
| 1032 |
+
soldnf = ((B & D & H & ~F) | (D & F & H & ~B) | (B & H & ~C & ~D) |
|
| 1033 |
+
(B & H & ~D & ~G) | (C & G & ~B & ~D) | (C & G & ~D & ~H) |
|
| 1034 |
+
(C & G & ~F & ~H) | (D & F & ~G & ~H) | (D & E & F & ~C & ~H) |
|
| 1035 |
+
(D & F & ~A & ~C & ~H) | (A & B & D & F & ~E & ~H))
|
| 1036 |
+
solcnf = ((B | C | D) & (B | D | G) & (C | D | H) & (C | F | H) &
|
| 1037 |
+
(D | G | H) & (F | G | H) & (B | F | ~D | ~H) &
|
| 1038 |
+
(~B | ~D | ~F | ~H) & (D | ~B | ~C | ~G | ~H) &
|
| 1039 |
+
(A | H | ~C | ~D | ~F | ~G) & (H | ~C | ~D | ~E | ~F | ~G) &
|
| 1040 |
+
(B | E | H | ~A | ~D | ~F | ~G))
|
| 1041 |
+
assert simplify_logic(q, "dnf") == soldnf
|
| 1042 |
+
assert simplify_logic(q, "cnf") == solcnf
|
| 1043 |
+
|
| 1044 |
+
minterms = [[0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 1, 0], [0, 1, 1, 1],
|
| 1045 |
+
[0, 0, 1, 1], [1, 0, 1, 1]]
|
| 1046 |
+
dontcares = [[1, 0, 0, 0], [1, 0, 0, 1], [1, 1, 0, 0], [1, 1, 0, 1]]
|
| 1047 |
+
assert SOPform([w, x, y, z], minterms) == (x & ~w) | (y & z & ~x)
|
| 1048 |
+
# Should not be more complicated with don't cares
|
| 1049 |
+
assert SOPform([w, x, y, z], minterms, dontcares) == \
|
| 1050 |
+
(x & ~w) | (y & z & ~x)
|
| 1051 |
+
|
| 1052 |
+
|
| 1053 |
+
def test_issue_25115():
|
| 1054 |
+
cond = Contains(x, S.Integers)
|
| 1055 |
+
# Previously this raised an exception:
|
| 1056 |
+
assert simplify_logic(cond) == cond
|
| 1057 |
+
|
| 1058 |
+
|
| 1059 |
+
def test_relational_simplification():
|
| 1060 |
+
w, x, y, z = symbols('w x y z', real=True)
|
| 1061 |
+
d, e = symbols('d e', real=False)
|
| 1062 |
+
# Test all combinations or sign and order
|
| 1063 |
+
assert Or(x >= y, x < y).simplify() == S.true
|
| 1064 |
+
assert Or(x >= y, y > x).simplify() == S.true
|
| 1065 |
+
assert Or(x >= y, -x > -y).simplify() == S.true
|
| 1066 |
+
assert Or(x >= y, -y < -x).simplify() == S.true
|
| 1067 |
+
assert Or(-x <= -y, x < y).simplify() == S.true
|
| 1068 |
+
assert Or(-x <= -y, -x > -y).simplify() == S.true
|
| 1069 |
+
assert Or(-x <= -y, y > x).simplify() == S.true
|
| 1070 |
+
assert Or(-x <= -y, -y < -x).simplify() == S.true
|
| 1071 |
+
assert Or(y <= x, x < y).simplify() == S.true
|
| 1072 |
+
assert Or(y <= x, y > x).simplify() == S.true
|
| 1073 |
+
assert Or(y <= x, -x > -y).simplify() == S.true
|
| 1074 |
+
assert Or(y <= x, -y < -x).simplify() == S.true
|
| 1075 |
+
assert Or(-y >= -x, x < y).simplify() == S.true
|
| 1076 |
+
assert Or(-y >= -x, y > x).simplify() == S.true
|
| 1077 |
+
assert Or(-y >= -x, -x > -y).simplify() == S.true
|
| 1078 |
+
assert Or(-y >= -x, -y < -x).simplify() == S.true
|
| 1079 |
+
|
| 1080 |
+
assert Or(x < y, x >= y).simplify() == S.true
|
| 1081 |
+
assert Or(y > x, x >= y).simplify() == S.true
|
| 1082 |
+
assert Or(-x > -y, x >= y).simplify() == S.true
|
| 1083 |
+
assert Or(-y < -x, x >= y).simplify() == S.true
|
| 1084 |
+
assert Or(x < y, -x <= -y).simplify() == S.true
|
| 1085 |
+
assert Or(-x > -y, -x <= -y).simplify() == S.true
|
| 1086 |
+
assert Or(y > x, -x <= -y).simplify() == S.true
|
| 1087 |
+
assert Or(-y < -x, -x <= -y).simplify() == S.true
|
| 1088 |
+
assert Or(x < y, y <= x).simplify() == S.true
|
| 1089 |
+
assert Or(y > x, y <= x).simplify() == S.true
|
| 1090 |
+
assert Or(-x > -y, y <= x).simplify() == S.true
|
| 1091 |
+
assert Or(-y < -x, y <= x).simplify() == S.true
|
| 1092 |
+
assert Or(x < y, -y >= -x).simplify() == S.true
|
| 1093 |
+
assert Or(y > x, -y >= -x).simplify() == S.true
|
| 1094 |
+
assert Or(-x > -y, -y >= -x).simplify() == S.true
|
| 1095 |
+
assert Or(-y < -x, -y >= -x).simplify() == S.true
|
| 1096 |
+
|
| 1097 |
+
# Some other tests
|
| 1098 |
+
assert Or(x >= y, w < z, x <= y).simplify() == S.true
|
| 1099 |
+
assert And(x >= y, x < y).simplify() == S.false
|
| 1100 |
+
assert Or(x >= y, Eq(y, x)).simplify() == (x >= y)
|
| 1101 |
+
assert And(x >= y, Eq(y, x)).simplify() == Eq(x, y)
|
| 1102 |
+
assert And(Eq(x, y), x >= 1, 2 < y, y >= 5, z < y).simplify() == \
|
| 1103 |
+
(Eq(x, y) & (x >= 1) & (y >= 5) & (y > z))
|
| 1104 |
+
assert Or(Eq(x, y), x >= y, w < y, z < y).simplify() == \
|
| 1105 |
+
(x >= y) | (y > z) | (w < y)
|
| 1106 |
+
assert And(Eq(x, y), x >= y, w < y, y >= z, z < y).simplify() == \
|
| 1107 |
+
Eq(x, y) & (y > z) & (w < y)
|
| 1108 |
+
# assert And(Eq(x, y), x >= y, w < y, y >= z, z < y).simplify(relational_minmax=True) == \
|
| 1109 |
+
# And(Eq(x, y), y > Max(w, z))
|
| 1110 |
+
# assert Or(Eq(x, y), x >= 1, 2 < y, y >= 5, z < y).simplify(relational_minmax=True) == \
|
| 1111 |
+
# (Eq(x, y) | (x >= 1) | (y > Min(2, z)))
|
| 1112 |
+
assert And(Eq(x, y), x >= 1, 2 < y, y >= 5, z < y).simplify() == \
|
| 1113 |
+
(Eq(x, y) & (x >= 1) & (y >= 5) & (y > z))
|
| 1114 |
+
assert (Eq(x, y) & Eq(d, e) & (x >= y) & (d >= e)).simplify() == \
|
| 1115 |
+
(Eq(x, y) & Eq(d, e) & (d >= e))
|
| 1116 |
+
assert And(Eq(x, y), Eq(x, -y)).simplify() == And(Eq(x, 0), Eq(y, 0))
|
| 1117 |
+
assert Xor(x >= y, x <= y).simplify() == Ne(x, y)
|
| 1118 |
+
assert And(x > 1, x < -1, Eq(x, y)).simplify() == S.false
|
| 1119 |
+
# From #16690
|
| 1120 |
+
assert And(x >= y, Eq(y, 0)).simplify() == And(x >= 0, Eq(y, 0))
|
| 1121 |
+
assert Or(Ne(x, 1), Ne(x, 2)).simplify() == S.true
|
| 1122 |
+
assert And(Eq(x, 1), Ne(2, x)).simplify() == Eq(x, 1)
|
| 1123 |
+
assert Or(Eq(x, 1), Ne(2, x)).simplify() == Ne(x, 2)
|
| 1124 |
+
|
| 1125 |
+
|
| 1126 |
+
def test_issue_8373():
|
| 1127 |
+
x = symbols('x', real=True)
|
| 1128 |
+
assert Or(x < 1, x > -1).simplify() == S.true
|
| 1129 |
+
assert Or(x < 1, x >= 1).simplify() == S.true
|
| 1130 |
+
assert And(x < 1, x >= 1).simplify() == S.false
|
| 1131 |
+
assert Or(x <= 1, x >= 1).simplify() == S.true
|
| 1132 |
+
|
| 1133 |
+
|
| 1134 |
+
def test_issue_7950():
|
| 1135 |
+
x = symbols('x', real=True)
|
| 1136 |
+
assert And(Eq(x, 1), Eq(x, 2)).simplify() == S.false
|
| 1137 |
+
|
| 1138 |
+
|
| 1139 |
+
@slow
|
| 1140 |
+
def test_relational_simplification_numerically():
|
| 1141 |
+
def test_simplification_numerically_function(original, simplified):
|
| 1142 |
+
symb = original.free_symbols
|
| 1143 |
+
n = len(symb)
|
| 1144 |
+
valuelist = list(set(combinations(list(range(-(n - 1), n)) * n, n)))
|
| 1145 |
+
for values in valuelist:
|
| 1146 |
+
sublist = dict(zip(symb, values))
|
| 1147 |
+
originalvalue = original.subs(sublist)
|
| 1148 |
+
simplifiedvalue = simplified.subs(sublist)
|
| 1149 |
+
assert originalvalue == simplifiedvalue, "Original: {}\nand" \
|
| 1150 |
+
" simplified: {}\ndo not evaluate to the same value for {}" \
|
| 1151 |
+
"".format(original, simplified, sublist)
|
| 1152 |
+
|
| 1153 |
+
w, x, y, z = symbols('w x y z', real=True)
|
| 1154 |
+
d, e = symbols('d e', real=False)
|
| 1155 |
+
|
| 1156 |
+
expressions = (And(Eq(x, y), x >= y, w < y, y >= z, z < y),
|
| 1157 |
+
And(Eq(x, y), x >= 1, 2 < y, y >= 5, z < y),
|
| 1158 |
+
Or(Eq(x, y), x >= 1, 2 < y, y >= 5, z < y),
|
| 1159 |
+
And(x >= y, Eq(y, x)),
|
| 1160 |
+
Or(And(Eq(x, y), x >= y, w < y, Or(y >= z, z < y)),
|
| 1161 |
+
And(Eq(x, y), x >= 1, 2 < y, y >= -1, z < y)),
|
| 1162 |
+
(Eq(x, y) & Eq(d, e) & (x >= y) & (d >= e)),
|
| 1163 |
+
)
|
| 1164 |
+
|
| 1165 |
+
for expression in expressions:
|
| 1166 |
+
test_simplification_numerically_function(expression,
|
| 1167 |
+
expression.simplify())
|
| 1168 |
+
|
| 1169 |
+
|
| 1170 |
+
def test_relational_simplification_patterns_numerically():
|
| 1171 |
+
from sympy.core import Wild
|
| 1172 |
+
from sympy.logic.boolalg import _simplify_patterns_and, \
|
| 1173 |
+
_simplify_patterns_or, _simplify_patterns_xor
|
| 1174 |
+
a = Wild('a')
|
| 1175 |
+
b = Wild('b')
|
| 1176 |
+
c = Wild('c')
|
| 1177 |
+
symb = [a, b, c]
|
| 1178 |
+
patternlists = [[And, _simplify_patterns_and()],
|
| 1179 |
+
[Or, _simplify_patterns_or()],
|
| 1180 |
+
[Xor, _simplify_patterns_xor()]]
|
| 1181 |
+
valuelist = list(set(combinations(list(range(-2, 3)) * 3, 3)))
|
| 1182 |
+
# Skip combinations of +/-2 and 0, except for all 0
|
| 1183 |
+
valuelist = [v for v in valuelist if any(w % 2 for w in v) or not any(v)]
|
| 1184 |
+
for func, patternlist in patternlists:
|
| 1185 |
+
for pattern in patternlist:
|
| 1186 |
+
original = func(*pattern[0].args)
|
| 1187 |
+
simplified = pattern[1]
|
| 1188 |
+
for values in valuelist:
|
| 1189 |
+
sublist = dict(zip(symb, values))
|
| 1190 |
+
originalvalue = original.xreplace(sublist)
|
| 1191 |
+
simplifiedvalue = simplified.xreplace(sublist)
|
| 1192 |
+
assert originalvalue == simplifiedvalue, "Original: {}\nand" \
|
| 1193 |
+
" simplified: {}\ndo not evaluate to the same value for" \
|
| 1194 |
+
"{}".format(pattern[0], simplified, sublist)
|
| 1195 |
+
|
| 1196 |
+
|
| 1197 |
+
def test_issue_16803():
|
| 1198 |
+
n = symbols('n')
|
| 1199 |
+
# No simplification done, but should not raise an exception
|
| 1200 |
+
assert ((n > 3) | (n < 0) | ((n > 0) & (n < 3))).simplify() == \
|
| 1201 |
+
(n > 3) | (n < 0) | ((n > 0) & (n < 3))
|
| 1202 |
+
|
| 1203 |
+
|
| 1204 |
+
def test_issue_17530():
|
| 1205 |
+
r = {x: oo, y: oo}
|
| 1206 |
+
assert Or(x + y > 0, x - y < 0).subs(r)
|
| 1207 |
+
assert not And(x + y < 0, x - y < 0).subs(r)
|
| 1208 |
+
raises(TypeError, lambda: Or(x + y < 0, x - y < 0).subs(r))
|
| 1209 |
+
raises(TypeError, lambda: And(x + y > 0, x - y < 0).subs(r))
|
| 1210 |
+
raises(TypeError, lambda: And(x + y > 0, x - y < 0).subs(r))
|
| 1211 |
+
|
| 1212 |
+
|
| 1213 |
+
def test_anf_coeffs():
|
| 1214 |
+
assert anf_coeffs([1, 0]) == [1, 1]
|
| 1215 |
+
assert anf_coeffs([0, 0, 0, 1]) == [0, 0, 0, 1]
|
| 1216 |
+
assert anf_coeffs([0, 1, 1, 1]) == [0, 1, 1, 1]
|
| 1217 |
+
assert anf_coeffs([1, 1, 1, 0]) == [1, 0, 0, 1]
|
| 1218 |
+
assert anf_coeffs([1, 0, 0, 0]) == [1, 1, 1, 1]
|
| 1219 |
+
assert anf_coeffs([1, 0, 0, 1]) == [1, 1, 1, 0]
|
| 1220 |
+
assert anf_coeffs([1, 1, 0, 1]) == [1, 0, 1, 1]
|
| 1221 |
+
|
| 1222 |
+
|
| 1223 |
+
def test_ANFform():
|
| 1224 |
+
x, y = symbols('x,y')
|
| 1225 |
+
assert ANFform([x], [1, 1]) == True
|
| 1226 |
+
assert ANFform([x], [0, 0]) == False
|
| 1227 |
+
assert ANFform([x], [1, 0]) == Xor(x, True, remove_true=False)
|
| 1228 |
+
assert ANFform([x, y], [1, 1, 1, 0]) == \
|
| 1229 |
+
Xor(True, And(x, y), remove_true=False)
|
| 1230 |
+
|
| 1231 |
+
|
| 1232 |
+
def test_bool_minterm():
|
| 1233 |
+
x, y = symbols('x,y')
|
| 1234 |
+
assert bool_minterm(3, [x, y]) == And(x, y)
|
| 1235 |
+
assert bool_minterm([1, 0], [x, y]) == And(Not(y), x)
|
| 1236 |
+
|
| 1237 |
+
|
| 1238 |
+
def test_bool_maxterm():
|
| 1239 |
+
x, y = symbols('x,y')
|
| 1240 |
+
assert bool_maxterm(2, [x, y]) == Or(Not(x), y)
|
| 1241 |
+
assert bool_maxterm([0, 1], [x, y]) == Or(Not(y), x)
|
| 1242 |
+
|
| 1243 |
+
|
| 1244 |
+
def test_bool_monomial():
|
| 1245 |
+
x, y = symbols('x,y')
|
| 1246 |
+
assert bool_monomial(1, [x, y]) == y
|
| 1247 |
+
assert bool_monomial([1, 1], [x, y]) == And(x, y)
|
| 1248 |
+
|
| 1249 |
+
|
| 1250 |
+
def test_check_pair():
|
| 1251 |
+
assert _check_pair([0, 1, 0], [0, 1, 1]) == 2
|
| 1252 |
+
assert _check_pair([0, 1, 0], [1, 1, 1]) == -1
|
| 1253 |
+
|
| 1254 |
+
|
| 1255 |
+
def test_issue_19114():
|
| 1256 |
+
expr = (B & C) | (A & ~C) | (~A & ~B)
|
| 1257 |
+
# Expression is minimal, but there are multiple minimal forms possible
|
| 1258 |
+
res1 = (A & B) | (C & ~A) | (~B & ~C)
|
| 1259 |
+
result = to_dnf(expr, simplify=True)
|
| 1260 |
+
assert result in (expr, res1)
|
| 1261 |
+
|
| 1262 |
+
|
| 1263 |
+
def test_issue_20870():
|
| 1264 |
+
result = SOPform([a, b, c, d], [1, 2, 3, 4, 5, 6, 8, 9, 11, 12, 14, 15])
|
| 1265 |
+
expected = ((d & ~b) | (a & b & c) | (a & ~c & ~d) |
|
| 1266 |
+
(b & ~a & ~c) | (c & ~a & ~d))
|
| 1267 |
+
assert result == expected
|
| 1268 |
+
|
| 1269 |
+
|
| 1270 |
+
def test_convert_to_varsSOP():
|
| 1271 |
+
assert _convert_to_varsSOP([0, 1, 0], [x, y, z]) == And(Not(x), y, Not(z))
|
| 1272 |
+
assert _convert_to_varsSOP([3, 1, 0], [x, y, z]) == And(y, Not(z))
|
| 1273 |
+
|
| 1274 |
+
|
| 1275 |
+
def test_convert_to_varsPOS():
|
| 1276 |
+
assert _convert_to_varsPOS([0, 1, 0], [x, y, z]) == Or(x, Not(y), z)
|
| 1277 |
+
assert _convert_to_varsPOS([3, 1, 0], [x, y, z]) == Or(Not(y), z)
|
| 1278 |
+
|
| 1279 |
+
|
| 1280 |
+
def test_gateinputcount():
|
| 1281 |
+
a, b, c, d, e = symbols('a:e')
|
| 1282 |
+
assert gateinputcount(And(a, b)) == 2
|
| 1283 |
+
assert gateinputcount(a | b & c & d ^ (e | a)) == 9
|
| 1284 |
+
assert gateinputcount(And(a, True)) == 0
|
| 1285 |
+
raises(TypeError, lambda: gateinputcount(a * b))
|
| 1286 |
+
|
| 1287 |
+
|
| 1288 |
+
def test_refine():
|
| 1289 |
+
# relational
|
| 1290 |
+
assert not refine(x < 0, ~(x < 0))
|
| 1291 |
+
assert refine(x < 0, (x < 0))
|
| 1292 |
+
assert refine(x < 0, (0 > x)) is S.true
|
| 1293 |
+
assert refine(x < 0, (y < 0)) == (x < 0)
|
| 1294 |
+
assert not refine(x <= 0, ~(x <= 0))
|
| 1295 |
+
assert refine(x <= 0, (x <= 0))
|
| 1296 |
+
assert refine(x <= 0, (0 >= x)) is S.true
|
| 1297 |
+
assert refine(x <= 0, (y <= 0)) == (x <= 0)
|
| 1298 |
+
assert not refine(x > 0, ~(x > 0))
|
| 1299 |
+
assert refine(x > 0, (x > 0))
|
| 1300 |
+
assert refine(x > 0, (0 < x)) is S.true
|
| 1301 |
+
assert refine(x > 0, (y > 0)) == (x > 0)
|
| 1302 |
+
assert not refine(x >= 0, ~(x >= 0))
|
| 1303 |
+
assert refine(x >= 0, (x >= 0))
|
| 1304 |
+
assert refine(x >= 0, (0 <= x)) is S.true
|
| 1305 |
+
assert refine(x >= 0, (y >= 0)) == (x >= 0)
|
| 1306 |
+
assert not refine(Eq(x, 0), ~(Eq(x, 0)))
|
| 1307 |
+
assert refine(Eq(x, 0), (Eq(x, 0)))
|
| 1308 |
+
assert refine(Eq(x, 0), (Eq(0, x))) is S.true
|
| 1309 |
+
assert refine(Eq(x, 0), (Eq(y, 0))) == Eq(x, 0)
|
| 1310 |
+
assert not refine(Ne(x, 0), ~(Ne(x, 0)))
|
| 1311 |
+
assert refine(Ne(x, 0), (Ne(0, x))) is S.true
|
| 1312 |
+
assert refine(Ne(x, 0), (Ne(x, 0)))
|
| 1313 |
+
assert refine(Ne(x, 0), (Ne(y, 0))) == (Ne(x, 0))
|
| 1314 |
+
|
| 1315 |
+
# boolean functions
|
| 1316 |
+
assert refine(And(x > 0, y > 0), (x > 0)) == (y > 0)
|
| 1317 |
+
assert refine(And(x > 0, y > 0), (x > 0) & (y > 0)) is S.true
|
| 1318 |
+
|
| 1319 |
+
# predicates
|
| 1320 |
+
assert refine(Q.positive(x), Q.positive(x)) is S.true
|
| 1321 |
+
assert refine(Q.positive(x), Q.negative(x)) is S.false
|
| 1322 |
+
assert refine(Q.positive(x), Q.real(x)) == Q.positive(x)
|
| 1323 |
+
|
| 1324 |
+
|
| 1325 |
+
def test_relational_threeterm_simplification_patterns_numerically():
|
| 1326 |
+
from sympy.core import Wild
|
| 1327 |
+
from sympy.logic.boolalg import _simplify_patterns_and3
|
| 1328 |
+
a = Wild('a')
|
| 1329 |
+
b = Wild('b')
|
| 1330 |
+
c = Wild('c')
|
| 1331 |
+
symb = [a, b, c]
|
| 1332 |
+
patternlists = [[And, _simplify_patterns_and3()]]
|
| 1333 |
+
valuelist = list(set(combinations(list(range(-2, 3)) * 3, 3)))
|
| 1334 |
+
# Skip combinations of +/-2 and 0, except for all 0
|
| 1335 |
+
valuelist = [v for v in valuelist if any(w % 2 for w in v) or not any(v)]
|
| 1336 |
+
for func, patternlist in patternlists:
|
| 1337 |
+
for pattern in patternlist:
|
| 1338 |
+
original = func(*pattern[0].args)
|
| 1339 |
+
simplified = pattern[1]
|
| 1340 |
+
for values in valuelist:
|
| 1341 |
+
sublist = dict(zip(symb, values))
|
| 1342 |
+
originalvalue = original.xreplace(sublist)
|
| 1343 |
+
simplifiedvalue = simplified.xreplace(sublist)
|
| 1344 |
+
assert originalvalue == simplifiedvalue, "Original: {}\nand" \
|
| 1345 |
+
" simplified: {}\ndo not evaluate to the same value for" \
|
| 1346 |
+
"{}".format(pattern[0], simplified, sublist)
|
| 1347 |
+
|
| 1348 |
+
|
| 1349 |
+
def test_issue_25451():
|
| 1350 |
+
x = Or(And(a, c), Eq(a, b))
|
| 1351 |
+
assert isinstance(x, Or)
|
| 1352 |
+
assert set(x.args) == {And(a, c), Eq(a, b)}
|
| 1353 |
+
|
| 1354 |
+
|
| 1355 |
+
def test_issue_26985():
|
| 1356 |
+
a, b, c, d = symbols('a b c d')
|
| 1357 |
+
|
| 1358 |
+
# Expression before applying to_anf
|
| 1359 |
+
x = Xor(c, And(a, b), And(a, c))
|
| 1360 |
+
y = Xor(a, b, And(a, c))
|
| 1361 |
+
|
| 1362 |
+
# Applying to_anf
|
| 1363 |
+
result = Xor(Xor(d, And(x, y)), And(x, y))
|
| 1364 |
+
result_anf = to_anf(Xor(to_anf(Xor(d, And(x, y))), And(x, y)))
|
| 1365 |
+
|
| 1366 |
+
assert result_anf == d
|
| 1367 |
+
assert result == d
|
phivenv/Lib/site-packages/sympy/logic/tests/test_dimacs.py
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Various tests on satisfiability using dimacs cnf file syntax
|
| 2 |
+
You can find lots of cnf files in
|
| 3 |
+
ftp://dimacs.rutgers.edu/pub/challenge/satisfiability/benchmarks/cnf/
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from sympy.logic.utilities.dimacs import load
|
| 7 |
+
from sympy.logic.algorithms.dpll import dpll_satisfiable
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def test_f1():
|
| 11 |
+
assert bool(dpll_satisfiable(load(f1)))
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_f2():
|
| 15 |
+
assert bool(dpll_satisfiable(load(f2)))
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def test_f3():
|
| 19 |
+
assert bool(dpll_satisfiable(load(f3)))
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test_f4():
|
| 23 |
+
assert not bool(dpll_satisfiable(load(f4)))
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def test_f5():
|
| 27 |
+
assert bool(dpll_satisfiable(load(f5)))
|
| 28 |
+
|
| 29 |
+
f1 = """c simple example
|
| 30 |
+
c Resolution: SATISFIABLE
|
| 31 |
+
c
|
| 32 |
+
p cnf 3 2
|
| 33 |
+
1 -3 0
|
| 34 |
+
2 3 -1 0
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
f2 = """c an example from Quinn's text, 16 variables and 18 clauses.
|
| 39 |
+
c Resolution: SATISFIABLE
|
| 40 |
+
c
|
| 41 |
+
p cnf 16 18
|
| 42 |
+
1 2 0
|
| 43 |
+
-2 -4 0
|
| 44 |
+
3 4 0
|
| 45 |
+
-4 -5 0
|
| 46 |
+
5 -6 0
|
| 47 |
+
6 -7 0
|
| 48 |
+
6 7 0
|
| 49 |
+
7 -16 0
|
| 50 |
+
8 -9 0
|
| 51 |
+
-8 -14 0
|
| 52 |
+
9 10 0
|
| 53 |
+
9 -10 0
|
| 54 |
+
-10 -11 0
|
| 55 |
+
10 12 0
|
| 56 |
+
11 12 0
|
| 57 |
+
13 14 0
|
| 58 |
+
14 -15 0
|
| 59 |
+
15 16 0
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
f3 = """c
|
| 63 |
+
p cnf 6 9
|
| 64 |
+
-1 0
|
| 65 |
+
-3 0
|
| 66 |
+
2 -1 0
|
| 67 |
+
2 -4 0
|
| 68 |
+
5 -4 0
|
| 69 |
+
-1 -3 0
|
| 70 |
+
-4 -6 0
|
| 71 |
+
1 3 -2 0
|
| 72 |
+
4 6 -2 -5 0
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
f4 = """c
|
| 76 |
+
c file: hole6.cnf [http://people.sc.fsu.edu/~jburkardt/data/cnf/hole6.cnf]
|
| 77 |
+
c
|
| 78 |
+
c SOURCE: John Hooker (jh38+@andrew.cmu.edu)
|
| 79 |
+
c
|
| 80 |
+
c DESCRIPTION: Pigeon hole problem of placing n (for file 'holen.cnf') pigeons
|
| 81 |
+
c in n+1 holes without placing 2 pigeons in the same hole
|
| 82 |
+
c
|
| 83 |
+
c NOTE: Part of the collection at the Forschungsinstitut fuer
|
| 84 |
+
c anwendungsorientierte Wissensverarbeitung in Ulm Germany.
|
| 85 |
+
c
|
| 86 |
+
c NOTE: Not satisfiable
|
| 87 |
+
c
|
| 88 |
+
p cnf 42 133
|
| 89 |
+
-1 -7 0
|
| 90 |
+
-1 -13 0
|
| 91 |
+
-1 -19 0
|
| 92 |
+
-1 -25 0
|
| 93 |
+
-1 -31 0
|
| 94 |
+
-1 -37 0
|
| 95 |
+
-7 -13 0
|
| 96 |
+
-7 -19 0
|
| 97 |
+
-7 -25 0
|
| 98 |
+
-7 -31 0
|
| 99 |
+
-7 -37 0
|
| 100 |
+
-13 -19 0
|
| 101 |
+
-13 -25 0
|
| 102 |
+
-13 -31 0
|
| 103 |
+
-13 -37 0
|
| 104 |
+
-19 -25 0
|
| 105 |
+
-19 -31 0
|
| 106 |
+
-19 -37 0
|
| 107 |
+
-25 -31 0
|
| 108 |
+
-25 -37 0
|
| 109 |
+
-31 -37 0
|
| 110 |
+
-2 -8 0
|
| 111 |
+
-2 -14 0
|
| 112 |
+
-2 -20 0
|
| 113 |
+
-2 -26 0
|
| 114 |
+
-2 -32 0
|
| 115 |
+
-2 -38 0
|
| 116 |
+
-8 -14 0
|
| 117 |
+
-8 -20 0
|
| 118 |
+
-8 -26 0
|
| 119 |
+
-8 -32 0
|
| 120 |
+
-8 -38 0
|
| 121 |
+
-14 -20 0
|
| 122 |
+
-14 -26 0
|
| 123 |
+
-14 -32 0
|
| 124 |
+
-14 -38 0
|
| 125 |
+
-20 -26 0
|
| 126 |
+
-20 -32 0
|
| 127 |
+
-20 -38 0
|
| 128 |
+
-26 -32 0
|
| 129 |
+
-26 -38 0
|
| 130 |
+
-32 -38 0
|
| 131 |
+
-3 -9 0
|
| 132 |
+
-3 -15 0
|
| 133 |
+
-3 -21 0
|
| 134 |
+
-3 -27 0
|
| 135 |
+
-3 -33 0
|
| 136 |
+
-3 -39 0
|
| 137 |
+
-9 -15 0
|
| 138 |
+
-9 -21 0
|
| 139 |
+
-9 -27 0
|
| 140 |
+
-9 -33 0
|
| 141 |
+
-9 -39 0
|
| 142 |
+
-15 -21 0
|
| 143 |
+
-15 -27 0
|
| 144 |
+
-15 -33 0
|
| 145 |
+
-15 -39 0
|
| 146 |
+
-21 -27 0
|
| 147 |
+
-21 -33 0
|
| 148 |
+
-21 -39 0
|
| 149 |
+
-27 -33 0
|
| 150 |
+
-27 -39 0
|
| 151 |
+
-33 -39 0
|
| 152 |
+
-4 -10 0
|
| 153 |
+
-4 -16 0
|
| 154 |
+
-4 -22 0
|
| 155 |
+
-4 -28 0
|
| 156 |
+
-4 -34 0
|
| 157 |
+
-4 -40 0
|
| 158 |
+
-10 -16 0
|
| 159 |
+
-10 -22 0
|
| 160 |
+
-10 -28 0
|
| 161 |
+
-10 -34 0
|
| 162 |
+
-10 -40 0
|
| 163 |
+
-16 -22 0
|
| 164 |
+
-16 -28 0
|
| 165 |
+
-16 -34 0
|
| 166 |
+
-16 -40 0
|
| 167 |
+
-22 -28 0
|
| 168 |
+
-22 -34 0
|
| 169 |
+
-22 -40 0
|
| 170 |
+
-28 -34 0
|
| 171 |
+
-28 -40 0
|
| 172 |
+
-34 -40 0
|
| 173 |
+
-5 -11 0
|
| 174 |
+
-5 -17 0
|
| 175 |
+
-5 -23 0
|
| 176 |
+
-5 -29 0
|
| 177 |
+
-5 -35 0
|
| 178 |
+
-5 -41 0
|
| 179 |
+
-11 -17 0
|
| 180 |
+
-11 -23 0
|
| 181 |
+
-11 -29 0
|
| 182 |
+
-11 -35 0
|
| 183 |
+
-11 -41 0
|
| 184 |
+
-17 -23 0
|
| 185 |
+
-17 -29 0
|
| 186 |
+
-17 -35 0
|
| 187 |
+
-17 -41 0
|
| 188 |
+
-23 -29 0
|
| 189 |
+
-23 -35 0
|
| 190 |
+
-23 -41 0
|
| 191 |
+
-29 -35 0
|
| 192 |
+
-29 -41 0
|
| 193 |
+
-35 -41 0
|
| 194 |
+
-6 -12 0
|
| 195 |
+
-6 -18 0
|
| 196 |
+
-6 -24 0
|
| 197 |
+
-6 -30 0
|
| 198 |
+
-6 -36 0
|
| 199 |
+
-6 -42 0
|
| 200 |
+
-12 -18 0
|
| 201 |
+
-12 -24 0
|
| 202 |
+
-12 -30 0
|
| 203 |
+
-12 -36 0
|
| 204 |
+
-12 -42 0
|
| 205 |
+
-18 -24 0
|
| 206 |
+
-18 -30 0
|
| 207 |
+
-18 -36 0
|
| 208 |
+
-18 -42 0
|
| 209 |
+
-24 -30 0
|
| 210 |
+
-24 -36 0
|
| 211 |
+
-24 -42 0
|
| 212 |
+
-30 -36 0
|
| 213 |
+
-30 -42 0
|
| 214 |
+
-36 -42 0
|
| 215 |
+
6 5 4 3 2 1 0
|
| 216 |
+
12 11 10 9 8 7 0
|
| 217 |
+
18 17 16 15 14 13 0
|
| 218 |
+
24 23 22 21 20 19 0
|
| 219 |
+
30 29 28 27 26 25 0
|
| 220 |
+
36 35 34 33 32 31 0
|
| 221 |
+
42 41 40 39 38 37 0
|
| 222 |
+
"""
|
| 223 |
+
|
| 224 |
+
f5 = """c simple example requiring variable selection
|
| 225 |
+
c
|
| 226 |
+
c NOTE: Satisfiable
|
| 227 |
+
c
|
| 228 |
+
p cnf 5 5
|
| 229 |
+
1 2 3 0
|
| 230 |
+
1 -2 3 0
|
| 231 |
+
4 5 -3 0
|
| 232 |
+
1 -4 -3 0
|
| 233 |
+
-1 -5 0
|
| 234 |
+
"""
|
phivenv/Lib/site-packages/sympy/logic/tests/test_inference.py
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""For more tests on satisfiability, see test_dimacs"""
|
| 2 |
+
|
| 3 |
+
from sympy.assumptions.ask import Q
|
| 4 |
+
from sympy.core.symbol import symbols
|
| 5 |
+
from sympy.core.relational import Unequality
|
| 6 |
+
from sympy.logic.boolalg import And, Or, Implies, Equivalent, true, false
|
| 7 |
+
from sympy.logic.inference import literal_symbol, \
|
| 8 |
+
pl_true, satisfiable, valid, entails, PropKB
|
| 9 |
+
from sympy.logic.algorithms.dpll import dpll, dpll_satisfiable, \
|
| 10 |
+
find_pure_symbol, find_unit_clause, unit_propagate, \
|
| 11 |
+
find_pure_symbol_int_repr, find_unit_clause_int_repr, \
|
| 12 |
+
unit_propagate_int_repr
|
| 13 |
+
from sympy.logic.algorithms.dpll2 import dpll_satisfiable as dpll2_satisfiable
|
| 14 |
+
|
| 15 |
+
from sympy.logic.algorithms.z3_wrapper import z3_satisfiable
|
| 16 |
+
from sympy.assumptions.cnf import CNF, EncodedCNF
|
| 17 |
+
from sympy.logic.tests.test_lra_theory import make_random_problem
|
| 18 |
+
from sympy.core.random import randint
|
| 19 |
+
|
| 20 |
+
from sympy.testing.pytest import raises, skip
|
| 21 |
+
from sympy.external import import_module
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def test_literal():
|
| 25 |
+
A, B = symbols('A,B')
|
| 26 |
+
assert literal_symbol(True) is True
|
| 27 |
+
assert literal_symbol(False) is False
|
| 28 |
+
assert literal_symbol(A) is A
|
| 29 |
+
assert literal_symbol(~A) is A
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def test_find_pure_symbol():
|
| 33 |
+
A, B, C = symbols('A,B,C')
|
| 34 |
+
assert find_pure_symbol([A], [A]) == (A, True)
|
| 35 |
+
assert find_pure_symbol([A, B], [~A | B, ~B | A]) == (None, None)
|
| 36 |
+
assert find_pure_symbol([A, B, C], [ A | ~B, ~B | ~C, C | A]) == (A, True)
|
| 37 |
+
assert find_pure_symbol([A, B, C], [~A | B, B | ~C, C | A]) == (B, True)
|
| 38 |
+
assert find_pure_symbol([A, B, C], [~A | ~B, ~B | ~C, C | A]) == (B, False)
|
| 39 |
+
assert find_pure_symbol(
|
| 40 |
+
[A, B, C], [~A | B, ~B | ~C, C | A]) == (None, None)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_find_pure_symbol_int_repr():
|
| 44 |
+
assert find_pure_symbol_int_repr([1], [{1}]) == (1, True)
|
| 45 |
+
assert find_pure_symbol_int_repr([1, 2],
|
| 46 |
+
[{-1, 2}, {-2, 1}]) == (None, None)
|
| 47 |
+
assert find_pure_symbol_int_repr([1, 2, 3],
|
| 48 |
+
[{1, -2}, {-2, -3}, {3, 1}]) == (1, True)
|
| 49 |
+
assert find_pure_symbol_int_repr([1, 2, 3],
|
| 50 |
+
[{-1, 2}, {2, -3}, {3, 1}]) == (2, True)
|
| 51 |
+
assert find_pure_symbol_int_repr([1, 2, 3],
|
| 52 |
+
[{-1, -2}, {-2, -3}, {3, 1}]) == (2, False)
|
| 53 |
+
assert find_pure_symbol_int_repr([1, 2, 3],
|
| 54 |
+
[{-1, 2}, {-2, -3}, {3, 1}]) == (None, None)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_unit_clause():
|
| 58 |
+
A, B, C = symbols('A,B,C')
|
| 59 |
+
assert find_unit_clause([A], {}) == (A, True)
|
| 60 |
+
assert find_unit_clause([A, ~A], {}) == (A, True) # Wrong ??
|
| 61 |
+
assert find_unit_clause([A | B], {A: True}) == (B, True)
|
| 62 |
+
assert find_unit_clause([A | B], {B: True}) == (A, True)
|
| 63 |
+
assert find_unit_clause(
|
| 64 |
+
[A | B | C, B | ~C, A | ~B], {A: True}) == (B, False)
|
| 65 |
+
assert find_unit_clause([A | B | C, B | ~C, A | B], {A: True}) == (B, True)
|
| 66 |
+
assert find_unit_clause([A | B | C, B | ~C, A ], {}) == (A, True)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def test_unit_clause_int_repr():
|
| 70 |
+
assert find_unit_clause_int_repr(map(set, [[1]]), {}) == (1, True)
|
| 71 |
+
assert find_unit_clause_int_repr(map(set, [[1], [-1]]), {}) == (1, True)
|
| 72 |
+
assert find_unit_clause_int_repr([{1, 2}], {1: True}) == (2, True)
|
| 73 |
+
assert find_unit_clause_int_repr([{1, 2}], {2: True}) == (1, True)
|
| 74 |
+
assert find_unit_clause_int_repr(map(set,
|
| 75 |
+
[[1, 2, 3], [2, -3], [1, -2]]), {1: True}) == (2, False)
|
| 76 |
+
assert find_unit_clause_int_repr(map(set,
|
| 77 |
+
[[1, 2, 3], [3, -3], [1, 2]]), {1: True}) == (2, True)
|
| 78 |
+
|
| 79 |
+
A, B, C = symbols('A,B,C')
|
| 80 |
+
assert find_unit_clause([A | B | C, B | ~C, A ], {}) == (A, True)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def test_unit_propagate():
|
| 84 |
+
A, B, C = symbols('A,B,C')
|
| 85 |
+
assert unit_propagate([A | B], A) == []
|
| 86 |
+
assert unit_propagate([A | B, ~A | C, ~C | B, A], A) == [C, ~C | B, A]
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def test_unit_propagate_int_repr():
|
| 90 |
+
assert unit_propagate_int_repr([{1, 2}], 1) == []
|
| 91 |
+
assert unit_propagate_int_repr(map(set,
|
| 92 |
+
[[1, 2], [-1, 3], [-3, 2], [1]]), 1) == [{3}, {-3, 2}]
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def test_dpll():
|
| 96 |
+
"""This is also tested in test_dimacs"""
|
| 97 |
+
A, B, C = symbols('A,B,C')
|
| 98 |
+
assert dpll([A | B], [A, B], {A: True, B: True}) == {A: True, B: True}
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def test_dpll_satisfiable():
|
| 102 |
+
A, B, C = symbols('A,B,C')
|
| 103 |
+
assert dpll_satisfiable( A & ~A ) is False
|
| 104 |
+
assert dpll_satisfiable( A & ~B ) == {A: True, B: False}
|
| 105 |
+
assert dpll_satisfiable(
|
| 106 |
+
A | B ) in ({A: True}, {B: True}, {A: True, B: True})
|
| 107 |
+
assert dpll_satisfiable(
|
| 108 |
+
(~A | B) & (~B | A) ) in ({A: True, B: True}, {A: False, B: False})
|
| 109 |
+
assert dpll_satisfiable( (A | B) & (~B | C) ) in ({A: True, B: False},
|
| 110 |
+
{A: True, C: True}, {B: True, C: True})
|
| 111 |
+
assert dpll_satisfiable( A & B & C ) == {A: True, B: True, C: True}
|
| 112 |
+
assert dpll_satisfiable( (A | B) & (A >> B) ) == {B: True}
|
| 113 |
+
assert dpll_satisfiable( Equivalent(A, B) & A ) == {A: True, B: True}
|
| 114 |
+
assert dpll_satisfiable( Equivalent(A, B) & ~A ) == {A: False, B: False}
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def test_dpll2_satisfiable():
|
| 118 |
+
A, B, C = symbols('A,B,C')
|
| 119 |
+
assert dpll2_satisfiable( A & ~A ) is False
|
| 120 |
+
assert dpll2_satisfiable( A & ~B ) == {A: True, B: False}
|
| 121 |
+
assert dpll2_satisfiable(
|
| 122 |
+
A | B ) in ({A: True}, {B: True}, {A: True, B: True})
|
| 123 |
+
assert dpll2_satisfiable(
|
| 124 |
+
(~A | B) & (~B | A) ) in ({A: True, B: True}, {A: False, B: False})
|
| 125 |
+
assert dpll2_satisfiable( (A | B) & (~B | C) ) in ({A: True, B: False, C: True},
|
| 126 |
+
{A: True, B: True, C: True})
|
| 127 |
+
assert dpll2_satisfiable( A & B & C ) == {A: True, B: True, C: True}
|
| 128 |
+
assert dpll2_satisfiable( (A | B) & (A >> B) ) in ({B: True, A: False},
|
| 129 |
+
{B: True, A: True})
|
| 130 |
+
assert dpll2_satisfiable( Equivalent(A, B) & A ) == {A: True, B: True}
|
| 131 |
+
assert dpll2_satisfiable( Equivalent(A, B) & ~A ) == {A: False, B: False}
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def test_minisat22_satisfiable():
|
| 135 |
+
A, B, C = symbols('A,B,C')
|
| 136 |
+
minisat22_satisfiable = lambda expr: satisfiable(expr, algorithm="minisat22")
|
| 137 |
+
assert minisat22_satisfiable( A & ~A ) is False
|
| 138 |
+
assert minisat22_satisfiable( A & ~B ) == {A: True, B: False}
|
| 139 |
+
assert minisat22_satisfiable(
|
| 140 |
+
A | B ) in ({A: True}, {B: False}, {A: False, B: True}, {A: True, B: True}, {A: True, B: False})
|
| 141 |
+
assert minisat22_satisfiable(
|
| 142 |
+
(~A | B) & (~B | A) ) in ({A: True, B: True}, {A: False, B: False})
|
| 143 |
+
assert minisat22_satisfiable( (A | B) & (~B | C) ) in ({A: True, B: False, C: True},
|
| 144 |
+
{A: True, B: True, C: True}, {A: False, B: True, C: True}, {A: True, B: False, C: False})
|
| 145 |
+
assert minisat22_satisfiable( A & B & C ) == {A: True, B: True, C: True}
|
| 146 |
+
assert minisat22_satisfiable( (A | B) & (A >> B) ) in ({B: True, A: False},
|
| 147 |
+
{B: True, A: True})
|
| 148 |
+
assert minisat22_satisfiable( Equivalent(A, B) & A ) == {A: True, B: True}
|
| 149 |
+
assert minisat22_satisfiable( Equivalent(A, B) & ~A ) == {A: False, B: False}
|
| 150 |
+
|
| 151 |
+
def test_minisat22_minimal_satisfiable():
|
| 152 |
+
A, B, C = symbols('A,B,C')
|
| 153 |
+
minisat22_satisfiable = lambda expr, minimal=True: satisfiable(expr, algorithm="minisat22", minimal=True)
|
| 154 |
+
assert minisat22_satisfiable( A & ~A ) is False
|
| 155 |
+
assert minisat22_satisfiable( A & ~B ) == {A: True, B: False}
|
| 156 |
+
assert minisat22_satisfiable(
|
| 157 |
+
A | B ) in ({A: True}, {B: False}, {A: False, B: True}, {A: True, B: True}, {A: True, B: False})
|
| 158 |
+
assert minisat22_satisfiable(
|
| 159 |
+
(~A | B) & (~B | A) ) in ({A: True, B: True}, {A: False, B: False})
|
| 160 |
+
assert minisat22_satisfiable( (A | B) & (~B | C) ) in ({A: True, B: False, C: True},
|
| 161 |
+
{A: True, B: True, C: True}, {A: False, B: True, C: True}, {A: True, B: False, C: False})
|
| 162 |
+
assert minisat22_satisfiable( A & B & C ) == {A: True, B: True, C: True}
|
| 163 |
+
assert minisat22_satisfiable( (A | B) & (A >> B) ) in ({B: True, A: False},
|
| 164 |
+
{B: True, A: True})
|
| 165 |
+
assert minisat22_satisfiable( Equivalent(A, B) & A ) == {A: True, B: True}
|
| 166 |
+
assert minisat22_satisfiable( Equivalent(A, B) & ~A ) == {A: False, B: False}
|
| 167 |
+
g = satisfiable((A | B | C),algorithm="minisat22",minimal=True,all_models=True)
|
| 168 |
+
sol = next(g)
|
| 169 |
+
first_solution = {key for key, value in sol.items() if value}
|
| 170 |
+
sol=next(g)
|
| 171 |
+
second_solution = {key for key, value in sol.items() if value}
|
| 172 |
+
sol=next(g)
|
| 173 |
+
third_solution = {key for key, value in sol.items() if value}
|
| 174 |
+
assert not first_solution <= second_solution
|
| 175 |
+
assert not second_solution <= third_solution
|
| 176 |
+
assert not first_solution <= third_solution
|
| 177 |
+
|
| 178 |
+
def test_satisfiable():
|
| 179 |
+
A, B, C = symbols('A,B,C')
|
| 180 |
+
assert satisfiable(A & (A >> B) & ~B) is False
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def test_valid():
|
| 184 |
+
A, B, C = symbols('A,B,C')
|
| 185 |
+
assert valid(A >> (B >> A)) is True
|
| 186 |
+
assert valid((A >> (B >> C)) >> ((A >> B) >> (A >> C))) is True
|
| 187 |
+
assert valid((~B >> ~A) >> (A >> B)) is True
|
| 188 |
+
assert valid(A | B | C) is False
|
| 189 |
+
assert valid(A >> B) is False
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def test_pl_true():
|
| 193 |
+
A, B, C = symbols('A,B,C')
|
| 194 |
+
assert pl_true(True) is True
|
| 195 |
+
assert pl_true( A & B, {A: True, B: True}) is True
|
| 196 |
+
assert pl_true( A | B, {A: True}) is True
|
| 197 |
+
assert pl_true( A | B, {B: True}) is True
|
| 198 |
+
assert pl_true( A | B, {A: None, B: True}) is True
|
| 199 |
+
assert pl_true( A >> B, {A: False}) is True
|
| 200 |
+
assert pl_true( A | B | ~C, {A: False, B: True, C: True}) is True
|
| 201 |
+
assert pl_true(Equivalent(A, B), {A: False, B: False}) is True
|
| 202 |
+
|
| 203 |
+
# test for false
|
| 204 |
+
assert pl_true(False) is False
|
| 205 |
+
assert pl_true( A & B, {A: False, B: False}) is False
|
| 206 |
+
assert pl_true( A & B, {A: False}) is False
|
| 207 |
+
assert pl_true( A & B, {B: False}) is False
|
| 208 |
+
assert pl_true( A | B, {A: False, B: False}) is False
|
| 209 |
+
|
| 210 |
+
#test for None
|
| 211 |
+
assert pl_true(B, {B: None}) is None
|
| 212 |
+
assert pl_true( A & B, {A: True, B: None}) is None
|
| 213 |
+
assert pl_true( A >> B, {A: True, B: None}) is None
|
| 214 |
+
assert pl_true(Equivalent(A, B), {A: None}) is None
|
| 215 |
+
assert pl_true(Equivalent(A, B), {A: True, B: None}) is None
|
| 216 |
+
|
| 217 |
+
# Test for deep
|
| 218 |
+
assert pl_true(A | B, {A: False}, deep=True) is None
|
| 219 |
+
assert pl_true(~A & ~B, {A: False}, deep=True) is None
|
| 220 |
+
assert pl_true(A | B, {A: False, B: False}, deep=True) is False
|
| 221 |
+
assert pl_true(A & B & (~A | ~B), {A: True}, deep=True) is False
|
| 222 |
+
assert pl_true((C >> A) >> (B >> A), {C: True}, deep=True) is True
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def test_pl_true_wrong_input():
|
| 226 |
+
from sympy.core.numbers import pi
|
| 227 |
+
raises(ValueError, lambda: pl_true('John Cleese'))
|
| 228 |
+
raises(ValueError, lambda: pl_true(42 + pi + pi ** 2))
|
| 229 |
+
raises(ValueError, lambda: pl_true(42))
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def test_entails():
|
| 233 |
+
A, B, C = symbols('A, B, C')
|
| 234 |
+
assert entails(A, [A >> B, ~B]) is False
|
| 235 |
+
assert entails(B, [Equivalent(A, B), A]) is True
|
| 236 |
+
assert entails((A >> B) >> (~A >> ~B)) is False
|
| 237 |
+
assert entails((A >> B) >> (~B >> ~A)) is True
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def test_PropKB():
|
| 241 |
+
A, B, C = symbols('A,B,C')
|
| 242 |
+
kb = PropKB()
|
| 243 |
+
assert kb.ask(A >> B) is False
|
| 244 |
+
assert kb.ask(A >> (B >> A)) is True
|
| 245 |
+
kb.tell(A >> B)
|
| 246 |
+
kb.tell(B >> C)
|
| 247 |
+
assert kb.ask(A) is False
|
| 248 |
+
assert kb.ask(B) is False
|
| 249 |
+
assert kb.ask(C) is False
|
| 250 |
+
assert kb.ask(~A) is False
|
| 251 |
+
assert kb.ask(~B) is False
|
| 252 |
+
assert kb.ask(~C) is False
|
| 253 |
+
assert kb.ask(A >> C) is True
|
| 254 |
+
kb.tell(A)
|
| 255 |
+
assert kb.ask(A) is True
|
| 256 |
+
assert kb.ask(B) is True
|
| 257 |
+
assert kb.ask(C) is True
|
| 258 |
+
assert kb.ask(~C) is False
|
| 259 |
+
kb.retract(A)
|
| 260 |
+
assert kb.ask(C) is False
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def test_propKB_tolerant():
|
| 264 |
+
""""tolerant to bad input"""
|
| 265 |
+
kb = PropKB()
|
| 266 |
+
A, B, C = symbols('A,B,C')
|
| 267 |
+
assert kb.ask(B) is False
|
| 268 |
+
|
| 269 |
+
def test_satisfiable_non_symbols():
|
| 270 |
+
x, y = symbols('x y')
|
| 271 |
+
assumptions = Q.zero(x*y)
|
| 272 |
+
facts = Implies(Q.zero(x*y), Q.zero(x) | Q.zero(y))
|
| 273 |
+
query = ~Q.zero(x) & ~Q.zero(y)
|
| 274 |
+
refutations = [
|
| 275 |
+
{Q.zero(x): True, Q.zero(x*y): True},
|
| 276 |
+
{Q.zero(y): True, Q.zero(x*y): True},
|
| 277 |
+
{Q.zero(x): True, Q.zero(y): True, Q.zero(x*y): True},
|
| 278 |
+
{Q.zero(x): True, Q.zero(y): False, Q.zero(x*y): True},
|
| 279 |
+
{Q.zero(x): False, Q.zero(y): True, Q.zero(x*y): True}]
|
| 280 |
+
assert not satisfiable(And(assumptions, facts, query), algorithm='dpll')
|
| 281 |
+
assert satisfiable(And(assumptions, facts, ~query), algorithm='dpll') in refutations
|
| 282 |
+
assert not satisfiable(And(assumptions, facts, query), algorithm='dpll2')
|
| 283 |
+
assert satisfiable(And(assumptions, facts, ~query), algorithm='dpll2') in refutations
|
| 284 |
+
|
| 285 |
+
def test_satisfiable_bool():
|
| 286 |
+
from sympy.core.singleton import S
|
| 287 |
+
assert satisfiable(true) == {true: true}
|
| 288 |
+
assert satisfiable(S.true) == {true: true}
|
| 289 |
+
assert satisfiable(false) is False
|
| 290 |
+
assert satisfiable(S.false) is False
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def test_satisfiable_all_models():
|
| 294 |
+
from sympy.abc import A, B
|
| 295 |
+
assert next(satisfiable(False, all_models=True)) is False
|
| 296 |
+
assert list(satisfiable((A >> ~A) & A, all_models=True)) == [False]
|
| 297 |
+
assert list(satisfiable(True, all_models=True)) == [{true: true}]
|
| 298 |
+
|
| 299 |
+
models = [{A: True, B: False}, {A: False, B: True}]
|
| 300 |
+
result = satisfiable(A ^ B, all_models=True)
|
| 301 |
+
models.remove(next(result))
|
| 302 |
+
models.remove(next(result))
|
| 303 |
+
raises(StopIteration, lambda: next(result))
|
| 304 |
+
assert not models
|
| 305 |
+
|
| 306 |
+
assert list(satisfiable(Equivalent(A, B), all_models=True)) == \
|
| 307 |
+
[{A: False, B: False}, {A: True, B: True}]
|
| 308 |
+
|
| 309 |
+
models = [{A: False, B: False}, {A: False, B: True}, {A: True, B: True}]
|
| 310 |
+
for model in satisfiable(A >> B, all_models=True):
|
| 311 |
+
models.remove(model)
|
| 312 |
+
assert not models
|
| 313 |
+
|
| 314 |
+
# This is a santiy test to check that only the required number
|
| 315 |
+
# of solutions are generated. The expr below has 2**100 - 1 models
|
| 316 |
+
# which would time out the test if all are generated at once.
|
| 317 |
+
from sympy.utilities.iterables import numbered_symbols
|
| 318 |
+
from sympy.logic.boolalg import Or
|
| 319 |
+
sym = numbered_symbols()
|
| 320 |
+
X = [next(sym) for i in range(100)]
|
| 321 |
+
result = satisfiable(Or(*X), all_models=True)
|
| 322 |
+
for i in range(10):
|
| 323 |
+
assert next(result)
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def test_z3():
|
| 327 |
+
z3 = import_module("z3")
|
| 328 |
+
|
| 329 |
+
if not z3:
|
| 330 |
+
skip("z3 not installed.")
|
| 331 |
+
A, B, C = symbols('A,B,C')
|
| 332 |
+
x, y, z = symbols('x,y,z')
|
| 333 |
+
assert z3_satisfiable((x >= 2) & (x < 1)) is False
|
| 334 |
+
assert z3_satisfiable( A & ~A ) is False
|
| 335 |
+
|
| 336 |
+
model = z3_satisfiable(A & (~A | B | C))
|
| 337 |
+
assert bool(model) is True
|
| 338 |
+
assert model[A] is True
|
| 339 |
+
|
| 340 |
+
# test nonlinear function
|
| 341 |
+
assert z3_satisfiable((x ** 2 >= 2) & (x < 1) & (x > -1)) is False
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def test_z3_vs_lra_dpll2():
|
| 345 |
+
z3 = import_module("z3")
|
| 346 |
+
if z3 is None:
|
| 347 |
+
skip("z3 not installed.")
|
| 348 |
+
|
| 349 |
+
def boolean_formula_to_encoded_cnf(bf):
|
| 350 |
+
cnf = CNF.from_prop(bf)
|
| 351 |
+
enc = EncodedCNF()
|
| 352 |
+
enc.from_cnf(cnf)
|
| 353 |
+
return enc
|
| 354 |
+
|
| 355 |
+
def make_random_cnf(num_clauses=5, num_constraints=10, num_var=2):
|
| 356 |
+
assert num_clauses <= num_constraints
|
| 357 |
+
constraints = make_random_problem(num_variables=num_var, num_constraints=num_constraints, rational=False)
|
| 358 |
+
clauses = [[cons] for cons in constraints[:num_clauses]]
|
| 359 |
+
for cons in constraints[num_clauses:]:
|
| 360 |
+
if isinstance(cons, Unequality):
|
| 361 |
+
cons = ~cons
|
| 362 |
+
i = randint(0, num_clauses-1)
|
| 363 |
+
clauses[i].append(cons)
|
| 364 |
+
|
| 365 |
+
clauses = [Or(*clause) for clause in clauses]
|
| 366 |
+
cnf = And(*clauses)
|
| 367 |
+
return boolean_formula_to_encoded_cnf(cnf)
|
| 368 |
+
|
| 369 |
+
lra_dpll2_satisfiable = lambda x: dpll2_satisfiable(x, use_lra_theory=True)
|
| 370 |
+
|
| 371 |
+
for _ in range(50):
|
| 372 |
+
cnf = make_random_cnf(num_clauses=10, num_constraints=15, num_var=2)
|
| 373 |
+
|
| 374 |
+
try:
|
| 375 |
+
z3_sat = z3_satisfiable(cnf)
|
| 376 |
+
except z3.z3types.Z3Exception:
|
| 377 |
+
continue
|
| 378 |
+
|
| 379 |
+
lra_dpll2_sat = lra_dpll2_satisfiable(cnf) is not False
|
| 380 |
+
|
| 381 |
+
assert z3_sat == lra_dpll2_sat
|
| 382 |
+
|
| 383 |
+
def test_issue_27733():
|
| 384 |
+
x, y = symbols('x,y')
|
| 385 |
+
clauses = [[1, -3, -2], [5, 7, -8, -6, -4], [-10, -9, 10, 11, -4], [-12, 13, 14], [-10, 9, -6, 11, -4],
|
| 386 |
+
[16, -15, 18, -19, -17], [11, -6, 10, -9], [9, 11, -10, -9], [2, -3, -1], [-13, 12], [-15, 3, -17],
|
| 387 |
+
[-16, -15, 19, -17], [-6, -9, 10, 11, -4], [20, -1, -2], [-23, -22, -21], [10, 11, -10, -9],
|
| 388 |
+
[9, 11, -4, -10], [24, -6, -4], [-14, 12], [-10, -9, 9, -6, 11], [25, -27, -26], [-15, 19, -18, -17],
|
| 389 |
+
[5, 8, -7, -6, -4], [-30, -29, 28], [12], [14]]
|
| 390 |
+
|
| 391 |
+
encoding = {Q.gt(y, i): i for i in range(1, 31) if i != 11 and i != 12}
|
| 392 |
+
encoding[Q.gt(x, 0)] = 11
|
| 393 |
+
encoding[Q.lt(x, 0)] = 12
|
| 394 |
+
|
| 395 |
+
cnf = EncodedCNF(clauses, encoding)
|
| 396 |
+
assert satisfiable(cnf, use_lra_theory=True) is False
|
phivenv/Lib/site-packages/sympy/logic/tests/test_lra_theory.py
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.numbers import Rational, I, oo
|
| 2 |
+
from sympy.core.relational import Eq
|
| 3 |
+
from sympy.core.symbol import symbols
|
| 4 |
+
from sympy.core.singleton import S
|
| 5 |
+
from sympy.matrices.dense import Matrix
|
| 6 |
+
from sympy.matrices.dense import randMatrix
|
| 7 |
+
from sympy.assumptions.ask import Q
|
| 8 |
+
from sympy.logic.boolalg import And
|
| 9 |
+
from sympy.abc import x, y, z
|
| 10 |
+
from sympy.assumptions.cnf import CNF, EncodedCNF
|
| 11 |
+
from sympy.functions.elementary.trigonometric import cos
|
| 12 |
+
from sympy.external import import_module
|
| 13 |
+
|
| 14 |
+
from sympy.logic.algorithms.lra_theory import LRASolver, UnhandledInput, LRARational, HANDLE_NEGATION
|
| 15 |
+
from sympy.core.random import random, choice, randint
|
| 16 |
+
from sympy.core.sympify import sympify
|
| 17 |
+
from sympy.ntheory.generate import randprime
|
| 18 |
+
from sympy.core.relational import StrictLessThan, StrictGreaterThan
|
| 19 |
+
import itertools
|
| 20 |
+
|
| 21 |
+
from sympy.testing.pytest import raises, XFAIL, skip
|
| 22 |
+
|
| 23 |
+
def make_random_problem(num_variables=2, num_constraints=2, sparsity=.1, rational=True,
|
| 24 |
+
disable_strict = False, disable_nonstrict=False, disable_equality=False):
|
| 25 |
+
def rand(sparsity=sparsity):
|
| 26 |
+
if random() < sparsity:
|
| 27 |
+
return sympify(0)
|
| 28 |
+
if rational:
|
| 29 |
+
int1, int2 = [randprime(0, 50) for _ in range(2)]
|
| 30 |
+
return Rational(int1, int2) * choice([-1, 1])
|
| 31 |
+
else:
|
| 32 |
+
return randint(1, 10) * choice([-1, 1])
|
| 33 |
+
|
| 34 |
+
variables = symbols('x1:%s' % (num_variables + 1))
|
| 35 |
+
constraints = []
|
| 36 |
+
for _ in range(num_constraints):
|
| 37 |
+
lhs, rhs = sum(rand() * x for x in variables), rand(sparsity=0) # sparsity=0 bc of bug with smtlib_code
|
| 38 |
+
options = []
|
| 39 |
+
if not disable_equality:
|
| 40 |
+
options += [Eq(lhs, rhs)]
|
| 41 |
+
if not disable_nonstrict:
|
| 42 |
+
options += [lhs <= rhs, lhs >= rhs]
|
| 43 |
+
if not disable_strict:
|
| 44 |
+
options += [lhs < rhs, lhs > rhs]
|
| 45 |
+
|
| 46 |
+
constraints.append(choice(options))
|
| 47 |
+
|
| 48 |
+
return constraints
|
| 49 |
+
|
| 50 |
+
def check_if_satisfiable_with_z3(constraints):
|
| 51 |
+
from sympy.external.importtools import import_module
|
| 52 |
+
from sympy.printing.smtlib import smtlib_code
|
| 53 |
+
from sympy.logic.boolalg import And
|
| 54 |
+
boolean_formula = And(*constraints)
|
| 55 |
+
z3 = import_module("z3")
|
| 56 |
+
if z3:
|
| 57 |
+
smtlib_string = smtlib_code(boolean_formula)
|
| 58 |
+
s = z3.Solver()
|
| 59 |
+
s.from_string(smtlib_string)
|
| 60 |
+
res = str(s.check())
|
| 61 |
+
if res == 'sat':
|
| 62 |
+
return True
|
| 63 |
+
elif res == 'unsat':
|
| 64 |
+
return False
|
| 65 |
+
else:
|
| 66 |
+
raise ValueError(f"z3 was not able to check the satisfiability of {boolean_formula}")
|
| 67 |
+
|
| 68 |
+
def find_rational_assignment(constr, assignment, iter=20):
|
| 69 |
+
eps = sympify(1)
|
| 70 |
+
|
| 71 |
+
for _ in range(iter):
|
| 72 |
+
assign = {key: val[0] + val[1]*eps for key, val in assignment.items()}
|
| 73 |
+
try:
|
| 74 |
+
for cons in constr:
|
| 75 |
+
assert cons.subs(assign) == True
|
| 76 |
+
return assign
|
| 77 |
+
except AssertionError:
|
| 78 |
+
eps = eps/2
|
| 79 |
+
|
| 80 |
+
return None
|
| 81 |
+
|
| 82 |
+
def boolean_formula_to_encoded_cnf(bf):
|
| 83 |
+
cnf = CNF.from_prop(bf)
|
| 84 |
+
enc = EncodedCNF()
|
| 85 |
+
enc.from_cnf(cnf)
|
| 86 |
+
return enc
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def test_from_encoded_cnf():
|
| 90 |
+
s1, s2 = symbols("s1 s2")
|
| 91 |
+
|
| 92 |
+
# Test preprocessing
|
| 93 |
+
# Example is from section 3 of paper.
|
| 94 |
+
phi = (x >= 0) & ((x + y <= 2) | (x + 2 * y - z >= 6)) & (Eq(x + y, 2) | (x + 2 * y - z > 4))
|
| 95 |
+
enc = boolean_formula_to_encoded_cnf(phi)
|
| 96 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 97 |
+
assert lra.A.shape == (2, 5)
|
| 98 |
+
assert str(lra.slack) == '[_s1, _s2]'
|
| 99 |
+
assert str(lra.nonslack) == '[x, y, z]'
|
| 100 |
+
assert lra.A == Matrix([[ 1, 1, 0, -1, 0],
|
| 101 |
+
[-1, -2, 1, 0, -1]])
|
| 102 |
+
assert {(str(b.var), b.bound, b.upper, b.equality, b.strict) for b in lra.enc_to_boundary.values()} == {('_s1', 2, None, True, False),
|
| 103 |
+
('_s1', 2, True, False, False),
|
| 104 |
+
('_s2', -4, True, False, True),
|
| 105 |
+
('_s2', -6, True, False, False),
|
| 106 |
+
('x', 0, False, False, False)}
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def test_problem():
|
| 110 |
+
from sympy.logic.algorithms.lra_theory import LRASolver
|
| 111 |
+
from sympy.assumptions.cnf import CNF, EncodedCNF
|
| 112 |
+
cons = [-2 * x - 2 * y >= 7, -9 * y >= 7, -6 * y >= 5]
|
| 113 |
+
cnf = CNF().from_prop(And(*cons))
|
| 114 |
+
enc = EncodedCNF()
|
| 115 |
+
enc.from_cnf(cnf)
|
| 116 |
+
lra, _ = LRASolver.from_encoded_cnf(enc)
|
| 117 |
+
lra.assert_lit(1)
|
| 118 |
+
lra.assert_lit(2)
|
| 119 |
+
lra.assert_lit(3)
|
| 120 |
+
is_sat, assignment = lra.check()
|
| 121 |
+
assert is_sat is True
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def test_random_problems():
|
| 125 |
+
z3 = import_module("z3")
|
| 126 |
+
if z3 is None:
|
| 127 |
+
skip("z3 is not installed")
|
| 128 |
+
|
| 129 |
+
special_cases = []; x1, x2, x3 = symbols("x1 x2 x3")
|
| 130 |
+
special_cases.append([x1 - 3 * x2 <= -5, 6 * x1 + 4 * x2 <= 0, -7 * x1 + 3 * x2 <= 3])
|
| 131 |
+
special_cases.append([-3 * x1 >= 3, Eq(4 * x1, -1)])
|
| 132 |
+
special_cases.append([-4 * x1 < 4, 6 * x1 <= -6])
|
| 133 |
+
special_cases.append([-3 * x2 >= 7, 6 * x1 <= -5, -3 * x2 <= -4])
|
| 134 |
+
special_cases.append([x + y >= 2, x + y <= 1])
|
| 135 |
+
special_cases.append([x >= 0, x + y <= 2, x + 2 * y - z >= 6]) # from paper example
|
| 136 |
+
special_cases.append([-2 * x1 - 2 * x2 >= 7, -9 * x1 >= 7, -6 * x1 >= 5])
|
| 137 |
+
special_cases.append([2 * x1 > -3, -9 * x1 < -6, 9 * x1 <= 6])
|
| 138 |
+
special_cases.append([-2*x1 < -4, 9*x1 > -9])
|
| 139 |
+
special_cases.append([-6*x1 >= -1, -8*x1 + x2 >= 5, -8*x1 + 7*x2 < 4, x1 > 7])
|
| 140 |
+
special_cases.append([Eq(x1, 2), Eq(5*x1, -2), Eq(-7*x2, -6), Eq(9*x1 + 10*x2, 9)])
|
| 141 |
+
special_cases.append([Eq(3*x1, 6), Eq(x1 - 8*x2, -9), Eq(-7*x1 + 5*x2, 3), Eq(3*x2, 7)])
|
| 142 |
+
special_cases.append([-4*x1 < 4, 6*x1 <= -6])
|
| 143 |
+
special_cases.append([-3*x1 + 8*x2 >= -8, -10*x2 > 9, 8*x1 - 4*x2 < 8, 10*x1 - 9*x2 >= -9])
|
| 144 |
+
special_cases.append([x1 + 5*x2 >= -6, 9*x1 - 3*x2 >= -9, 6*x1 + 6*x2 < -10, -3*x1 + 3*x2 < -7])
|
| 145 |
+
special_cases.append([-9*x1 < 7, -5*x1 - 7*x2 < -1, 3*x1 + 7*x2 > 1, -6*x1 - 6*x2 > 9])
|
| 146 |
+
special_cases.append([9*x1 - 6*x2 >= -7, 9*x1 + 4*x2 < -8, -7*x2 <= 1, 10*x2 <= -7])
|
| 147 |
+
|
| 148 |
+
feasible_count = 0
|
| 149 |
+
for i in range(50):
|
| 150 |
+
if i % 8 == 0:
|
| 151 |
+
constraints = make_random_problem(num_variables=1, num_constraints=2, rational=False)
|
| 152 |
+
elif i % 8 == 1:
|
| 153 |
+
constraints = make_random_problem(num_variables=2, num_constraints=4, rational=False, disable_equality=True,
|
| 154 |
+
disable_nonstrict=True)
|
| 155 |
+
elif i % 8 == 2:
|
| 156 |
+
constraints = make_random_problem(num_variables=2, num_constraints=4, rational=False, disable_strict=True)
|
| 157 |
+
elif i % 8 == 3:
|
| 158 |
+
constraints = make_random_problem(num_variables=3, num_constraints=12, rational=False)
|
| 159 |
+
else:
|
| 160 |
+
constraints = make_random_problem(num_variables=3, num_constraints=6, rational=False)
|
| 161 |
+
|
| 162 |
+
if i < len(special_cases):
|
| 163 |
+
constraints = special_cases[i]
|
| 164 |
+
|
| 165 |
+
if False in constraints or True in constraints:
|
| 166 |
+
continue
|
| 167 |
+
|
| 168 |
+
phi = And(*constraints)
|
| 169 |
+
if phi == False:
|
| 170 |
+
continue
|
| 171 |
+
cnf = CNF.from_prop(phi); enc = EncodedCNF()
|
| 172 |
+
enc.from_cnf(cnf)
|
| 173 |
+
assert all(0 not in clause for clause in enc.data)
|
| 174 |
+
|
| 175 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 176 |
+
s_subs = lra.s_subs
|
| 177 |
+
|
| 178 |
+
lra.run_checks = True
|
| 179 |
+
s_subs_rev = {value: key for key, value in s_subs.items()}
|
| 180 |
+
lits = {lit for clause in enc.data for lit in clause}
|
| 181 |
+
|
| 182 |
+
bounds = [(lra.enc_to_boundary[l], l) for l in lits if l in lra.enc_to_boundary]
|
| 183 |
+
bounds = sorted(bounds, key=lambda x: (str(x[0].var), x[0].bound, str(x[0].upper))) # to remove nondeterminism
|
| 184 |
+
|
| 185 |
+
for b, l in bounds:
|
| 186 |
+
if lra.result and lra.result[0] == False:
|
| 187 |
+
break
|
| 188 |
+
lra.assert_lit(l)
|
| 189 |
+
|
| 190 |
+
feasible = lra.check()
|
| 191 |
+
|
| 192 |
+
if feasible[0] == True:
|
| 193 |
+
feasible_count += 1
|
| 194 |
+
assert check_if_satisfiable_with_z3(constraints) is True
|
| 195 |
+
cons_funcs = [cons.func for cons in constraints]
|
| 196 |
+
assignment = feasible[1]
|
| 197 |
+
assignment = {key.var : value for key, value in assignment.items()}
|
| 198 |
+
if not (StrictLessThan in cons_funcs or StrictGreaterThan in cons_funcs):
|
| 199 |
+
assignment = {key: value[0] for key, value in assignment.items()}
|
| 200 |
+
for cons in constraints:
|
| 201 |
+
assert cons.subs(assignment) == True
|
| 202 |
+
|
| 203 |
+
else:
|
| 204 |
+
rat_assignment = find_rational_assignment(constraints, assignment)
|
| 205 |
+
assert rat_assignment is not None
|
| 206 |
+
else:
|
| 207 |
+
assert check_if_satisfiable_with_z3(constraints) is False
|
| 208 |
+
|
| 209 |
+
conflict = feasible[1]
|
| 210 |
+
assert len(conflict) >= 2
|
| 211 |
+
conflict = {lra.enc_to_boundary[-l].get_inequality() for l in conflict}
|
| 212 |
+
conflict = {clause.subs(s_subs_rev) for clause in conflict}
|
| 213 |
+
assert check_if_satisfiable_with_z3(conflict) is False
|
| 214 |
+
|
| 215 |
+
# check that conflict clause is probably minimal
|
| 216 |
+
for subset in itertools.combinations(conflict, len(conflict)-1):
|
| 217 |
+
assert check_if_satisfiable_with_z3(subset) is True
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
@XFAIL
|
| 221 |
+
def test_pos_neg_zero():
|
| 222 |
+
bf = Q.positive(x) & Q.negative(x) & Q.zero(y)
|
| 223 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 224 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 225 |
+
for lit in enc.encoding.values():
|
| 226 |
+
if lra.assert_lit(lit) is not None:
|
| 227 |
+
break
|
| 228 |
+
assert len(lra.enc_to_boundary) == 3
|
| 229 |
+
assert lra.check()[0] == False
|
| 230 |
+
|
| 231 |
+
bf = Q.positive(x) & Q.lt(x, -1)
|
| 232 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 233 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 234 |
+
for lit in enc.encoding.values():
|
| 235 |
+
if lra.assert_lit(lit) is not None:
|
| 236 |
+
break
|
| 237 |
+
assert len(lra.enc_to_boundary) == 2
|
| 238 |
+
assert lra.check()[0] == False
|
| 239 |
+
|
| 240 |
+
bf = Q.positive(x) & Q.zero(x)
|
| 241 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 242 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 243 |
+
for lit in enc.encoding.values():
|
| 244 |
+
if lra.assert_lit(lit) is not None:
|
| 245 |
+
break
|
| 246 |
+
assert len(lra.enc_to_boundary) == 2
|
| 247 |
+
assert lra.check()[0] == False
|
| 248 |
+
|
| 249 |
+
bf = Q.positive(x) & Q.zero(y)
|
| 250 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 251 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 252 |
+
for lit in enc.encoding.values():
|
| 253 |
+
if lra.assert_lit(lit) is not None:
|
| 254 |
+
break
|
| 255 |
+
assert len(lra.enc_to_boundary) == 2
|
| 256 |
+
assert lra.check()[0] == True
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
@XFAIL
|
| 260 |
+
def test_pos_neg_infinite():
|
| 261 |
+
bf = Q.positive_infinite(x) & Q.lt(x, 10000000) & Q.positive_infinite(y)
|
| 262 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 263 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 264 |
+
for lit in enc.encoding.values():
|
| 265 |
+
if lra.assert_lit(lit) is not None:
|
| 266 |
+
break
|
| 267 |
+
assert len(lra.enc_to_boundary) == 3
|
| 268 |
+
assert lra.check()[0] == False
|
| 269 |
+
|
| 270 |
+
bf = Q.positive_infinite(x) & Q.gt(x, 10000000) & Q.positive_infinite(y)
|
| 271 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 272 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 273 |
+
for lit in enc.encoding.values():
|
| 274 |
+
if lra.assert_lit(lit) is not None:
|
| 275 |
+
break
|
| 276 |
+
assert len(lra.enc_to_boundary) == 3
|
| 277 |
+
assert lra.check()[0] == True
|
| 278 |
+
|
| 279 |
+
bf = Q.positive_infinite(x) & Q.negative_infinite(x)
|
| 280 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 281 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 282 |
+
for lit in enc.encoding.values():
|
| 283 |
+
if lra.assert_lit(lit) is not None:
|
| 284 |
+
break
|
| 285 |
+
assert len(lra.enc_to_boundary) == 2
|
| 286 |
+
assert lra.check()[0] == False
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
def test_binrel_evaluation():
|
| 290 |
+
bf = Q.gt(3, 2)
|
| 291 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 292 |
+
lra, conflicts = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 293 |
+
assert len(lra.enc_to_boundary) == 0
|
| 294 |
+
assert conflicts == [[1]]
|
| 295 |
+
|
| 296 |
+
bf = Q.lt(3, 2)
|
| 297 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 298 |
+
lra, conflicts = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 299 |
+
assert len(lra.enc_to_boundary) == 0
|
| 300 |
+
assert conflicts == [[-1]]
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
def test_negation():
|
| 304 |
+
assert HANDLE_NEGATION is True
|
| 305 |
+
bf = Q.gt(x, 1) & ~Q.gt(x, 0)
|
| 306 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 307 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 308 |
+
for clause in enc.data:
|
| 309 |
+
for lit in clause:
|
| 310 |
+
lra.assert_lit(lit)
|
| 311 |
+
assert len(lra.enc_to_boundary) == 2
|
| 312 |
+
assert lra.check()[0] == False
|
| 313 |
+
assert sorted(lra.check()[1]) in [[-1, 2], [-2, 1]]
|
| 314 |
+
|
| 315 |
+
bf = ~Q.gt(x, 1) & ~Q.lt(x, 0)
|
| 316 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 317 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 318 |
+
for clause in enc.data:
|
| 319 |
+
for lit in clause:
|
| 320 |
+
lra.assert_lit(lit)
|
| 321 |
+
assert len(lra.enc_to_boundary) == 2
|
| 322 |
+
assert lra.check()[0] == True
|
| 323 |
+
|
| 324 |
+
bf = ~Q.gt(x, 0) & ~Q.lt(x, 1)
|
| 325 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 326 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 327 |
+
for clause in enc.data:
|
| 328 |
+
for lit in clause:
|
| 329 |
+
lra.assert_lit(lit)
|
| 330 |
+
assert len(lra.enc_to_boundary) == 2
|
| 331 |
+
assert lra.check()[0] == False
|
| 332 |
+
|
| 333 |
+
bf = ~Q.gt(x, 0) & ~Q.le(x, 0)
|
| 334 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 335 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 336 |
+
for clause in enc.data:
|
| 337 |
+
for lit in clause:
|
| 338 |
+
lra.assert_lit(lit)
|
| 339 |
+
assert len(lra.enc_to_boundary) == 2
|
| 340 |
+
assert lra.check()[0] == False
|
| 341 |
+
|
| 342 |
+
bf = ~Q.le(x+y, 2) & ~Q.ge(x-y, 2) & ~Q.ge(y, 0)
|
| 343 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 344 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 345 |
+
for clause in enc.data:
|
| 346 |
+
for lit in clause:
|
| 347 |
+
lra.assert_lit(lit)
|
| 348 |
+
assert len(lra.enc_to_boundary) == 3
|
| 349 |
+
assert lra.check()[0] == False
|
| 350 |
+
assert len(lra.check()[1]) == 3
|
| 351 |
+
assert all(i > 0 for i in lra.check()[1])
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def test_unhandled_input():
|
| 355 |
+
nan = S.NaN
|
| 356 |
+
bf = Q.gt(3, nan) & Q.gt(x, nan)
|
| 357 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 358 |
+
raises(ValueError, lambda: LRASolver.from_encoded_cnf(enc, testing_mode=True))
|
| 359 |
+
|
| 360 |
+
bf = Q.gt(3, I) & Q.gt(x, I)
|
| 361 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 362 |
+
raises(UnhandledInput, lambda: LRASolver.from_encoded_cnf(enc, testing_mode=True))
|
| 363 |
+
|
| 364 |
+
bf = Q.gt(3, float("inf")) & Q.gt(x, float("inf"))
|
| 365 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 366 |
+
raises(UnhandledInput, lambda: LRASolver.from_encoded_cnf(enc, testing_mode=True))
|
| 367 |
+
|
| 368 |
+
bf = Q.gt(3, oo) & Q.gt(x, oo)
|
| 369 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 370 |
+
raises(UnhandledInput, lambda: LRASolver.from_encoded_cnf(enc, testing_mode=True))
|
| 371 |
+
|
| 372 |
+
# test non-linearity
|
| 373 |
+
bf = Q.gt(x**2 + x, 2)
|
| 374 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 375 |
+
raises(UnhandledInput, lambda: LRASolver.from_encoded_cnf(enc, testing_mode=True))
|
| 376 |
+
|
| 377 |
+
bf = Q.gt(cos(x) + x, 2)
|
| 378 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 379 |
+
raises(UnhandledInput, lambda: LRASolver.from_encoded_cnf(enc, testing_mode=True))
|
| 380 |
+
|
| 381 |
+
@XFAIL
|
| 382 |
+
def test_infinite_strict_inequalities():
|
| 383 |
+
# Extensive testing of the interaction between strict inequalities
|
| 384 |
+
# and constraints containing infinity is needed because
|
| 385 |
+
# the paper's rule for strict inequalities don't work when
|
| 386 |
+
# infinite numbers are allowed. Using the paper's rules you
|
| 387 |
+
# can end up with situations where oo + delta > oo is considered
|
| 388 |
+
# True when oo + delta should be equal to oo.
|
| 389 |
+
# See https://math.stackexchange.com/questions/4757069/can-this-method-of-converting-strict-inequalities-to-equisatisfiable-nonstrict-i
|
| 390 |
+
bf = (-x - y >= -float("inf")) & (x > 0) & (y >= float("inf"))
|
| 391 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 392 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 393 |
+
for lit in sorted(enc.encoding.values()):
|
| 394 |
+
if lra.assert_lit(lit) is not None:
|
| 395 |
+
break
|
| 396 |
+
assert len(lra.enc_to_boundary) == 3
|
| 397 |
+
assert lra.check()[0] == True
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def test_pivot():
|
| 401 |
+
for _ in range(10):
|
| 402 |
+
m = randMatrix(5)
|
| 403 |
+
rref = m.rref()
|
| 404 |
+
for _ in range(5):
|
| 405 |
+
i, j = randint(0, 4), randint(0, 4)
|
| 406 |
+
if m[i, j] != 0:
|
| 407 |
+
assert LRASolver._pivot(m, i, j).rref() == rref
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
def test_reset_bounds():
|
| 411 |
+
bf = Q.ge(x, 1) & Q.lt(x, 1)
|
| 412 |
+
enc = boolean_formula_to_encoded_cnf(bf)
|
| 413 |
+
lra, _ = LRASolver.from_encoded_cnf(enc, testing_mode=True)
|
| 414 |
+
for clause in enc.data:
|
| 415 |
+
for lit in clause:
|
| 416 |
+
lra.assert_lit(lit)
|
| 417 |
+
assert len(lra.enc_to_boundary) == 2
|
| 418 |
+
assert lra.check()[0] == False
|
| 419 |
+
|
| 420 |
+
lra.reset_bounds()
|
| 421 |
+
assert lra.check()[0] == True
|
| 422 |
+
for var in lra.all_var:
|
| 423 |
+
assert var.upper == LRARational(float("inf"), 0)
|
| 424 |
+
assert var.upper_from_eq == False
|
| 425 |
+
assert var.upper_from_neg == False
|
| 426 |
+
assert var.lower == LRARational(-float("inf"), 0)
|
| 427 |
+
assert var.lower_from_eq == False
|
| 428 |
+
assert var.lower_from_neg == False
|
| 429 |
+
assert var.assign == LRARational(0, 0)
|
| 430 |
+
assert var.var is not None
|
| 431 |
+
assert var.col_idx is not None
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def test_empty_cnf():
|
| 435 |
+
cnf = CNF()
|
| 436 |
+
enc = EncodedCNF()
|
| 437 |
+
enc.from_cnf(cnf)
|
| 438 |
+
lra, conflict = LRASolver.from_encoded_cnf(enc)
|
| 439 |
+
assert len(conflict) == 0
|
| 440 |
+
assert lra.check() == (True, {})
|
phivenv/Lib/site-packages/sympy/logic/utilities/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .dimacs import load_file
|
| 2 |
+
|
| 3 |
+
__all__ = ['load_file']
|
phivenv/Lib/site-packages/sympy/logic/utilities/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (227 Bytes). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/utilities/__pycache__/dimacs.cpython-39.pyc
ADDED
|
Binary file (1.49 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/logic/utilities/dimacs.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""For reading in DIMACS file format
|
| 2 |
+
|
| 3 |
+
www.cs.ubc.ca/~hoos/SATLIB/Benchmarks/SAT/satformat.ps
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from sympy.core import Symbol
|
| 8 |
+
from sympy.logic.boolalg import And, Or
|
| 9 |
+
import re
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def load(s):
|
| 14 |
+
"""Loads a boolean expression from a string.
|
| 15 |
+
|
| 16 |
+
Examples
|
| 17 |
+
========
|
| 18 |
+
|
| 19 |
+
>>> from sympy.logic.utilities.dimacs import load
|
| 20 |
+
>>> load('1')
|
| 21 |
+
cnf_1
|
| 22 |
+
>>> load('1 2')
|
| 23 |
+
cnf_1 | cnf_2
|
| 24 |
+
>>> load('1 \\n 2')
|
| 25 |
+
cnf_1 & cnf_2
|
| 26 |
+
>>> load('1 2 \\n 3')
|
| 27 |
+
cnf_3 & (cnf_1 | cnf_2)
|
| 28 |
+
"""
|
| 29 |
+
clauses = []
|
| 30 |
+
|
| 31 |
+
lines = s.split('\n')
|
| 32 |
+
|
| 33 |
+
pComment = re.compile(r'c.*')
|
| 34 |
+
pStats = re.compile(r'p\s*cnf\s*(\d*)\s*(\d*)')
|
| 35 |
+
|
| 36 |
+
while len(lines) > 0:
|
| 37 |
+
line = lines.pop(0)
|
| 38 |
+
|
| 39 |
+
# Only deal with lines that aren't comments
|
| 40 |
+
if not pComment.match(line):
|
| 41 |
+
m = pStats.match(line)
|
| 42 |
+
|
| 43 |
+
if not m:
|
| 44 |
+
nums = line.rstrip('\n').split(' ')
|
| 45 |
+
list = []
|
| 46 |
+
for lit in nums:
|
| 47 |
+
if lit != '':
|
| 48 |
+
if int(lit) == 0:
|
| 49 |
+
continue
|
| 50 |
+
num = abs(int(lit))
|
| 51 |
+
sign = True
|
| 52 |
+
if int(lit) < 0:
|
| 53 |
+
sign = False
|
| 54 |
+
|
| 55 |
+
if sign:
|
| 56 |
+
list.append(Symbol("cnf_%s" % num))
|
| 57 |
+
else:
|
| 58 |
+
list.append(~Symbol("cnf_%s" % num))
|
| 59 |
+
|
| 60 |
+
if len(list) > 0:
|
| 61 |
+
clauses.append(Or(*list))
|
| 62 |
+
|
| 63 |
+
return And(*clauses)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def load_file(location):
|
| 67 |
+
"""Loads a boolean expression from a file."""
|
| 68 |
+
s = Path(location).read_text()
|
| 69 |
+
return load(s)
|
phivenv/Lib/site-packages/sympy/matrices/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A module that handles matrices.
|
| 2 |
+
|
| 3 |
+
Includes functions for fast creating matrices like zero, one/eye, random
|
| 4 |
+
matrix, etc.
|
| 5 |
+
"""
|
| 6 |
+
from .exceptions import ShapeError, NonSquareMatrixError
|
| 7 |
+
from .kind import MatrixKind
|
| 8 |
+
from .dense import (
|
| 9 |
+
GramSchmidt, casoratian, diag, eye, hessian, jordan_cell,
|
| 10 |
+
list2numpy, matrix2numpy, matrix_multiply_elementwise, ones,
|
| 11 |
+
randMatrix, rot_axis1, rot_axis2, rot_axis3, rot_ccw_axis1,
|
| 12 |
+
rot_ccw_axis2, rot_ccw_axis3, rot_givens,
|
| 13 |
+
symarray, wronskian, zeros)
|
| 14 |
+
from .dense import MutableDenseMatrix
|
| 15 |
+
from .matrixbase import DeferredVector, MatrixBase
|
| 16 |
+
|
| 17 |
+
MutableMatrix = MutableDenseMatrix
|
| 18 |
+
Matrix = MutableMatrix
|
| 19 |
+
|
| 20 |
+
from .sparse import MutableSparseMatrix
|
| 21 |
+
from .sparsetools import banded
|
| 22 |
+
from .immutable import ImmutableDenseMatrix, ImmutableSparseMatrix
|
| 23 |
+
|
| 24 |
+
ImmutableMatrix = ImmutableDenseMatrix
|
| 25 |
+
SparseMatrix = MutableSparseMatrix
|
| 26 |
+
|
| 27 |
+
from .expressions import (
|
| 28 |
+
MatrixSlice, BlockDiagMatrix, BlockMatrix, FunctionMatrix, Identity,
|
| 29 |
+
Inverse, MatAdd, MatMul, MatPow, MatrixExpr, MatrixSymbol, Trace,
|
| 30 |
+
Transpose, ZeroMatrix, OneMatrix, blockcut, block_collapse, matrix_symbols, Adjoint,
|
| 31 |
+
hadamard_product, HadamardProduct, HadamardPower, Determinant, det,
|
| 32 |
+
diagonalize_vector, DiagMatrix, DiagonalMatrix, DiagonalOf, trace,
|
| 33 |
+
DotProduct, kronecker_product, KroneckerProduct,
|
| 34 |
+
PermutationMatrix, MatrixPermute, MatrixSet, Permanent, per)
|
| 35 |
+
|
| 36 |
+
from .utilities import dotprodsimp
|
| 37 |
+
|
| 38 |
+
__all__ = [
|
| 39 |
+
'ShapeError', 'NonSquareMatrixError', 'MatrixKind',
|
| 40 |
+
|
| 41 |
+
'GramSchmidt', 'casoratian', 'diag', 'eye', 'hessian', 'jordan_cell',
|
| 42 |
+
'list2numpy', 'matrix2numpy', 'matrix_multiply_elementwise', 'ones',
|
| 43 |
+
'randMatrix', 'rot_axis1', 'rot_axis2', 'rot_axis3', 'symarray',
|
| 44 |
+
'wronskian', 'zeros', 'rot_ccw_axis1', 'rot_ccw_axis2', 'rot_ccw_axis3',
|
| 45 |
+
'rot_givens',
|
| 46 |
+
|
| 47 |
+
'MutableDenseMatrix',
|
| 48 |
+
|
| 49 |
+
'DeferredVector', 'MatrixBase',
|
| 50 |
+
|
| 51 |
+
'Matrix', 'MutableMatrix',
|
| 52 |
+
|
| 53 |
+
'MutableSparseMatrix',
|
| 54 |
+
|
| 55 |
+
'banded',
|
| 56 |
+
|
| 57 |
+
'ImmutableDenseMatrix', 'ImmutableSparseMatrix',
|
| 58 |
+
|
| 59 |
+
'ImmutableMatrix', 'SparseMatrix',
|
| 60 |
+
|
| 61 |
+
'MatrixSlice', 'BlockDiagMatrix', 'BlockMatrix', 'FunctionMatrix',
|
| 62 |
+
'Identity', 'Inverse', 'MatAdd', 'MatMul', 'MatPow', 'MatrixExpr',
|
| 63 |
+
'MatrixSymbol', 'Trace', 'Transpose', 'ZeroMatrix', 'OneMatrix',
|
| 64 |
+
'blockcut', 'block_collapse', 'matrix_symbols', 'Adjoint',
|
| 65 |
+
'hadamard_product', 'HadamardProduct', 'HadamardPower', 'Determinant',
|
| 66 |
+
'det', 'diagonalize_vector', 'DiagMatrix', 'DiagonalMatrix',
|
| 67 |
+
'DiagonalOf', 'trace', 'DotProduct', 'kronecker_product',
|
| 68 |
+
'KroneckerProduct', 'PermutationMatrix', 'MatrixPermute', 'MatrixSet',
|
| 69 |
+
'Permanent', 'per',
|
| 70 |
+
|
| 71 |
+
'dotprodsimp',
|
| 72 |
+
]
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (2.49 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/decompositions.cpython-39.pyc
ADDED
|
Binary file (41.3 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/dense.cpython-39.pyc
ADDED
|
Binary file (31.4 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/determinant.cpython-39.pyc
ADDED
|
Binary file (27.5 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/eigen.cpython-39.pyc
ADDED
|
Binary file (36.9 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/exceptions.cpython-39.pyc
ADDED
|
Binary file (1.13 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/graph.cpython-39.pyc
ADDED
|
Binary file (9.09 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/immutable.cpython-39.pyc
ADDED
|
Binary file (6.41 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/inverse.cpython-39.pyc
ADDED
|
Binary file (12 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/kind.cpython-39.pyc
ADDED
|
Binary file (2.95 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/matrices.cpython-39.pyc
ADDED
|
Binary file (24.4 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/normalforms.cpython-39.pyc
ADDED
|
Binary file (5.21 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/reductions.cpython-39.pyc
ADDED
|
Binary file (10.9 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/repmatrix.cpython-39.pyc
ADDED
|
Binary file (29.1 kB). View file
|
|
|
phivenv/Lib/site-packages/sympy/matrices/__pycache__/solvers.cpython-39.pyc
ADDED
|
Binary file (24 kB). View file
|
|
|