MNghia commited on
Commit
029af54
·
verified ·
1 Parent(s): d1e8e6b

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.10/site-packages/sympy/__pycache__/__init__.cpython-310.pyc +0 -0
  2. lib/python3.10/site-packages/sympy/__pycache__/abc.cpython-310.pyc +0 -0
  3. lib/python3.10/site-packages/sympy/__pycache__/conftest.cpython-310.pyc +0 -0
  4. lib/python3.10/site-packages/sympy/__pycache__/galgebra.cpython-310.pyc +0 -0
  5. lib/python3.10/site-packages/sympy/__pycache__/release.cpython-310.pyc +0 -0
  6. lib/python3.10/site-packages/sympy/__pycache__/this.cpython-310.pyc +0 -0
  7. lib/python3.10/site-packages/sympy/algebras/__init__.py +3 -0
  8. lib/python3.10/site-packages/sympy/algebras/__pycache__/__init__.cpython-310.pyc +0 -0
  9. lib/python3.10/site-packages/sympy/algebras/__pycache__/quaternion.cpython-310.pyc +0 -0
  10. lib/python3.10/site-packages/sympy/algebras/quaternion.py +1667 -0
  11. lib/python3.10/site-packages/sympy/algebras/tests/__init__.py +0 -0
  12. lib/python3.10/site-packages/sympy/algebras/tests/__pycache__/__init__.cpython-310.pyc +0 -0
  13. lib/python3.10/site-packages/sympy/algebras/tests/__pycache__/test_quaternion.cpython-310.pyc +0 -0
  14. lib/python3.10/site-packages/sympy/algebras/tests/test_quaternion.py +428 -0
  15. lib/python3.10/site-packages/sympy/assumptions/__init__.py +18 -0
  16. lib/python3.10/site-packages/sympy/assumptions/__pycache__/__init__.cpython-310.pyc +0 -0
  17. lib/python3.10/site-packages/sympy/assumptions/__pycache__/ask.cpython-310.pyc +0 -0
  18. lib/python3.10/site-packages/sympy/assumptions/__pycache__/ask_generated.cpython-310.pyc +0 -0
  19. lib/python3.10/site-packages/sympy/assumptions/__pycache__/assume.cpython-310.pyc +0 -0
  20. lib/python3.10/site-packages/sympy/assumptions/__pycache__/cnf.cpython-310.pyc +0 -0
  21. lib/python3.10/site-packages/sympy/assumptions/__pycache__/facts.cpython-310.pyc +0 -0
  22. lib/python3.10/site-packages/sympy/assumptions/__pycache__/lra_satask.cpython-310.pyc +0 -0
  23. lib/python3.10/site-packages/sympy/assumptions/__pycache__/refine.cpython-310.pyc +0 -0
  24. lib/python3.10/site-packages/sympy/assumptions/__pycache__/satask.cpython-310.pyc +0 -0
  25. lib/python3.10/site-packages/sympy/assumptions/__pycache__/sathandlers.cpython-310.pyc +0 -0
  26. lib/python3.10/site-packages/sympy/assumptions/__pycache__/wrapper.cpython-310.pyc +0 -0
  27. lib/python3.10/site-packages/sympy/assumptions/ask.py +651 -0
  28. lib/python3.10/site-packages/sympy/assumptions/ask_generated.py +352 -0
  29. lib/python3.10/site-packages/sympy/assumptions/assume.py +485 -0
  30. lib/python3.10/site-packages/sympy/assumptions/cnf.py +445 -0
  31. lib/python3.10/site-packages/sympy/assumptions/facts.py +270 -0
  32. lib/python3.10/site-packages/sympy/assumptions/handlers/__init__.py +13 -0
  33. lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/__init__.cpython-310.pyc +0 -0
  34. lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/calculus.cpython-310.pyc +0 -0
  35. lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/common.cpython-310.pyc +0 -0
  36. lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/matrices.cpython-310.pyc +0 -0
  37. lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/ntheory.cpython-310.pyc +0 -0
  38. lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/order.cpython-310.pyc +0 -0
  39. lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/sets.cpython-310.pyc +0 -0
  40. lib/python3.10/site-packages/sympy/assumptions/handlers/calculus.py +258 -0
  41. lib/python3.10/site-packages/sympy/assumptions/handlers/common.py +156 -0
  42. lib/python3.10/site-packages/sympy/assumptions/handlers/matrices.py +716 -0
  43. lib/python3.10/site-packages/sympy/assumptions/handlers/ntheory.py +269 -0
  44. lib/python3.10/site-packages/sympy/assumptions/handlers/order.py +436 -0
  45. lib/python3.10/site-packages/sympy/assumptions/handlers/sets.py +772 -0
  46. lib/python3.10/site-packages/sympy/assumptions/lra_satask.py +286 -0
  47. lib/python3.10/site-packages/sympy/assumptions/predicates/__init__.py +5 -0
  48. lib/python3.10/site-packages/sympy/assumptions/predicates/__pycache__/__init__.cpython-310.pyc +0 -0
  49. lib/python3.10/site-packages/sympy/assumptions/predicates/__pycache__/calculus.cpython-310.pyc +0 -0
  50. lib/python3.10/site-packages/sympy/assumptions/predicates/__pycache__/common.cpython-310.pyc +0 -0
lib/python3.10/site-packages/sympy/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (28.3 kB). View file
 
lib/python3.10/site-packages/sympy/__pycache__/abc.cpython-310.pyc ADDED
Binary file (3.55 kB). View file
 
lib/python3.10/site-packages/sympy/__pycache__/conftest.cpython-310.pyc ADDED
Binary file (2.98 kB). View file
 
lib/python3.10/site-packages/sympy/__pycache__/galgebra.cpython-310.pyc ADDED
Binary file (309 Bytes). View file
 
lib/python3.10/site-packages/sympy/__pycache__/release.cpython-310.pyc ADDED
Binary file (217 Bytes). View file
 
lib/python3.10/site-packages/sympy/__pycache__/this.cpython-310.pyc ADDED
Binary file (756 Bytes). View file
 
lib/python3.10/site-packages/sympy/algebras/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .quaternion import Quaternion
2
+
3
+ __all__ = ["Quaternion",]
lib/python3.10/site-packages/sympy/algebras/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (272 Bytes). View file
 
lib/python3.10/site-packages/sympy/algebras/__pycache__/quaternion.cpython-310.pyc ADDED
Binary file (47.2 kB). View file
 
lib/python3.10/site-packages/sympy/algebras/quaternion.py ADDED
@@ -0,0 +1,1667 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.numbers import Rational
2
+ from sympy.core.singleton import S
3
+ from sympy.core.relational import is_eq
4
+ from sympy.functions.elementary.complexes import (conjugate, im, re, sign)
5
+ from sympy.functions.elementary.exponential import (exp, log as ln)
6
+ from sympy.functions.elementary.miscellaneous import sqrt
7
+ from sympy.functions.elementary.trigonometric import (acos, asin, atan2)
8
+ from sympy.functions.elementary.trigonometric import (cos, sin)
9
+ from sympy.simplify.trigsimp import trigsimp
10
+ from sympy.integrals.integrals import integrate
11
+ from sympy.matrices.dense import MutableDenseMatrix as Matrix
12
+ from sympy.core.sympify import sympify, _sympify
13
+ from sympy.core.expr import Expr
14
+ from sympy.core.logic import fuzzy_not, fuzzy_or
15
+ from sympy.utilities.misc import as_int
16
+
17
+ from mpmath.libmp.libmpf import prec_to_dps
18
+
19
+
20
+ def _check_norm(elements, norm):
21
+ """validate if input norm is consistent"""
22
+ if norm is not None and norm.is_number:
23
+ if norm.is_positive is False:
24
+ raise ValueError("Input norm must be positive.")
25
+
26
+ numerical = all(i.is_number and i.is_real is True for i in elements)
27
+ if numerical and is_eq(norm**2, sum(i**2 for i in elements)) is False:
28
+ raise ValueError("Incompatible value for norm.")
29
+
30
+
31
+ def _is_extrinsic(seq):
32
+ """validate seq and return True if seq is lowercase and False if uppercase"""
33
+ if type(seq) != str:
34
+ raise ValueError('Expected seq to be a string.')
35
+ if len(seq) != 3:
36
+ raise ValueError("Expected 3 axes, got `{}`.".format(seq))
37
+
38
+ intrinsic = seq.isupper()
39
+ extrinsic = seq.islower()
40
+ if not (intrinsic or extrinsic):
41
+ raise ValueError("seq must either be fully uppercase (for extrinsic "
42
+ "rotations), or fully lowercase, for intrinsic "
43
+ "rotations).")
44
+
45
+ i, j, k = seq.lower()
46
+ if (i == j) or (j == k):
47
+ raise ValueError("Consecutive axes must be different")
48
+
49
+ bad = set(seq) - set('xyzXYZ')
50
+ if bad:
51
+ raise ValueError("Expected axes from `seq` to be from "
52
+ "['x', 'y', 'z'] or ['X', 'Y', 'Z'], "
53
+ "got {}".format(''.join(bad)))
54
+
55
+ return extrinsic
56
+
57
+
58
+ class Quaternion(Expr):
59
+ """Provides basic quaternion operations.
60
+ Quaternion objects can be instantiated as ``Quaternion(a, b, c, d)``
61
+ as in $q = a + bi + cj + dk$.
62
+
63
+ Parameters
64
+ ==========
65
+
66
+ norm : None or number
67
+ Pre-defined quaternion norm. If a value is given, Quaternion.norm
68
+ returns this pre-defined value instead of calculating the norm
69
+
70
+ Examples
71
+ ========
72
+
73
+ >>> from sympy import Quaternion
74
+ >>> q = Quaternion(1, 2, 3, 4)
75
+ >>> q
76
+ 1 + 2*i + 3*j + 4*k
77
+
78
+ Quaternions over complex fields can be defined as:
79
+
80
+ >>> from sympy import Quaternion
81
+ >>> from sympy import symbols, I
82
+ >>> x = symbols('x')
83
+ >>> q1 = Quaternion(x, x**3, x, x**2, real_field = False)
84
+ >>> q2 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
85
+ >>> q1
86
+ x + x**3*i + x*j + x**2*k
87
+ >>> q2
88
+ (3 + 4*I) + (2 + 5*I)*i + 0*j + (7 + 8*I)*k
89
+
90
+ Defining symbolic unit quaternions:
91
+
92
+ >>> from sympy import Quaternion
93
+ >>> from sympy.abc import w, x, y, z
94
+ >>> q = Quaternion(w, x, y, z, norm=1)
95
+ >>> q
96
+ w + x*i + y*j + z*k
97
+ >>> q.norm()
98
+ 1
99
+
100
+ References
101
+ ==========
102
+
103
+ .. [1] https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/
104
+ .. [2] https://en.wikipedia.org/wiki/Quaternion
105
+
106
+ """
107
+ _op_priority = 11.0
108
+
109
+ is_commutative = False
110
+
111
+ def __new__(cls, a=0, b=0, c=0, d=0, real_field=True, norm=None):
112
+ a, b, c, d = map(sympify, (a, b, c, d))
113
+
114
+ if any(i.is_commutative is False for i in [a, b, c, d]):
115
+ raise ValueError("arguments have to be commutative")
116
+ obj = super().__new__(cls, a, b, c, d)
117
+ obj._real_field = real_field
118
+ obj.set_norm(norm)
119
+ return obj
120
+
121
+ def set_norm(self, norm):
122
+ """Sets norm of an already instantiated quaternion.
123
+
124
+ Parameters
125
+ ==========
126
+
127
+ norm : None or number
128
+ Pre-defined quaternion norm. If a value is given, Quaternion.norm
129
+ returns this pre-defined value instead of calculating the norm
130
+
131
+ Examples
132
+ ========
133
+
134
+ >>> from sympy import Quaternion
135
+ >>> from sympy.abc import a, b, c, d
136
+ >>> q = Quaternion(a, b, c, d)
137
+ >>> q.norm()
138
+ sqrt(a**2 + b**2 + c**2 + d**2)
139
+
140
+ Setting the norm:
141
+
142
+ >>> q.set_norm(1)
143
+ >>> q.norm()
144
+ 1
145
+
146
+ Removing set norm:
147
+
148
+ >>> q.set_norm(None)
149
+ >>> q.norm()
150
+ sqrt(a**2 + b**2 + c**2 + d**2)
151
+
152
+ """
153
+ norm = sympify(norm)
154
+ _check_norm(self.args, norm)
155
+ self._norm = norm
156
+
157
+ @property
158
+ def a(self):
159
+ return self.args[0]
160
+
161
+ @property
162
+ def b(self):
163
+ return self.args[1]
164
+
165
+ @property
166
+ def c(self):
167
+ return self.args[2]
168
+
169
+ @property
170
+ def d(self):
171
+ return self.args[3]
172
+
173
+ @property
174
+ def real_field(self):
175
+ return self._real_field
176
+
177
+ @property
178
+ def product_matrix_left(self):
179
+ r"""Returns 4 x 4 Matrix equivalent to a Hamilton product from the
180
+ left. This can be useful when treating quaternion elements as column
181
+ vectors. Given a quaternion $q = a + bi + cj + dk$ where a, b, c and d
182
+ are real numbers, the product matrix from the left is:
183
+
184
+ .. math::
185
+
186
+ M = \begin{bmatrix} a &-b &-c &-d \\
187
+ b & a &-d & c \\
188
+ c & d & a &-b \\
189
+ d &-c & b & a \end{bmatrix}
190
+
191
+ Examples
192
+ ========
193
+
194
+ >>> from sympy import Quaternion
195
+ >>> from sympy.abc import a, b, c, d
196
+ >>> q1 = Quaternion(1, 0, 0, 1)
197
+ >>> q2 = Quaternion(a, b, c, d)
198
+ >>> q1.product_matrix_left
199
+ Matrix([
200
+ [1, 0, 0, -1],
201
+ [0, 1, -1, 0],
202
+ [0, 1, 1, 0],
203
+ [1, 0, 0, 1]])
204
+
205
+ >>> q1.product_matrix_left * q2.to_Matrix()
206
+ Matrix([
207
+ [a - d],
208
+ [b - c],
209
+ [b + c],
210
+ [a + d]])
211
+
212
+ This is equivalent to:
213
+
214
+ >>> (q1 * q2).to_Matrix()
215
+ Matrix([
216
+ [a - d],
217
+ [b - c],
218
+ [b + c],
219
+ [a + d]])
220
+ """
221
+ return Matrix([
222
+ [self.a, -self.b, -self.c, -self.d],
223
+ [self.b, self.a, -self.d, self.c],
224
+ [self.c, self.d, self.a, -self.b],
225
+ [self.d, -self.c, self.b, self.a]])
226
+
227
+ @property
228
+ def product_matrix_right(self):
229
+ r"""Returns 4 x 4 Matrix equivalent to a Hamilton product from the
230
+ right. This can be useful when treating quaternion elements as column
231
+ vectors. Given a quaternion $q = a + bi + cj + dk$ where a, b, c and d
232
+ are real numbers, the product matrix from the left is:
233
+
234
+ .. math::
235
+
236
+ M = \begin{bmatrix} a &-b &-c &-d \\
237
+ b & a & d &-c \\
238
+ c &-d & a & b \\
239
+ d & c &-b & a \end{bmatrix}
240
+
241
+
242
+ Examples
243
+ ========
244
+
245
+ >>> from sympy import Quaternion
246
+ >>> from sympy.abc import a, b, c, d
247
+ >>> q1 = Quaternion(a, b, c, d)
248
+ >>> q2 = Quaternion(1, 0, 0, 1)
249
+ >>> q2.product_matrix_right
250
+ Matrix([
251
+ [1, 0, 0, -1],
252
+ [0, 1, 1, 0],
253
+ [0, -1, 1, 0],
254
+ [1, 0, 0, 1]])
255
+
256
+ Note the switched arguments: the matrix represents the quaternion on
257
+ the right, but is still considered as a matrix multiplication from the
258
+ left.
259
+
260
+ >>> q2.product_matrix_right * q1.to_Matrix()
261
+ Matrix([
262
+ [ a - d],
263
+ [ b + c],
264
+ [-b + c],
265
+ [ a + d]])
266
+
267
+ This is equivalent to:
268
+
269
+ >>> (q1 * q2).to_Matrix()
270
+ Matrix([
271
+ [ a - d],
272
+ [ b + c],
273
+ [-b + c],
274
+ [ a + d]])
275
+ """
276
+ return Matrix([
277
+ [self.a, -self.b, -self.c, -self.d],
278
+ [self.b, self.a, self.d, -self.c],
279
+ [self.c, -self.d, self.a, self.b],
280
+ [self.d, self.c, -self.b, self.a]])
281
+
282
+ def to_Matrix(self, vector_only=False):
283
+ """Returns elements of quaternion as a column vector.
284
+ By default, a ``Matrix`` of length 4 is returned, with the real part as the
285
+ first element.
286
+ If ``vector_only`` is ``True``, returns only imaginary part as a Matrix of
287
+ length 3.
288
+
289
+ Parameters
290
+ ==========
291
+
292
+ vector_only : bool
293
+ If True, only imaginary part is returned.
294
+ Default value: False
295
+
296
+ Returns
297
+ =======
298
+
299
+ Matrix
300
+ A column vector constructed by the elements of the quaternion.
301
+
302
+ Examples
303
+ ========
304
+
305
+ >>> from sympy import Quaternion
306
+ >>> from sympy.abc import a, b, c, d
307
+ >>> q = Quaternion(a, b, c, d)
308
+ >>> q
309
+ a + b*i + c*j + d*k
310
+
311
+ >>> q.to_Matrix()
312
+ Matrix([
313
+ [a],
314
+ [b],
315
+ [c],
316
+ [d]])
317
+
318
+
319
+ >>> q.to_Matrix(vector_only=True)
320
+ Matrix([
321
+ [b],
322
+ [c],
323
+ [d]])
324
+
325
+ """
326
+ if vector_only:
327
+ return Matrix(self.args[1:])
328
+ else:
329
+ return Matrix(self.args)
330
+
331
+ @classmethod
332
+ def from_Matrix(cls, elements):
333
+ """Returns quaternion from elements of a column vector`.
334
+ If vector_only is True, returns only imaginary part as a Matrix of
335
+ length 3.
336
+
337
+ Parameters
338
+ ==========
339
+
340
+ elements : Matrix, list or tuple of length 3 or 4. If length is 3,
341
+ assume real part is zero.
342
+ Default value: False
343
+
344
+ Returns
345
+ =======
346
+
347
+ Quaternion
348
+ A quaternion created from the input elements.
349
+
350
+ Examples
351
+ ========
352
+
353
+ >>> from sympy import Quaternion
354
+ >>> from sympy.abc import a, b, c, d
355
+ >>> q = Quaternion.from_Matrix([a, b, c, d])
356
+ >>> q
357
+ a + b*i + c*j + d*k
358
+
359
+ >>> q = Quaternion.from_Matrix([b, c, d])
360
+ >>> q
361
+ 0 + b*i + c*j + d*k
362
+
363
+ """
364
+ length = len(elements)
365
+ if length != 3 and length != 4:
366
+ raise ValueError("Input elements must have length 3 or 4, got {} "
367
+ "elements".format(length))
368
+
369
+ if length == 3:
370
+ return Quaternion(0, *elements)
371
+ else:
372
+ return Quaternion(*elements)
373
+
374
+ @classmethod
375
+ def from_euler(cls, angles, seq):
376
+ """Returns quaternion equivalent to rotation represented by the Euler
377
+ angles, in the sequence defined by ``seq``.
378
+
379
+ Parameters
380
+ ==========
381
+
382
+ angles : list, tuple or Matrix of 3 numbers
383
+ The Euler angles (in radians).
384
+ seq : string of length 3
385
+ Represents the sequence of rotations.
386
+ For extrinsic rotations, seq must be all lowercase and its elements
387
+ must be from the set ``{'x', 'y', 'z'}``
388
+ For intrinsic rotations, seq must be all uppercase and its elements
389
+ must be from the set ``{'X', 'Y', 'Z'}``
390
+
391
+ Returns
392
+ =======
393
+
394
+ Quaternion
395
+ The normalized rotation quaternion calculated from the Euler angles
396
+ in the given sequence.
397
+
398
+ Examples
399
+ ========
400
+
401
+ >>> from sympy import Quaternion
402
+ >>> from sympy import pi
403
+ >>> q = Quaternion.from_euler([pi/2, 0, 0], 'xyz')
404
+ >>> q
405
+ sqrt(2)/2 + sqrt(2)/2*i + 0*j + 0*k
406
+
407
+ >>> q = Quaternion.from_euler([0, pi/2, pi] , 'zyz')
408
+ >>> q
409
+ 0 + (-sqrt(2)/2)*i + 0*j + sqrt(2)/2*k
410
+
411
+ >>> q = Quaternion.from_euler([0, pi/2, pi] , 'ZYZ')
412
+ >>> q
413
+ 0 + sqrt(2)/2*i + 0*j + sqrt(2)/2*k
414
+
415
+ """
416
+
417
+ if len(angles) != 3:
418
+ raise ValueError("3 angles must be given.")
419
+
420
+ extrinsic = _is_extrinsic(seq)
421
+ i, j, k = seq.lower()
422
+
423
+ # get elementary basis vectors
424
+ ei = [1 if n == i else 0 for n in 'xyz']
425
+ ej = [1 if n == j else 0 for n in 'xyz']
426
+ ek = [1 if n == k else 0 for n in 'xyz']
427
+
428
+ # calculate distinct quaternions
429
+ qi = cls.from_axis_angle(ei, angles[0])
430
+ qj = cls.from_axis_angle(ej, angles[1])
431
+ qk = cls.from_axis_angle(ek, angles[2])
432
+
433
+ if extrinsic:
434
+ return trigsimp(qk * qj * qi)
435
+ else:
436
+ return trigsimp(qi * qj * qk)
437
+
438
+ def to_euler(self, seq, angle_addition=True, avoid_square_root=False):
439
+ r"""Returns Euler angles representing same rotation as the quaternion,
440
+ in the sequence given by ``seq``. This implements the method described
441
+ in [1]_.
442
+
443
+ For degenerate cases (gymbal lock cases), the third angle is
444
+ set to zero.
445
+
446
+ Parameters
447
+ ==========
448
+
449
+ seq : string of length 3
450
+ Represents the sequence of rotations.
451
+ For extrinsic rotations, seq must be all lowercase and its elements
452
+ must be from the set ``{'x', 'y', 'z'}``
453
+ For intrinsic rotations, seq must be all uppercase and its elements
454
+ must be from the set ``{'X', 'Y', 'Z'}``
455
+
456
+ angle_addition : bool
457
+ When True, first and third angles are given as an addition and
458
+ subtraction of two simpler ``atan2`` expressions. When False, the
459
+ first and third angles are each given by a single more complicated
460
+ ``atan2`` expression. This equivalent expression is given by:
461
+
462
+ .. math::
463
+
464
+ \operatorname{atan_2} (b,a) \pm \operatorname{atan_2} (d,c) =
465
+ \operatorname{atan_2} (bc\pm ad, ac\mp bd)
466
+
467
+ Default value: True
468
+
469
+ avoid_square_root : bool
470
+ When True, the second angle is calculated with an expression based
471
+ on ``acos``, which is slightly more complicated but avoids a square
472
+ root. When False, second angle is calculated with ``atan2``, which
473
+ is simpler and can be better for numerical reasons (some
474
+ numerical implementations of ``acos`` have problems near zero).
475
+ Default value: False
476
+
477
+
478
+ Returns
479
+ =======
480
+
481
+ Tuple
482
+ The Euler angles calculated from the quaternion
483
+
484
+ Examples
485
+ ========
486
+
487
+ >>> from sympy import Quaternion
488
+ >>> from sympy.abc import a, b, c, d
489
+ >>> euler = Quaternion(a, b, c, d).to_euler('zyz')
490
+ >>> euler
491
+ (-atan2(-b, c) + atan2(d, a),
492
+ 2*atan2(sqrt(b**2 + c**2), sqrt(a**2 + d**2)),
493
+ atan2(-b, c) + atan2(d, a))
494
+
495
+
496
+ References
497
+ ==========
498
+
499
+ .. [1] https://doi.org/10.1371/journal.pone.0276302
500
+
501
+ """
502
+ if self.is_zero_quaternion():
503
+ raise ValueError('Cannot convert a quaternion with norm 0.')
504
+
505
+ angles = [0, 0, 0]
506
+
507
+ extrinsic = _is_extrinsic(seq)
508
+ i, j, k = seq.lower()
509
+
510
+ # get index corresponding to elementary basis vectors
511
+ i = 'xyz'.index(i) + 1
512
+ j = 'xyz'.index(j) + 1
513
+ k = 'xyz'.index(k) + 1
514
+
515
+ if not extrinsic:
516
+ i, k = k, i
517
+
518
+ # check if sequence is symmetric
519
+ symmetric = i == k
520
+ if symmetric:
521
+ k = 6 - i - j
522
+
523
+ # parity of the permutation
524
+ sign = (i - j) * (j - k) * (k - i) // 2
525
+
526
+ # permutate elements
527
+ elements = [self.a, self.b, self.c, self.d]
528
+ a = elements[0]
529
+ b = elements[i]
530
+ c = elements[j]
531
+ d = elements[k] * sign
532
+
533
+ if not symmetric:
534
+ a, b, c, d = a - c, b + d, c + a, d - b
535
+
536
+ if avoid_square_root:
537
+ if symmetric:
538
+ n2 = self.norm()**2
539
+ angles[1] = acos((a * a + b * b - c * c - d * d) / n2)
540
+ else:
541
+ n2 = 2 * self.norm()**2
542
+ angles[1] = asin((c * c + d * d - a * a - b * b) / n2)
543
+ else:
544
+ angles[1] = 2 * atan2(sqrt(c * c + d * d), sqrt(a * a + b * b))
545
+ if not symmetric:
546
+ angles[1] -= S.Pi / 2
547
+
548
+ # Check for singularities in numerical cases
549
+ case = 0
550
+ if is_eq(c, S.Zero) and is_eq(d, S.Zero):
551
+ case = 1
552
+ if is_eq(a, S.Zero) and is_eq(b, S.Zero):
553
+ case = 2
554
+
555
+ if case == 0:
556
+ if angle_addition:
557
+ angles[0] = atan2(b, a) + atan2(d, c)
558
+ angles[2] = atan2(b, a) - atan2(d, c)
559
+ else:
560
+ angles[0] = atan2(b*c + a*d, a*c - b*d)
561
+ angles[2] = atan2(b*c - a*d, a*c + b*d)
562
+
563
+ else: # any degenerate case
564
+ angles[2 * (not extrinsic)] = S.Zero
565
+ if case == 1:
566
+ angles[2 * extrinsic] = 2 * atan2(b, a)
567
+ else:
568
+ angles[2 * extrinsic] = 2 * atan2(d, c)
569
+ angles[2 * extrinsic] *= (-1 if extrinsic else 1)
570
+
571
+ # for Tait-Bryan angles
572
+ if not symmetric:
573
+ angles[0] *= sign
574
+
575
+ if extrinsic:
576
+ return tuple(angles[::-1])
577
+ else:
578
+ return tuple(angles)
579
+
580
+ @classmethod
581
+ def from_axis_angle(cls, vector, angle):
582
+ """Returns a rotation quaternion given the axis and the angle of rotation.
583
+
584
+ Parameters
585
+ ==========
586
+
587
+ vector : tuple of three numbers
588
+ The vector representation of the given axis.
589
+ angle : number
590
+ The angle by which axis is rotated (in radians).
591
+
592
+ Returns
593
+ =======
594
+
595
+ Quaternion
596
+ The normalized rotation quaternion calculated from the given axis and the angle of rotation.
597
+
598
+ Examples
599
+ ========
600
+
601
+ >>> from sympy import Quaternion
602
+ >>> from sympy import pi, sqrt
603
+ >>> q = Quaternion.from_axis_angle((sqrt(3)/3, sqrt(3)/3, sqrt(3)/3), 2*pi/3)
604
+ >>> q
605
+ 1/2 + 1/2*i + 1/2*j + 1/2*k
606
+
607
+ """
608
+ (x, y, z) = vector
609
+ norm = sqrt(x**2 + y**2 + z**2)
610
+ (x, y, z) = (x / norm, y / norm, z / norm)
611
+ s = sin(angle * S.Half)
612
+ a = cos(angle * S.Half)
613
+ b = x * s
614
+ c = y * s
615
+ d = z * s
616
+
617
+ # note that this quaternion is already normalized by construction:
618
+ # c^2 + (s*x)^2 + (s*y)^2 + (s*z)^2 = c^2 + s^2*(x^2 + y^2 + z^2) = c^2 + s^2 * 1 = c^2 + s^2 = 1
619
+ # so, what we return is a normalized quaternion
620
+
621
+ return cls(a, b, c, d)
622
+
623
+ @classmethod
624
+ def from_rotation_matrix(cls, M):
625
+ """Returns the equivalent quaternion of a matrix. The quaternion will be normalized
626
+ only if the matrix is special orthogonal (orthogonal and det(M) = 1).
627
+
628
+ Parameters
629
+ ==========
630
+
631
+ M : Matrix
632
+ Input matrix to be converted to equivalent quaternion. M must be special
633
+ orthogonal (orthogonal and det(M) = 1) for the quaternion to be normalized.
634
+
635
+ Returns
636
+ =======
637
+
638
+ Quaternion
639
+ The quaternion equivalent to given matrix.
640
+
641
+ Examples
642
+ ========
643
+
644
+ >>> from sympy import Quaternion
645
+ >>> from sympy import Matrix, symbols, cos, sin, trigsimp
646
+ >>> x = symbols('x')
647
+ >>> M = Matrix([[cos(x), -sin(x), 0], [sin(x), cos(x), 0], [0, 0, 1]])
648
+ >>> q = trigsimp(Quaternion.from_rotation_matrix(M))
649
+ >>> q
650
+ sqrt(2)*sqrt(cos(x) + 1)/2 + 0*i + 0*j + sqrt(2 - 2*cos(x))*sign(sin(x))/2*k
651
+
652
+ """
653
+
654
+ absQ = M.det()**Rational(1, 3)
655
+
656
+ a = sqrt(absQ + M[0, 0] + M[1, 1] + M[2, 2]) / 2
657
+ b = sqrt(absQ + M[0, 0] - M[1, 1] - M[2, 2]) / 2
658
+ c = sqrt(absQ - M[0, 0] + M[1, 1] - M[2, 2]) / 2
659
+ d = sqrt(absQ - M[0, 0] - M[1, 1] + M[2, 2]) / 2
660
+
661
+ b = b * sign(M[2, 1] - M[1, 2])
662
+ c = c * sign(M[0, 2] - M[2, 0])
663
+ d = d * sign(M[1, 0] - M[0, 1])
664
+
665
+ return Quaternion(a, b, c, d)
666
+
667
+ def __add__(self, other):
668
+ return self.add(other)
669
+
670
+ def __radd__(self, other):
671
+ return self.add(other)
672
+
673
+ def __sub__(self, other):
674
+ return self.add(other*-1)
675
+
676
+ def __mul__(self, other):
677
+ return self._generic_mul(self, _sympify(other))
678
+
679
+ def __rmul__(self, other):
680
+ return self._generic_mul(_sympify(other), self)
681
+
682
+ def __pow__(self, p):
683
+ return self.pow(p)
684
+
685
+ def __neg__(self):
686
+ return Quaternion(-self.a, -self.b, -self.c, -self.d)
687
+
688
+ def __truediv__(self, other):
689
+ return self * sympify(other)**-1
690
+
691
+ def __rtruediv__(self, other):
692
+ return sympify(other) * self**-1
693
+
694
+ def _eval_Integral(self, *args):
695
+ return self.integrate(*args)
696
+
697
+ def diff(self, *symbols, **kwargs):
698
+ kwargs.setdefault('evaluate', True)
699
+ return self.func(*[a.diff(*symbols, **kwargs) for a in self.args])
700
+
701
+ def add(self, other):
702
+ """Adds quaternions.
703
+
704
+ Parameters
705
+ ==========
706
+
707
+ other : Quaternion
708
+ The quaternion to add to current (self) quaternion.
709
+
710
+ Returns
711
+ =======
712
+
713
+ Quaternion
714
+ The resultant quaternion after adding self to other
715
+
716
+ Examples
717
+ ========
718
+
719
+ >>> from sympy import Quaternion
720
+ >>> from sympy import symbols
721
+ >>> q1 = Quaternion(1, 2, 3, 4)
722
+ >>> q2 = Quaternion(5, 6, 7, 8)
723
+ >>> q1.add(q2)
724
+ 6 + 8*i + 10*j + 12*k
725
+ >>> q1 + 5
726
+ 6 + 2*i + 3*j + 4*k
727
+ >>> x = symbols('x', real = True)
728
+ >>> q1.add(x)
729
+ (x + 1) + 2*i + 3*j + 4*k
730
+
731
+ Quaternions over complex fields :
732
+
733
+ >>> from sympy import Quaternion
734
+ >>> from sympy import I
735
+ >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
736
+ >>> q3.add(2 + 3*I)
737
+ (5 + 7*I) + (2 + 5*I)*i + 0*j + (7 + 8*I)*k
738
+
739
+ """
740
+ q1 = self
741
+ q2 = sympify(other)
742
+
743
+ # If q2 is a number or a SymPy expression instead of a quaternion
744
+ if not isinstance(q2, Quaternion):
745
+ if q1.real_field and q2.is_complex:
746
+ return Quaternion(re(q2) + q1.a, im(q2) + q1.b, q1.c, q1.d)
747
+ elif q2.is_commutative:
748
+ return Quaternion(q1.a + q2, q1.b, q1.c, q1.d)
749
+ else:
750
+ raise ValueError("Only commutative expressions can be added with a Quaternion.")
751
+
752
+ return Quaternion(q1.a + q2.a, q1.b + q2.b, q1.c + q2.c, q1.d
753
+ + q2.d)
754
+
755
+ def mul(self, other):
756
+ """Multiplies quaternions.
757
+
758
+ Parameters
759
+ ==========
760
+
761
+ other : Quaternion or symbol
762
+ The quaternion to multiply to current (self) quaternion.
763
+
764
+ Returns
765
+ =======
766
+
767
+ Quaternion
768
+ The resultant quaternion after multiplying self with other
769
+
770
+ Examples
771
+ ========
772
+
773
+ >>> from sympy import Quaternion
774
+ >>> from sympy import symbols
775
+ >>> q1 = Quaternion(1, 2, 3, 4)
776
+ >>> q2 = Quaternion(5, 6, 7, 8)
777
+ >>> q1.mul(q2)
778
+ (-60) + 12*i + 30*j + 24*k
779
+ >>> q1.mul(2)
780
+ 2 + 4*i + 6*j + 8*k
781
+ >>> x = symbols('x', real = True)
782
+ >>> q1.mul(x)
783
+ x + 2*x*i + 3*x*j + 4*x*k
784
+
785
+ Quaternions over complex fields :
786
+
787
+ >>> from sympy import Quaternion
788
+ >>> from sympy import I
789
+ >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
790
+ >>> q3.mul(2 + 3*I)
791
+ (2 + 3*I)*(3 + 4*I) + (2 + 3*I)*(2 + 5*I)*i + 0*j + (2 + 3*I)*(7 + 8*I)*k
792
+
793
+ """
794
+ return self._generic_mul(self, _sympify(other))
795
+
796
+ @staticmethod
797
+ def _generic_mul(q1, q2):
798
+ """Generic multiplication.
799
+
800
+ Parameters
801
+ ==========
802
+
803
+ q1 : Quaternion or symbol
804
+ q2 : Quaternion or symbol
805
+
806
+ It is important to note that if neither q1 nor q2 is a Quaternion,
807
+ this function simply returns q1 * q2.
808
+
809
+ Returns
810
+ =======
811
+
812
+ Quaternion
813
+ The resultant quaternion after multiplying q1 and q2
814
+
815
+ Examples
816
+ ========
817
+
818
+ >>> from sympy import Quaternion
819
+ >>> from sympy import Symbol, S
820
+ >>> q1 = Quaternion(1, 2, 3, 4)
821
+ >>> q2 = Quaternion(5, 6, 7, 8)
822
+ >>> Quaternion._generic_mul(q1, q2)
823
+ (-60) + 12*i + 30*j + 24*k
824
+ >>> Quaternion._generic_mul(q1, S(2))
825
+ 2 + 4*i + 6*j + 8*k
826
+ >>> x = Symbol('x', real = True)
827
+ >>> Quaternion._generic_mul(q1, x)
828
+ x + 2*x*i + 3*x*j + 4*x*k
829
+
830
+ Quaternions over complex fields :
831
+
832
+ >>> from sympy import I
833
+ >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
834
+ >>> Quaternion._generic_mul(q3, 2 + 3*I)
835
+ (2 + 3*I)*(3 + 4*I) + (2 + 3*I)*(2 + 5*I)*i + 0*j + (2 + 3*I)*(7 + 8*I)*k
836
+
837
+ """
838
+ # None is a Quaternion:
839
+ if not isinstance(q1, Quaternion) and not isinstance(q2, Quaternion):
840
+ return q1 * q2
841
+
842
+ # If q1 is a number or a SymPy expression instead of a quaternion
843
+ if not isinstance(q1, Quaternion):
844
+ if q2.real_field and q1.is_complex:
845
+ return Quaternion(re(q1), im(q1), 0, 0) * q2
846
+ elif q1.is_commutative:
847
+ return Quaternion(q1 * q2.a, q1 * q2.b, q1 * q2.c, q1 * q2.d)
848
+ else:
849
+ raise ValueError("Only commutative expressions can be multiplied with a Quaternion.")
850
+
851
+ # If q2 is a number or a SymPy expression instead of a quaternion
852
+ if not isinstance(q2, Quaternion):
853
+ if q1.real_field and q2.is_complex:
854
+ return q1 * Quaternion(re(q2), im(q2), 0, 0)
855
+ elif q2.is_commutative:
856
+ return Quaternion(q2 * q1.a, q2 * q1.b, q2 * q1.c, q2 * q1.d)
857
+ else:
858
+ raise ValueError("Only commutative expressions can be multiplied with a Quaternion.")
859
+
860
+ # If any of the quaternions has a fixed norm, pre-compute norm
861
+ if q1._norm is None and q2._norm is None:
862
+ norm = None
863
+ else:
864
+ norm = q1.norm() * q2.norm()
865
+
866
+ return Quaternion(-q1.b*q2.b - q1.c*q2.c - q1.d*q2.d + q1.a*q2.a,
867
+ q1.b*q2.a + q1.c*q2.d - q1.d*q2.c + q1.a*q2.b,
868
+ -q1.b*q2.d + q1.c*q2.a + q1.d*q2.b + q1.a*q2.c,
869
+ q1.b*q2.c - q1.c*q2.b + q1.d*q2.a + q1.a * q2.d,
870
+ norm=norm)
871
+
872
+ def _eval_conjugate(self):
873
+ """Returns the conjugate of the quaternion."""
874
+ q = self
875
+ return Quaternion(q.a, -q.b, -q.c, -q.d, norm=q._norm)
876
+
877
+ def norm(self):
878
+ """Returns the norm of the quaternion."""
879
+ if self._norm is None: # check if norm is pre-defined
880
+ q = self
881
+ # trigsimp is used to simplify sin(x)^2 + cos(x)^2 (these terms
882
+ # arise when from_axis_angle is used).
883
+ return sqrt(trigsimp(q.a**2 + q.b**2 + q.c**2 + q.d**2))
884
+
885
+ return self._norm
886
+
887
+ def normalize(self):
888
+ """Returns the normalized form of the quaternion."""
889
+ q = self
890
+ return q * (1/q.norm())
891
+
892
+ def inverse(self):
893
+ """Returns the inverse of the quaternion."""
894
+ q = self
895
+ if not q.norm():
896
+ raise ValueError("Cannot compute inverse for a quaternion with zero norm")
897
+ return conjugate(q) * (1/q.norm()**2)
898
+
899
+ def pow(self, p):
900
+ """Finds the pth power of the quaternion.
901
+
902
+ Parameters
903
+ ==========
904
+
905
+ p : int
906
+ Power to be applied on quaternion.
907
+
908
+ Returns
909
+ =======
910
+
911
+ Quaternion
912
+ Returns the p-th power of the current quaternion.
913
+ Returns the inverse if p = -1.
914
+
915
+ Examples
916
+ ========
917
+
918
+ >>> from sympy import Quaternion
919
+ >>> q = Quaternion(1, 2, 3, 4)
920
+ >>> q.pow(4)
921
+ 668 + (-224)*i + (-336)*j + (-448)*k
922
+
923
+ """
924
+ try:
925
+ q, p = self, as_int(p)
926
+ except ValueError:
927
+ return NotImplemented
928
+
929
+ if p < 0:
930
+ q, p = q.inverse(), -p
931
+
932
+ if p == 1:
933
+ return q
934
+
935
+ res = Quaternion(1, 0, 0, 0)
936
+ while p > 0:
937
+ if p & 1:
938
+ res *= q
939
+ q *= q
940
+ p >>= 1
941
+
942
+ return res
943
+
944
+ def exp(self):
945
+ """Returns the exponential of $q$, given by $e^q$.
946
+
947
+ Returns
948
+ =======
949
+
950
+ Quaternion
951
+ The exponential of the quaternion.
952
+
953
+ Examples
954
+ ========
955
+
956
+ >>> from sympy import Quaternion
957
+ >>> q = Quaternion(1, 2, 3, 4)
958
+ >>> q.exp()
959
+ E*cos(sqrt(29))
960
+ + 2*sqrt(29)*E*sin(sqrt(29))/29*i
961
+ + 3*sqrt(29)*E*sin(sqrt(29))/29*j
962
+ + 4*sqrt(29)*E*sin(sqrt(29))/29*k
963
+
964
+ """
965
+ # exp(q) = e^a(cos||v|| + v/||v||*sin||v||)
966
+ q = self
967
+ vector_norm = sqrt(q.b**2 + q.c**2 + q.d**2)
968
+ a = exp(q.a) * cos(vector_norm)
969
+ b = exp(q.a) * sin(vector_norm) * q.b / vector_norm
970
+ c = exp(q.a) * sin(vector_norm) * q.c / vector_norm
971
+ d = exp(q.a) * sin(vector_norm) * q.d / vector_norm
972
+
973
+ return Quaternion(a, b, c, d)
974
+
975
+ def log(self):
976
+ r"""Returns the logarithm of the quaternion, given by $\log q$.
977
+
978
+ Examples
979
+ ========
980
+
981
+ >>> from sympy import Quaternion
982
+ >>> q = Quaternion(1, 2, 3, 4)
983
+ >>> q.log()
984
+ log(sqrt(30))
985
+ + 2*sqrt(29)*acos(sqrt(30)/30)/29*i
986
+ + 3*sqrt(29)*acos(sqrt(30)/30)/29*j
987
+ + 4*sqrt(29)*acos(sqrt(30)/30)/29*k
988
+
989
+ """
990
+ # log(q) = log||q|| + v/||v||*arccos(a/||q||)
991
+ q = self
992
+ vector_norm = sqrt(q.b**2 + q.c**2 + q.d**2)
993
+ q_norm = q.norm()
994
+ a = ln(q_norm)
995
+ b = q.b * acos(q.a / q_norm) / vector_norm
996
+ c = q.c * acos(q.a / q_norm) / vector_norm
997
+ d = q.d * acos(q.a / q_norm) / vector_norm
998
+
999
+ return Quaternion(a, b, c, d)
1000
+
1001
+ def _eval_subs(self, *args):
1002
+ elements = [i.subs(*args) for i in self.args]
1003
+ norm = self._norm
1004
+ if norm is not None:
1005
+ norm = norm.subs(*args)
1006
+ _check_norm(elements, norm)
1007
+ return Quaternion(*elements, norm=norm)
1008
+
1009
+ def _eval_evalf(self, prec):
1010
+ """Returns the floating point approximations (decimal numbers) of the quaternion.
1011
+
1012
+ Returns
1013
+ =======
1014
+
1015
+ Quaternion
1016
+ Floating point approximations of quaternion(self)
1017
+
1018
+ Examples
1019
+ ========
1020
+
1021
+ >>> from sympy import Quaternion
1022
+ >>> from sympy import sqrt
1023
+ >>> q = Quaternion(1/sqrt(1), 1/sqrt(2), 1/sqrt(3), 1/sqrt(4))
1024
+ >>> q.evalf()
1025
+ 1.00000000000000
1026
+ + 0.707106781186547*i
1027
+ + 0.577350269189626*j
1028
+ + 0.500000000000000*k
1029
+
1030
+ """
1031
+ nprec = prec_to_dps(prec)
1032
+ return Quaternion(*[arg.evalf(n=nprec) for arg in self.args])
1033
+
1034
+ def pow_cos_sin(self, p):
1035
+ """Computes the pth power in the cos-sin form.
1036
+
1037
+ Parameters
1038
+ ==========
1039
+
1040
+ p : int
1041
+ Power to be applied on quaternion.
1042
+
1043
+ Returns
1044
+ =======
1045
+
1046
+ Quaternion
1047
+ The p-th power in the cos-sin form.
1048
+
1049
+ Examples
1050
+ ========
1051
+
1052
+ >>> from sympy import Quaternion
1053
+ >>> q = Quaternion(1, 2, 3, 4)
1054
+ >>> q.pow_cos_sin(4)
1055
+ 900*cos(4*acos(sqrt(30)/30))
1056
+ + 1800*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*i
1057
+ + 2700*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*j
1058
+ + 3600*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*k
1059
+
1060
+ """
1061
+ # q = ||q||*(cos(a) + u*sin(a))
1062
+ # q^p = ||q||^p * (cos(p*a) + u*sin(p*a))
1063
+
1064
+ q = self
1065
+ (v, angle) = q.to_axis_angle()
1066
+ q2 = Quaternion.from_axis_angle(v, p * angle)
1067
+ return q2 * (q.norm()**p)
1068
+
1069
+ def integrate(self, *args):
1070
+ """Computes integration of quaternion.
1071
+
1072
+ Returns
1073
+ =======
1074
+
1075
+ Quaternion
1076
+ Integration of the quaternion(self) with the given variable.
1077
+
1078
+ Examples
1079
+ ========
1080
+
1081
+ Indefinite Integral of quaternion :
1082
+
1083
+ >>> from sympy import Quaternion
1084
+ >>> from sympy.abc import x
1085
+ >>> q = Quaternion(1, 2, 3, 4)
1086
+ >>> q.integrate(x)
1087
+ x + 2*x*i + 3*x*j + 4*x*k
1088
+
1089
+ Definite integral of quaternion :
1090
+
1091
+ >>> from sympy import Quaternion
1092
+ >>> from sympy.abc import x
1093
+ >>> q = Quaternion(1, 2, 3, 4)
1094
+ >>> q.integrate((x, 1, 5))
1095
+ 4 + 8*i + 12*j + 16*k
1096
+
1097
+ """
1098
+ # TODO: is this expression correct?
1099
+ return Quaternion(integrate(self.a, *args), integrate(self.b, *args),
1100
+ integrate(self.c, *args), integrate(self.d, *args))
1101
+
1102
+ @staticmethod
1103
+ def rotate_point(pin, r):
1104
+ """Returns the coordinates of the point pin (a 3 tuple) after rotation.
1105
+
1106
+ Parameters
1107
+ ==========
1108
+
1109
+ pin : tuple
1110
+ A 3-element tuple of coordinates of a point which needs to be
1111
+ rotated.
1112
+ r : Quaternion or tuple
1113
+ Axis and angle of rotation.
1114
+
1115
+ It's important to note that when r is a tuple, it must be of the form
1116
+ (axis, angle)
1117
+
1118
+ Returns
1119
+ =======
1120
+
1121
+ tuple
1122
+ The coordinates of the point after rotation.
1123
+
1124
+ Examples
1125
+ ========
1126
+
1127
+ >>> from sympy import Quaternion
1128
+ >>> from sympy import symbols, trigsimp, cos, sin
1129
+ >>> x = symbols('x')
1130
+ >>> q = Quaternion(cos(x/2), 0, 0, sin(x/2))
1131
+ >>> trigsimp(Quaternion.rotate_point((1, 1, 1), q))
1132
+ (sqrt(2)*cos(x + pi/4), sqrt(2)*sin(x + pi/4), 1)
1133
+ >>> (axis, angle) = q.to_axis_angle()
1134
+ >>> trigsimp(Quaternion.rotate_point((1, 1, 1), (axis, angle)))
1135
+ (sqrt(2)*cos(x + pi/4), sqrt(2)*sin(x + pi/4), 1)
1136
+
1137
+ """
1138
+ if isinstance(r, tuple):
1139
+ # if r is of the form (vector, angle)
1140
+ q = Quaternion.from_axis_angle(r[0], r[1])
1141
+ else:
1142
+ # if r is a quaternion
1143
+ q = r.normalize()
1144
+ pout = q * Quaternion(0, pin[0], pin[1], pin[2]) * conjugate(q)
1145
+ return (pout.b, pout.c, pout.d)
1146
+
1147
+ def to_axis_angle(self):
1148
+ """Returns the axis and angle of rotation of a quaternion.
1149
+
1150
+ Returns
1151
+ =======
1152
+
1153
+ tuple
1154
+ Tuple of (axis, angle)
1155
+
1156
+ Examples
1157
+ ========
1158
+
1159
+ >>> from sympy import Quaternion
1160
+ >>> q = Quaternion(1, 1, 1, 1)
1161
+ >>> (axis, angle) = q.to_axis_angle()
1162
+ >>> axis
1163
+ (sqrt(3)/3, sqrt(3)/3, sqrt(3)/3)
1164
+ >>> angle
1165
+ 2*pi/3
1166
+
1167
+ """
1168
+ q = self
1169
+ if q.a.is_negative:
1170
+ q = q * -1
1171
+
1172
+ q = q.normalize()
1173
+ angle = trigsimp(2 * acos(q.a))
1174
+
1175
+ # Since quaternion is normalised, q.a is less than 1.
1176
+ s = sqrt(1 - q.a*q.a)
1177
+
1178
+ x = trigsimp(q.b / s)
1179
+ y = trigsimp(q.c / s)
1180
+ z = trigsimp(q.d / s)
1181
+
1182
+ v = (x, y, z)
1183
+ t = (v, angle)
1184
+
1185
+ return t
1186
+
1187
+ def to_rotation_matrix(self, v=None, homogeneous=True):
1188
+ """Returns the equivalent rotation transformation matrix of the quaternion
1189
+ which represents rotation about the origin if ``v`` is not passed.
1190
+
1191
+ Parameters
1192
+ ==========
1193
+
1194
+ v : tuple or None
1195
+ Default value: None
1196
+ homogeneous : bool
1197
+ When True, gives an expression that may be more efficient for
1198
+ symbolic calculations but less so for direct evaluation. Both
1199
+ formulas are mathematically equivalent.
1200
+ Default value: True
1201
+
1202
+ Returns
1203
+ =======
1204
+
1205
+ tuple
1206
+ Returns the equivalent rotation transformation matrix of the quaternion
1207
+ which represents rotation about the origin if v is not passed.
1208
+
1209
+ Examples
1210
+ ========
1211
+
1212
+ >>> from sympy import Quaternion
1213
+ >>> from sympy import symbols, trigsimp, cos, sin
1214
+ >>> x = symbols('x')
1215
+ >>> q = Quaternion(cos(x/2), 0, 0, sin(x/2))
1216
+ >>> trigsimp(q.to_rotation_matrix())
1217
+ Matrix([
1218
+ [cos(x), -sin(x), 0],
1219
+ [sin(x), cos(x), 0],
1220
+ [ 0, 0, 1]])
1221
+
1222
+ Generates a 4x4 transformation matrix (used for rotation about a point
1223
+ other than the origin) if the point(v) is passed as an argument.
1224
+ """
1225
+
1226
+ q = self
1227
+ s = q.norm()**-2
1228
+
1229
+ # diagonal elements are different according to parameter normal
1230
+ if homogeneous:
1231
+ m00 = s*(q.a**2 + q.b**2 - q.c**2 - q.d**2)
1232
+ m11 = s*(q.a**2 - q.b**2 + q.c**2 - q.d**2)
1233
+ m22 = s*(q.a**2 - q.b**2 - q.c**2 + q.d**2)
1234
+ else:
1235
+ m00 = 1 - 2*s*(q.c**2 + q.d**2)
1236
+ m11 = 1 - 2*s*(q.b**2 + q.d**2)
1237
+ m22 = 1 - 2*s*(q.b**2 + q.c**2)
1238
+
1239
+ m01 = 2*s*(q.b*q.c - q.d*q.a)
1240
+ m02 = 2*s*(q.b*q.d + q.c*q.a)
1241
+
1242
+ m10 = 2*s*(q.b*q.c + q.d*q.a)
1243
+ m12 = 2*s*(q.c*q.d - q.b*q.a)
1244
+
1245
+ m20 = 2*s*(q.b*q.d - q.c*q.a)
1246
+ m21 = 2*s*(q.c*q.d + q.b*q.a)
1247
+
1248
+ if not v:
1249
+ return Matrix([[m00, m01, m02], [m10, m11, m12], [m20, m21, m22]])
1250
+
1251
+ else:
1252
+ (x, y, z) = v
1253
+
1254
+ m03 = x - x*m00 - y*m01 - z*m02
1255
+ m13 = y - x*m10 - y*m11 - z*m12
1256
+ m23 = z - x*m20 - y*m21 - z*m22
1257
+ m30 = m31 = m32 = 0
1258
+ m33 = 1
1259
+
1260
+ return Matrix([[m00, m01, m02, m03], [m10, m11, m12, m13],
1261
+ [m20, m21, m22, m23], [m30, m31, m32, m33]])
1262
+
1263
+ def scalar_part(self):
1264
+ r"""Returns scalar part($\mathbf{S}(q)$) of the quaternion q.
1265
+
1266
+ Explanation
1267
+ ===========
1268
+
1269
+ Given a quaternion $q = a + bi + cj + dk$, returns $\mathbf{S}(q) = a$.
1270
+
1271
+ Examples
1272
+ ========
1273
+
1274
+ >>> from sympy.algebras.quaternion import Quaternion
1275
+ >>> q = Quaternion(4, 8, 13, 12)
1276
+ >>> q.scalar_part()
1277
+ 4
1278
+
1279
+ """
1280
+
1281
+ return self.a
1282
+
1283
+ def vector_part(self):
1284
+ r"""
1285
+ Returns $\mathbf{V}(q)$, the vector part of the quaternion $q$.
1286
+
1287
+ Explanation
1288
+ ===========
1289
+
1290
+ Given a quaternion $q = a + bi + cj + dk$, returns $\mathbf{V}(q) = bi + cj + dk$.
1291
+
1292
+ Examples
1293
+ ========
1294
+
1295
+ >>> from sympy.algebras.quaternion import Quaternion
1296
+ >>> q = Quaternion(1, 1, 1, 1)
1297
+ >>> q.vector_part()
1298
+ 0 + 1*i + 1*j + 1*k
1299
+
1300
+ >>> q = Quaternion(4, 8, 13, 12)
1301
+ >>> q.vector_part()
1302
+ 0 + 8*i + 13*j + 12*k
1303
+
1304
+ """
1305
+
1306
+ return Quaternion(0, self.b, self.c, self.d)
1307
+
1308
+ def axis(self):
1309
+ r"""
1310
+ Returns $\mathbf{Ax}(q)$, the axis of the quaternion $q$.
1311
+
1312
+ Explanation
1313
+ ===========
1314
+
1315
+ Given a quaternion $q = a + bi + cj + dk$, returns $\mathbf{Ax}(q)$ i.e., the versor of the vector part of that quaternion
1316
+ equal to $\mathbf{U}[\mathbf{V}(q)]$.
1317
+ The axis is always an imaginary unit with square equal to $-1 + 0i + 0j + 0k$.
1318
+
1319
+ Examples
1320
+ ========
1321
+
1322
+ >>> from sympy.algebras.quaternion import Quaternion
1323
+ >>> q = Quaternion(1, 1, 1, 1)
1324
+ >>> q.axis()
1325
+ 0 + sqrt(3)/3*i + sqrt(3)/3*j + sqrt(3)/3*k
1326
+
1327
+ See Also
1328
+ ========
1329
+
1330
+ vector_part
1331
+
1332
+ """
1333
+ axis = self.vector_part().normalize()
1334
+
1335
+ return Quaternion(0, axis.b, axis.c, axis.d)
1336
+
1337
+ def is_pure(self):
1338
+ """
1339
+ Returns true if the quaternion is pure, false if the quaternion is not pure
1340
+ or returns none if it is unknown.
1341
+
1342
+ Explanation
1343
+ ===========
1344
+
1345
+ A pure quaternion (also a vector quaternion) is a quaternion with scalar
1346
+ part equal to 0.
1347
+
1348
+ Examples
1349
+ ========
1350
+
1351
+ >>> from sympy.algebras.quaternion import Quaternion
1352
+ >>> q = Quaternion(0, 8, 13, 12)
1353
+ >>> q.is_pure()
1354
+ True
1355
+
1356
+ See Also
1357
+ ========
1358
+ scalar_part
1359
+
1360
+ """
1361
+
1362
+ return self.a.is_zero
1363
+
1364
+ def is_zero_quaternion(self):
1365
+ """
1366
+ Returns true if the quaternion is a zero quaternion or false if it is not a zero quaternion
1367
+ and None if the value is unknown.
1368
+
1369
+ Explanation
1370
+ ===========
1371
+
1372
+ A zero quaternion is a quaternion with both scalar part and
1373
+ vector part equal to 0.
1374
+
1375
+ Examples
1376
+ ========
1377
+
1378
+ >>> from sympy.algebras.quaternion import Quaternion
1379
+ >>> q = Quaternion(1, 0, 0, 0)
1380
+ >>> q.is_zero_quaternion()
1381
+ False
1382
+
1383
+ >>> q = Quaternion(0, 0, 0, 0)
1384
+ >>> q.is_zero_quaternion()
1385
+ True
1386
+
1387
+ See Also
1388
+ ========
1389
+ scalar_part
1390
+ vector_part
1391
+
1392
+ """
1393
+
1394
+ return self.norm().is_zero
1395
+
1396
+ def angle(self):
1397
+ r"""
1398
+ Returns the angle of the quaternion measured in the real-axis plane.
1399
+
1400
+ Explanation
1401
+ ===========
1402
+
1403
+ Given a quaternion $q = a + bi + cj + dk$ where $a$, $b$, $c$ and $d$
1404
+ are real numbers, returns the angle of the quaternion given by
1405
+
1406
+ .. math::
1407
+ \theta := 2 \operatorname{atan_2}\left(\sqrt{b^2 + c^2 + d^2}, {a}\right)
1408
+
1409
+ Examples
1410
+ ========
1411
+
1412
+ >>> from sympy.algebras.quaternion import Quaternion
1413
+ >>> q = Quaternion(1, 4, 4, 4)
1414
+ >>> q.angle()
1415
+ 2*atan(4*sqrt(3))
1416
+
1417
+ """
1418
+
1419
+ return 2 * atan2(self.vector_part().norm(), self.scalar_part())
1420
+
1421
+
1422
+ def arc_coplanar(self, other):
1423
+ """
1424
+ Returns True if the transformation arcs represented by the input quaternions happen in the same plane.
1425
+
1426
+ Explanation
1427
+ ===========
1428
+
1429
+ Two quaternions are said to be coplanar (in this arc sense) when their axes are parallel.
1430
+ The plane of a quaternion is the one normal to its axis.
1431
+
1432
+ Parameters
1433
+ ==========
1434
+
1435
+ other : a Quaternion
1436
+
1437
+ Returns
1438
+ =======
1439
+
1440
+ True : if the planes of the two quaternions are the same, apart from its orientation/sign.
1441
+ False : if the planes of the two quaternions are not the same, apart from its orientation/sign.
1442
+ None : if plane of either of the quaternion is unknown.
1443
+
1444
+ Examples
1445
+ ========
1446
+
1447
+ >>> from sympy.algebras.quaternion import Quaternion
1448
+ >>> q1 = Quaternion(1, 4, 4, 4)
1449
+ >>> q2 = Quaternion(3, 8, 8, 8)
1450
+ >>> Quaternion.arc_coplanar(q1, q2)
1451
+ True
1452
+
1453
+ >>> q1 = Quaternion(2, 8, 13, 12)
1454
+ >>> Quaternion.arc_coplanar(q1, q2)
1455
+ False
1456
+
1457
+ See Also
1458
+ ========
1459
+
1460
+ vector_coplanar
1461
+ is_pure
1462
+
1463
+ """
1464
+ if (self.is_zero_quaternion()) or (other.is_zero_quaternion()):
1465
+ raise ValueError('Neither of the given quaternions can be 0')
1466
+
1467
+ return fuzzy_or([(self.axis() - other.axis()).is_zero_quaternion(), (self.axis() + other.axis()).is_zero_quaternion()])
1468
+
1469
+ @classmethod
1470
+ def vector_coplanar(cls, q1, q2, q3):
1471
+ r"""
1472
+ Returns True if the axis of the pure quaternions seen as 3D vectors
1473
+ ``q1``, ``q2``, and ``q3`` are coplanar.
1474
+
1475
+ Explanation
1476
+ ===========
1477
+
1478
+ Three pure quaternions are vector coplanar if the quaternions seen as 3D vectors are coplanar.
1479
+
1480
+ Parameters
1481
+ ==========
1482
+
1483
+ q1
1484
+ A pure Quaternion.
1485
+ q2
1486
+ A pure Quaternion.
1487
+ q3
1488
+ A pure Quaternion.
1489
+
1490
+ Returns
1491
+ =======
1492
+
1493
+ True : if the axis of the pure quaternions seen as 3D vectors
1494
+ q1, q2, and q3 are coplanar.
1495
+ False : if the axis of the pure quaternions seen as 3D vectors
1496
+ q1, q2, and q3 are not coplanar.
1497
+ None : if the axis of the pure quaternions seen as 3D vectors
1498
+ q1, q2, and q3 are coplanar is unknown.
1499
+
1500
+ Examples
1501
+ ========
1502
+
1503
+ >>> from sympy.algebras.quaternion import Quaternion
1504
+ >>> q1 = Quaternion(0, 4, 4, 4)
1505
+ >>> q2 = Quaternion(0, 8, 8, 8)
1506
+ >>> q3 = Quaternion(0, 24, 24, 24)
1507
+ >>> Quaternion.vector_coplanar(q1, q2, q3)
1508
+ True
1509
+
1510
+ >>> q1 = Quaternion(0, 8, 16, 8)
1511
+ >>> q2 = Quaternion(0, 8, 3, 12)
1512
+ >>> Quaternion.vector_coplanar(q1, q2, q3)
1513
+ False
1514
+
1515
+ See Also
1516
+ ========
1517
+
1518
+ axis
1519
+ is_pure
1520
+
1521
+ """
1522
+
1523
+ if fuzzy_not(q1.is_pure()) or fuzzy_not(q2.is_pure()) or fuzzy_not(q3.is_pure()):
1524
+ raise ValueError('The given quaternions must be pure')
1525
+
1526
+ M = Matrix([[q1.b, q1.c, q1.d], [q2.b, q2.c, q2.d], [q3.b, q3.c, q3.d]]).det()
1527
+ return M.is_zero
1528
+
1529
+ def parallel(self, other):
1530
+ """
1531
+ Returns True if the two pure quaternions seen as 3D vectors are parallel.
1532
+
1533
+ Explanation
1534
+ ===========
1535
+
1536
+ Two pure quaternions are called parallel when their vector product is commutative which
1537
+ implies that the quaternions seen as 3D vectors have same direction.
1538
+
1539
+ Parameters
1540
+ ==========
1541
+
1542
+ other : a Quaternion
1543
+
1544
+ Returns
1545
+ =======
1546
+
1547
+ True : if the two pure quaternions seen as 3D vectors are parallel.
1548
+ False : if the two pure quaternions seen as 3D vectors are not parallel.
1549
+ None : if the two pure quaternions seen as 3D vectors are parallel is unknown.
1550
+
1551
+ Examples
1552
+ ========
1553
+
1554
+ >>> from sympy.algebras.quaternion import Quaternion
1555
+ >>> q = Quaternion(0, 4, 4, 4)
1556
+ >>> q1 = Quaternion(0, 8, 8, 8)
1557
+ >>> q.parallel(q1)
1558
+ True
1559
+
1560
+ >>> q1 = Quaternion(0, 8, 13, 12)
1561
+ >>> q.parallel(q1)
1562
+ False
1563
+
1564
+ """
1565
+
1566
+ if fuzzy_not(self.is_pure()) or fuzzy_not(other.is_pure()):
1567
+ raise ValueError('The provided quaternions must be pure')
1568
+
1569
+ return (self*other - other*self).is_zero_quaternion()
1570
+
1571
+ def orthogonal(self, other):
1572
+ """
1573
+ Returns the orthogonality of two quaternions.
1574
+
1575
+ Explanation
1576
+ ===========
1577
+
1578
+ Two pure quaternions are called orthogonal when their product is anti-commutative.
1579
+
1580
+ Parameters
1581
+ ==========
1582
+
1583
+ other : a Quaternion
1584
+
1585
+ Returns
1586
+ =======
1587
+
1588
+ True : if the two pure quaternions seen as 3D vectors are orthogonal.
1589
+ False : if the two pure quaternions seen as 3D vectors are not orthogonal.
1590
+ None : if the two pure quaternions seen as 3D vectors are orthogonal is unknown.
1591
+
1592
+ Examples
1593
+ ========
1594
+
1595
+ >>> from sympy.algebras.quaternion import Quaternion
1596
+ >>> q = Quaternion(0, 4, 4, 4)
1597
+ >>> q1 = Quaternion(0, 8, 8, 8)
1598
+ >>> q.orthogonal(q1)
1599
+ False
1600
+
1601
+ >>> q1 = Quaternion(0, 2, 2, 0)
1602
+ >>> q = Quaternion(0, 2, -2, 0)
1603
+ >>> q.orthogonal(q1)
1604
+ True
1605
+
1606
+ """
1607
+
1608
+ if fuzzy_not(self.is_pure()) or fuzzy_not(other.is_pure()):
1609
+ raise ValueError('The given quaternions must be pure')
1610
+
1611
+ return (self*other + other*self).is_zero_quaternion()
1612
+
1613
+ def index_vector(self):
1614
+ r"""
1615
+ Returns the index vector of the quaternion.
1616
+
1617
+ Explanation
1618
+ ===========
1619
+
1620
+ The index vector is given by $\mathbf{T}(q)$, the norm (or magnitude) of
1621
+ the quaternion $q$, multiplied by $\mathbf{Ax}(q)$, the axis of $q$.
1622
+
1623
+ Returns
1624
+ =======
1625
+
1626
+ Quaternion: representing index vector of the provided quaternion.
1627
+
1628
+ Examples
1629
+ ========
1630
+
1631
+ >>> from sympy.algebras.quaternion import Quaternion
1632
+ >>> q = Quaternion(2, 4, 2, 4)
1633
+ >>> q.index_vector()
1634
+ 0 + 4*sqrt(10)/3*i + 2*sqrt(10)/3*j + 4*sqrt(10)/3*k
1635
+
1636
+ See Also
1637
+ ========
1638
+
1639
+ axis
1640
+ norm
1641
+
1642
+ """
1643
+
1644
+ return self.norm() * self.axis()
1645
+
1646
+ def mensor(self):
1647
+ """
1648
+ Returns the natural logarithm of the norm(magnitude) of the quaternion.
1649
+
1650
+ Examples
1651
+ ========
1652
+
1653
+ >>> from sympy.algebras.quaternion import Quaternion
1654
+ >>> q = Quaternion(2, 4, 2, 4)
1655
+ >>> q.mensor()
1656
+ log(2*sqrt(10))
1657
+ >>> q.norm()
1658
+ 2*sqrt(10)
1659
+
1660
+ See Also
1661
+ ========
1662
+
1663
+ norm
1664
+
1665
+ """
1666
+
1667
+ return ln(self.norm())
lib/python3.10/site-packages/sympy/algebras/tests/__init__.py ADDED
File without changes
lib/python3.10/site-packages/sympy/algebras/tests/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (211 Bytes). View file
 
lib/python3.10/site-packages/sympy/algebras/tests/__pycache__/test_quaternion.cpython-310.pyc ADDED
Binary file (16 kB). View file
 
lib/python3.10/site-packages/sympy/algebras/tests/test_quaternion.py ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.testing.pytest import slow
2
+ from sympy.core.function import diff
3
+ from sympy.core.function import expand
4
+ from sympy.core.numbers import (E, I, Rational, pi)
5
+ from sympy.core.singleton import S
6
+ from sympy.core.symbol import (Symbol, symbols)
7
+ from sympy.functions.elementary.complexes import (Abs, conjugate, im, re, sign)
8
+ from sympy.functions.elementary.exponential import log
9
+ from sympy.functions.elementary.miscellaneous import sqrt
10
+ from sympy.functions.elementary.trigonometric import (acos, asin, cos, sin, atan2, atan)
11
+ from sympy.integrals.integrals import integrate
12
+ from sympy.matrices.dense import Matrix
13
+ from sympy.simplify import simplify
14
+ from sympy.simplify.trigsimp import trigsimp
15
+ from sympy.algebras.quaternion import Quaternion
16
+ from sympy.testing.pytest import raises
17
+ import math
18
+ from itertools import permutations, product
19
+
20
+ w, x, y, z = symbols('w:z')
21
+ phi = symbols('phi')
22
+
23
+ def test_quaternion_construction():
24
+ q = Quaternion(w, x, y, z)
25
+ assert q + q == Quaternion(2*w, 2*x, 2*y, 2*z)
26
+
27
+ q2 = Quaternion.from_axis_angle((sqrt(3)/3, sqrt(3)/3, sqrt(3)/3),
28
+ pi*Rational(2, 3))
29
+ assert q2 == Quaternion(S.Half, S.Half,
30
+ S.Half, S.Half)
31
+
32
+ M = Matrix([[cos(phi), -sin(phi), 0], [sin(phi), cos(phi), 0], [0, 0, 1]])
33
+ q3 = trigsimp(Quaternion.from_rotation_matrix(M))
34
+ assert q3 == Quaternion(
35
+ sqrt(2)*sqrt(cos(phi) + 1)/2, 0, 0, sqrt(2 - 2*cos(phi))*sign(sin(phi))/2)
36
+
37
+ nc = Symbol('nc', commutative=False)
38
+ raises(ValueError, lambda: Quaternion(w, x, nc, z))
39
+
40
+
41
+ def test_quaternion_construction_norm():
42
+ q1 = Quaternion(*symbols('a:d'))
43
+
44
+ q2 = Quaternion(w, x, y, z)
45
+ assert expand((q1*q2).norm()**2 - (q1.norm()**2 * q2.norm()**2)) == 0
46
+
47
+ q3 = Quaternion(w, x, y, z, norm=1)
48
+ assert (q1 * q3).norm() == q1.norm()
49
+
50
+
51
+ def test_issue_25254():
52
+ # calculating the inverse cached the norm which caused problems
53
+ # when multiplying
54
+ p = Quaternion(1, 0, 0, 0)
55
+ q = Quaternion.from_axis_angle((1, 1, 1), 3 * math.pi/4)
56
+ qi = q.inverse() # this operation cached the norm
57
+ test = q * p * qi
58
+ assert ((test - p).norm() < 1E-10)
59
+
60
+
61
+ def test_to_and_from_Matrix():
62
+ q = Quaternion(w, x, y, z)
63
+ q_full = Quaternion.from_Matrix(q.to_Matrix())
64
+ q_vect = Quaternion.from_Matrix(q.to_Matrix(True))
65
+ assert (q - q_full).is_zero_quaternion()
66
+ assert (q.vector_part() - q_vect).is_zero_quaternion()
67
+
68
+
69
+ def test_product_matrices():
70
+ q1 = Quaternion(w, x, y, z)
71
+ q2 = Quaternion(*(symbols("a:d")))
72
+ assert (q1 * q2).to_Matrix() == q1.product_matrix_left * q2.to_Matrix()
73
+ assert (q1 * q2).to_Matrix() == q2.product_matrix_right * q1.to_Matrix()
74
+
75
+ R1 = (q1.product_matrix_left * q1.product_matrix_right.T)[1:, 1:]
76
+ R2 = simplify(q1.to_rotation_matrix()*q1.norm()**2)
77
+ assert R1 == R2
78
+
79
+
80
+ def test_quaternion_axis_angle():
81
+
82
+ test_data = [ # axis, angle, expected_quaternion
83
+ ((1, 0, 0), 0, (1, 0, 0, 0)),
84
+ ((1, 0, 0), pi/2, (sqrt(2)/2, sqrt(2)/2, 0, 0)),
85
+ ((0, 1, 0), pi/2, (sqrt(2)/2, 0, sqrt(2)/2, 0)),
86
+ ((0, 0, 1), pi/2, (sqrt(2)/2, 0, 0, sqrt(2)/2)),
87
+ ((1, 0, 0), pi, (0, 1, 0, 0)),
88
+ ((0, 1, 0), pi, (0, 0, 1, 0)),
89
+ ((0, 0, 1), pi, (0, 0, 0, 1)),
90
+ ((1, 1, 1), pi, (0, 1/sqrt(3),1/sqrt(3),1/sqrt(3))),
91
+ ((sqrt(3)/3, sqrt(3)/3, sqrt(3)/3), pi*2/3, (S.Half, S.Half, S.Half, S.Half))
92
+ ]
93
+
94
+ for axis, angle, expected in test_data:
95
+ assert Quaternion.from_axis_angle(axis, angle) == Quaternion(*expected)
96
+
97
+
98
+ def test_quaternion_axis_angle_simplification():
99
+ result = Quaternion.from_axis_angle((1, 2, 3), asin(4))
100
+ assert result.a == cos(asin(4)/2)
101
+ assert result.b == sqrt(14)*sin(asin(4)/2)/14
102
+ assert result.c == sqrt(14)*sin(asin(4)/2)/7
103
+ assert result.d == 3*sqrt(14)*sin(asin(4)/2)/14
104
+
105
+ def test_quaternion_complex_real_addition():
106
+ a = symbols("a", complex=True)
107
+ b = symbols("b", real=True)
108
+ # This symbol is not complex:
109
+ c = symbols("c", commutative=False)
110
+
111
+ q = Quaternion(w, x, y, z)
112
+ assert a + q == Quaternion(w + re(a), x + im(a), y, z)
113
+ assert 1 + q == Quaternion(1 + w, x, y, z)
114
+ assert I + q == Quaternion(w, 1 + x, y, z)
115
+ assert b + q == Quaternion(w + b, x, y, z)
116
+ raises(ValueError, lambda: c + q)
117
+ raises(ValueError, lambda: q * c)
118
+ raises(ValueError, lambda: c * q)
119
+
120
+ assert -q == Quaternion(-w, -x, -y, -z)
121
+
122
+ q1 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
123
+ q2 = Quaternion(1, 4, 7, 8)
124
+
125
+ assert q1 + (2 + 3*I) == Quaternion(5 + 7*I, 2 + 5*I, 0, 7 + 8*I)
126
+ assert q2 + (2 + 3*I) == Quaternion(3, 7, 7, 8)
127
+ assert q1 * (2 + 3*I) == \
128
+ Quaternion((2 + 3*I)*(3 + 4*I), (2 + 3*I)*(2 + 5*I), 0, (2 + 3*I)*(7 + 8*I))
129
+ assert q2 * (2 + 3*I) == Quaternion(-10, 11, 38, -5)
130
+
131
+ q1 = Quaternion(1, 2, 3, 4)
132
+ q0 = Quaternion(0, 0, 0, 0)
133
+ assert q1 + q0 == q1
134
+ assert q1 - q0 == q1
135
+ assert q1 - q1 == q0
136
+
137
+
138
+ def test_quaternion_subs():
139
+ q = Quaternion.from_axis_angle((0, 0, 1), phi)
140
+ assert q.subs(phi, 0) == Quaternion(1, 0, 0, 0)
141
+
142
+
143
+ def test_quaternion_evalf():
144
+ assert (Quaternion(sqrt(2), 0, 0, sqrt(3)).evalf() ==
145
+ Quaternion(sqrt(2).evalf(), 0, 0, sqrt(3).evalf()))
146
+ assert (Quaternion(1/sqrt(2), 0, 0, 1/sqrt(2)).evalf() ==
147
+ Quaternion((1/sqrt(2)).evalf(), 0, 0, (1/sqrt(2)).evalf()))
148
+
149
+
150
+ def test_quaternion_functions():
151
+ q = Quaternion(w, x, y, z)
152
+ q1 = Quaternion(1, 2, 3, 4)
153
+ q0 = Quaternion(0, 0, 0, 0)
154
+
155
+ assert conjugate(q) == Quaternion(w, -x, -y, -z)
156
+ assert q.norm() == sqrt(w**2 + x**2 + y**2 + z**2)
157
+ assert q.normalize() == Quaternion(w, x, y, z) / sqrt(w**2 + x**2 + y**2 + z**2)
158
+ assert q.inverse() == Quaternion(w, -x, -y, -z) / (w**2 + x**2 + y**2 + z**2)
159
+ assert q.inverse() == q.pow(-1)
160
+ raises(ValueError, lambda: q0.inverse())
161
+ assert q.pow(2) == Quaternion(w**2 - x**2 - y**2 - z**2, 2*w*x, 2*w*y, 2*w*z)
162
+ assert q**(2) == Quaternion(w**2 - x**2 - y**2 - z**2, 2*w*x, 2*w*y, 2*w*z)
163
+ assert q1.pow(-2) == Quaternion(
164
+ Rational(-7, 225), Rational(-1, 225), Rational(-1, 150), Rational(-2, 225))
165
+ assert q1**(-2) == Quaternion(
166
+ Rational(-7, 225), Rational(-1, 225), Rational(-1, 150), Rational(-2, 225))
167
+ assert q1.pow(-0.5) == NotImplemented
168
+ raises(TypeError, lambda: q1**(-0.5))
169
+
170
+ assert q1.exp() == \
171
+ Quaternion(E * cos(sqrt(29)),
172
+ 2 * sqrt(29) * E * sin(sqrt(29)) / 29,
173
+ 3 * sqrt(29) * E * sin(sqrt(29)) / 29,
174
+ 4 * sqrt(29) * E * sin(sqrt(29)) / 29)
175
+ assert q1.log() == \
176
+ Quaternion(log(sqrt(30)),
177
+ 2 * sqrt(29) * acos(sqrt(30)/30) / 29,
178
+ 3 * sqrt(29) * acos(sqrt(30)/30) / 29,
179
+ 4 * sqrt(29) * acos(sqrt(30)/30) / 29)
180
+
181
+ assert q1.pow_cos_sin(2) == \
182
+ Quaternion(30 * cos(2 * acos(sqrt(30)/30)),
183
+ 60 * sqrt(29) * sin(2 * acos(sqrt(30)/30)) / 29,
184
+ 90 * sqrt(29) * sin(2 * acos(sqrt(30)/30)) / 29,
185
+ 120 * sqrt(29) * sin(2 * acos(sqrt(30)/30)) / 29)
186
+
187
+ assert diff(Quaternion(x, x, x, x), x) == Quaternion(1, 1, 1, 1)
188
+
189
+ assert integrate(Quaternion(x, x, x, x), x) == \
190
+ Quaternion(x**2 / 2, x**2 / 2, x**2 / 2, x**2 / 2)
191
+
192
+ assert Quaternion.rotate_point((1, 1, 1), q1) == (S.One / 5, 1, S(7) / 5)
193
+ n = Symbol('n')
194
+ raises(TypeError, lambda: q1**n)
195
+ n = Symbol('n', integer=True)
196
+ raises(TypeError, lambda: q1**n)
197
+
198
+ assert Quaternion(22, 23, 55, 8).scalar_part() == 22
199
+ assert Quaternion(w, x, y, z).scalar_part() == w
200
+
201
+ assert Quaternion(22, 23, 55, 8).vector_part() == Quaternion(0, 23, 55, 8)
202
+ assert Quaternion(w, x, y, z).vector_part() == Quaternion(0, x, y, z)
203
+
204
+ assert q1.axis() == Quaternion(0, 2*sqrt(29)/29, 3*sqrt(29)/29, 4*sqrt(29)/29)
205
+ assert q1.axis().pow(2) == Quaternion(-1, 0, 0, 0)
206
+ assert q0.axis().scalar_part() == 0
207
+ assert (q.axis() == Quaternion(0,
208
+ x/sqrt(x**2 + y**2 + z**2),
209
+ y/sqrt(x**2 + y**2 + z**2),
210
+ z/sqrt(x**2 + y**2 + z**2)))
211
+
212
+ assert q0.is_pure() is True
213
+ assert q1.is_pure() is False
214
+ assert Quaternion(0, 0, 0, 3).is_pure() is True
215
+ assert Quaternion(0, 2, 10, 3).is_pure() is True
216
+ assert Quaternion(w, 2, 10, 3).is_pure() is None
217
+
218
+ assert q1.angle() == 2*atan(sqrt(29))
219
+ assert q.angle() == 2*atan2(sqrt(x**2 + y**2 + z**2), w)
220
+
221
+ assert Quaternion.arc_coplanar(q1, Quaternion(2, 4, 6, 8)) is True
222
+ assert Quaternion.arc_coplanar(q1, Quaternion(1, -2, -3, -4)) is True
223
+ assert Quaternion.arc_coplanar(q1, Quaternion(1, 8, 12, 16)) is True
224
+ assert Quaternion.arc_coplanar(q1, Quaternion(1, 2, 3, 4)) is True
225
+ assert Quaternion.arc_coplanar(q1, Quaternion(w, 4, 6, 8)) is True
226
+ assert Quaternion.arc_coplanar(q1, Quaternion(2, 7, 4, 1)) is False
227
+ assert Quaternion.arc_coplanar(q1, Quaternion(w, x, y, z)) is None
228
+ raises(ValueError, lambda: Quaternion.arc_coplanar(q1, q0))
229
+
230
+ assert Quaternion.vector_coplanar(
231
+ Quaternion(0, 8, 12, 16),
232
+ Quaternion(0, 4, 6, 8),
233
+ Quaternion(0, 2, 3, 4)) is True
234
+ assert Quaternion.vector_coplanar(
235
+ Quaternion(0, 0, 0, 0), Quaternion(0, 4, 6, 8), Quaternion(0, 2, 3, 4)) is True
236
+ assert Quaternion.vector_coplanar(
237
+ Quaternion(0, 8, 2, 6), Quaternion(0, 1, 6, 6), Quaternion(0, 0, 3, 4)) is False
238
+ assert Quaternion.vector_coplanar(
239
+ Quaternion(0, 1, 3, 4),
240
+ Quaternion(0, 4, w, 6),
241
+ Quaternion(0, 6, 8, 1)) is None
242
+ raises(ValueError, lambda:
243
+ Quaternion.vector_coplanar(q0, Quaternion(0, 4, 6, 8), q1))
244
+
245
+ assert Quaternion(0, 1, 2, 3).parallel(Quaternion(0, 2, 4, 6)) is True
246
+ assert Quaternion(0, 1, 2, 3).parallel(Quaternion(0, 2, 2, 6)) is False
247
+ assert Quaternion(0, 1, 2, 3).parallel(Quaternion(w, x, y, 6)) is None
248
+ raises(ValueError, lambda: q0.parallel(q1))
249
+
250
+ assert Quaternion(0, 1, 2, 3).orthogonal(Quaternion(0, -2, 1, 0)) is True
251
+ assert Quaternion(0, 2, 4, 7).orthogonal(Quaternion(0, 2, 2, 6)) is False
252
+ assert Quaternion(0, 2, 4, 7).orthogonal(Quaternion(w, x, y, 6)) is None
253
+ raises(ValueError, lambda: q0.orthogonal(q1))
254
+
255
+ assert q1.index_vector() == Quaternion(
256
+ 0, 2*sqrt(870)/29,
257
+ 3*sqrt(870)/29,
258
+ 4*sqrt(870)/29)
259
+ assert Quaternion(0, 3, 9, 4).index_vector() == Quaternion(0, 3, 9, 4)
260
+
261
+ assert Quaternion(4, 3, 9, 4).mensor() == log(sqrt(122))
262
+ assert Quaternion(3, 3, 0, 2).mensor() == log(sqrt(22))
263
+
264
+ assert q0.is_zero_quaternion() is True
265
+ assert q1.is_zero_quaternion() is False
266
+ assert Quaternion(w, 0, 0, 0).is_zero_quaternion() is None
267
+
268
+ def test_quaternion_conversions():
269
+ q1 = Quaternion(1, 2, 3, 4)
270
+
271
+ assert q1.to_axis_angle() == ((2 * sqrt(29)/29,
272
+ 3 * sqrt(29)/29,
273
+ 4 * sqrt(29)/29),
274
+ 2 * acos(sqrt(30)/30))
275
+
276
+ assert (q1.to_rotation_matrix() ==
277
+ Matrix([[Rational(-2, 3), Rational(2, 15), Rational(11, 15)],
278
+ [Rational(2, 3), Rational(-1, 3), Rational(2, 3)],
279
+ [Rational(1, 3), Rational(14, 15), Rational(2, 15)]]))
280
+
281
+ assert (q1.to_rotation_matrix((1, 1, 1)) ==
282
+ Matrix([
283
+ [Rational(-2, 3), Rational(2, 15), Rational(11, 15), Rational(4, 5)],
284
+ [Rational(2, 3), Rational(-1, 3), Rational(2, 3), S.Zero],
285
+ [Rational(1, 3), Rational(14, 15), Rational(2, 15), Rational(-2, 5)],
286
+ [S.Zero, S.Zero, S.Zero, S.One]]))
287
+
288
+ theta = symbols("theta", real=True)
289
+ q2 = Quaternion(cos(theta/2), 0, 0, sin(theta/2))
290
+
291
+ assert trigsimp(q2.to_rotation_matrix()) == Matrix([
292
+ [cos(theta), -sin(theta), 0],
293
+ [sin(theta), cos(theta), 0],
294
+ [0, 0, 1]])
295
+
296
+ assert q2.to_axis_angle() == ((0, 0, sin(theta/2)/Abs(sin(theta/2))),
297
+ 2*acos(cos(theta/2)))
298
+
299
+ assert trigsimp(q2.to_rotation_matrix((1, 1, 1))) == Matrix([
300
+ [cos(theta), -sin(theta), 0, sin(theta) - cos(theta) + 1],
301
+ [sin(theta), cos(theta), 0, -sin(theta) - cos(theta) + 1],
302
+ [0, 0, 1, 0],
303
+ [0, 0, 0, 1]])
304
+
305
+
306
+ def test_rotation_matrix_homogeneous():
307
+ q = Quaternion(w, x, y, z)
308
+ R1 = q.to_rotation_matrix(homogeneous=True) * q.norm()**2
309
+ R2 = simplify(q.to_rotation_matrix(homogeneous=False) * q.norm()**2)
310
+ assert R1 == R2
311
+
312
+
313
+ def test_quaternion_rotation_iss1593():
314
+ """
315
+ There was a sign mistake in the definition,
316
+ of the rotation matrix. This tests that particular sign mistake.
317
+ See issue 1593 for reference.
318
+ See wikipedia
319
+ https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix
320
+ for the correct definition
321
+ """
322
+ q = Quaternion(cos(phi/2), sin(phi/2), 0, 0)
323
+ assert(trigsimp(q.to_rotation_matrix()) == Matrix([
324
+ [1, 0, 0],
325
+ [0, cos(phi), -sin(phi)],
326
+ [0, sin(phi), cos(phi)]]))
327
+
328
+
329
+ def test_quaternion_multiplication():
330
+ q1 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
331
+ q2 = Quaternion(1, 2, 3, 5)
332
+ q3 = Quaternion(1, 1, 1, y)
333
+
334
+ assert Quaternion._generic_mul(S(4), S.One) == 4
335
+ assert (Quaternion._generic_mul(S(4), q1) ==
336
+ Quaternion(12 + 16*I, 8 + 20*I, 0, 28 + 32*I))
337
+ assert q2.mul(2) == Quaternion(2, 4, 6, 10)
338
+ assert q2.mul(q3) == Quaternion(-5*y - 4, 3*y - 2, 9 - 2*y, y + 4)
339
+ assert q2.mul(q3) == q2*q3
340
+
341
+ z = symbols('z', complex=True)
342
+ z_quat = Quaternion(re(z), im(z), 0, 0)
343
+ q = Quaternion(*symbols('q:4', real=True))
344
+
345
+ assert z * q == z_quat * q
346
+ assert q * z == q * z_quat
347
+
348
+
349
+ def test_issue_16318():
350
+ #for rtruediv
351
+ q0 = Quaternion(0, 0, 0, 0)
352
+ raises(ValueError, lambda: 1/q0)
353
+ #for rotate_point
354
+ q = Quaternion(1, 2, 3, 4)
355
+ (axis, angle) = q.to_axis_angle()
356
+ assert Quaternion.rotate_point((1, 1, 1), (axis, angle)) == (S.One / 5, 1, S(7) / 5)
357
+ #test for to_axis_angle
358
+ q = Quaternion(-1, 1, 1, 1)
359
+ axis = (-sqrt(3)/3, -sqrt(3)/3, -sqrt(3)/3)
360
+ angle = 2*pi/3
361
+ assert (axis, angle) == q.to_axis_angle()
362
+
363
+
364
+ @slow
365
+ def test_to_euler():
366
+ q = Quaternion(w, x, y, z)
367
+ q_normalized = q.normalize()
368
+
369
+ seqs = ['zxy', 'zyx', 'zyz', 'zxz']
370
+ seqs += [seq.upper() for seq in seqs]
371
+
372
+ for seq in seqs:
373
+ euler_from_q = q.to_euler(seq)
374
+ q_back = simplify(Quaternion.from_euler(euler_from_q, seq))
375
+ assert q_back == q_normalized
376
+
377
+
378
+ def test_to_euler_iss24504():
379
+ """
380
+ There was a mistake in the degenerate case testing
381
+ See issue 24504 for reference.
382
+ """
383
+ q = Quaternion.from_euler((phi, 0, 0), 'zyz')
384
+ assert trigsimp(q.to_euler('zyz'), inverse=True) == (phi, 0, 0)
385
+
386
+
387
+ def test_to_euler_numerical_singilarities():
388
+
389
+ def test_one_case(angles, seq):
390
+ q = Quaternion.from_euler(angles, seq)
391
+ assert q.to_euler(seq) == angles
392
+
393
+ # symmetric
394
+ test_one_case((pi/2, 0, 0), 'zyz')
395
+ test_one_case((pi/2, 0, 0), 'ZYZ')
396
+ test_one_case((pi/2, pi, 0), 'zyz')
397
+ test_one_case((pi/2, pi, 0), 'ZYZ')
398
+
399
+ # asymmetric
400
+ test_one_case((pi/2, pi/2, 0), 'zyx')
401
+ test_one_case((pi/2, -pi/2, 0), 'zyx')
402
+ test_one_case((pi/2, pi/2, 0), 'ZYX')
403
+ test_one_case((pi/2, -pi/2, 0), 'ZYX')
404
+
405
+
406
+ @slow
407
+ def test_to_euler_options():
408
+ def test_one_case(q):
409
+ angles1 = Matrix(q.to_euler(seq, True, True))
410
+ angles2 = Matrix(q.to_euler(seq, False, False))
411
+ angle_errors = simplify(angles1-angles2).evalf()
412
+ for angle_error in angle_errors:
413
+ # forcing angles to set {-pi, pi}
414
+ angle_error = (angle_error + pi) % (2 * pi) - pi
415
+ assert angle_error < 10e-7
416
+
417
+ for xyz in ('xyz', 'XYZ'):
418
+ for seq_tuple in permutations(xyz):
419
+ for symmetric in (True, False):
420
+ if symmetric:
421
+ seq = ''.join([seq_tuple[0], seq_tuple[1], seq_tuple[0]])
422
+ else:
423
+ seq = ''.join(seq_tuple)
424
+
425
+ for elements in product([-1, 0, 1], repeat=4):
426
+ q = Quaternion(*elements)
427
+ if not q.is_zero_quaternion():
428
+ test_one_case(q)
lib/python3.10/site-packages/sympy/assumptions/__init__.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ A module to implement logical predicates and assumption system.
3
+ """
4
+
5
+ from .assume import (
6
+ AppliedPredicate, Predicate, AssumptionsContext, assuming,
7
+ global_assumptions
8
+ )
9
+ from .ask import Q, ask, register_handler, remove_handler
10
+ from .refine import refine
11
+ from .relation import BinaryRelation, AppliedBinaryRelation
12
+
13
+ __all__ = [
14
+ 'AppliedPredicate', 'Predicate', 'AssumptionsContext', 'assuming',
15
+ 'global_assumptions', 'Q', 'ask', 'register_handler', 'remove_handler',
16
+ 'refine',
17
+ 'BinaryRelation', 'AppliedBinaryRelation'
18
+ ]
lib/python3.10/site-packages/sympy/assumptions/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (713 Bytes). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/ask.cpython-310.pyc ADDED
Binary file (18.8 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/ask_generated.cpython-310.pyc ADDED
Binary file (11.1 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/assume.cpython-310.pyc ADDED
Binary file (14.5 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/cnf.cpython-310.pyc ADDED
Binary file (16.7 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/facts.cpython-310.pyc ADDED
Binary file (6.79 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/lra_satask.cpython-310.pyc ADDED
Binary file (6.53 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/refine.cpython-310.pyc ADDED
Binary file (9.96 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/satask.cpython-310.pyc ADDED
Binary file (11.1 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/sathandlers.cpython-310.pyc ADDED
Binary file (10.4 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/__pycache__/wrapper.cpython-310.pyc ADDED
Binary file (5.39 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/ask.py ADDED
@@ -0,0 +1,651 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module for querying SymPy objects about assumptions."""
2
+
3
+ from sympy.assumptions.assume import (global_assumptions, Predicate,
4
+ AppliedPredicate)
5
+ from sympy.assumptions.cnf import CNF, EncodedCNF, Literal
6
+ from sympy.core import sympify
7
+ from sympy.core.kind import BooleanKind
8
+ from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le
9
+ from sympy.logic.inference import satisfiable
10
+ from sympy.utilities.decorator import memoize_property
11
+ from sympy.utilities.exceptions import (sympy_deprecation_warning,
12
+ SymPyDeprecationWarning,
13
+ ignore_warnings)
14
+
15
+
16
+ # Memoization is necessary for the properties of AssumptionKeys to
17
+ # ensure that only one object of Predicate objects are created.
18
+ # This is because assumption handlers are registered on those objects.
19
+
20
+
21
+ class AssumptionKeys:
22
+ """
23
+ This class contains all the supported keys by ``ask``.
24
+ It should be accessed via the instance ``sympy.Q``.
25
+
26
+ """
27
+
28
+ # DO NOT add methods or properties other than predicate keys.
29
+ # SAT solver checks the properties of Q and use them to compute the
30
+ # fact system. Non-predicate attributes will break this.
31
+
32
+ @memoize_property
33
+ def hermitian(self):
34
+ from .handlers.sets import HermitianPredicate
35
+ return HermitianPredicate()
36
+
37
+ @memoize_property
38
+ def antihermitian(self):
39
+ from .handlers.sets import AntihermitianPredicate
40
+ return AntihermitianPredicate()
41
+
42
+ @memoize_property
43
+ def real(self):
44
+ from .handlers.sets import RealPredicate
45
+ return RealPredicate()
46
+
47
+ @memoize_property
48
+ def extended_real(self):
49
+ from .handlers.sets import ExtendedRealPredicate
50
+ return ExtendedRealPredicate()
51
+
52
+ @memoize_property
53
+ def imaginary(self):
54
+ from .handlers.sets import ImaginaryPredicate
55
+ return ImaginaryPredicate()
56
+
57
+ @memoize_property
58
+ def complex(self):
59
+ from .handlers.sets import ComplexPredicate
60
+ return ComplexPredicate()
61
+
62
+ @memoize_property
63
+ def algebraic(self):
64
+ from .handlers.sets import AlgebraicPredicate
65
+ return AlgebraicPredicate()
66
+
67
+ @memoize_property
68
+ def transcendental(self):
69
+ from .predicates.sets import TranscendentalPredicate
70
+ return TranscendentalPredicate()
71
+
72
+ @memoize_property
73
+ def integer(self):
74
+ from .handlers.sets import IntegerPredicate
75
+ return IntegerPredicate()
76
+
77
+ @memoize_property
78
+ def noninteger(self):
79
+ from .predicates.sets import NonIntegerPredicate
80
+ return NonIntegerPredicate()
81
+
82
+ @memoize_property
83
+ def rational(self):
84
+ from .handlers.sets import RationalPredicate
85
+ return RationalPredicate()
86
+
87
+ @memoize_property
88
+ def irrational(self):
89
+ from .handlers.sets import IrrationalPredicate
90
+ return IrrationalPredicate()
91
+
92
+ @memoize_property
93
+ def finite(self):
94
+ from .handlers.calculus import FinitePredicate
95
+ return FinitePredicate()
96
+
97
+ @memoize_property
98
+ def infinite(self):
99
+ from .handlers.calculus import InfinitePredicate
100
+ return InfinitePredicate()
101
+
102
+ @memoize_property
103
+ def positive_infinite(self):
104
+ from .handlers.calculus import PositiveInfinitePredicate
105
+ return PositiveInfinitePredicate()
106
+
107
+ @memoize_property
108
+ def negative_infinite(self):
109
+ from .handlers.calculus import NegativeInfinitePredicate
110
+ return NegativeInfinitePredicate()
111
+
112
+ @memoize_property
113
+ def positive(self):
114
+ from .handlers.order import PositivePredicate
115
+ return PositivePredicate()
116
+
117
+ @memoize_property
118
+ def negative(self):
119
+ from .handlers.order import NegativePredicate
120
+ return NegativePredicate()
121
+
122
+ @memoize_property
123
+ def zero(self):
124
+ from .handlers.order import ZeroPredicate
125
+ return ZeroPredicate()
126
+
127
+ @memoize_property
128
+ def extended_positive(self):
129
+ from .handlers.order import ExtendedPositivePredicate
130
+ return ExtendedPositivePredicate()
131
+
132
+ @memoize_property
133
+ def extended_negative(self):
134
+ from .handlers.order import ExtendedNegativePredicate
135
+ return ExtendedNegativePredicate()
136
+
137
+ @memoize_property
138
+ def nonzero(self):
139
+ from .handlers.order import NonZeroPredicate
140
+ return NonZeroPredicate()
141
+
142
+ @memoize_property
143
+ def nonpositive(self):
144
+ from .handlers.order import NonPositivePredicate
145
+ return NonPositivePredicate()
146
+
147
+ @memoize_property
148
+ def nonnegative(self):
149
+ from .handlers.order import NonNegativePredicate
150
+ return NonNegativePredicate()
151
+
152
+ @memoize_property
153
+ def extended_nonzero(self):
154
+ from .handlers.order import ExtendedNonZeroPredicate
155
+ return ExtendedNonZeroPredicate()
156
+
157
+ @memoize_property
158
+ def extended_nonpositive(self):
159
+ from .handlers.order import ExtendedNonPositivePredicate
160
+ return ExtendedNonPositivePredicate()
161
+
162
+ @memoize_property
163
+ def extended_nonnegative(self):
164
+ from .handlers.order import ExtendedNonNegativePredicate
165
+ return ExtendedNonNegativePredicate()
166
+
167
+ @memoize_property
168
+ def even(self):
169
+ from .handlers.ntheory import EvenPredicate
170
+ return EvenPredicate()
171
+
172
+ @memoize_property
173
+ def odd(self):
174
+ from .handlers.ntheory import OddPredicate
175
+ return OddPredicate()
176
+
177
+ @memoize_property
178
+ def prime(self):
179
+ from .handlers.ntheory import PrimePredicate
180
+ return PrimePredicate()
181
+
182
+ @memoize_property
183
+ def composite(self):
184
+ from .handlers.ntheory import CompositePredicate
185
+ return CompositePredicate()
186
+
187
+ @memoize_property
188
+ def commutative(self):
189
+ from .handlers.common import CommutativePredicate
190
+ return CommutativePredicate()
191
+
192
+ @memoize_property
193
+ def is_true(self):
194
+ from .handlers.common import IsTruePredicate
195
+ return IsTruePredicate()
196
+
197
+ @memoize_property
198
+ def symmetric(self):
199
+ from .handlers.matrices import SymmetricPredicate
200
+ return SymmetricPredicate()
201
+
202
+ @memoize_property
203
+ def invertible(self):
204
+ from .handlers.matrices import InvertiblePredicate
205
+ return InvertiblePredicate()
206
+
207
+ @memoize_property
208
+ def orthogonal(self):
209
+ from .handlers.matrices import OrthogonalPredicate
210
+ return OrthogonalPredicate()
211
+
212
+ @memoize_property
213
+ def unitary(self):
214
+ from .handlers.matrices import UnitaryPredicate
215
+ return UnitaryPredicate()
216
+
217
+ @memoize_property
218
+ def positive_definite(self):
219
+ from .handlers.matrices import PositiveDefinitePredicate
220
+ return PositiveDefinitePredicate()
221
+
222
+ @memoize_property
223
+ def upper_triangular(self):
224
+ from .handlers.matrices import UpperTriangularPredicate
225
+ return UpperTriangularPredicate()
226
+
227
+ @memoize_property
228
+ def lower_triangular(self):
229
+ from .handlers.matrices import LowerTriangularPredicate
230
+ return LowerTriangularPredicate()
231
+
232
+ @memoize_property
233
+ def diagonal(self):
234
+ from .handlers.matrices import DiagonalPredicate
235
+ return DiagonalPredicate()
236
+
237
+ @memoize_property
238
+ def fullrank(self):
239
+ from .handlers.matrices import FullRankPredicate
240
+ return FullRankPredicate()
241
+
242
+ @memoize_property
243
+ def square(self):
244
+ from .handlers.matrices import SquarePredicate
245
+ return SquarePredicate()
246
+
247
+ @memoize_property
248
+ def integer_elements(self):
249
+ from .handlers.matrices import IntegerElementsPredicate
250
+ return IntegerElementsPredicate()
251
+
252
+ @memoize_property
253
+ def real_elements(self):
254
+ from .handlers.matrices import RealElementsPredicate
255
+ return RealElementsPredicate()
256
+
257
+ @memoize_property
258
+ def complex_elements(self):
259
+ from .handlers.matrices import ComplexElementsPredicate
260
+ return ComplexElementsPredicate()
261
+
262
+ @memoize_property
263
+ def singular(self):
264
+ from .predicates.matrices import SingularPredicate
265
+ return SingularPredicate()
266
+
267
+ @memoize_property
268
+ def normal(self):
269
+ from .predicates.matrices import NormalPredicate
270
+ return NormalPredicate()
271
+
272
+ @memoize_property
273
+ def triangular(self):
274
+ from .predicates.matrices import TriangularPredicate
275
+ return TriangularPredicate()
276
+
277
+ @memoize_property
278
+ def unit_triangular(self):
279
+ from .predicates.matrices import UnitTriangularPredicate
280
+ return UnitTriangularPredicate()
281
+
282
+ @memoize_property
283
+ def eq(self):
284
+ from .relation.equality import EqualityPredicate
285
+ return EqualityPredicate()
286
+
287
+ @memoize_property
288
+ def ne(self):
289
+ from .relation.equality import UnequalityPredicate
290
+ return UnequalityPredicate()
291
+
292
+ @memoize_property
293
+ def gt(self):
294
+ from .relation.equality import StrictGreaterThanPredicate
295
+ return StrictGreaterThanPredicate()
296
+
297
+ @memoize_property
298
+ def ge(self):
299
+ from .relation.equality import GreaterThanPredicate
300
+ return GreaterThanPredicate()
301
+
302
+ @memoize_property
303
+ def lt(self):
304
+ from .relation.equality import StrictLessThanPredicate
305
+ return StrictLessThanPredicate()
306
+
307
+ @memoize_property
308
+ def le(self):
309
+ from .relation.equality import LessThanPredicate
310
+ return LessThanPredicate()
311
+
312
+
313
+ Q = AssumptionKeys()
314
+
315
+ def _extract_all_facts(assump, exprs):
316
+ """
317
+ Extract all relevant assumptions from *assump* with respect to given *exprs*.
318
+
319
+ Parameters
320
+ ==========
321
+
322
+ assump : sympy.assumptions.cnf.CNF
323
+
324
+ exprs : tuple of expressions
325
+
326
+ Returns
327
+ =======
328
+
329
+ sympy.assumptions.cnf.CNF
330
+
331
+ Examples
332
+ ========
333
+
334
+ >>> from sympy import Q
335
+ >>> from sympy.assumptions.cnf import CNF
336
+ >>> from sympy.assumptions.ask import _extract_all_facts
337
+ >>> from sympy.abc import x, y
338
+ >>> assump = CNF.from_prop(Q.positive(x) & Q.integer(y))
339
+ >>> exprs = (x,)
340
+ >>> cnf = _extract_all_facts(assump, exprs)
341
+ >>> cnf.clauses
342
+ {frozenset({Literal(Q.positive, False)})}
343
+
344
+ """
345
+ facts = set()
346
+
347
+ for clause in assump.clauses:
348
+ args = []
349
+ for literal in clause:
350
+ if isinstance(literal.lit, AppliedPredicate) and len(literal.lit.arguments) == 1:
351
+ if literal.lit.arg in exprs:
352
+ # Add literal if it has matching in it
353
+ args.append(Literal(literal.lit.function, literal.is_Not))
354
+ else:
355
+ # If any of the literals doesn't have matching expr don't add the whole clause.
356
+ break
357
+ else:
358
+ # If any of the literals aren't unary predicate don't add the whole clause.
359
+ break
360
+
361
+ else:
362
+ if args:
363
+ facts.add(frozenset(args))
364
+ return CNF(facts)
365
+
366
+
367
+ def ask(proposition, assumptions=True, context=global_assumptions):
368
+ """
369
+ Function to evaluate the proposition with assumptions.
370
+
371
+ Explanation
372
+ ===========
373
+
374
+ This function evaluates the proposition to ``True`` or ``False`` if
375
+ the truth value can be determined. If not, it returns ``None``.
376
+
377
+ It should be discerned from :func:`~.refine()` which, when applied to a
378
+ proposition, simplifies the argument to symbolic ``Boolean`` instead of
379
+ Python built-in ``True``, ``False`` or ``None``.
380
+
381
+ **Syntax**
382
+
383
+ * ask(proposition)
384
+ Evaluate the *proposition* in global assumption context.
385
+
386
+ * ask(proposition, assumptions)
387
+ Evaluate the *proposition* with respect to *assumptions* in
388
+ global assumption context.
389
+
390
+ Parameters
391
+ ==========
392
+
393
+ proposition : Boolean
394
+ Proposition which will be evaluated to boolean value. If this is
395
+ not ``AppliedPredicate``, it will be wrapped by ``Q.is_true``.
396
+
397
+ assumptions : Boolean, optional
398
+ Local assumptions to evaluate the *proposition*.
399
+
400
+ context : AssumptionsContext, optional
401
+ Default assumptions to evaluate the *proposition*. By default,
402
+ this is ``sympy.assumptions.global_assumptions`` variable.
403
+
404
+ Returns
405
+ =======
406
+
407
+ ``True``, ``False``, or ``None``
408
+
409
+ Raises
410
+ ======
411
+
412
+ TypeError : *proposition* or *assumptions* is not valid logical expression.
413
+
414
+ ValueError : assumptions are inconsistent.
415
+
416
+ Examples
417
+ ========
418
+
419
+ >>> from sympy import ask, Q, pi
420
+ >>> from sympy.abc import x, y
421
+ >>> ask(Q.rational(pi))
422
+ False
423
+ >>> ask(Q.even(x*y), Q.even(x) & Q.integer(y))
424
+ True
425
+ >>> ask(Q.prime(4*x), Q.integer(x))
426
+ False
427
+
428
+ If the truth value cannot be determined, ``None`` will be returned.
429
+
430
+ >>> print(ask(Q.odd(3*x))) # cannot determine unless we know x
431
+ None
432
+
433
+ ``ValueError`` is raised if assumptions are inconsistent.
434
+
435
+ >>> ask(Q.integer(x), Q.even(x) & Q.odd(x))
436
+ Traceback (most recent call last):
437
+ ...
438
+ ValueError: inconsistent assumptions Q.even(x) & Q.odd(x)
439
+
440
+ Notes
441
+ =====
442
+
443
+ Relations in assumptions are not implemented (yet), so the following
444
+ will not give a meaningful result.
445
+
446
+ >>> ask(Q.positive(x), x > 0)
447
+
448
+ It is however a work in progress.
449
+
450
+ See Also
451
+ ========
452
+
453
+ sympy.assumptions.refine.refine : Simplification using assumptions.
454
+ Proposition is not reduced to ``None`` if the truth value cannot
455
+ be determined.
456
+ """
457
+ from sympy.assumptions.satask import satask
458
+ from sympy.assumptions.lra_satask import lra_satask
459
+ from sympy.logic.algorithms.lra_theory import UnhandledInput
460
+
461
+ proposition = sympify(proposition)
462
+ assumptions = sympify(assumptions)
463
+
464
+ if isinstance(proposition, Predicate) or proposition.kind is not BooleanKind:
465
+ raise TypeError("proposition must be a valid logical expression")
466
+
467
+ if isinstance(assumptions, Predicate) or assumptions.kind is not BooleanKind:
468
+ raise TypeError("assumptions must be a valid logical expression")
469
+
470
+ binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le}
471
+ if isinstance(proposition, AppliedPredicate):
472
+ key, args = proposition.function, proposition.arguments
473
+ elif proposition.func in binrelpreds:
474
+ key, args = binrelpreds[type(proposition)], proposition.args
475
+ else:
476
+ key, args = Q.is_true, (proposition,)
477
+
478
+ # convert local and global assumptions to CNF
479
+ assump_cnf = CNF.from_prop(assumptions)
480
+ assump_cnf.extend(context)
481
+
482
+ # extract the relevant facts from assumptions with respect to args
483
+ local_facts = _extract_all_facts(assump_cnf, args)
484
+
485
+ # convert default facts and assumed facts to encoded CNF
486
+ known_facts_cnf = get_all_known_facts()
487
+ enc_cnf = EncodedCNF()
488
+ enc_cnf.from_cnf(CNF(known_facts_cnf))
489
+ enc_cnf.add_from_cnf(local_facts)
490
+
491
+ # check the satisfiability of given assumptions
492
+ if local_facts.clauses and satisfiable(enc_cnf) is False:
493
+ raise ValueError("inconsistent assumptions %s" % assumptions)
494
+
495
+ # quick computation for single fact
496
+ res = _ask_single_fact(key, local_facts)
497
+ if res is not None:
498
+ return res
499
+
500
+ # direct resolution method, no logic
501
+ res = key(*args)._eval_ask(assumptions)
502
+ if res is not None:
503
+ return bool(res)
504
+
505
+ # using satask (still costly)
506
+ res = satask(proposition, assumptions=assumptions, context=context)
507
+ if res is not None:
508
+ return res
509
+
510
+ try:
511
+ res = lra_satask(proposition, assumptions=assumptions, context=context)
512
+ except UnhandledInput:
513
+ return None
514
+
515
+ return res
516
+
517
+
518
+ def _ask_single_fact(key, local_facts):
519
+ """
520
+ Compute the truth value of single predicate using assumptions.
521
+
522
+ Parameters
523
+ ==========
524
+
525
+ key : sympy.assumptions.assume.Predicate
526
+ Proposition predicate.
527
+
528
+ local_facts : sympy.assumptions.cnf.CNF
529
+ Local assumption in CNF form.
530
+
531
+ Returns
532
+ =======
533
+
534
+ ``True``, ``False`` or ``None``
535
+
536
+ Examples
537
+ ========
538
+
539
+ >>> from sympy import Q
540
+ >>> from sympy.assumptions.cnf import CNF
541
+ >>> from sympy.assumptions.ask import _ask_single_fact
542
+
543
+ If prerequisite of proposition is rejected by the assumption,
544
+ return ``False``.
545
+
546
+ >>> key, assump = Q.zero, ~Q.zero
547
+ >>> local_facts = CNF.from_prop(assump)
548
+ >>> _ask_single_fact(key, local_facts)
549
+ False
550
+ >>> key, assump = Q.zero, ~Q.even
551
+ >>> local_facts = CNF.from_prop(assump)
552
+ >>> _ask_single_fact(key, local_facts)
553
+ False
554
+
555
+ If assumption implies the proposition, return ``True``.
556
+
557
+ >>> key, assump = Q.even, Q.zero
558
+ >>> local_facts = CNF.from_prop(assump)
559
+ >>> _ask_single_fact(key, local_facts)
560
+ True
561
+
562
+ If proposition rejects the assumption, return ``False``.
563
+
564
+ >>> key, assump = Q.even, Q.odd
565
+ >>> local_facts = CNF.from_prop(assump)
566
+ >>> _ask_single_fact(key, local_facts)
567
+ False
568
+ """
569
+ if local_facts.clauses:
570
+
571
+ known_facts_dict = get_known_facts_dict()
572
+
573
+ if len(local_facts.clauses) == 1:
574
+ cl, = local_facts.clauses
575
+ if len(cl) == 1:
576
+ f, = cl
577
+ prop_facts = known_facts_dict.get(key, None)
578
+ prop_req = prop_facts[0] if prop_facts is not None else set()
579
+ if f.is_Not and f.arg in prop_req:
580
+ # the prerequisite of proposition is rejected
581
+ return False
582
+
583
+ for clause in local_facts.clauses:
584
+ if len(clause) == 1:
585
+ f, = clause
586
+ prop_facts = known_facts_dict.get(f.arg, None) if not f.is_Not else None
587
+ if prop_facts is None:
588
+ continue
589
+
590
+ prop_req, prop_rej = prop_facts
591
+ if key in prop_req:
592
+ # assumption implies the proposition
593
+ return True
594
+ elif key in prop_rej:
595
+ # proposition rejects the assumption
596
+ return False
597
+
598
+ return None
599
+
600
+
601
+ def register_handler(key, handler):
602
+ """
603
+ Register a handler in the ask system. key must be a string and handler a
604
+ class inheriting from AskHandler.
605
+
606
+ .. deprecated:: 1.8.
607
+ Use multipledispatch handler instead. See :obj:`~.Predicate`.
608
+
609
+ """
610
+ sympy_deprecation_warning(
611
+ """
612
+ The AskHandler system is deprecated. The register_handler() function
613
+ should be replaced with the multipledispatch handler of Predicate.
614
+ """,
615
+ deprecated_since_version="1.8",
616
+ active_deprecations_target='deprecated-askhandler',
617
+ )
618
+ if isinstance(key, Predicate):
619
+ key = key.name.name
620
+ Qkey = getattr(Q, key, None)
621
+ if Qkey is not None:
622
+ Qkey.add_handler(handler)
623
+ else:
624
+ setattr(Q, key, Predicate(key, handlers=[handler]))
625
+
626
+
627
+ def remove_handler(key, handler):
628
+ """
629
+ Removes a handler from the ask system.
630
+
631
+ .. deprecated:: 1.8.
632
+ Use multipledispatch handler instead. See :obj:`~.Predicate`.
633
+
634
+ """
635
+ sympy_deprecation_warning(
636
+ """
637
+ The AskHandler system is deprecated. The remove_handler() function
638
+ should be replaced with the multipledispatch handler of Predicate.
639
+ """,
640
+ deprecated_since_version="1.8",
641
+ active_deprecations_target='deprecated-askhandler',
642
+ )
643
+ if isinstance(key, Predicate):
644
+ key = key.name.name
645
+ # Don't show the same warning again recursively
646
+ with ignore_warnings(SymPyDeprecationWarning):
647
+ getattr(Q, key).remove_handler(handler)
648
+
649
+
650
+ from sympy.assumptions.ask_generated import (get_all_known_facts,
651
+ get_known_facts_dict)
lib/python3.10/site-packages/sympy/assumptions/ask_generated.py ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Do NOT manually edit this file.
3
+ Instead, run ./bin/ask_update.py.
4
+ """
5
+
6
+ from sympy.assumptions.ask import Q
7
+ from sympy.assumptions.cnf import Literal
8
+ from sympy.core.cache import cacheit
9
+
10
+ @cacheit
11
+ def get_all_known_facts():
12
+ """
13
+ Known facts between unary predicates as CNF clauses.
14
+ """
15
+ return {
16
+ frozenset((Literal(Q.algebraic, False), Literal(Q.imaginary, True), Literal(Q.transcendental, False))),
17
+ frozenset((Literal(Q.algebraic, False), Literal(Q.negative, True), Literal(Q.transcendental, False))),
18
+ frozenset((Literal(Q.algebraic, False), Literal(Q.positive, True), Literal(Q.transcendental, False))),
19
+ frozenset((Literal(Q.algebraic, False), Literal(Q.rational, True))),
20
+ frozenset((Literal(Q.algebraic, False), Literal(Q.transcendental, False), Literal(Q.zero, True))),
21
+ frozenset((Literal(Q.algebraic, True), Literal(Q.finite, False))),
22
+ frozenset((Literal(Q.algebraic, True), Literal(Q.transcendental, True))),
23
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.hermitian, False), Literal(Q.zero, True))),
24
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.imaginary, True))),
25
+ frozenset((Literal(Q.commutative, False), Literal(Q.finite, True))),
26
+ frozenset((Literal(Q.commutative, False), Literal(Q.infinite, True))),
27
+ frozenset((Literal(Q.complex_elements, False), Literal(Q.real_elements, True))),
28
+ frozenset((Literal(Q.composite, False), Literal(Q.even, True), Literal(Q.positive, True), Literal(Q.prime, False))),
29
+ frozenset((Literal(Q.composite, True), Literal(Q.even, False), Literal(Q.odd, False))),
30
+ frozenset((Literal(Q.composite, True), Literal(Q.positive, False))),
31
+ frozenset((Literal(Q.composite, True), Literal(Q.prime, True))),
32
+ frozenset((Literal(Q.diagonal, False), Literal(Q.lower_triangular, True), Literal(Q.upper_triangular, True))),
33
+ frozenset((Literal(Q.diagonal, True), Literal(Q.lower_triangular, False))),
34
+ frozenset((Literal(Q.diagonal, True), Literal(Q.normal, False))),
35
+ frozenset((Literal(Q.diagonal, True), Literal(Q.symmetric, False))),
36
+ frozenset((Literal(Q.diagonal, True), Literal(Q.upper_triangular, False))),
37
+ frozenset((Literal(Q.even, False), Literal(Q.odd, False), Literal(Q.prime, True))),
38
+ frozenset((Literal(Q.even, False), Literal(Q.zero, True))),
39
+ frozenset((Literal(Q.even, True), Literal(Q.odd, True))),
40
+ frozenset((Literal(Q.even, True), Literal(Q.rational, False))),
41
+ frozenset((Literal(Q.finite, False), Literal(Q.transcendental, True))),
42
+ frozenset((Literal(Q.finite, True), Literal(Q.infinite, True))),
43
+ frozenset((Literal(Q.fullrank, False), Literal(Q.invertible, True))),
44
+ frozenset((Literal(Q.fullrank, True), Literal(Q.invertible, False), Literal(Q.square, True))),
45
+ frozenset((Literal(Q.hermitian, False), Literal(Q.negative, True))),
46
+ frozenset((Literal(Q.hermitian, False), Literal(Q.positive, True))),
47
+ frozenset((Literal(Q.hermitian, False), Literal(Q.zero, True))),
48
+ frozenset((Literal(Q.imaginary, True), Literal(Q.negative, True))),
49
+ frozenset((Literal(Q.imaginary, True), Literal(Q.positive, True))),
50
+ frozenset((Literal(Q.imaginary, True), Literal(Q.zero, True))),
51
+ frozenset((Literal(Q.infinite, False), Literal(Q.negative_infinite, True))),
52
+ frozenset((Literal(Q.infinite, False), Literal(Q.positive_infinite, True))),
53
+ frozenset((Literal(Q.integer_elements, True), Literal(Q.real_elements, False))),
54
+ frozenset((Literal(Q.invertible, False), Literal(Q.positive_definite, True))),
55
+ frozenset((Literal(Q.invertible, False), Literal(Q.singular, False))),
56
+ frozenset((Literal(Q.invertible, False), Literal(Q.unitary, True))),
57
+ frozenset((Literal(Q.invertible, True), Literal(Q.singular, True))),
58
+ frozenset((Literal(Q.invertible, True), Literal(Q.square, False))),
59
+ frozenset((Literal(Q.irrational, False), Literal(Q.negative, True), Literal(Q.rational, False))),
60
+ frozenset((Literal(Q.irrational, False), Literal(Q.positive, True), Literal(Q.rational, False))),
61
+ frozenset((Literal(Q.irrational, False), Literal(Q.rational, False), Literal(Q.zero, True))),
62
+ frozenset((Literal(Q.irrational, True), Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.zero, False))),
63
+ frozenset((Literal(Q.irrational, True), Literal(Q.rational, True))),
64
+ frozenset((Literal(Q.lower_triangular, False), Literal(Q.triangular, True), Literal(Q.upper_triangular, False))),
65
+ frozenset((Literal(Q.lower_triangular, True), Literal(Q.triangular, False))),
66
+ frozenset((Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.rational, True), Literal(Q.zero, False))),
67
+ frozenset((Literal(Q.negative, True), Literal(Q.negative_infinite, True))),
68
+ frozenset((Literal(Q.negative, True), Literal(Q.positive, True))),
69
+ frozenset((Literal(Q.negative, True), Literal(Q.positive_infinite, True))),
70
+ frozenset((Literal(Q.negative, True), Literal(Q.zero, True))),
71
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive, True))),
72
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive_infinite, True))),
73
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.zero, True))),
74
+ frozenset((Literal(Q.normal, False), Literal(Q.unitary, True))),
75
+ frozenset((Literal(Q.normal, True), Literal(Q.square, False))),
76
+ frozenset((Literal(Q.odd, True), Literal(Q.rational, False))),
77
+ frozenset((Literal(Q.orthogonal, False), Literal(Q.real_elements, True), Literal(Q.unitary, True))),
78
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.positive_definite, False))),
79
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.unitary, False))),
80
+ frozenset((Literal(Q.positive, False), Literal(Q.prime, True))),
81
+ frozenset((Literal(Q.positive, True), Literal(Q.positive_infinite, True))),
82
+ frozenset((Literal(Q.positive, True), Literal(Q.zero, True))),
83
+ frozenset((Literal(Q.positive_infinite, True), Literal(Q.zero, True))),
84
+ frozenset((Literal(Q.square, False), Literal(Q.symmetric, True))),
85
+ frozenset((Literal(Q.triangular, False), Literal(Q.unit_triangular, True))),
86
+ frozenset((Literal(Q.triangular, False), Literal(Q.upper_triangular, True)))
87
+ }
88
+
89
+ @cacheit
90
+ def get_all_known_matrix_facts():
91
+ """
92
+ Known facts between unary predicates for matrices as CNF clauses.
93
+ """
94
+ return {
95
+ frozenset((Literal(Q.complex_elements, False), Literal(Q.real_elements, True))),
96
+ frozenset((Literal(Q.diagonal, False), Literal(Q.lower_triangular, True), Literal(Q.upper_triangular, True))),
97
+ frozenset((Literal(Q.diagonal, True), Literal(Q.lower_triangular, False))),
98
+ frozenset((Literal(Q.diagonal, True), Literal(Q.normal, False))),
99
+ frozenset((Literal(Q.diagonal, True), Literal(Q.symmetric, False))),
100
+ frozenset((Literal(Q.diagonal, True), Literal(Q.upper_triangular, False))),
101
+ frozenset((Literal(Q.fullrank, False), Literal(Q.invertible, True))),
102
+ frozenset((Literal(Q.fullrank, True), Literal(Q.invertible, False), Literal(Q.square, True))),
103
+ frozenset((Literal(Q.integer_elements, True), Literal(Q.real_elements, False))),
104
+ frozenset((Literal(Q.invertible, False), Literal(Q.positive_definite, True))),
105
+ frozenset((Literal(Q.invertible, False), Literal(Q.singular, False))),
106
+ frozenset((Literal(Q.invertible, False), Literal(Q.unitary, True))),
107
+ frozenset((Literal(Q.invertible, True), Literal(Q.singular, True))),
108
+ frozenset((Literal(Q.invertible, True), Literal(Q.square, False))),
109
+ frozenset((Literal(Q.lower_triangular, False), Literal(Q.triangular, True), Literal(Q.upper_triangular, False))),
110
+ frozenset((Literal(Q.lower_triangular, True), Literal(Q.triangular, False))),
111
+ frozenset((Literal(Q.normal, False), Literal(Q.unitary, True))),
112
+ frozenset((Literal(Q.normal, True), Literal(Q.square, False))),
113
+ frozenset((Literal(Q.orthogonal, False), Literal(Q.real_elements, True), Literal(Q.unitary, True))),
114
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.positive_definite, False))),
115
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.unitary, False))),
116
+ frozenset((Literal(Q.square, False), Literal(Q.symmetric, True))),
117
+ frozenset((Literal(Q.triangular, False), Literal(Q.unit_triangular, True))),
118
+ frozenset((Literal(Q.triangular, False), Literal(Q.upper_triangular, True)))
119
+ }
120
+
121
+ @cacheit
122
+ def get_all_known_number_facts():
123
+ """
124
+ Known facts between unary predicates for numbers as CNF clauses.
125
+ """
126
+ return {
127
+ frozenset((Literal(Q.algebraic, False), Literal(Q.imaginary, True), Literal(Q.transcendental, False))),
128
+ frozenset((Literal(Q.algebraic, False), Literal(Q.negative, True), Literal(Q.transcendental, False))),
129
+ frozenset((Literal(Q.algebraic, False), Literal(Q.positive, True), Literal(Q.transcendental, False))),
130
+ frozenset((Literal(Q.algebraic, False), Literal(Q.rational, True))),
131
+ frozenset((Literal(Q.algebraic, False), Literal(Q.transcendental, False), Literal(Q.zero, True))),
132
+ frozenset((Literal(Q.algebraic, True), Literal(Q.finite, False))),
133
+ frozenset((Literal(Q.algebraic, True), Literal(Q.transcendental, True))),
134
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.hermitian, False), Literal(Q.zero, True))),
135
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.imaginary, True))),
136
+ frozenset((Literal(Q.commutative, False), Literal(Q.finite, True))),
137
+ frozenset((Literal(Q.commutative, False), Literal(Q.infinite, True))),
138
+ frozenset((Literal(Q.composite, False), Literal(Q.even, True), Literal(Q.positive, True), Literal(Q.prime, False))),
139
+ frozenset((Literal(Q.composite, True), Literal(Q.even, False), Literal(Q.odd, False))),
140
+ frozenset((Literal(Q.composite, True), Literal(Q.positive, False))),
141
+ frozenset((Literal(Q.composite, True), Literal(Q.prime, True))),
142
+ frozenset((Literal(Q.even, False), Literal(Q.odd, False), Literal(Q.prime, True))),
143
+ frozenset((Literal(Q.even, False), Literal(Q.zero, True))),
144
+ frozenset((Literal(Q.even, True), Literal(Q.odd, True))),
145
+ frozenset((Literal(Q.even, True), Literal(Q.rational, False))),
146
+ frozenset((Literal(Q.finite, False), Literal(Q.transcendental, True))),
147
+ frozenset((Literal(Q.finite, True), Literal(Q.infinite, True))),
148
+ frozenset((Literal(Q.hermitian, False), Literal(Q.negative, True))),
149
+ frozenset((Literal(Q.hermitian, False), Literal(Q.positive, True))),
150
+ frozenset((Literal(Q.hermitian, False), Literal(Q.zero, True))),
151
+ frozenset((Literal(Q.imaginary, True), Literal(Q.negative, True))),
152
+ frozenset((Literal(Q.imaginary, True), Literal(Q.positive, True))),
153
+ frozenset((Literal(Q.imaginary, True), Literal(Q.zero, True))),
154
+ frozenset((Literal(Q.infinite, False), Literal(Q.negative_infinite, True))),
155
+ frozenset((Literal(Q.infinite, False), Literal(Q.positive_infinite, True))),
156
+ frozenset((Literal(Q.irrational, False), Literal(Q.negative, True), Literal(Q.rational, False))),
157
+ frozenset((Literal(Q.irrational, False), Literal(Q.positive, True), Literal(Q.rational, False))),
158
+ frozenset((Literal(Q.irrational, False), Literal(Q.rational, False), Literal(Q.zero, True))),
159
+ frozenset((Literal(Q.irrational, True), Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.zero, False))),
160
+ frozenset((Literal(Q.irrational, True), Literal(Q.rational, True))),
161
+ frozenset((Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.rational, True), Literal(Q.zero, False))),
162
+ frozenset((Literal(Q.negative, True), Literal(Q.negative_infinite, True))),
163
+ frozenset((Literal(Q.negative, True), Literal(Q.positive, True))),
164
+ frozenset((Literal(Q.negative, True), Literal(Q.positive_infinite, True))),
165
+ frozenset((Literal(Q.negative, True), Literal(Q.zero, True))),
166
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive, True))),
167
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive_infinite, True))),
168
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.zero, True))),
169
+ frozenset((Literal(Q.odd, True), Literal(Q.rational, False))),
170
+ frozenset((Literal(Q.positive, False), Literal(Q.prime, True))),
171
+ frozenset((Literal(Q.positive, True), Literal(Q.positive_infinite, True))),
172
+ frozenset((Literal(Q.positive, True), Literal(Q.zero, True))),
173
+ frozenset((Literal(Q.positive_infinite, True), Literal(Q.zero, True)))
174
+ }
175
+
176
+ @cacheit
177
+ def get_known_facts_dict():
178
+ """
179
+ Logical relations between unary predicates as dictionary.
180
+
181
+ Each key is a predicate, and item is two groups of predicates.
182
+ First group contains the predicates which are implied by the key, and
183
+ second group contains the predicates which are rejected by the key.
184
+
185
+ """
186
+ return {
187
+ Q.algebraic: (set([Q.algebraic, Q.commutative, Q.complex, Q.finite]),
188
+ set([Q.infinite, Q.negative_infinite, Q.positive_infinite,
189
+ Q.transcendental])),
190
+ Q.antihermitian: (set([Q.antihermitian]), set([])),
191
+ Q.commutative: (set([Q.commutative]), set([])),
192
+ Q.complex: (set([Q.commutative, Q.complex, Q.finite]),
193
+ set([Q.infinite, Q.negative_infinite, Q.positive_infinite])),
194
+ Q.complex_elements: (set([Q.complex_elements]), set([])),
195
+ Q.composite: (set([Q.algebraic, Q.commutative, Q.complex, Q.composite,
196
+ Q.extended_nonnegative, Q.extended_nonzero,
197
+ Q.extended_positive, Q.extended_real, Q.finite, Q.hermitian,
198
+ Q.integer, Q.nonnegative, Q.nonzero, Q.positive, Q.rational,
199
+ Q.real]), set([Q.extended_negative, Q.extended_nonpositive,
200
+ Q.imaginary, Q.infinite, Q.irrational, Q.negative,
201
+ Q.negative_infinite, Q.nonpositive, Q.positive_infinite,
202
+ Q.prime, Q.transcendental, Q.zero])),
203
+ Q.diagonal: (set([Q.diagonal, Q.lower_triangular, Q.normal, Q.square,
204
+ Q.symmetric, Q.triangular, Q.upper_triangular]), set([])),
205
+ Q.even: (set([Q.algebraic, Q.commutative, Q.complex, Q.even,
206
+ Q.extended_real, Q.finite, Q.hermitian, Q.integer, Q.rational,
207
+ Q.real]), set([Q.imaginary, Q.infinite, Q.irrational,
208
+ Q.negative_infinite, Q.odd, Q.positive_infinite,
209
+ Q.transcendental])),
210
+ Q.extended_negative: (set([Q.commutative, Q.extended_negative,
211
+ Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real]),
212
+ set([Q.composite, Q.extended_nonnegative, Q.extended_positive,
213
+ Q.imaginary, Q.nonnegative, Q.positive, Q.positive_infinite,
214
+ Q.prime, Q.zero])),
215
+ Q.extended_nonnegative: (set([Q.commutative, Q.extended_nonnegative,
216
+ Q.extended_real]), set([Q.extended_negative, Q.imaginary,
217
+ Q.negative, Q.negative_infinite])),
218
+ Q.extended_nonpositive: (set([Q.commutative, Q.extended_nonpositive,
219
+ Q.extended_real]), set([Q.composite, Q.extended_positive,
220
+ Q.imaginary, Q.positive, Q.positive_infinite, Q.prime])),
221
+ Q.extended_nonzero: (set([Q.commutative, Q.extended_nonzero,
222
+ Q.extended_real]), set([Q.imaginary, Q.zero])),
223
+ Q.extended_positive: (set([Q.commutative, Q.extended_nonnegative,
224
+ Q.extended_nonzero, Q.extended_positive, Q.extended_real]),
225
+ set([Q.extended_negative, Q.extended_nonpositive, Q.imaginary,
226
+ Q.negative, Q.negative_infinite, Q.nonpositive, Q.zero])),
227
+ Q.extended_real: (set([Q.commutative, Q.extended_real]),
228
+ set([Q.imaginary])),
229
+ Q.finite: (set([Q.commutative, Q.finite]), set([Q.infinite,
230
+ Q.negative_infinite, Q.positive_infinite])),
231
+ Q.fullrank: (set([Q.fullrank]), set([])),
232
+ Q.hermitian: (set([Q.hermitian]), set([])),
233
+ Q.imaginary: (set([Q.antihermitian, Q.commutative, Q.complex,
234
+ Q.finite, Q.imaginary]), set([Q.composite, Q.even,
235
+ Q.extended_negative, Q.extended_nonnegative,
236
+ Q.extended_nonpositive, Q.extended_nonzero,
237
+ Q.extended_positive, Q.extended_real, Q.infinite, Q.integer,
238
+ Q.irrational, Q.negative, Q.negative_infinite, Q.nonnegative,
239
+ Q.nonpositive, Q.nonzero, Q.odd, Q.positive,
240
+ Q.positive_infinite, Q.prime, Q.rational, Q.real, Q.zero])),
241
+ Q.infinite: (set([Q.commutative, Q.infinite]), set([Q.algebraic,
242
+ Q.complex, Q.composite, Q.even, Q.finite, Q.imaginary,
243
+ Q.integer, Q.irrational, Q.negative, Q.nonnegative,
244
+ Q.nonpositive, Q.nonzero, Q.odd, Q.positive, Q.prime,
245
+ Q.rational, Q.real, Q.transcendental, Q.zero])),
246
+ Q.integer: (set([Q.algebraic, Q.commutative, Q.complex,
247
+ Q.extended_real, Q.finite, Q.hermitian, Q.integer, Q.rational,
248
+ Q.real]), set([Q.imaginary, Q.infinite, Q.irrational,
249
+ Q.negative_infinite, Q.positive_infinite, Q.transcendental])),
250
+ Q.integer_elements: (set([Q.complex_elements, Q.integer_elements,
251
+ Q.real_elements]), set([])),
252
+ Q.invertible: (set([Q.fullrank, Q.invertible, Q.square]),
253
+ set([Q.singular])),
254
+ Q.irrational: (set([Q.commutative, Q.complex, Q.extended_nonzero,
255
+ Q.extended_real, Q.finite, Q.hermitian, Q.irrational,
256
+ Q.nonzero, Q.real]), set([Q.composite, Q.even, Q.imaginary,
257
+ Q.infinite, Q.integer, Q.negative_infinite, Q.odd,
258
+ Q.positive_infinite, Q.prime, Q.rational, Q.zero])),
259
+ Q.is_true: (set([Q.is_true]), set([])),
260
+ Q.lower_triangular: (set([Q.lower_triangular, Q.triangular]), set([])),
261
+ Q.negative: (set([Q.commutative, Q.complex, Q.extended_negative,
262
+ Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real,
263
+ Q.finite, Q.hermitian, Q.negative, Q.nonpositive, Q.nonzero,
264
+ Q.real]), set([Q.composite, Q.extended_nonnegative,
265
+ Q.extended_positive, Q.imaginary, Q.infinite,
266
+ Q.negative_infinite, Q.nonnegative, Q.positive,
267
+ Q.positive_infinite, Q.prime, Q.zero])),
268
+ Q.negative_infinite: (set([Q.commutative, Q.extended_negative,
269
+ Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real,
270
+ Q.infinite, Q.negative_infinite]), set([Q.algebraic,
271
+ Q.complex, Q.composite, Q.even, Q.extended_nonnegative,
272
+ Q.extended_positive, Q.finite, Q.imaginary, Q.integer,
273
+ Q.irrational, Q.negative, Q.nonnegative, Q.nonpositive,
274
+ Q.nonzero, Q.odd, Q.positive, Q.positive_infinite, Q.prime,
275
+ Q.rational, Q.real, Q.transcendental, Q.zero])),
276
+ Q.noninteger: (set([Q.noninteger]), set([])),
277
+ Q.nonnegative: (set([Q.commutative, Q.complex, Q.extended_nonnegative,
278
+ Q.extended_real, Q.finite, Q.hermitian, Q.nonnegative,
279
+ Q.real]), set([Q.extended_negative, Q.imaginary, Q.infinite,
280
+ Q.negative, Q.negative_infinite, Q.positive_infinite])),
281
+ Q.nonpositive: (set([Q.commutative, Q.complex, Q.extended_nonpositive,
282
+ Q.extended_real, Q.finite, Q.hermitian, Q.nonpositive,
283
+ Q.real]), set([Q.composite, Q.extended_positive, Q.imaginary,
284
+ Q.infinite, Q.negative_infinite, Q.positive,
285
+ Q.positive_infinite, Q.prime])),
286
+ Q.nonzero: (set([Q.commutative, Q.complex, Q.extended_nonzero,
287
+ Q.extended_real, Q.finite, Q.hermitian, Q.nonzero, Q.real]),
288
+ set([Q.imaginary, Q.infinite, Q.negative_infinite,
289
+ Q.positive_infinite, Q.zero])),
290
+ Q.normal: (set([Q.normal, Q.square]), set([])),
291
+ Q.odd: (set([Q.algebraic, Q.commutative, Q.complex,
292
+ Q.extended_nonzero, Q.extended_real, Q.finite, Q.hermitian,
293
+ Q.integer, Q.nonzero, Q.odd, Q.rational, Q.real]),
294
+ set([Q.even, Q.imaginary, Q.infinite, Q.irrational,
295
+ Q.negative_infinite, Q.positive_infinite, Q.transcendental,
296
+ Q.zero])),
297
+ Q.orthogonal: (set([Q.fullrank, Q.invertible, Q.normal, Q.orthogonal,
298
+ Q.positive_definite, Q.square, Q.unitary]), set([Q.singular])),
299
+ Q.positive: (set([Q.commutative, Q.complex, Q.extended_nonnegative,
300
+ Q.extended_nonzero, Q.extended_positive, Q.extended_real,
301
+ Q.finite, Q.hermitian, Q.nonnegative, Q.nonzero, Q.positive,
302
+ Q.real]), set([Q.extended_negative, Q.extended_nonpositive,
303
+ Q.imaginary, Q.infinite, Q.negative, Q.negative_infinite,
304
+ Q.nonpositive, Q.positive_infinite, Q.zero])),
305
+ Q.positive_definite: (set([Q.fullrank, Q.invertible,
306
+ Q.positive_definite, Q.square]), set([Q.singular])),
307
+ Q.positive_infinite: (set([Q.commutative, Q.extended_nonnegative,
308
+ Q.extended_nonzero, Q.extended_positive, Q.extended_real,
309
+ Q.infinite, Q.positive_infinite]), set([Q.algebraic,
310
+ Q.complex, Q.composite, Q.even, Q.extended_negative,
311
+ Q.extended_nonpositive, Q.finite, Q.imaginary, Q.integer,
312
+ Q.irrational, Q.negative, Q.negative_infinite, Q.nonnegative,
313
+ Q.nonpositive, Q.nonzero, Q.odd, Q.positive, Q.prime,
314
+ Q.rational, Q.real, Q.transcendental, Q.zero])),
315
+ Q.prime: (set([Q.algebraic, Q.commutative, Q.complex,
316
+ Q.extended_nonnegative, Q.extended_nonzero,
317
+ Q.extended_positive, Q.extended_real, Q.finite, Q.hermitian,
318
+ Q.integer, Q.nonnegative, Q.nonzero, Q.positive, Q.prime,
319
+ Q.rational, Q.real]), set([Q.composite, Q.extended_negative,
320
+ Q.extended_nonpositive, Q.imaginary, Q.infinite, Q.irrational,
321
+ Q.negative, Q.negative_infinite, Q.nonpositive,
322
+ Q.positive_infinite, Q.transcendental, Q.zero])),
323
+ Q.rational: (set([Q.algebraic, Q.commutative, Q.complex,
324
+ Q.extended_real, Q.finite, Q.hermitian, Q.rational, Q.real]),
325
+ set([Q.imaginary, Q.infinite, Q.irrational,
326
+ Q.negative_infinite, Q.positive_infinite, Q.transcendental])),
327
+ Q.real: (set([Q.commutative, Q.complex, Q.extended_real, Q.finite,
328
+ Q.hermitian, Q.real]), set([Q.imaginary, Q.infinite,
329
+ Q.negative_infinite, Q.positive_infinite])),
330
+ Q.real_elements: (set([Q.complex_elements, Q.real_elements]), set([])),
331
+ Q.singular: (set([Q.singular]), set([Q.invertible, Q.orthogonal,
332
+ Q.positive_definite, Q.unitary])),
333
+ Q.square: (set([Q.square]), set([])),
334
+ Q.symmetric: (set([Q.square, Q.symmetric]), set([])),
335
+ Q.transcendental: (set([Q.commutative, Q.complex, Q.finite,
336
+ Q.transcendental]), set([Q.algebraic, Q.composite, Q.even,
337
+ Q.infinite, Q.integer, Q.negative_infinite, Q.odd,
338
+ Q.positive_infinite, Q.prime, Q.rational, Q.zero])),
339
+ Q.triangular: (set([Q.triangular]), set([])),
340
+ Q.unit_triangular: (set([Q.triangular, Q.unit_triangular]), set([])),
341
+ Q.unitary: (set([Q.fullrank, Q.invertible, Q.normal, Q.square,
342
+ Q.unitary]), set([Q.singular])),
343
+ Q.upper_triangular: (set([Q.triangular, Q.upper_triangular]), set([])),
344
+ Q.zero: (set([Q.algebraic, Q.commutative, Q.complex, Q.even,
345
+ Q.extended_nonnegative, Q.extended_nonpositive,
346
+ Q.extended_real, Q.finite, Q.hermitian, Q.integer,
347
+ Q.nonnegative, Q.nonpositive, Q.rational, Q.real, Q.zero]),
348
+ set([Q.composite, Q.extended_negative, Q.extended_nonzero,
349
+ Q.extended_positive, Q.imaginary, Q.infinite, Q.irrational,
350
+ Q.negative, Q.negative_infinite, Q.nonzero, Q.odd, Q.positive,
351
+ Q.positive_infinite, Q.prime, Q.transcendental])),
352
+ }
lib/python3.10/site-packages/sympy/assumptions/assume.py ADDED
@@ -0,0 +1,485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A module which implements predicates and assumption context."""
2
+
3
+ from contextlib import contextmanager
4
+ import inspect
5
+ from sympy.core.symbol import Str
6
+ from sympy.core.sympify import _sympify
7
+ from sympy.logic.boolalg import Boolean, false, true
8
+ from sympy.multipledispatch.dispatcher import Dispatcher, str_signature
9
+ from sympy.utilities.exceptions import sympy_deprecation_warning
10
+ from sympy.utilities.iterables import is_sequence
11
+ from sympy.utilities.source import get_class
12
+
13
+
14
+ class AssumptionsContext(set):
15
+ """
16
+ Set containing default assumptions which are applied to the ``ask()``
17
+ function.
18
+
19
+ Explanation
20
+ ===========
21
+
22
+ This is used to represent global assumptions, but you can also use this
23
+ class to create your own local assumptions contexts. It is basically a thin
24
+ wrapper to Python's set, so see its documentation for advanced usage.
25
+
26
+ Examples
27
+ ========
28
+
29
+ The default assumption context is ``global_assumptions``, which is initially empty:
30
+
31
+ >>> from sympy import ask, Q
32
+ >>> from sympy.assumptions import global_assumptions
33
+ >>> global_assumptions
34
+ AssumptionsContext()
35
+
36
+ You can add default assumptions:
37
+
38
+ >>> from sympy.abc import x
39
+ >>> global_assumptions.add(Q.real(x))
40
+ >>> global_assumptions
41
+ AssumptionsContext({Q.real(x)})
42
+ >>> ask(Q.real(x))
43
+ True
44
+
45
+ And remove them:
46
+
47
+ >>> global_assumptions.remove(Q.real(x))
48
+ >>> print(ask(Q.real(x)))
49
+ None
50
+
51
+ The ``clear()`` method removes every assumption:
52
+
53
+ >>> global_assumptions.add(Q.positive(x))
54
+ >>> global_assumptions
55
+ AssumptionsContext({Q.positive(x)})
56
+ >>> global_assumptions.clear()
57
+ >>> global_assumptions
58
+ AssumptionsContext()
59
+
60
+ See Also
61
+ ========
62
+
63
+ assuming
64
+
65
+ """
66
+
67
+ def add(self, *assumptions):
68
+ """Add assumptions."""
69
+ for a in assumptions:
70
+ super().add(a)
71
+
72
+ def _sympystr(self, printer):
73
+ if not self:
74
+ return "%s()" % self.__class__.__name__
75
+ return "{}({})".format(self.__class__.__name__, printer._print_set(self))
76
+
77
+ global_assumptions = AssumptionsContext()
78
+
79
+
80
+ class AppliedPredicate(Boolean):
81
+ """
82
+ The class of expressions resulting from applying ``Predicate`` to
83
+ the arguments. ``AppliedPredicate`` merely wraps its argument and
84
+ remain unevaluated. To evaluate it, use the ``ask()`` function.
85
+
86
+ Examples
87
+ ========
88
+
89
+ >>> from sympy import Q, ask
90
+ >>> Q.integer(1)
91
+ Q.integer(1)
92
+
93
+ The ``function`` attribute returns the predicate, and the ``arguments``
94
+ attribute returns the tuple of arguments.
95
+
96
+ >>> type(Q.integer(1))
97
+ <class 'sympy.assumptions.assume.AppliedPredicate'>
98
+ >>> Q.integer(1).function
99
+ Q.integer
100
+ >>> Q.integer(1).arguments
101
+ (1,)
102
+
103
+ Applied predicates can be evaluated to a boolean value with ``ask``:
104
+
105
+ >>> ask(Q.integer(1))
106
+ True
107
+
108
+ """
109
+ __slots__ = ()
110
+
111
+ def __new__(cls, predicate, *args):
112
+ if not isinstance(predicate, Predicate):
113
+ raise TypeError("%s is not a Predicate." % predicate)
114
+ args = map(_sympify, args)
115
+ return super().__new__(cls, predicate, *args)
116
+
117
+ @property
118
+ def arg(self):
119
+ """
120
+ Return the expression used by this assumption.
121
+
122
+ Examples
123
+ ========
124
+
125
+ >>> from sympy import Q, Symbol
126
+ >>> x = Symbol('x')
127
+ >>> a = Q.integer(x + 1)
128
+ >>> a.arg
129
+ x + 1
130
+
131
+ """
132
+ # Will be deprecated
133
+ args = self._args
134
+ if len(args) == 2:
135
+ # backwards compatibility
136
+ return args[1]
137
+ raise TypeError("'arg' property is allowed only for unary predicates.")
138
+
139
+ @property
140
+ def function(self):
141
+ """
142
+ Return the predicate.
143
+ """
144
+ # Will be changed to self.args[0] after args overriding is removed
145
+ return self._args[0]
146
+
147
+ @property
148
+ def arguments(self):
149
+ """
150
+ Return the arguments which are applied to the predicate.
151
+ """
152
+ # Will be changed to self.args[1:] after args overriding is removed
153
+ return self._args[1:]
154
+
155
+ def _eval_ask(self, assumptions):
156
+ return self.function.eval(self.arguments, assumptions)
157
+
158
+ @property
159
+ def binary_symbols(self):
160
+ from .ask import Q
161
+ if self.function == Q.is_true:
162
+ i = self.arguments[0]
163
+ if i.is_Boolean or i.is_Symbol:
164
+ return i.binary_symbols
165
+ if self.function in (Q.eq, Q.ne):
166
+ if true in self.arguments or false in self.arguments:
167
+ if self.arguments[0].is_Symbol:
168
+ return {self.arguments[0]}
169
+ elif self.arguments[1].is_Symbol:
170
+ return {self.arguments[1]}
171
+ return set()
172
+
173
+
174
+ class PredicateMeta(type):
175
+ def __new__(cls, clsname, bases, dct):
176
+ # If handler is not defined, assign empty dispatcher.
177
+ if "handler" not in dct:
178
+ name = f"Ask{clsname.capitalize()}Handler"
179
+ handler = Dispatcher(name, doc="Handler for key %s" % name)
180
+ dct["handler"] = handler
181
+
182
+ dct["_orig_doc"] = dct.get("__doc__", "")
183
+
184
+ return super().__new__(cls, clsname, bases, dct)
185
+
186
+ @property
187
+ def __doc__(cls):
188
+ handler = cls.handler
189
+ doc = cls._orig_doc
190
+ if cls is not Predicate and handler is not None:
191
+ doc += "Handler\n"
192
+ doc += " =======\n\n"
193
+
194
+ # Append the handler's doc without breaking sphinx documentation.
195
+ docs = [" Multiply dispatched method: %s" % handler.name]
196
+ if handler.doc:
197
+ for line in handler.doc.splitlines():
198
+ if not line:
199
+ continue
200
+ docs.append(" %s" % line)
201
+ other = []
202
+ for sig in handler.ordering[::-1]:
203
+ func = handler.funcs[sig]
204
+ if func.__doc__:
205
+ s = ' Inputs: <%s>' % str_signature(sig)
206
+ lines = []
207
+ for line in func.__doc__.splitlines():
208
+ lines.append(" %s" % line)
209
+ s += "\n".join(lines)
210
+ docs.append(s)
211
+ else:
212
+ other.append(str_signature(sig))
213
+ if other:
214
+ othersig = " Other signatures:"
215
+ for line in other:
216
+ othersig += "\n * %s" % line
217
+ docs.append(othersig)
218
+
219
+ doc += '\n\n'.join(docs)
220
+
221
+ return doc
222
+
223
+
224
+ class Predicate(Boolean, metaclass=PredicateMeta):
225
+ """
226
+ Base class for mathematical predicates. It also serves as a
227
+ constructor for undefined predicate objects.
228
+
229
+ Explanation
230
+ ===========
231
+
232
+ Predicate is a function that returns a boolean value [1].
233
+
234
+ Predicate function is object, and it is instance of predicate class.
235
+ When a predicate is applied to arguments, ``AppliedPredicate``
236
+ instance is returned. This merely wraps the argument and remain
237
+ unevaluated. To obtain the truth value of applied predicate, use the
238
+ function ``ask``.
239
+
240
+ Evaluation of predicate is done by multiple dispatching. You can
241
+ register new handler to the predicate to support new types.
242
+
243
+ Every predicate in SymPy can be accessed via the property of ``Q``.
244
+ For example, ``Q.even`` returns the predicate which checks if the
245
+ argument is even number.
246
+
247
+ To define a predicate which can be evaluated, you must subclass this
248
+ class, make an instance of it, and register it to ``Q``. After then,
249
+ dispatch the handler by argument types.
250
+
251
+ If you directly construct predicate using this class, you will get
252
+ ``UndefinedPredicate`` which cannot be dispatched. This is useful
253
+ when you are building boolean expressions which do not need to be
254
+ evaluated.
255
+
256
+ Examples
257
+ ========
258
+
259
+ Applying and evaluating to boolean value:
260
+
261
+ >>> from sympy import Q, ask
262
+ >>> ask(Q.prime(7))
263
+ True
264
+
265
+ You can define a new predicate by subclassing and dispatching. Here,
266
+ we define a predicate for sexy primes [2] as an example.
267
+
268
+ >>> from sympy import Predicate, Integer
269
+ >>> class SexyPrimePredicate(Predicate):
270
+ ... name = "sexyprime"
271
+ >>> Q.sexyprime = SexyPrimePredicate()
272
+ >>> @Q.sexyprime.register(Integer, Integer)
273
+ ... def _(int1, int2, assumptions):
274
+ ... args = sorted([int1, int2])
275
+ ... if not all(ask(Q.prime(a), assumptions) for a in args):
276
+ ... return False
277
+ ... return args[1] - args[0] == 6
278
+ >>> ask(Q.sexyprime(5, 11))
279
+ True
280
+
281
+ Direct constructing returns ``UndefinedPredicate``, which can be
282
+ applied but cannot be dispatched.
283
+
284
+ >>> from sympy import Predicate, Integer
285
+ >>> Q.P = Predicate("P")
286
+ >>> type(Q.P)
287
+ <class 'sympy.assumptions.assume.UndefinedPredicate'>
288
+ >>> Q.P(1)
289
+ Q.P(1)
290
+ >>> Q.P.register(Integer)(lambda expr, assump: True)
291
+ Traceback (most recent call last):
292
+ ...
293
+ TypeError: <class 'sympy.assumptions.assume.UndefinedPredicate'> cannot be dispatched.
294
+
295
+ References
296
+ ==========
297
+
298
+ .. [1] https://en.wikipedia.org/wiki/Predicate_%28mathematical_logic%29
299
+ .. [2] https://en.wikipedia.org/wiki/Sexy_prime
300
+
301
+ """
302
+
303
+ is_Atom = True
304
+
305
+ def __new__(cls, *args, **kwargs):
306
+ if cls is Predicate:
307
+ return UndefinedPredicate(*args, **kwargs)
308
+ obj = super().__new__(cls, *args)
309
+ return obj
310
+
311
+ @property
312
+ def name(self):
313
+ # May be overridden
314
+ return type(self).__name__
315
+
316
+ @classmethod
317
+ def register(cls, *types, **kwargs):
318
+ """
319
+ Register the signature to the handler.
320
+ """
321
+ if cls.handler is None:
322
+ raise TypeError("%s cannot be dispatched." % type(cls))
323
+ return cls.handler.register(*types, **kwargs)
324
+
325
+ @classmethod
326
+ def register_many(cls, *types, **kwargs):
327
+ """
328
+ Register multiple signatures to same handler.
329
+ """
330
+ def _(func):
331
+ for t in types:
332
+ if not is_sequence(t):
333
+ t = (t,) # for convenience, allow passing `type` to mean `(type,)`
334
+ cls.register(*t, **kwargs)(func)
335
+ return _
336
+
337
+ def __call__(self, *args):
338
+ return AppliedPredicate(self, *args)
339
+
340
+ def eval(self, args, assumptions=True):
341
+ """
342
+ Evaluate ``self(*args)`` under the given assumptions.
343
+
344
+ This uses only direct resolution methods, not logical inference.
345
+ """
346
+ result = None
347
+ try:
348
+ result = self.handler(*args, assumptions=assumptions)
349
+ except NotImplementedError:
350
+ pass
351
+ return result
352
+
353
+ def _eval_refine(self, assumptions):
354
+ # When Predicate is no longer Boolean, delete this method
355
+ return self
356
+
357
+
358
+ class UndefinedPredicate(Predicate):
359
+ """
360
+ Predicate without handler.
361
+
362
+ Explanation
363
+ ===========
364
+
365
+ This predicate is generated by using ``Predicate`` directly for
366
+ construction. It does not have a handler, and evaluating this with
367
+ arguments is done by SAT solver.
368
+
369
+ Examples
370
+ ========
371
+
372
+ >>> from sympy import Predicate, Q
373
+ >>> Q.P = Predicate('P')
374
+ >>> Q.P.func
375
+ <class 'sympy.assumptions.assume.UndefinedPredicate'>
376
+ >>> Q.P.name
377
+ Str('P')
378
+
379
+ """
380
+
381
+ handler = None
382
+
383
+ def __new__(cls, name, handlers=None):
384
+ # "handlers" parameter supports old design
385
+ if not isinstance(name, Str):
386
+ name = Str(name)
387
+ obj = super(Boolean, cls).__new__(cls, name)
388
+ obj.handlers = handlers or []
389
+ return obj
390
+
391
+ @property
392
+ def name(self):
393
+ return self.args[0]
394
+
395
+ def _hashable_content(self):
396
+ return (self.name,)
397
+
398
+ def __getnewargs__(self):
399
+ return (self.name,)
400
+
401
+ def __call__(self, expr):
402
+ return AppliedPredicate(self, expr)
403
+
404
+ def add_handler(self, handler):
405
+ sympy_deprecation_warning(
406
+ """
407
+ The AskHandler system is deprecated. Predicate.add_handler()
408
+ should be replaced with the multipledispatch handler of Predicate.
409
+ """,
410
+ deprecated_since_version="1.8",
411
+ active_deprecations_target='deprecated-askhandler',
412
+ )
413
+ self.handlers.append(handler)
414
+
415
+ def remove_handler(self, handler):
416
+ sympy_deprecation_warning(
417
+ """
418
+ The AskHandler system is deprecated. Predicate.remove_handler()
419
+ should be replaced with the multipledispatch handler of Predicate.
420
+ """,
421
+ deprecated_since_version="1.8",
422
+ active_deprecations_target='deprecated-askhandler',
423
+ )
424
+ self.handlers.remove(handler)
425
+
426
+ def eval(self, args, assumptions=True):
427
+ # Support for deprecated design
428
+ # When old design is removed, this will always return None
429
+ sympy_deprecation_warning(
430
+ """
431
+ The AskHandler system is deprecated. Evaluating UndefinedPredicate
432
+ objects should be replaced with the multipledispatch handler of
433
+ Predicate.
434
+ """,
435
+ deprecated_since_version="1.8",
436
+ active_deprecations_target='deprecated-askhandler',
437
+ stacklevel=5,
438
+ )
439
+ expr, = args
440
+ res, _res = None, None
441
+ mro = inspect.getmro(type(expr))
442
+ for handler in self.handlers:
443
+ cls = get_class(handler)
444
+ for subclass in mro:
445
+ eval_ = getattr(cls, subclass.__name__, None)
446
+ if eval_ is None:
447
+ continue
448
+ res = eval_(expr, assumptions)
449
+ # Do not stop if value returned is None
450
+ # Try to check for higher classes
451
+ if res is None:
452
+ continue
453
+ if _res is None:
454
+ _res = res
455
+ else:
456
+ # only check consistency if both resolutors have concluded
457
+ if _res != res:
458
+ raise ValueError('incompatible resolutors')
459
+ break
460
+ return res
461
+
462
+
463
+ @contextmanager
464
+ def assuming(*assumptions):
465
+ """
466
+ Context manager for assumptions.
467
+
468
+ Examples
469
+ ========
470
+
471
+ >>> from sympy import assuming, Q, ask
472
+ >>> from sympy.abc import x, y
473
+ >>> print(ask(Q.integer(x + y)))
474
+ None
475
+ >>> with assuming(Q.integer(x), Q.integer(y)):
476
+ ... print(ask(Q.integer(x + y)))
477
+ True
478
+ """
479
+ old_global_assumptions = global_assumptions.copy()
480
+ global_assumptions.update(assumptions)
481
+ try:
482
+ yield
483
+ finally:
484
+ global_assumptions.clear()
485
+ global_assumptions.update(old_global_assumptions)
lib/python3.10/site-packages/sympy/assumptions/cnf.py ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ The classes used here are for the internal use of assumptions system
3
+ only and should not be used anywhere else as these do not possess the
4
+ signatures common to SymPy objects. For general use of logic constructs
5
+ please refer to sympy.logic classes And, Or, Not, etc.
6
+ """
7
+ from itertools import combinations, product, zip_longest
8
+ from sympy.assumptions.assume import AppliedPredicate, Predicate
9
+ from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le
10
+ from sympy.core.singleton import S
11
+ from sympy.logic.boolalg import Or, And, Not, Xnor
12
+ from sympy.logic.boolalg import (Equivalent, ITE, Implies, Nand, Nor, Xor)
13
+
14
+
15
+ class Literal:
16
+ """
17
+ The smallest element of a CNF object.
18
+
19
+ Parameters
20
+ ==========
21
+
22
+ lit : Boolean expression
23
+
24
+ is_Not : bool
25
+
26
+ Examples
27
+ ========
28
+
29
+ >>> from sympy import Q
30
+ >>> from sympy.assumptions.cnf import Literal
31
+ >>> from sympy.abc import x
32
+ >>> Literal(Q.even(x))
33
+ Literal(Q.even(x), False)
34
+ >>> Literal(~Q.even(x))
35
+ Literal(Q.even(x), True)
36
+ """
37
+
38
+ def __new__(cls, lit, is_Not=False):
39
+ if isinstance(lit, Not):
40
+ lit = lit.args[0]
41
+ is_Not = True
42
+ elif isinstance(lit, (AND, OR, Literal)):
43
+ return ~lit if is_Not else lit
44
+ obj = super().__new__(cls)
45
+ obj.lit = lit
46
+ obj.is_Not = is_Not
47
+ return obj
48
+
49
+ @property
50
+ def arg(self):
51
+ return self.lit
52
+
53
+ def rcall(self, expr):
54
+ if callable(self.lit):
55
+ lit = self.lit(expr)
56
+ else:
57
+ lit = self.lit.apply(expr)
58
+ return type(self)(lit, self.is_Not)
59
+
60
+ def __invert__(self):
61
+ is_Not = not self.is_Not
62
+ return Literal(self.lit, is_Not)
63
+
64
+ def __str__(self):
65
+ return '{}({}, {})'.format(type(self).__name__, self.lit, self.is_Not)
66
+
67
+ __repr__ = __str__
68
+
69
+ def __eq__(self, other):
70
+ return self.arg == other.arg and self.is_Not == other.is_Not
71
+
72
+ def __hash__(self):
73
+ h = hash((type(self).__name__, self.arg, self.is_Not))
74
+ return h
75
+
76
+
77
+ class OR:
78
+ """
79
+ A low-level implementation for Or
80
+ """
81
+ def __init__(self, *args):
82
+ self._args = args
83
+
84
+ @property
85
+ def args(self):
86
+ return sorted(self._args, key=str)
87
+
88
+ def rcall(self, expr):
89
+ return type(self)(*[arg.rcall(expr)
90
+ for arg in self._args
91
+ ])
92
+
93
+ def __invert__(self):
94
+ return AND(*[~arg for arg in self._args])
95
+
96
+ def __hash__(self):
97
+ return hash((type(self).__name__,) + tuple(self.args))
98
+
99
+ def __eq__(self, other):
100
+ return self.args == other.args
101
+
102
+ def __str__(self):
103
+ s = '(' + ' | '.join([str(arg) for arg in self.args]) + ')'
104
+ return s
105
+
106
+ __repr__ = __str__
107
+
108
+
109
+ class AND:
110
+ """
111
+ A low-level implementation for And
112
+ """
113
+ def __init__(self, *args):
114
+ self._args = args
115
+
116
+ def __invert__(self):
117
+ return OR(*[~arg for arg in self._args])
118
+
119
+ @property
120
+ def args(self):
121
+ return sorted(self._args, key=str)
122
+
123
+ def rcall(self, expr):
124
+ return type(self)(*[arg.rcall(expr)
125
+ for arg in self._args
126
+ ])
127
+
128
+ def __hash__(self):
129
+ return hash((type(self).__name__,) + tuple(self.args))
130
+
131
+ def __eq__(self, other):
132
+ return self.args == other.args
133
+
134
+ def __str__(self):
135
+ s = '('+' & '.join([str(arg) for arg in self.args])+')'
136
+ return s
137
+
138
+ __repr__ = __str__
139
+
140
+
141
+ def to_NNF(expr, composite_map=None):
142
+ """
143
+ Generates the Negation Normal Form of any boolean expression in terms
144
+ of AND, OR, and Literal objects.
145
+
146
+ Examples
147
+ ========
148
+
149
+ >>> from sympy import Q, Eq
150
+ >>> from sympy.assumptions.cnf import to_NNF
151
+ >>> from sympy.abc import x, y
152
+ >>> expr = Q.even(x) & ~Q.positive(x)
153
+ >>> to_NNF(expr)
154
+ (Literal(Q.even(x), False) & Literal(Q.positive(x), True))
155
+
156
+ Supported boolean objects are converted to corresponding predicates.
157
+
158
+ >>> to_NNF(Eq(x, y))
159
+ Literal(Q.eq(x, y), False)
160
+
161
+ If ``composite_map`` argument is given, ``to_NNF`` decomposes the
162
+ specified predicate into a combination of primitive predicates.
163
+
164
+ >>> cmap = {Q.nonpositive: Q.negative | Q.zero}
165
+ >>> to_NNF(Q.nonpositive, cmap)
166
+ (Literal(Q.negative, False) | Literal(Q.zero, False))
167
+ >>> to_NNF(Q.nonpositive(x), cmap)
168
+ (Literal(Q.negative(x), False) | Literal(Q.zero(x), False))
169
+ """
170
+ from sympy.assumptions.ask import Q
171
+
172
+ if composite_map is None:
173
+ composite_map = {}
174
+
175
+
176
+ binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le}
177
+ if type(expr) in binrelpreds:
178
+ pred = binrelpreds[type(expr)]
179
+ expr = pred(*expr.args)
180
+
181
+ if isinstance(expr, Not):
182
+ arg = expr.args[0]
183
+ tmp = to_NNF(arg, composite_map) # Strategy: negate the NNF of expr
184
+ return ~tmp
185
+
186
+ if isinstance(expr, Or):
187
+ return OR(*[to_NNF(x, composite_map) for x in Or.make_args(expr)])
188
+
189
+ if isinstance(expr, And):
190
+ return AND(*[to_NNF(x, composite_map) for x in And.make_args(expr)])
191
+
192
+ if isinstance(expr, Nand):
193
+ tmp = AND(*[to_NNF(x, composite_map) for x in expr.args])
194
+ return ~tmp
195
+
196
+ if isinstance(expr, Nor):
197
+ tmp = OR(*[to_NNF(x, composite_map) for x in expr.args])
198
+ return ~tmp
199
+
200
+ if isinstance(expr, Xor):
201
+ cnfs = []
202
+ for i in range(0, len(expr.args) + 1, 2):
203
+ for neg in combinations(expr.args, i):
204
+ clause = [~to_NNF(s, composite_map) if s in neg else to_NNF(s, composite_map)
205
+ for s in expr.args]
206
+ cnfs.append(OR(*clause))
207
+ return AND(*cnfs)
208
+
209
+ if isinstance(expr, Xnor):
210
+ cnfs = []
211
+ for i in range(0, len(expr.args) + 1, 2):
212
+ for neg in combinations(expr.args, i):
213
+ clause = [~to_NNF(s, composite_map) if s in neg else to_NNF(s, composite_map)
214
+ for s in expr.args]
215
+ cnfs.append(OR(*clause))
216
+ return ~AND(*cnfs)
217
+
218
+ if isinstance(expr, Implies):
219
+ L, R = to_NNF(expr.args[0], composite_map), to_NNF(expr.args[1], composite_map)
220
+ return OR(~L, R)
221
+
222
+ if isinstance(expr, Equivalent):
223
+ cnfs = []
224
+ for a, b in zip_longest(expr.args, expr.args[1:], fillvalue=expr.args[0]):
225
+ a = to_NNF(a, composite_map)
226
+ b = to_NNF(b, composite_map)
227
+ cnfs.append(OR(~a, b))
228
+ return AND(*cnfs)
229
+
230
+ if isinstance(expr, ITE):
231
+ L = to_NNF(expr.args[0], composite_map)
232
+ M = to_NNF(expr.args[1], composite_map)
233
+ R = to_NNF(expr.args[2], composite_map)
234
+ return AND(OR(~L, M), OR(L, R))
235
+
236
+ if isinstance(expr, AppliedPredicate):
237
+ pred, args = expr.function, expr.arguments
238
+ newpred = composite_map.get(pred, None)
239
+ if newpred is not None:
240
+ return to_NNF(newpred.rcall(*args), composite_map)
241
+
242
+ if isinstance(expr, Predicate):
243
+ newpred = composite_map.get(expr, None)
244
+ if newpred is not None:
245
+ return to_NNF(newpred, composite_map)
246
+
247
+ return Literal(expr)
248
+
249
+
250
+ def distribute_AND_over_OR(expr):
251
+ """
252
+ Distributes AND over OR in the NNF expression.
253
+ Returns the result( Conjunctive Normal Form of expression)
254
+ as a CNF object.
255
+ """
256
+ if not isinstance(expr, (AND, OR)):
257
+ tmp = set()
258
+ tmp.add(frozenset((expr,)))
259
+ return CNF(tmp)
260
+
261
+ if isinstance(expr, OR):
262
+ return CNF.all_or(*[distribute_AND_over_OR(arg)
263
+ for arg in expr._args])
264
+
265
+ if isinstance(expr, AND):
266
+ return CNF.all_and(*[distribute_AND_over_OR(arg)
267
+ for arg in expr._args])
268
+
269
+
270
+ class CNF:
271
+ """
272
+ Class to represent CNF of a Boolean expression.
273
+ Consists of set of clauses, which themselves are stored as
274
+ frozenset of Literal objects.
275
+
276
+ Examples
277
+ ========
278
+
279
+ >>> from sympy import Q
280
+ >>> from sympy.assumptions.cnf import CNF
281
+ >>> from sympy.abc import x
282
+ >>> cnf = CNF.from_prop(Q.real(x) & ~Q.zero(x))
283
+ >>> cnf.clauses
284
+ {frozenset({Literal(Q.zero(x), True)}),
285
+ frozenset({Literal(Q.negative(x), False),
286
+ Literal(Q.positive(x), False), Literal(Q.zero(x), False)})}
287
+ """
288
+ def __init__(self, clauses=None):
289
+ if not clauses:
290
+ clauses = set()
291
+ self.clauses = clauses
292
+
293
+ def add(self, prop):
294
+ clauses = CNF.to_CNF(prop).clauses
295
+ self.add_clauses(clauses)
296
+
297
+ def __str__(self):
298
+ s = ' & '.join(
299
+ ['(' + ' | '.join([str(lit) for lit in clause]) +')'
300
+ for clause in self.clauses]
301
+ )
302
+ return s
303
+
304
+ def extend(self, props):
305
+ for p in props:
306
+ self.add(p)
307
+ return self
308
+
309
+ def copy(self):
310
+ return CNF(set(self.clauses))
311
+
312
+ def add_clauses(self, clauses):
313
+ self.clauses |= clauses
314
+
315
+ @classmethod
316
+ def from_prop(cls, prop):
317
+ res = cls()
318
+ res.add(prop)
319
+ return res
320
+
321
+ def __iand__(self, other):
322
+ self.add_clauses(other.clauses)
323
+ return self
324
+
325
+ def all_predicates(self):
326
+ predicates = set()
327
+ for c in self.clauses:
328
+ predicates |= {arg.lit for arg in c}
329
+ return predicates
330
+
331
+ def _or(self, cnf):
332
+ clauses = set()
333
+ for a, b in product(self.clauses, cnf.clauses):
334
+ tmp = set(a)
335
+ tmp.update(b)
336
+ clauses.add(frozenset(tmp))
337
+ return CNF(clauses)
338
+
339
+ def _and(self, cnf):
340
+ clauses = self.clauses.union(cnf.clauses)
341
+ return CNF(clauses)
342
+
343
+ def _not(self):
344
+ clss = list(self.clauses)
345
+ ll = {frozenset((~x,)) for x in clss[-1]}
346
+ ll = CNF(ll)
347
+
348
+ for rest in clss[:-1]:
349
+ p = {frozenset((~x,)) for x in rest}
350
+ ll = ll._or(CNF(p))
351
+ return ll
352
+
353
+ def rcall(self, expr):
354
+ clause_list = []
355
+ for clause in self.clauses:
356
+ lits = [arg.rcall(expr) for arg in clause]
357
+ clause_list.append(OR(*lits))
358
+ expr = AND(*clause_list)
359
+ return distribute_AND_over_OR(expr)
360
+
361
+ @classmethod
362
+ def all_or(cls, *cnfs):
363
+ b = cnfs[0].copy()
364
+ for rest in cnfs[1:]:
365
+ b = b._or(rest)
366
+ return b
367
+
368
+ @classmethod
369
+ def all_and(cls, *cnfs):
370
+ b = cnfs[0].copy()
371
+ for rest in cnfs[1:]:
372
+ b = b._and(rest)
373
+ return b
374
+
375
+ @classmethod
376
+ def to_CNF(cls, expr):
377
+ from sympy.assumptions.facts import get_composite_predicates
378
+ expr = to_NNF(expr, get_composite_predicates())
379
+ expr = distribute_AND_over_OR(expr)
380
+ return expr
381
+
382
+ @classmethod
383
+ def CNF_to_cnf(cls, cnf):
384
+ """
385
+ Converts CNF object to SymPy's boolean expression
386
+ retaining the form of expression.
387
+ """
388
+ def remove_literal(arg):
389
+ return Not(arg.lit) if arg.is_Not else arg.lit
390
+
391
+ return And(*(Or(*(remove_literal(arg) for arg in clause)) for clause in cnf.clauses))
392
+
393
+
394
+ class EncodedCNF:
395
+ """
396
+ Class for encoding the CNF expression.
397
+ """
398
+ def __init__(self, data=None, encoding=None):
399
+ if not data and not encoding:
400
+ data = []
401
+ encoding = {}
402
+ self.data = data
403
+ self.encoding = encoding
404
+ self._symbols = list(encoding.keys())
405
+
406
+ def from_cnf(self, cnf):
407
+ self._symbols = list(cnf.all_predicates())
408
+ n = len(self._symbols)
409
+ self.encoding = dict(zip(self._symbols, range(1, n + 1)))
410
+ self.data = [self.encode(clause) for clause in cnf.clauses]
411
+
412
+ @property
413
+ def symbols(self):
414
+ return self._symbols
415
+
416
+ @property
417
+ def variables(self):
418
+ return range(1, len(self._symbols) + 1)
419
+
420
+ def copy(self):
421
+ new_data = [set(clause) for clause in self.data]
422
+ return EncodedCNF(new_data, dict(self.encoding))
423
+
424
+ def add_prop(self, prop):
425
+ cnf = CNF.from_prop(prop)
426
+ self.add_from_cnf(cnf)
427
+
428
+ def add_from_cnf(self, cnf):
429
+ clauses = [self.encode(clause) for clause in cnf.clauses]
430
+ self.data += clauses
431
+
432
+ def encode_arg(self, arg):
433
+ literal = arg.lit
434
+ value = self.encoding.get(literal, None)
435
+ if value is None:
436
+ n = len(self._symbols)
437
+ self._symbols.append(literal)
438
+ value = self.encoding[literal] = n + 1
439
+ if arg.is_Not:
440
+ return -value
441
+ else:
442
+ return value
443
+
444
+ def encode(self, clause):
445
+ return {self.encode_arg(arg) if not arg.lit == S.false else 0 for arg in clause}
lib/python3.10/site-packages/sympy/assumptions/facts.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Known facts in assumptions module.
3
+
4
+ This module defines the facts between unary predicates in ``get_known_facts()``,
5
+ and supports functions to generate the contents in
6
+ ``sympy.assumptions.ask_generated`` file.
7
+ """
8
+
9
+ from sympy.assumptions.ask import Q
10
+ from sympy.assumptions.assume import AppliedPredicate
11
+ from sympy.core.cache import cacheit
12
+ from sympy.core.symbol import Symbol
13
+ from sympy.logic.boolalg import (to_cnf, And, Not, Implies, Equivalent,
14
+ Exclusive,)
15
+ from sympy.logic.inference import satisfiable
16
+
17
+
18
+ @cacheit
19
+ def get_composite_predicates():
20
+ # To reduce the complexity of sat solver, these predicates are
21
+ # transformed into the combination of primitive predicates.
22
+ return {
23
+ Q.real : Q.negative | Q.zero | Q.positive,
24
+ Q.integer : Q.even | Q.odd,
25
+ Q.nonpositive : Q.negative | Q.zero,
26
+ Q.nonzero : Q.negative | Q.positive,
27
+ Q.nonnegative : Q.zero | Q.positive,
28
+ Q.extended_real : Q.negative_infinite | Q.negative | Q.zero | Q.positive | Q.positive_infinite,
29
+ Q.extended_positive: Q.positive | Q.positive_infinite,
30
+ Q.extended_negative: Q.negative | Q.negative_infinite,
31
+ Q.extended_nonzero: Q.negative_infinite | Q.negative | Q.positive | Q.positive_infinite,
32
+ Q.extended_nonpositive: Q.negative_infinite | Q.negative | Q.zero,
33
+ Q.extended_nonnegative: Q.zero | Q.positive | Q.positive_infinite,
34
+ Q.complex : Q.algebraic | Q.transcendental
35
+ }
36
+
37
+
38
+ @cacheit
39
+ def get_known_facts(x=None):
40
+ """
41
+ Facts between unary predicates.
42
+
43
+ Parameters
44
+ ==========
45
+
46
+ x : Symbol, optional
47
+ Placeholder symbol for unary facts. Default is ``Symbol('x')``.
48
+
49
+ Returns
50
+ =======
51
+
52
+ fact : Known facts in conjugated normal form.
53
+
54
+ """
55
+ if x is None:
56
+ x = Symbol('x')
57
+
58
+ fact = And(
59
+ get_number_facts(x),
60
+ get_matrix_facts(x)
61
+ )
62
+ return fact
63
+
64
+
65
+ @cacheit
66
+ def get_number_facts(x = None):
67
+ """
68
+ Facts between unary number predicates.
69
+
70
+ Parameters
71
+ ==========
72
+
73
+ x : Symbol, optional
74
+ Placeholder symbol for unary facts. Default is ``Symbol('x')``.
75
+
76
+ Returns
77
+ =======
78
+
79
+ fact : Known facts in conjugated normal form.
80
+
81
+ """
82
+ if x is None:
83
+ x = Symbol('x')
84
+
85
+ fact = And(
86
+ # primitive predicates for extended real exclude each other.
87
+ Exclusive(Q.negative_infinite(x), Q.negative(x), Q.zero(x),
88
+ Q.positive(x), Q.positive_infinite(x)),
89
+
90
+ # build complex plane
91
+ Exclusive(Q.real(x), Q.imaginary(x)),
92
+ Implies(Q.real(x) | Q.imaginary(x), Q.complex(x)),
93
+
94
+ # other subsets of complex
95
+ Exclusive(Q.transcendental(x), Q.algebraic(x)),
96
+ Equivalent(Q.real(x), Q.rational(x) | Q.irrational(x)),
97
+ Exclusive(Q.irrational(x), Q.rational(x)),
98
+ Implies(Q.rational(x), Q.algebraic(x)),
99
+
100
+ # integers
101
+ Exclusive(Q.even(x), Q.odd(x)),
102
+ Implies(Q.integer(x), Q.rational(x)),
103
+ Implies(Q.zero(x), Q.even(x)),
104
+ Exclusive(Q.composite(x), Q.prime(x)),
105
+ Implies(Q.composite(x) | Q.prime(x), Q.integer(x) & Q.positive(x)),
106
+ Implies(Q.even(x) & Q.positive(x) & ~Q.prime(x), Q.composite(x)),
107
+
108
+ # hermitian and antihermitian
109
+ Implies(Q.real(x), Q.hermitian(x)),
110
+ Implies(Q.imaginary(x), Q.antihermitian(x)),
111
+ Implies(Q.zero(x), Q.hermitian(x) | Q.antihermitian(x)),
112
+
113
+ # define finity and infinity, and build extended real line
114
+ Exclusive(Q.infinite(x), Q.finite(x)),
115
+ Implies(Q.complex(x), Q.finite(x)),
116
+ Implies(Q.negative_infinite(x) | Q.positive_infinite(x), Q.infinite(x)),
117
+
118
+ # commutativity
119
+ Implies(Q.finite(x) | Q.infinite(x), Q.commutative(x)),
120
+ )
121
+ return fact
122
+
123
+
124
+ @cacheit
125
+ def get_matrix_facts(x = None):
126
+ """
127
+ Facts between unary matrix predicates.
128
+
129
+ Parameters
130
+ ==========
131
+
132
+ x : Symbol, optional
133
+ Placeholder symbol for unary facts. Default is ``Symbol('x')``.
134
+
135
+ Returns
136
+ =======
137
+
138
+ fact : Known facts in conjugated normal form.
139
+
140
+ """
141
+ if x is None:
142
+ x = Symbol('x')
143
+
144
+ fact = And(
145
+ # matrices
146
+ Implies(Q.orthogonal(x), Q.positive_definite(x)),
147
+ Implies(Q.orthogonal(x), Q.unitary(x)),
148
+ Implies(Q.unitary(x) & Q.real_elements(x), Q.orthogonal(x)),
149
+ Implies(Q.unitary(x), Q.normal(x)),
150
+ Implies(Q.unitary(x), Q.invertible(x)),
151
+ Implies(Q.normal(x), Q.square(x)),
152
+ Implies(Q.diagonal(x), Q.normal(x)),
153
+ Implies(Q.positive_definite(x), Q.invertible(x)),
154
+ Implies(Q.diagonal(x), Q.upper_triangular(x)),
155
+ Implies(Q.diagonal(x), Q.lower_triangular(x)),
156
+ Implies(Q.lower_triangular(x), Q.triangular(x)),
157
+ Implies(Q.upper_triangular(x), Q.triangular(x)),
158
+ Implies(Q.triangular(x), Q.upper_triangular(x) | Q.lower_triangular(x)),
159
+ Implies(Q.upper_triangular(x) & Q.lower_triangular(x), Q.diagonal(x)),
160
+ Implies(Q.diagonal(x), Q.symmetric(x)),
161
+ Implies(Q.unit_triangular(x), Q.triangular(x)),
162
+ Implies(Q.invertible(x), Q.fullrank(x)),
163
+ Implies(Q.invertible(x), Q.square(x)),
164
+ Implies(Q.symmetric(x), Q.square(x)),
165
+ Implies(Q.fullrank(x) & Q.square(x), Q.invertible(x)),
166
+ Equivalent(Q.invertible(x), ~Q.singular(x)),
167
+ Implies(Q.integer_elements(x), Q.real_elements(x)),
168
+ Implies(Q.real_elements(x), Q.complex_elements(x)),
169
+ )
170
+ return fact
171
+
172
+
173
+
174
+ def generate_known_facts_dict(keys, fact):
175
+ """
176
+ Computes and returns a dictionary which contains the relations between
177
+ unary predicates.
178
+
179
+ Each key is a predicate, and item is two groups of predicates.
180
+ First group contains the predicates which are implied by the key, and
181
+ second group contains the predicates which are rejected by the key.
182
+
183
+ All predicates in *keys* and *fact* must be unary and have same placeholder
184
+ symbol.
185
+
186
+ Parameters
187
+ ==========
188
+
189
+ keys : list of AppliedPredicate instances.
190
+
191
+ fact : Fact between predicates in conjugated normal form.
192
+
193
+ Examples
194
+ ========
195
+
196
+ >>> from sympy import Q, And, Implies
197
+ >>> from sympy.assumptions.facts import generate_known_facts_dict
198
+ >>> from sympy.abc import x
199
+ >>> keys = [Q.even(x), Q.odd(x), Q.zero(x)]
200
+ >>> fact = And(Implies(Q.even(x), ~Q.odd(x)),
201
+ ... Implies(Q.zero(x), Q.even(x)))
202
+ >>> generate_known_facts_dict(keys, fact)
203
+ {Q.even: ({Q.even}, {Q.odd}),
204
+ Q.odd: ({Q.odd}, {Q.even, Q.zero}),
205
+ Q.zero: ({Q.even, Q.zero}, {Q.odd})}
206
+ """
207
+ fact_cnf = to_cnf(fact)
208
+ mapping = single_fact_lookup(keys, fact_cnf)
209
+
210
+ ret = {}
211
+ for key, value in mapping.items():
212
+ implied = set()
213
+ rejected = set()
214
+ for expr in value:
215
+ if isinstance(expr, AppliedPredicate):
216
+ implied.add(expr.function)
217
+ elif isinstance(expr, Not):
218
+ pred = expr.args[0]
219
+ rejected.add(pred.function)
220
+ ret[key.function] = (implied, rejected)
221
+ return ret
222
+
223
+
224
+ @cacheit
225
+ def get_known_facts_keys():
226
+ """
227
+ Return every unary predicates registered to ``Q``.
228
+
229
+ This function is used to generate the keys for
230
+ ``generate_known_facts_dict``.
231
+
232
+ """
233
+ # exclude polyadic predicates
234
+ exclude = {Q.eq, Q.ne, Q.gt, Q.lt, Q.ge, Q.le}
235
+
236
+ result = []
237
+ for attr in Q.__class__.__dict__:
238
+ if attr.startswith('__'):
239
+ continue
240
+ pred = getattr(Q, attr)
241
+ if pred in exclude:
242
+ continue
243
+ result.append(pred)
244
+ return result
245
+
246
+
247
+ def single_fact_lookup(known_facts_keys, known_facts_cnf):
248
+ # Return the dictionary for quick lookup of single fact
249
+ mapping = {}
250
+ for key in known_facts_keys:
251
+ mapping[key] = {key}
252
+ for other_key in known_facts_keys:
253
+ if other_key != key:
254
+ if ask_full_inference(other_key, key, known_facts_cnf):
255
+ mapping[key].add(other_key)
256
+ if ask_full_inference(~other_key, key, known_facts_cnf):
257
+ mapping[key].add(~other_key)
258
+ return mapping
259
+
260
+
261
+ def ask_full_inference(proposition, assumptions, known_facts_cnf):
262
+ """
263
+ Method for inferring properties about objects.
264
+
265
+ """
266
+ if not satisfiable(And(known_facts_cnf, assumptions, proposition)):
267
+ return False
268
+ if not satisfiable(And(known_facts_cnf, assumptions, Not(proposition))):
269
+ return True
270
+ return None
lib/python3.10/site-packages/sympy/assumptions/handlers/__init__.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Multipledispatch handlers for ``Predicate`` are implemented here.
3
+ Handlers in this module are not directly imported to other modules in
4
+ order to avoid circular import problem.
5
+ """
6
+
7
+ from .common import (AskHandler, CommonHandler,
8
+ test_closed_group)
9
+
10
+ __all__ = [
11
+ 'AskHandler', 'CommonHandler',
12
+ 'test_closed_group'
13
+ ]
lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (523 Bytes). View file
 
lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/calculus.cpython-310.pyc ADDED
Binary file (6.4 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/common.cpython-310.pyc ADDED
Binary file (4.66 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/matrices.cpython-310.pyc ADDED
Binary file (18.6 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/ntheory.cpython-310.pyc ADDED
Binary file (6.07 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/order.cpython-310.pyc ADDED
Binary file (10.4 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/handlers/__pycache__/sets.cpython-310.pyc ADDED
Binary file (17.9 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/handlers/calculus.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains query handlers responsible for calculus queries:
3
+ infinitesimal, finite, etc.
4
+ """
5
+
6
+ from sympy.assumptions import Q, ask
7
+ from sympy.core import Add, Mul, Pow, Symbol
8
+ from sympy.core.numbers import (NegativeInfinity, GoldenRatio,
9
+ Infinity, Exp1, ComplexInfinity, ImaginaryUnit, NaN, Number, Pi, E,
10
+ TribonacciConstant)
11
+ from sympy.functions import cos, exp, log, sign, sin
12
+ from sympy.logic.boolalg import conjuncts
13
+
14
+ from ..predicates.calculus import (FinitePredicate, InfinitePredicate,
15
+ PositiveInfinitePredicate, NegativeInfinitePredicate)
16
+
17
+
18
+ # FinitePredicate
19
+
20
+
21
+ @FinitePredicate.register(Symbol)
22
+ def _(expr, assumptions):
23
+ """
24
+ Handles Symbol.
25
+ """
26
+ if expr.is_finite is not None:
27
+ return expr.is_finite
28
+ if Q.finite(expr) in conjuncts(assumptions):
29
+ return True
30
+ return None
31
+
32
+ @FinitePredicate.register(Add)
33
+ def _(expr, assumptions):
34
+ """
35
+ Return True if expr is bounded, False if not and None if unknown.
36
+
37
+ Truth Table:
38
+
39
+ +-------+-----+-----------+-----------+
40
+ | | | | |
41
+ | | B | U | ? |
42
+ | | | | |
43
+ +-------+-----+---+---+---+---+---+---+
44
+ | | | | | | | | |
45
+ | | |'+'|'-'|'x'|'+'|'-'|'x'|
46
+ | | | | | | | | |
47
+ +-------+-----+---+---+---+---+---+---+
48
+ | | | | |
49
+ | B | B | U | ? |
50
+ | | | | |
51
+ +---+---+-----+---+---+---+---+---+---+
52
+ | | | | | | | | | |
53
+ | |'+'| | U | ? | ? | U | ? | ? |
54
+ | | | | | | | | | |
55
+ | +---+-----+---+---+---+---+---+---+
56
+ | | | | | | | | | |
57
+ | U |'-'| | ? | U | ? | ? | U | ? |
58
+ | | | | | | | | | |
59
+ | +---+-----+---+---+---+---+---+---+
60
+ | | | | | |
61
+ | |'x'| | ? | ? |
62
+ | | | | | |
63
+ +---+---+-----+---+---+---+---+---+---+
64
+ | | | | |
65
+ | ? | | | ? |
66
+ | | | | |
67
+ +-------+-----+-----------+---+---+---+
68
+
69
+ * 'B' = Bounded
70
+
71
+ * 'U' = Unbounded
72
+
73
+ * '?' = unknown boundedness
74
+
75
+ * '+' = positive sign
76
+
77
+ * '-' = negative sign
78
+
79
+ * 'x' = sign unknown
80
+
81
+ * All Bounded -> True
82
+
83
+ * 1 Unbounded and the rest Bounded -> False
84
+
85
+ * >1 Unbounded, all with same known sign -> False
86
+
87
+ * Any Unknown and unknown sign -> None
88
+
89
+ * Else -> None
90
+
91
+ When the signs are not the same you can have an undefined
92
+ result as in oo - oo, hence 'bounded' is also undefined.
93
+ """
94
+ sign = -1 # sign of unknown or infinite
95
+ result = True
96
+ for arg in expr.args:
97
+ _bounded = ask(Q.finite(arg), assumptions)
98
+ if _bounded:
99
+ continue
100
+ s = ask(Q.extended_positive(arg), assumptions)
101
+ # if there has been more than one sign or if the sign of this arg
102
+ # is None and Bounded is None or there was already
103
+ # an unknown sign, return None
104
+ if sign != -1 and s != sign or \
105
+ s is None and None in (_bounded, sign):
106
+ return None
107
+ else:
108
+ sign = s
109
+ # once False, do not change
110
+ if result is not False:
111
+ result = _bounded
112
+ return result
113
+
114
+ @FinitePredicate.register(Mul)
115
+ def _(expr, assumptions):
116
+ """
117
+ Return True if expr is bounded, False if not and None if unknown.
118
+
119
+ Truth Table:
120
+
121
+ +---+---+---+--------+
122
+ | | | | |
123
+ | | B | U | ? |
124
+ | | | | |
125
+ +---+---+---+---+----+
126
+ | | | | | |
127
+ | | | | s | /s |
128
+ | | | | | |
129
+ +---+---+---+---+----+
130
+ | | | | |
131
+ | B | B | U | ? |
132
+ | | | | |
133
+ +---+---+---+---+----+
134
+ | | | | | |
135
+ | U | | U | U | ? |
136
+ | | | | | |
137
+ +---+---+---+---+----+
138
+ | | | | |
139
+ | ? | | | ? |
140
+ | | | | |
141
+ +---+---+---+---+----+
142
+
143
+ * B = Bounded
144
+
145
+ * U = Unbounded
146
+
147
+ * ? = unknown boundedness
148
+
149
+ * s = signed (hence nonzero)
150
+
151
+ * /s = not signed
152
+ """
153
+ result = True
154
+ for arg in expr.args:
155
+ _bounded = ask(Q.finite(arg), assumptions)
156
+ if _bounded:
157
+ continue
158
+ elif _bounded is None:
159
+ if result is None:
160
+ return None
161
+ if ask(Q.extended_nonzero(arg), assumptions) is None:
162
+ return None
163
+ if result is not False:
164
+ result = None
165
+ else:
166
+ result = False
167
+ return result
168
+
169
+ @FinitePredicate.register(Pow)
170
+ def _(expr, assumptions):
171
+ """
172
+ * Unbounded ** NonZero -> Unbounded
173
+
174
+ * Bounded ** Bounded -> Bounded
175
+
176
+ * Abs()<=1 ** Positive -> Bounded
177
+
178
+ * Abs()>=1 ** Negative -> Bounded
179
+
180
+ * Otherwise unknown
181
+ """
182
+ if expr.base == E:
183
+ return ask(Q.finite(expr.exp), assumptions)
184
+
185
+ base_bounded = ask(Q.finite(expr.base), assumptions)
186
+ exp_bounded = ask(Q.finite(expr.exp), assumptions)
187
+ if base_bounded is None and exp_bounded is None: # Common Case
188
+ return None
189
+ if base_bounded is False and ask(Q.extended_nonzero(expr.exp), assumptions):
190
+ return False
191
+ if base_bounded and exp_bounded:
192
+ return True
193
+ if (abs(expr.base) <= 1) == True and ask(Q.extended_positive(expr.exp), assumptions):
194
+ return True
195
+ if (abs(expr.base) >= 1) == True and ask(Q.extended_negative(expr.exp), assumptions):
196
+ return True
197
+ if (abs(expr.base) >= 1) == True and exp_bounded is False:
198
+ return False
199
+ return None
200
+
201
+ @FinitePredicate.register(exp)
202
+ def _(expr, assumptions):
203
+ return ask(Q.finite(expr.exp), assumptions)
204
+
205
+ @FinitePredicate.register(log)
206
+ def _(expr, assumptions):
207
+ # After complex -> finite fact is registered to new assumption system,
208
+ # querying Q.infinite may be removed.
209
+ if ask(Q.infinite(expr.args[0]), assumptions):
210
+ return False
211
+ return ask(~Q.zero(expr.args[0]), assumptions)
212
+
213
+ @FinitePredicate.register_many(cos, sin, Number, Pi, Exp1, GoldenRatio,
214
+ TribonacciConstant, ImaginaryUnit, sign)
215
+ def _(expr, assumptions):
216
+ return True
217
+
218
+ @FinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
219
+ def _(expr, assumptions):
220
+ return False
221
+
222
+ @FinitePredicate.register(NaN)
223
+ def _(expr, assumptions):
224
+ return None
225
+
226
+
227
+ # InfinitePredicate
228
+
229
+
230
+ @InfinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
231
+ def _(expr, assumptions):
232
+ return True
233
+
234
+
235
+ # PositiveInfinitePredicate
236
+
237
+
238
+ @PositiveInfinitePredicate.register(Infinity)
239
+ def _(expr, assumptions):
240
+ return True
241
+
242
+
243
+ @PositiveInfinitePredicate.register_many(NegativeInfinity, ComplexInfinity)
244
+ def _(expr, assumptions):
245
+ return False
246
+
247
+
248
+ # NegativeInfinitePredicate
249
+
250
+
251
+ @NegativeInfinitePredicate.register(NegativeInfinity)
252
+ def _(expr, assumptions):
253
+ return True
254
+
255
+
256
+ @NegativeInfinitePredicate.register_many(Infinity, ComplexInfinity)
257
+ def _(expr, assumptions):
258
+ return False
lib/python3.10/site-packages/sympy/assumptions/handlers/common.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module defines base class for handlers and some core handlers:
3
+ ``Q.commutative`` and ``Q.is_true``.
4
+ """
5
+
6
+ from sympy.assumptions import Q, ask, AppliedPredicate
7
+ from sympy.core import Basic, Symbol
8
+ from sympy.core.logic import _fuzzy_group
9
+ from sympy.core.numbers import NaN, Number
10
+ from sympy.logic.boolalg import (And, BooleanTrue, BooleanFalse, conjuncts,
11
+ Equivalent, Implies, Not, Or)
12
+ from sympy.utilities.exceptions import sympy_deprecation_warning
13
+
14
+ from ..predicates.common import CommutativePredicate, IsTruePredicate
15
+
16
+
17
+ class AskHandler:
18
+ """Base class that all Ask Handlers must inherit."""
19
+ def __new__(cls, *args, **kwargs):
20
+ sympy_deprecation_warning(
21
+ """
22
+ The AskHandler system is deprecated. The AskHandler class should
23
+ be replaced with the multipledispatch handler of Predicate
24
+ """,
25
+ deprecated_since_version="1.8",
26
+ active_deprecations_target='deprecated-askhandler',
27
+ )
28
+ return super().__new__(cls, *args, **kwargs)
29
+
30
+
31
+ class CommonHandler(AskHandler):
32
+ # Deprecated
33
+ """Defines some useful methods common to most Handlers. """
34
+
35
+ @staticmethod
36
+ def AlwaysTrue(expr, assumptions):
37
+ return True
38
+
39
+ @staticmethod
40
+ def AlwaysFalse(expr, assumptions):
41
+ return False
42
+
43
+ @staticmethod
44
+ def AlwaysNone(expr, assumptions):
45
+ return None
46
+
47
+ NaN = AlwaysFalse
48
+
49
+
50
+ # CommutativePredicate
51
+
52
+ @CommutativePredicate.register(Symbol)
53
+ def _(expr, assumptions):
54
+ """Objects are expected to be commutative unless otherwise stated"""
55
+ assumps = conjuncts(assumptions)
56
+ if expr.is_commutative is not None:
57
+ return expr.is_commutative and not ~Q.commutative(expr) in assumps
58
+ if Q.commutative(expr) in assumps:
59
+ return True
60
+ elif ~Q.commutative(expr) in assumps:
61
+ return False
62
+ return True
63
+
64
+ @CommutativePredicate.register(Basic)
65
+ def _(expr, assumptions):
66
+ for arg in expr.args:
67
+ if not ask(Q.commutative(arg), assumptions):
68
+ return False
69
+ return True
70
+
71
+ @CommutativePredicate.register(Number)
72
+ def _(expr, assumptions):
73
+ return True
74
+
75
+ @CommutativePredicate.register(NaN)
76
+ def _(expr, assumptions):
77
+ return True
78
+
79
+
80
+ # IsTruePredicate
81
+
82
+ @IsTruePredicate.register(bool)
83
+ def _(expr, assumptions):
84
+ return expr
85
+
86
+ @IsTruePredicate.register(BooleanTrue)
87
+ def _(expr, assumptions):
88
+ return True
89
+
90
+ @IsTruePredicate.register(BooleanFalse)
91
+ def _(expr, assumptions):
92
+ return False
93
+
94
+ @IsTruePredicate.register(AppliedPredicate)
95
+ def _(expr, assumptions):
96
+ return ask(expr, assumptions)
97
+
98
+ @IsTruePredicate.register(Not)
99
+ def _(expr, assumptions):
100
+ arg = expr.args[0]
101
+ if arg.is_Symbol:
102
+ # symbol used as abstract boolean object
103
+ return None
104
+ value = ask(arg, assumptions=assumptions)
105
+ if value in (True, False):
106
+ return not value
107
+ else:
108
+ return None
109
+
110
+ @IsTruePredicate.register(Or)
111
+ def _(expr, assumptions):
112
+ result = False
113
+ for arg in expr.args:
114
+ p = ask(arg, assumptions=assumptions)
115
+ if p is True:
116
+ return True
117
+ if p is None:
118
+ result = None
119
+ return result
120
+
121
+ @IsTruePredicate.register(And)
122
+ def _(expr, assumptions):
123
+ result = True
124
+ for arg in expr.args:
125
+ p = ask(arg, assumptions=assumptions)
126
+ if p is False:
127
+ return False
128
+ if p is None:
129
+ result = None
130
+ return result
131
+
132
+ @IsTruePredicate.register(Implies)
133
+ def _(expr, assumptions):
134
+ p, q = expr.args
135
+ return ask(~p | q, assumptions=assumptions)
136
+
137
+ @IsTruePredicate.register(Equivalent)
138
+ def _(expr, assumptions):
139
+ p, q = expr.args
140
+ pt = ask(p, assumptions=assumptions)
141
+ if pt is None:
142
+ return None
143
+ qt = ask(q, assumptions=assumptions)
144
+ if qt is None:
145
+ return None
146
+ return pt == qt
147
+
148
+
149
+ #### Helper methods
150
+ def test_closed_group(expr, assumptions, key):
151
+ """
152
+ Test for membership in a group with respect
153
+ to the current operation.
154
+ """
155
+ return _fuzzy_group(
156
+ (ask(key(a), assumptions) for a in expr.args), quick_exit=True)
lib/python3.10/site-packages/sympy/assumptions/handlers/matrices.py ADDED
@@ -0,0 +1,716 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains query handlers responsible for Matrices queries:
3
+ Square, Symmetric, Invertible etc.
4
+ """
5
+
6
+ from sympy.logic.boolalg import conjuncts
7
+ from sympy.assumptions import Q, ask
8
+ from sympy.assumptions.handlers import test_closed_group
9
+ from sympy.matrices import MatrixBase
10
+ from sympy.matrices.expressions import (BlockMatrix, BlockDiagMatrix, Determinant,
11
+ DiagMatrix, DiagonalMatrix, HadamardProduct, Identity, Inverse, MatAdd, MatMul,
12
+ MatPow, MatrixExpr, MatrixSlice, MatrixSymbol, OneMatrix, Trace, Transpose,
13
+ ZeroMatrix)
14
+ from sympy.matrices.expressions.blockmatrix import reblock_2x2
15
+ from sympy.matrices.expressions.factorizations import Factorization
16
+ from sympy.matrices.expressions.fourier import DFT
17
+ from sympy.core.logic import fuzzy_and
18
+ from sympy.utilities.iterables import sift
19
+ from sympy.core import Basic
20
+
21
+ from ..predicates.matrices import (SquarePredicate, SymmetricPredicate,
22
+ InvertiblePredicate, OrthogonalPredicate, UnitaryPredicate,
23
+ FullRankPredicate, PositiveDefinitePredicate, UpperTriangularPredicate,
24
+ LowerTriangularPredicate, DiagonalPredicate, IntegerElementsPredicate,
25
+ RealElementsPredicate, ComplexElementsPredicate)
26
+
27
+
28
+ def _Factorization(predicate, expr, assumptions):
29
+ if predicate in expr.predicates:
30
+ return True
31
+
32
+
33
+ # SquarePredicate
34
+
35
+ @SquarePredicate.register(MatrixExpr)
36
+ def _(expr, assumptions):
37
+ return expr.shape[0] == expr.shape[1]
38
+
39
+
40
+ # SymmetricPredicate
41
+
42
+ @SymmetricPredicate.register(MatMul)
43
+ def _(expr, assumptions):
44
+ factor, mmul = expr.as_coeff_mmul()
45
+ if all(ask(Q.symmetric(arg), assumptions) for arg in mmul.args):
46
+ return True
47
+ # TODO: implement sathandlers system for the matrices.
48
+ # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
49
+ if ask(Q.diagonal(expr), assumptions):
50
+ return True
51
+ if len(mmul.args) >= 2 and mmul.args[0] == mmul.args[-1].T:
52
+ if len(mmul.args) == 2:
53
+ return True
54
+ return ask(Q.symmetric(MatMul(*mmul.args[1:-1])), assumptions)
55
+
56
+ @SymmetricPredicate.register(MatPow)
57
+ def _(expr, assumptions):
58
+ # only for integer powers
59
+ base, exp = expr.args
60
+ int_exp = ask(Q.integer(exp), assumptions)
61
+ if not int_exp:
62
+ return None
63
+ non_negative = ask(~Q.negative(exp), assumptions)
64
+ if (non_negative or non_negative == False
65
+ and ask(Q.invertible(base), assumptions)):
66
+ return ask(Q.symmetric(base), assumptions)
67
+ return None
68
+
69
+ @SymmetricPredicate.register(MatAdd)
70
+ def _(expr, assumptions):
71
+ return all(ask(Q.symmetric(arg), assumptions) for arg in expr.args)
72
+
73
+ @SymmetricPredicate.register(MatrixSymbol)
74
+ def _(expr, assumptions):
75
+ if not expr.is_square:
76
+ return False
77
+ # TODO: implement sathandlers system for the matrices.
78
+ # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
79
+ if ask(Q.diagonal(expr), assumptions):
80
+ return True
81
+ if Q.symmetric(expr) in conjuncts(assumptions):
82
+ return True
83
+
84
+ @SymmetricPredicate.register_many(OneMatrix, ZeroMatrix)
85
+ def _(expr, assumptions):
86
+ return ask(Q.square(expr), assumptions)
87
+
88
+ @SymmetricPredicate.register_many(Inverse, Transpose)
89
+ def _(expr, assumptions):
90
+ return ask(Q.symmetric(expr.arg), assumptions)
91
+
92
+ @SymmetricPredicate.register(MatrixSlice)
93
+ def _(expr, assumptions):
94
+ # TODO: implement sathandlers system for the matrices.
95
+ # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
96
+ if ask(Q.diagonal(expr), assumptions):
97
+ return True
98
+ if not expr.on_diag:
99
+ return None
100
+ else:
101
+ return ask(Q.symmetric(expr.parent), assumptions)
102
+
103
+ @SymmetricPredicate.register(Identity)
104
+ def _(expr, assumptions):
105
+ return True
106
+
107
+
108
+ # InvertiblePredicate
109
+
110
+ @InvertiblePredicate.register(MatMul)
111
+ def _(expr, assumptions):
112
+ factor, mmul = expr.as_coeff_mmul()
113
+ if all(ask(Q.invertible(arg), assumptions) for arg in mmul.args):
114
+ return True
115
+ if any(ask(Q.invertible(arg), assumptions) is False
116
+ for arg in mmul.args):
117
+ return False
118
+
119
+ @InvertiblePredicate.register(MatPow)
120
+ def _(expr, assumptions):
121
+ # only for integer powers
122
+ base, exp = expr.args
123
+ int_exp = ask(Q.integer(exp), assumptions)
124
+ if not int_exp:
125
+ return None
126
+ if exp.is_negative == False:
127
+ return ask(Q.invertible(base), assumptions)
128
+ return None
129
+
130
+ @InvertiblePredicate.register(MatAdd)
131
+ def _(expr, assumptions):
132
+ return None
133
+
134
+ @InvertiblePredicate.register(MatrixSymbol)
135
+ def _(expr, assumptions):
136
+ if not expr.is_square:
137
+ return False
138
+ if Q.invertible(expr) in conjuncts(assumptions):
139
+ return True
140
+
141
+ @InvertiblePredicate.register_many(Identity, Inverse)
142
+ def _(expr, assumptions):
143
+ return True
144
+
145
+ @InvertiblePredicate.register(ZeroMatrix)
146
+ def _(expr, assumptions):
147
+ return False
148
+
149
+ @InvertiblePredicate.register(OneMatrix)
150
+ def _(expr, assumptions):
151
+ return expr.shape[0] == 1 and expr.shape[1] == 1
152
+
153
+ @InvertiblePredicate.register(Transpose)
154
+ def _(expr, assumptions):
155
+ return ask(Q.invertible(expr.arg), assumptions)
156
+
157
+ @InvertiblePredicate.register(MatrixSlice)
158
+ def _(expr, assumptions):
159
+ if not expr.on_diag:
160
+ return None
161
+ else:
162
+ return ask(Q.invertible(expr.parent), assumptions)
163
+
164
+ @InvertiblePredicate.register(MatrixBase)
165
+ def _(expr, assumptions):
166
+ if not expr.is_square:
167
+ return False
168
+ return expr.rank() == expr.rows
169
+
170
+ @InvertiblePredicate.register(MatrixExpr)
171
+ def _(expr, assumptions):
172
+ if not expr.is_square:
173
+ return False
174
+ return None
175
+
176
+ @InvertiblePredicate.register(BlockMatrix)
177
+ def _(expr, assumptions):
178
+ if not expr.is_square:
179
+ return False
180
+ if expr.blockshape == (1, 1):
181
+ return ask(Q.invertible(expr.blocks[0, 0]), assumptions)
182
+ expr = reblock_2x2(expr)
183
+ if expr.blockshape == (2, 2):
184
+ [[A, B], [C, D]] = expr.blocks.tolist()
185
+ if ask(Q.invertible(A), assumptions) == True:
186
+ invertible = ask(Q.invertible(D - C * A.I * B), assumptions)
187
+ if invertible is not None:
188
+ return invertible
189
+ if ask(Q.invertible(B), assumptions) == True:
190
+ invertible = ask(Q.invertible(C - D * B.I * A), assumptions)
191
+ if invertible is not None:
192
+ return invertible
193
+ if ask(Q.invertible(C), assumptions) == True:
194
+ invertible = ask(Q.invertible(B - A * C.I * D), assumptions)
195
+ if invertible is not None:
196
+ return invertible
197
+ if ask(Q.invertible(D), assumptions) == True:
198
+ invertible = ask(Q.invertible(A - B * D.I * C), assumptions)
199
+ if invertible is not None:
200
+ return invertible
201
+ return None
202
+
203
+ @InvertiblePredicate.register(BlockDiagMatrix)
204
+ def _(expr, assumptions):
205
+ if expr.rowblocksizes != expr.colblocksizes:
206
+ return None
207
+ return fuzzy_and([ask(Q.invertible(a), assumptions) for a in expr.diag])
208
+
209
+
210
+ # OrthogonalPredicate
211
+
212
+ @OrthogonalPredicate.register(MatMul)
213
+ def _(expr, assumptions):
214
+ factor, mmul = expr.as_coeff_mmul()
215
+ if (all(ask(Q.orthogonal(arg), assumptions) for arg in mmul.args) and
216
+ factor == 1):
217
+ return True
218
+ if any(ask(Q.invertible(arg), assumptions) is False
219
+ for arg in mmul.args):
220
+ return False
221
+
222
+ @OrthogonalPredicate.register(MatPow)
223
+ def _(expr, assumptions):
224
+ # only for integer powers
225
+ base, exp = expr.args
226
+ int_exp = ask(Q.integer(exp), assumptions)
227
+ if int_exp:
228
+ return ask(Q.orthogonal(base), assumptions)
229
+ return None
230
+
231
+ @OrthogonalPredicate.register(MatAdd)
232
+ def _(expr, assumptions):
233
+ if (len(expr.args) == 1 and
234
+ ask(Q.orthogonal(expr.args[0]), assumptions)):
235
+ return True
236
+
237
+ @OrthogonalPredicate.register(MatrixSymbol)
238
+ def _(expr, assumptions):
239
+ if (not expr.is_square or
240
+ ask(Q.invertible(expr), assumptions) is False):
241
+ return False
242
+ if Q.orthogonal(expr) in conjuncts(assumptions):
243
+ return True
244
+
245
+ @OrthogonalPredicate.register(Identity)
246
+ def _(expr, assumptions):
247
+ return True
248
+
249
+ @OrthogonalPredicate.register(ZeroMatrix)
250
+ def _(expr, assumptions):
251
+ return False
252
+
253
+ @OrthogonalPredicate.register_many(Inverse, Transpose)
254
+ def _(expr, assumptions):
255
+ return ask(Q.orthogonal(expr.arg), assumptions)
256
+
257
+ @OrthogonalPredicate.register(MatrixSlice)
258
+ def _(expr, assumptions):
259
+ if not expr.on_diag:
260
+ return None
261
+ else:
262
+ return ask(Q.orthogonal(expr.parent), assumptions)
263
+
264
+ @OrthogonalPredicate.register(Factorization)
265
+ def _(expr, assumptions):
266
+ return _Factorization(Q.orthogonal, expr, assumptions)
267
+
268
+
269
+ # UnitaryPredicate
270
+
271
+ @UnitaryPredicate.register(MatMul)
272
+ def _(expr, assumptions):
273
+ factor, mmul = expr.as_coeff_mmul()
274
+ if (all(ask(Q.unitary(arg), assumptions) for arg in mmul.args) and
275
+ abs(factor) == 1):
276
+ return True
277
+ if any(ask(Q.invertible(arg), assumptions) is False
278
+ for arg in mmul.args):
279
+ return False
280
+
281
+ @UnitaryPredicate.register(MatPow)
282
+ def _(expr, assumptions):
283
+ # only for integer powers
284
+ base, exp = expr.args
285
+ int_exp = ask(Q.integer(exp), assumptions)
286
+ if int_exp:
287
+ return ask(Q.unitary(base), assumptions)
288
+ return None
289
+
290
+ @UnitaryPredicate.register(MatrixSymbol)
291
+ def _(expr, assumptions):
292
+ if (not expr.is_square or
293
+ ask(Q.invertible(expr), assumptions) is False):
294
+ return False
295
+ if Q.unitary(expr) in conjuncts(assumptions):
296
+ return True
297
+
298
+ @UnitaryPredicate.register_many(Inverse, Transpose)
299
+ def _(expr, assumptions):
300
+ return ask(Q.unitary(expr.arg), assumptions)
301
+
302
+ @UnitaryPredicate.register(MatrixSlice)
303
+ def _(expr, assumptions):
304
+ if not expr.on_diag:
305
+ return None
306
+ else:
307
+ return ask(Q.unitary(expr.parent), assumptions)
308
+
309
+ @UnitaryPredicate.register_many(DFT, Identity)
310
+ def _(expr, assumptions):
311
+ return True
312
+
313
+ @UnitaryPredicate.register(ZeroMatrix)
314
+ def _(expr, assumptions):
315
+ return False
316
+
317
+ @UnitaryPredicate.register(Factorization)
318
+ def _(expr, assumptions):
319
+ return _Factorization(Q.unitary, expr, assumptions)
320
+
321
+
322
+ # FullRankPredicate
323
+
324
+ @FullRankPredicate.register(MatMul)
325
+ def _(expr, assumptions):
326
+ if all(ask(Q.fullrank(arg), assumptions) for arg in expr.args):
327
+ return True
328
+
329
+ @FullRankPredicate.register(MatPow)
330
+ def _(expr, assumptions):
331
+ # only for integer powers
332
+ base, exp = expr.args
333
+ int_exp = ask(Q.integer(exp), assumptions)
334
+ if int_exp and ask(~Q.negative(exp), assumptions):
335
+ return ask(Q.fullrank(base), assumptions)
336
+ return None
337
+
338
+ @FullRankPredicate.register(Identity)
339
+ def _(expr, assumptions):
340
+ return True
341
+
342
+ @FullRankPredicate.register(ZeroMatrix)
343
+ def _(expr, assumptions):
344
+ return False
345
+
346
+ @FullRankPredicate.register(OneMatrix)
347
+ def _(expr, assumptions):
348
+ return expr.shape[0] == 1 and expr.shape[1] == 1
349
+
350
+ @FullRankPredicate.register_many(Inverse, Transpose)
351
+ def _(expr, assumptions):
352
+ return ask(Q.fullrank(expr.arg), assumptions)
353
+
354
+ @FullRankPredicate.register(MatrixSlice)
355
+ def _(expr, assumptions):
356
+ if ask(Q.orthogonal(expr.parent), assumptions):
357
+ return True
358
+
359
+
360
+ # PositiveDefinitePredicate
361
+
362
+ @PositiveDefinitePredicate.register(MatMul)
363
+ def _(expr, assumptions):
364
+ factor, mmul = expr.as_coeff_mmul()
365
+ if (all(ask(Q.positive_definite(arg), assumptions)
366
+ for arg in mmul.args) and factor > 0):
367
+ return True
368
+ if (len(mmul.args) >= 2
369
+ and mmul.args[0] == mmul.args[-1].T
370
+ and ask(Q.fullrank(mmul.args[0]), assumptions)):
371
+ return ask(Q.positive_definite(
372
+ MatMul(*mmul.args[1:-1])), assumptions)
373
+
374
+ @PositiveDefinitePredicate.register(MatPow)
375
+ def _(expr, assumptions):
376
+ # a power of a positive definite matrix is positive definite
377
+ if ask(Q.positive_definite(expr.args[0]), assumptions):
378
+ return True
379
+
380
+ @PositiveDefinitePredicate.register(MatAdd)
381
+ def _(expr, assumptions):
382
+ if all(ask(Q.positive_definite(arg), assumptions)
383
+ for arg in expr.args):
384
+ return True
385
+
386
+ @PositiveDefinitePredicate.register(MatrixSymbol)
387
+ def _(expr, assumptions):
388
+ if not expr.is_square:
389
+ return False
390
+ if Q.positive_definite(expr) in conjuncts(assumptions):
391
+ return True
392
+
393
+ @PositiveDefinitePredicate.register(Identity)
394
+ def _(expr, assumptions):
395
+ return True
396
+
397
+ @PositiveDefinitePredicate.register(ZeroMatrix)
398
+ def _(expr, assumptions):
399
+ return False
400
+
401
+ @PositiveDefinitePredicate.register(OneMatrix)
402
+ def _(expr, assumptions):
403
+ return expr.shape[0] == 1 and expr.shape[1] == 1
404
+
405
+ @PositiveDefinitePredicate.register_many(Inverse, Transpose)
406
+ def _(expr, assumptions):
407
+ return ask(Q.positive_definite(expr.arg), assumptions)
408
+
409
+ @PositiveDefinitePredicate.register(MatrixSlice)
410
+ def _(expr, assumptions):
411
+ if not expr.on_diag:
412
+ return None
413
+ else:
414
+ return ask(Q.positive_definite(expr.parent), assumptions)
415
+
416
+
417
+ # UpperTriangularPredicate
418
+
419
+ @UpperTriangularPredicate.register(MatMul)
420
+ def _(expr, assumptions):
421
+ factor, matrices = expr.as_coeff_matrices()
422
+ if all(ask(Q.upper_triangular(m), assumptions) for m in matrices):
423
+ return True
424
+
425
+ @UpperTriangularPredicate.register(MatAdd)
426
+ def _(expr, assumptions):
427
+ if all(ask(Q.upper_triangular(arg), assumptions) for arg in expr.args):
428
+ return True
429
+
430
+ @UpperTriangularPredicate.register(MatPow)
431
+ def _(expr, assumptions):
432
+ # only for integer powers
433
+ base, exp = expr.args
434
+ int_exp = ask(Q.integer(exp), assumptions)
435
+ if not int_exp:
436
+ return None
437
+ non_negative = ask(~Q.negative(exp), assumptions)
438
+ if (non_negative or non_negative == False
439
+ and ask(Q.invertible(base), assumptions)):
440
+ return ask(Q.upper_triangular(base), assumptions)
441
+ return None
442
+
443
+ @UpperTriangularPredicate.register(MatrixSymbol)
444
+ def _(expr, assumptions):
445
+ if Q.upper_triangular(expr) in conjuncts(assumptions):
446
+ return True
447
+
448
+ @UpperTriangularPredicate.register_many(Identity, ZeroMatrix)
449
+ def _(expr, assumptions):
450
+ return True
451
+
452
+ @UpperTriangularPredicate.register(OneMatrix)
453
+ def _(expr, assumptions):
454
+ return expr.shape[0] == 1 and expr.shape[1] == 1
455
+
456
+ @UpperTriangularPredicate.register(Transpose)
457
+ def _(expr, assumptions):
458
+ return ask(Q.lower_triangular(expr.arg), assumptions)
459
+
460
+ @UpperTriangularPredicate.register(Inverse)
461
+ def _(expr, assumptions):
462
+ return ask(Q.upper_triangular(expr.arg), assumptions)
463
+
464
+ @UpperTriangularPredicate.register(MatrixSlice)
465
+ def _(expr, assumptions):
466
+ if not expr.on_diag:
467
+ return None
468
+ else:
469
+ return ask(Q.upper_triangular(expr.parent), assumptions)
470
+
471
+ @UpperTriangularPredicate.register(Factorization)
472
+ def _(expr, assumptions):
473
+ return _Factorization(Q.upper_triangular, expr, assumptions)
474
+
475
+ # LowerTriangularPredicate
476
+
477
+ @LowerTriangularPredicate.register(MatMul)
478
+ def _(expr, assumptions):
479
+ factor, matrices = expr.as_coeff_matrices()
480
+ if all(ask(Q.lower_triangular(m), assumptions) for m in matrices):
481
+ return True
482
+
483
+ @LowerTriangularPredicate.register(MatAdd)
484
+ def _(expr, assumptions):
485
+ if all(ask(Q.lower_triangular(arg), assumptions) for arg in expr.args):
486
+ return True
487
+
488
+ @LowerTriangularPredicate.register(MatPow)
489
+ def _(expr, assumptions):
490
+ # only for integer powers
491
+ base, exp = expr.args
492
+ int_exp = ask(Q.integer(exp), assumptions)
493
+ if not int_exp:
494
+ return None
495
+ non_negative = ask(~Q.negative(exp), assumptions)
496
+ if (non_negative or non_negative == False
497
+ and ask(Q.invertible(base), assumptions)):
498
+ return ask(Q.lower_triangular(base), assumptions)
499
+ return None
500
+
501
+ @LowerTriangularPredicate.register(MatrixSymbol)
502
+ def _(expr, assumptions):
503
+ if Q.lower_triangular(expr) in conjuncts(assumptions):
504
+ return True
505
+
506
+ @LowerTriangularPredicate.register_many(Identity, ZeroMatrix)
507
+ def _(expr, assumptions):
508
+ return True
509
+
510
+ @LowerTriangularPredicate.register(OneMatrix)
511
+ def _(expr, assumptions):
512
+ return expr.shape[0] == 1 and expr.shape[1] == 1
513
+
514
+ @LowerTriangularPredicate.register(Transpose)
515
+ def _(expr, assumptions):
516
+ return ask(Q.upper_triangular(expr.arg), assumptions)
517
+
518
+ @LowerTriangularPredicate.register(Inverse)
519
+ def _(expr, assumptions):
520
+ return ask(Q.lower_triangular(expr.arg), assumptions)
521
+
522
+ @LowerTriangularPredicate.register(MatrixSlice)
523
+ def _(expr, assumptions):
524
+ if not expr.on_diag:
525
+ return None
526
+ else:
527
+ return ask(Q.lower_triangular(expr.parent), assumptions)
528
+
529
+ @LowerTriangularPredicate.register(Factorization)
530
+ def _(expr, assumptions):
531
+ return _Factorization(Q.lower_triangular, expr, assumptions)
532
+
533
+
534
+ # DiagonalPredicate
535
+
536
+ def _is_empty_or_1x1(expr):
537
+ return expr.shape in ((0, 0), (1, 1))
538
+
539
+ @DiagonalPredicate.register(MatMul)
540
+ def _(expr, assumptions):
541
+ if _is_empty_or_1x1(expr):
542
+ return True
543
+ factor, matrices = expr.as_coeff_matrices()
544
+ if all(ask(Q.diagonal(m), assumptions) for m in matrices):
545
+ return True
546
+
547
+ @DiagonalPredicate.register(MatPow)
548
+ def _(expr, assumptions):
549
+ # only for integer powers
550
+ base, exp = expr.args
551
+ int_exp = ask(Q.integer(exp), assumptions)
552
+ if not int_exp:
553
+ return None
554
+ non_negative = ask(~Q.negative(exp), assumptions)
555
+ if (non_negative or non_negative == False
556
+ and ask(Q.invertible(base), assumptions)):
557
+ return ask(Q.diagonal(base), assumptions)
558
+ return None
559
+
560
+ @DiagonalPredicate.register(MatAdd)
561
+ def _(expr, assumptions):
562
+ if all(ask(Q.diagonal(arg), assumptions) for arg in expr.args):
563
+ return True
564
+
565
+ @DiagonalPredicate.register(MatrixSymbol)
566
+ def _(expr, assumptions):
567
+ if _is_empty_or_1x1(expr):
568
+ return True
569
+ if Q.diagonal(expr) in conjuncts(assumptions):
570
+ return True
571
+
572
+ @DiagonalPredicate.register(OneMatrix)
573
+ def _(expr, assumptions):
574
+ return expr.shape[0] == 1 and expr.shape[1] == 1
575
+
576
+ @DiagonalPredicate.register_many(Inverse, Transpose)
577
+ def _(expr, assumptions):
578
+ return ask(Q.diagonal(expr.arg), assumptions)
579
+
580
+ @DiagonalPredicate.register(MatrixSlice)
581
+ def _(expr, assumptions):
582
+ if _is_empty_or_1x1(expr):
583
+ return True
584
+ if not expr.on_diag:
585
+ return None
586
+ else:
587
+ return ask(Q.diagonal(expr.parent), assumptions)
588
+
589
+ @DiagonalPredicate.register_many(DiagonalMatrix, DiagMatrix, Identity, ZeroMatrix)
590
+ def _(expr, assumptions):
591
+ return True
592
+
593
+ @DiagonalPredicate.register(Factorization)
594
+ def _(expr, assumptions):
595
+ return _Factorization(Q.diagonal, expr, assumptions)
596
+
597
+
598
+ # IntegerElementsPredicate
599
+
600
+ def BM_elements(predicate, expr, assumptions):
601
+ """ Block Matrix elements. """
602
+ return all(ask(predicate(b), assumptions) for b in expr.blocks)
603
+
604
+ def MS_elements(predicate, expr, assumptions):
605
+ """ Matrix Slice elements. """
606
+ return ask(predicate(expr.parent), assumptions)
607
+
608
+ def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
609
+ d = sift(expr.args, lambda x: isinstance(x, MatrixExpr))
610
+ factors, matrices = d[False], d[True]
611
+ return fuzzy_and([
612
+ test_closed_group(Basic(*factors), assumptions, scalar_predicate),
613
+ test_closed_group(Basic(*matrices), assumptions, matrix_predicate)])
614
+
615
+
616
+ @IntegerElementsPredicate.register_many(Determinant, HadamardProduct, MatAdd,
617
+ Trace, Transpose)
618
+ def _(expr, assumptions):
619
+ return test_closed_group(expr, assumptions, Q.integer_elements)
620
+
621
+ @IntegerElementsPredicate.register(MatPow)
622
+ def _(expr, assumptions):
623
+ # only for integer powers
624
+ base, exp = expr.args
625
+ int_exp = ask(Q.integer(exp), assumptions)
626
+ if not int_exp:
627
+ return None
628
+ if exp.is_negative == False:
629
+ return ask(Q.integer_elements(base), assumptions)
630
+ return None
631
+
632
+ @IntegerElementsPredicate.register_many(Identity, OneMatrix, ZeroMatrix)
633
+ def _(expr, assumptions):
634
+ return True
635
+
636
+ @IntegerElementsPredicate.register(MatMul)
637
+ def _(expr, assumptions):
638
+ return MatMul_elements(Q.integer_elements, Q.integer, expr, assumptions)
639
+
640
+ @IntegerElementsPredicate.register(MatrixSlice)
641
+ def _(expr, assumptions):
642
+ return MS_elements(Q.integer_elements, expr, assumptions)
643
+
644
+ @IntegerElementsPredicate.register(BlockMatrix)
645
+ def _(expr, assumptions):
646
+ return BM_elements(Q.integer_elements, expr, assumptions)
647
+
648
+
649
+ # RealElementsPredicate
650
+
651
+ @RealElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
652
+ MatAdd, Trace, Transpose)
653
+ def _(expr, assumptions):
654
+ return test_closed_group(expr, assumptions, Q.real_elements)
655
+
656
+ @RealElementsPredicate.register(MatPow)
657
+ def _(expr, assumptions):
658
+ # only for integer powers
659
+ base, exp = expr.args
660
+ int_exp = ask(Q.integer(exp), assumptions)
661
+ if not int_exp:
662
+ return None
663
+ non_negative = ask(~Q.negative(exp), assumptions)
664
+ if (non_negative or non_negative == False
665
+ and ask(Q.invertible(base), assumptions)):
666
+ return ask(Q.real_elements(base), assumptions)
667
+ return None
668
+
669
+ @RealElementsPredicate.register(MatMul)
670
+ def _(expr, assumptions):
671
+ return MatMul_elements(Q.real_elements, Q.real, expr, assumptions)
672
+
673
+ @RealElementsPredicate.register(MatrixSlice)
674
+ def _(expr, assumptions):
675
+ return MS_elements(Q.real_elements, expr, assumptions)
676
+
677
+ @RealElementsPredicate.register(BlockMatrix)
678
+ def _(expr, assumptions):
679
+ return BM_elements(Q.real_elements, expr, assumptions)
680
+
681
+
682
+ # ComplexElementsPredicate
683
+
684
+ @ComplexElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
685
+ Inverse, MatAdd, Trace, Transpose)
686
+ def _(expr, assumptions):
687
+ return test_closed_group(expr, assumptions, Q.complex_elements)
688
+
689
+ @ComplexElementsPredicate.register(MatPow)
690
+ def _(expr, assumptions):
691
+ # only for integer powers
692
+ base, exp = expr.args
693
+ int_exp = ask(Q.integer(exp), assumptions)
694
+ if not int_exp:
695
+ return None
696
+ non_negative = ask(~Q.negative(exp), assumptions)
697
+ if (non_negative or non_negative == False
698
+ and ask(Q.invertible(base), assumptions)):
699
+ return ask(Q.complex_elements(base), assumptions)
700
+ return None
701
+
702
+ @ComplexElementsPredicate.register(MatMul)
703
+ def _(expr, assumptions):
704
+ return MatMul_elements(Q.complex_elements, Q.complex, expr, assumptions)
705
+
706
+ @ComplexElementsPredicate.register(MatrixSlice)
707
+ def _(expr, assumptions):
708
+ return MS_elements(Q.complex_elements, expr, assumptions)
709
+
710
+ @ComplexElementsPredicate.register(BlockMatrix)
711
+ def _(expr, assumptions):
712
+ return BM_elements(Q.complex_elements, expr, assumptions)
713
+
714
+ @ComplexElementsPredicate.register(DFT)
715
+ def _(expr, assumptions):
716
+ return True
lib/python3.10/site-packages/sympy/assumptions/handlers/ntheory.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Handlers for keys related to number theory: prime, even, odd, etc.
3
+ """
4
+
5
+ from sympy.assumptions import Q, ask
6
+ from sympy.core import Add, Basic, Expr, Float, Mul, Pow, S
7
+ from sympy.core.numbers import (ImaginaryUnit, Infinity, Integer, NaN,
8
+ NegativeInfinity, NumberSymbol, Rational, int_valued)
9
+ from sympy.functions import Abs, im, re
10
+ from sympy.ntheory import isprime
11
+
12
+ from sympy.multipledispatch import MDNotImplementedError
13
+
14
+ from ..predicates.ntheory import (PrimePredicate, CompositePredicate,
15
+ EvenPredicate, OddPredicate)
16
+
17
+
18
+ # PrimePredicate
19
+
20
+ def _PrimePredicate_number(expr, assumptions):
21
+ # helper method
22
+ exact = not expr.atoms(Float)
23
+ try:
24
+ i = int(expr.round())
25
+ if (expr - i).equals(0) is False:
26
+ raise TypeError
27
+ except TypeError:
28
+ return False
29
+ if exact:
30
+ return isprime(i)
31
+ # when not exact, we won't give a True or False
32
+ # since the number represents an approximate value
33
+
34
+ @PrimePredicate.register(Expr)
35
+ def _(expr, assumptions):
36
+ ret = expr.is_prime
37
+ if ret is None:
38
+ raise MDNotImplementedError
39
+ return ret
40
+
41
+ @PrimePredicate.register(Basic)
42
+ def _(expr, assumptions):
43
+ if expr.is_number:
44
+ return _PrimePredicate_number(expr, assumptions)
45
+
46
+ @PrimePredicate.register(Mul)
47
+ def _(expr, assumptions):
48
+ if expr.is_number:
49
+ return _PrimePredicate_number(expr, assumptions)
50
+ for arg in expr.args:
51
+ if not ask(Q.integer(arg), assumptions):
52
+ return None
53
+ for arg in expr.args:
54
+ if arg.is_number and arg.is_composite:
55
+ return False
56
+
57
+ @PrimePredicate.register(Pow)
58
+ def _(expr, assumptions):
59
+ """
60
+ Integer**Integer -> !Prime
61
+ """
62
+ if expr.is_number:
63
+ return _PrimePredicate_number(expr, assumptions)
64
+ if ask(Q.integer(expr.exp), assumptions) and \
65
+ ask(Q.integer(expr.base), assumptions):
66
+ return False
67
+
68
+ @PrimePredicate.register(Integer)
69
+ def _(expr, assumptions):
70
+ return isprime(expr)
71
+
72
+ @PrimePredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
73
+ def _(expr, assumptions):
74
+ return False
75
+
76
+ @PrimePredicate.register(Float)
77
+ def _(expr, assumptions):
78
+ return _PrimePredicate_number(expr, assumptions)
79
+
80
+ @PrimePredicate.register(NumberSymbol)
81
+ def _(expr, assumptions):
82
+ return _PrimePredicate_number(expr, assumptions)
83
+
84
+ @PrimePredicate.register(NaN)
85
+ def _(expr, assumptions):
86
+ return None
87
+
88
+
89
+ # CompositePredicate
90
+
91
+ @CompositePredicate.register(Expr)
92
+ def _(expr, assumptions):
93
+ ret = expr.is_composite
94
+ if ret is None:
95
+ raise MDNotImplementedError
96
+ return ret
97
+
98
+ @CompositePredicate.register(Basic)
99
+ def _(expr, assumptions):
100
+ _positive = ask(Q.positive(expr), assumptions)
101
+ if _positive:
102
+ _integer = ask(Q.integer(expr), assumptions)
103
+ if _integer:
104
+ _prime = ask(Q.prime(expr), assumptions)
105
+ if _prime is None:
106
+ return
107
+ # Positive integer which is not prime is not
108
+ # necessarily composite
109
+ if expr.equals(1):
110
+ return False
111
+ return not _prime
112
+ else:
113
+ return _integer
114
+ else:
115
+ return _positive
116
+
117
+
118
+ # EvenPredicate
119
+
120
+ def _EvenPredicate_number(expr, assumptions):
121
+ # helper method
122
+ if isinstance(expr, (float, Float)):
123
+ if int_valued(expr):
124
+ return None
125
+ return False
126
+ try:
127
+ i = int(expr.round())
128
+ except TypeError:
129
+ return False
130
+ if not (expr - i).equals(0):
131
+ return False
132
+ return i % 2 == 0
133
+
134
+ @EvenPredicate.register(Expr)
135
+ def _(expr, assumptions):
136
+ ret = expr.is_even
137
+ if ret is None:
138
+ raise MDNotImplementedError
139
+ return ret
140
+
141
+ @EvenPredicate.register(Basic)
142
+ def _(expr, assumptions):
143
+ if expr.is_number:
144
+ return _EvenPredicate_number(expr, assumptions)
145
+
146
+ @EvenPredicate.register(Mul)
147
+ def _(expr, assumptions):
148
+ """
149
+ Even * Integer -> Even
150
+ Even * Odd -> Even
151
+ Integer * Odd -> ?
152
+ Odd * Odd -> Odd
153
+ Even * Even -> Even
154
+ Integer * Integer -> Even if Integer + Integer = Odd
155
+ otherwise -> ?
156
+ """
157
+ if expr.is_number:
158
+ return _EvenPredicate_number(expr, assumptions)
159
+ even, odd, irrational, acc = False, 0, False, 1
160
+ for arg in expr.args:
161
+ # check for all integers and at least one even
162
+ if ask(Q.integer(arg), assumptions):
163
+ if ask(Q.even(arg), assumptions):
164
+ even = True
165
+ elif ask(Q.odd(arg), assumptions):
166
+ odd += 1
167
+ elif not even and acc != 1:
168
+ if ask(Q.odd(acc + arg), assumptions):
169
+ even = True
170
+ elif ask(Q.irrational(arg), assumptions):
171
+ # one irrational makes the result False
172
+ # two makes it undefined
173
+ if irrational:
174
+ break
175
+ irrational = True
176
+ else:
177
+ break
178
+ acc = arg
179
+ else:
180
+ if irrational:
181
+ return False
182
+ if even:
183
+ return True
184
+ if odd == len(expr.args):
185
+ return False
186
+
187
+ @EvenPredicate.register(Add)
188
+ def _(expr, assumptions):
189
+ """
190
+ Even + Odd -> Odd
191
+ Even + Even -> Even
192
+ Odd + Odd -> Even
193
+
194
+ """
195
+ if expr.is_number:
196
+ return _EvenPredicate_number(expr, assumptions)
197
+ _result = True
198
+ for arg in expr.args:
199
+ if ask(Q.even(arg), assumptions):
200
+ pass
201
+ elif ask(Q.odd(arg), assumptions):
202
+ _result = not _result
203
+ else:
204
+ break
205
+ else:
206
+ return _result
207
+
208
+ @EvenPredicate.register(Pow)
209
+ def _(expr, assumptions):
210
+ if expr.is_number:
211
+ return _EvenPredicate_number(expr, assumptions)
212
+ if ask(Q.integer(expr.exp), assumptions):
213
+ if ask(Q.positive(expr.exp), assumptions):
214
+ return ask(Q.even(expr.base), assumptions)
215
+ elif ask(~Q.negative(expr.exp) & Q.odd(expr.base), assumptions):
216
+ return False
217
+ elif expr.base is S.NegativeOne:
218
+ return False
219
+
220
+ @EvenPredicate.register(Integer)
221
+ def _(expr, assumptions):
222
+ return not bool(expr.p & 1)
223
+
224
+ @EvenPredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
225
+ def _(expr, assumptions):
226
+ return False
227
+
228
+ @EvenPredicate.register(NumberSymbol)
229
+ def _(expr, assumptions):
230
+ return _EvenPredicate_number(expr, assumptions)
231
+
232
+ @EvenPredicate.register(Abs)
233
+ def _(expr, assumptions):
234
+ if ask(Q.real(expr.args[0]), assumptions):
235
+ return ask(Q.even(expr.args[0]), assumptions)
236
+
237
+ @EvenPredicate.register(re)
238
+ def _(expr, assumptions):
239
+ if ask(Q.real(expr.args[0]), assumptions):
240
+ return ask(Q.even(expr.args[0]), assumptions)
241
+
242
+ @EvenPredicate.register(im)
243
+ def _(expr, assumptions):
244
+ if ask(Q.real(expr.args[0]), assumptions):
245
+ return True
246
+
247
+ @EvenPredicate.register(NaN)
248
+ def _(expr, assumptions):
249
+ return None
250
+
251
+
252
+ # OddPredicate
253
+
254
+ @OddPredicate.register(Expr)
255
+ def _(expr, assumptions):
256
+ ret = expr.is_odd
257
+ if ret is None:
258
+ raise MDNotImplementedError
259
+ return ret
260
+
261
+ @OddPredicate.register(Basic)
262
+ def _(expr, assumptions):
263
+ _integer = ask(Q.integer(expr), assumptions)
264
+ if _integer:
265
+ _even = ask(Q.even(expr), assumptions)
266
+ if _even is None:
267
+ return None
268
+ return not _even
269
+ return _integer
lib/python3.10/site-packages/sympy/assumptions/handlers/order.py ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Handlers related to order relations: positive, negative, etc.
3
+ """
4
+
5
+ from sympy.assumptions import Q, ask
6
+ from sympy.core import Add, Basic, Expr, Mul, Pow
7
+ from sympy.core.logic import fuzzy_not, fuzzy_and, fuzzy_or
8
+ from sympy.core.numbers import E, ImaginaryUnit, NaN, I, pi
9
+ from sympy.functions import Abs, acos, acot, asin, atan, exp, factorial, log
10
+ from sympy.matrices import Determinant, Trace
11
+ from sympy.matrices.expressions.matexpr import MatrixElement
12
+
13
+ from sympy.multipledispatch import MDNotImplementedError
14
+
15
+ from ..predicates.order import (NegativePredicate, NonNegativePredicate,
16
+ NonZeroPredicate, ZeroPredicate, NonPositivePredicate, PositivePredicate,
17
+ ExtendedNegativePredicate, ExtendedNonNegativePredicate,
18
+ ExtendedNonPositivePredicate, ExtendedNonZeroPredicate,
19
+ ExtendedPositivePredicate,)
20
+
21
+
22
+ # NegativePredicate
23
+
24
+ def _NegativePredicate_number(expr, assumptions):
25
+ r, i = expr.as_real_imag()
26
+ # If the imaginary part can symbolically be shown to be zero then
27
+ # we just evaluate the real part; otherwise we evaluate the imaginary
28
+ # part to see if it actually evaluates to zero and if it does then
29
+ # we make the comparison between the real part and zero.
30
+ if not i:
31
+ r = r.evalf(2)
32
+ if r._prec != 1:
33
+ return r < 0
34
+ else:
35
+ i = i.evalf(2)
36
+ if i._prec != 1:
37
+ if i != 0:
38
+ return False
39
+ r = r.evalf(2)
40
+ if r._prec != 1:
41
+ return r < 0
42
+
43
+ @NegativePredicate.register(Basic)
44
+ def _(expr, assumptions):
45
+ if expr.is_number:
46
+ return _NegativePredicate_number(expr, assumptions)
47
+
48
+ @NegativePredicate.register(Expr)
49
+ def _(expr, assumptions):
50
+ ret = expr.is_negative
51
+ if ret is None:
52
+ raise MDNotImplementedError
53
+ return ret
54
+
55
+ @NegativePredicate.register(Add)
56
+ def _(expr, assumptions):
57
+ """
58
+ Positive + Positive -> Positive,
59
+ Negative + Negative -> Negative
60
+ """
61
+ if expr.is_number:
62
+ return _NegativePredicate_number(expr, assumptions)
63
+
64
+ r = ask(Q.real(expr), assumptions)
65
+ if r is not True:
66
+ return r
67
+
68
+ nonpos = 0
69
+ for arg in expr.args:
70
+ if ask(Q.negative(arg), assumptions) is not True:
71
+ if ask(Q.positive(arg), assumptions) is False:
72
+ nonpos += 1
73
+ else:
74
+ break
75
+ else:
76
+ if nonpos < len(expr.args):
77
+ return True
78
+
79
+ @NegativePredicate.register(Mul)
80
+ def _(expr, assumptions):
81
+ if expr.is_number:
82
+ return _NegativePredicate_number(expr, assumptions)
83
+ result = None
84
+ for arg in expr.args:
85
+ if result is None:
86
+ result = False
87
+ if ask(Q.negative(arg), assumptions):
88
+ result = not result
89
+ elif ask(Q.positive(arg), assumptions):
90
+ pass
91
+ else:
92
+ return
93
+ return result
94
+
95
+ @NegativePredicate.register(Pow)
96
+ def _(expr, assumptions):
97
+ """
98
+ Real ** Even -> NonNegative
99
+ Real ** Odd -> same_as_base
100
+ NonNegative ** Positive -> NonNegative
101
+ """
102
+ if expr.base == E:
103
+ # Exponential is always positive:
104
+ if ask(Q.real(expr.exp), assumptions):
105
+ return False
106
+ return
107
+
108
+ if expr.is_number:
109
+ return _NegativePredicate_number(expr, assumptions)
110
+ if ask(Q.real(expr.base), assumptions):
111
+ if ask(Q.positive(expr.base), assumptions):
112
+ if ask(Q.real(expr.exp), assumptions):
113
+ return False
114
+ if ask(Q.even(expr.exp), assumptions):
115
+ return False
116
+ if ask(Q.odd(expr.exp), assumptions):
117
+ return ask(Q.negative(expr.base), assumptions)
118
+
119
+ @NegativePredicate.register_many(Abs, ImaginaryUnit)
120
+ def _(expr, assumptions):
121
+ return False
122
+
123
+ @NegativePredicate.register(exp)
124
+ def _(expr, assumptions):
125
+ if ask(Q.real(expr.exp), assumptions):
126
+ return False
127
+ raise MDNotImplementedError
128
+
129
+
130
+ # NonNegativePredicate
131
+
132
+ @NonNegativePredicate.register(Basic)
133
+ def _(expr, assumptions):
134
+ if expr.is_number:
135
+ notnegative = fuzzy_not(_NegativePredicate_number(expr, assumptions))
136
+ if notnegative:
137
+ return ask(Q.real(expr), assumptions)
138
+ else:
139
+ return notnegative
140
+
141
+ @NonNegativePredicate.register(Expr)
142
+ def _(expr, assumptions):
143
+ ret = expr.is_nonnegative
144
+ if ret is None:
145
+ raise MDNotImplementedError
146
+ return ret
147
+
148
+
149
+ # NonZeroPredicate
150
+
151
+ @NonZeroPredicate.register(Expr)
152
+ def _(expr, assumptions):
153
+ ret = expr.is_nonzero
154
+ if ret is None:
155
+ raise MDNotImplementedError
156
+ return ret
157
+
158
+ @NonZeroPredicate.register(Basic)
159
+ def _(expr, assumptions):
160
+ if ask(Q.real(expr)) is False:
161
+ return False
162
+ if expr.is_number:
163
+ # if there are no symbols just evalf
164
+ i = expr.evalf(2)
165
+ def nonz(i):
166
+ if i._prec != 1:
167
+ return i != 0
168
+ return fuzzy_or(nonz(i) for i in i.as_real_imag())
169
+
170
+ @NonZeroPredicate.register(Add)
171
+ def _(expr, assumptions):
172
+ if all(ask(Q.positive(x), assumptions) for x in expr.args) \
173
+ or all(ask(Q.negative(x), assumptions) for x in expr.args):
174
+ return True
175
+
176
+ @NonZeroPredicate.register(Mul)
177
+ def _(expr, assumptions):
178
+ for arg in expr.args:
179
+ result = ask(Q.nonzero(arg), assumptions)
180
+ if result:
181
+ continue
182
+ return result
183
+ return True
184
+
185
+ @NonZeroPredicate.register(Pow)
186
+ def _(expr, assumptions):
187
+ return ask(Q.nonzero(expr.base), assumptions)
188
+
189
+ @NonZeroPredicate.register(Abs)
190
+ def _(expr, assumptions):
191
+ return ask(Q.nonzero(expr.args[0]), assumptions)
192
+
193
+ @NonZeroPredicate.register(NaN)
194
+ def _(expr, assumptions):
195
+ return None
196
+
197
+
198
+ # ZeroPredicate
199
+
200
+ @ZeroPredicate.register(Expr)
201
+ def _(expr, assumptions):
202
+ ret = expr.is_zero
203
+ if ret is None:
204
+ raise MDNotImplementedError
205
+ return ret
206
+
207
+ @ZeroPredicate.register(Basic)
208
+ def _(expr, assumptions):
209
+ return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)),
210
+ ask(Q.real(expr), assumptions)])
211
+
212
+ @ZeroPredicate.register(Mul)
213
+ def _(expr, assumptions):
214
+ # TODO: This should be deducible from the nonzero handler
215
+ return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args)
216
+
217
+
218
+ # NonPositivePredicate
219
+
220
+ @NonPositivePredicate.register(Expr)
221
+ def _(expr, assumptions):
222
+ ret = expr.is_nonpositive
223
+ if ret is None:
224
+ raise MDNotImplementedError
225
+ return ret
226
+
227
+ @NonPositivePredicate.register(Basic)
228
+ def _(expr, assumptions):
229
+ if expr.is_number:
230
+ notpositive = fuzzy_not(_PositivePredicate_number(expr, assumptions))
231
+ if notpositive:
232
+ return ask(Q.real(expr), assumptions)
233
+ else:
234
+ return notpositive
235
+
236
+
237
+ # PositivePredicate
238
+
239
+ def _PositivePredicate_number(expr, assumptions):
240
+ r, i = expr.as_real_imag()
241
+ # If the imaginary part can symbolically be shown to be zero then
242
+ # we just evaluate the real part; otherwise we evaluate the imaginary
243
+ # part to see if it actually evaluates to zero and if it does then
244
+ # we make the comparison between the real part and zero.
245
+ if not i:
246
+ r = r.evalf(2)
247
+ if r._prec != 1:
248
+ return r > 0
249
+ else:
250
+ i = i.evalf(2)
251
+ if i._prec != 1:
252
+ if i != 0:
253
+ return False
254
+ r = r.evalf(2)
255
+ if r._prec != 1:
256
+ return r > 0
257
+
258
+ @PositivePredicate.register(Expr)
259
+ def _(expr, assumptions):
260
+ ret = expr.is_positive
261
+ if ret is None:
262
+ raise MDNotImplementedError
263
+ return ret
264
+
265
+ @PositivePredicate.register(Basic)
266
+ def _(expr, assumptions):
267
+ if expr.is_number:
268
+ return _PositivePredicate_number(expr, assumptions)
269
+
270
+ @PositivePredicate.register(Mul)
271
+ def _(expr, assumptions):
272
+ if expr.is_number:
273
+ return _PositivePredicate_number(expr, assumptions)
274
+ result = True
275
+ for arg in expr.args:
276
+ if ask(Q.positive(arg), assumptions):
277
+ continue
278
+ elif ask(Q.negative(arg), assumptions):
279
+ result = result ^ True
280
+ else:
281
+ return
282
+ return result
283
+
284
+ @PositivePredicate.register(Add)
285
+ def _(expr, assumptions):
286
+ if expr.is_number:
287
+ return _PositivePredicate_number(expr, assumptions)
288
+
289
+ r = ask(Q.real(expr), assumptions)
290
+ if r is not True:
291
+ return r
292
+
293
+ nonneg = 0
294
+ for arg in expr.args:
295
+ if ask(Q.positive(arg), assumptions) is not True:
296
+ if ask(Q.negative(arg), assumptions) is False:
297
+ nonneg += 1
298
+ else:
299
+ break
300
+ else:
301
+ if nonneg < len(expr.args):
302
+ return True
303
+
304
+ @PositivePredicate.register(Pow)
305
+ def _(expr, assumptions):
306
+ if expr.base == E:
307
+ if ask(Q.real(expr.exp), assumptions):
308
+ return True
309
+ if ask(Q.imaginary(expr.exp), assumptions):
310
+ return ask(Q.even(expr.exp/(I*pi)), assumptions)
311
+ return
312
+
313
+ if expr.is_number:
314
+ return _PositivePredicate_number(expr, assumptions)
315
+ if ask(Q.positive(expr.base), assumptions):
316
+ if ask(Q.real(expr.exp), assumptions):
317
+ return True
318
+ if ask(Q.negative(expr.base), assumptions):
319
+ if ask(Q.even(expr.exp), assumptions):
320
+ return True
321
+ if ask(Q.odd(expr.exp), assumptions):
322
+ return False
323
+
324
+ @PositivePredicate.register(exp)
325
+ def _(expr, assumptions):
326
+ if ask(Q.real(expr.exp), assumptions):
327
+ return True
328
+ if ask(Q.imaginary(expr.exp), assumptions):
329
+ return ask(Q.even(expr.exp/(I*pi)), assumptions)
330
+
331
+ @PositivePredicate.register(log)
332
+ def _(expr, assumptions):
333
+ r = ask(Q.real(expr.args[0]), assumptions)
334
+ if r is not True:
335
+ return r
336
+ if ask(Q.positive(expr.args[0] - 1), assumptions):
337
+ return True
338
+ if ask(Q.negative(expr.args[0] - 1), assumptions):
339
+ return False
340
+
341
+ @PositivePredicate.register(factorial)
342
+ def _(expr, assumptions):
343
+ x = expr.args[0]
344
+ if ask(Q.integer(x) & Q.positive(x), assumptions):
345
+ return True
346
+
347
+ @PositivePredicate.register(ImaginaryUnit)
348
+ def _(expr, assumptions):
349
+ return False
350
+
351
+ @PositivePredicate.register(Abs)
352
+ def _(expr, assumptions):
353
+ return ask(Q.nonzero(expr), assumptions)
354
+
355
+ @PositivePredicate.register(Trace)
356
+ def _(expr, assumptions):
357
+ if ask(Q.positive_definite(expr.arg), assumptions):
358
+ return True
359
+
360
+ @PositivePredicate.register(Determinant)
361
+ def _(expr, assumptions):
362
+ if ask(Q.positive_definite(expr.arg), assumptions):
363
+ return True
364
+
365
+ @PositivePredicate.register(MatrixElement)
366
+ def _(expr, assumptions):
367
+ if (expr.i == expr.j
368
+ and ask(Q.positive_definite(expr.parent), assumptions)):
369
+ return True
370
+
371
+ @PositivePredicate.register(atan)
372
+ def _(expr, assumptions):
373
+ return ask(Q.positive(expr.args[0]), assumptions)
374
+
375
+ @PositivePredicate.register(asin)
376
+ def _(expr, assumptions):
377
+ x = expr.args[0]
378
+ if ask(Q.positive(x) & Q.nonpositive(x - 1), assumptions):
379
+ return True
380
+ if ask(Q.negative(x) & Q.nonnegative(x + 1), assumptions):
381
+ return False
382
+
383
+ @PositivePredicate.register(acos)
384
+ def _(expr, assumptions):
385
+ x = expr.args[0]
386
+ if ask(Q.nonpositive(x - 1) & Q.nonnegative(x + 1), assumptions):
387
+ return True
388
+
389
+ @PositivePredicate.register(acot)
390
+ def _(expr, assumptions):
391
+ return ask(Q.real(expr.args[0]), assumptions)
392
+
393
+ @PositivePredicate.register(NaN)
394
+ def _(expr, assumptions):
395
+ return None
396
+
397
+
398
+ # ExtendedNegativePredicate
399
+
400
+ @ExtendedNegativePredicate.register(object)
401
+ def _(expr, assumptions):
402
+ return ask(Q.negative(expr) | Q.negative_infinite(expr), assumptions)
403
+
404
+
405
+ # ExtendedPositivePredicate
406
+
407
+ @ExtendedPositivePredicate.register(object)
408
+ def _(expr, assumptions):
409
+ return ask(Q.positive(expr) | Q.positive_infinite(expr), assumptions)
410
+
411
+
412
+ # ExtendedNonZeroPredicate
413
+
414
+ @ExtendedNonZeroPredicate.register(object)
415
+ def _(expr, assumptions):
416
+ return ask(
417
+ Q.negative_infinite(expr) | Q.negative(expr) | Q.positive(expr) | Q.positive_infinite(expr),
418
+ assumptions)
419
+
420
+
421
+ # ExtendedNonPositivePredicate
422
+
423
+ @ExtendedNonPositivePredicate.register(object)
424
+ def _(expr, assumptions):
425
+ return ask(
426
+ Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr),
427
+ assumptions)
428
+
429
+
430
+ # ExtendedNonNegativePredicate
431
+
432
+ @ExtendedNonNegativePredicate.register(object)
433
+ def _(expr, assumptions):
434
+ return ask(
435
+ Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr),
436
+ assumptions)
lib/python3.10/site-packages/sympy/assumptions/handlers/sets.py ADDED
@@ -0,0 +1,772 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Handlers for predicates related to set membership: integer, rational, etc.
3
+ """
4
+
5
+ from sympy.assumptions import Q, ask
6
+ from sympy.core import Add, Basic, Expr, Mul, Pow, S
7
+ from sympy.core.numbers import (AlgebraicNumber, ComplexInfinity, Exp1, Float,
8
+ GoldenRatio, ImaginaryUnit, Infinity, Integer, NaN, NegativeInfinity,
9
+ Number, NumberSymbol, Pi, pi, Rational, TribonacciConstant, E)
10
+ from sympy.core.logic import fuzzy_bool
11
+ from sympy.functions import (Abs, acos, acot, asin, atan, cos, cot, exp, im,
12
+ log, re, sin, tan)
13
+ from sympy.core.numbers import I
14
+ from sympy.core.relational import Eq
15
+ from sympy.functions.elementary.complexes import conjugate
16
+ from sympy.matrices import Determinant, MatrixBase, Trace
17
+ from sympy.matrices.expressions.matexpr import MatrixElement
18
+
19
+ from sympy.multipledispatch import MDNotImplementedError
20
+
21
+ from .common import test_closed_group
22
+ from ..predicates.sets import (IntegerPredicate, RationalPredicate,
23
+ IrrationalPredicate, RealPredicate, ExtendedRealPredicate,
24
+ HermitianPredicate, ComplexPredicate, ImaginaryPredicate,
25
+ AntihermitianPredicate, AlgebraicPredicate)
26
+
27
+
28
+ # IntegerPredicate
29
+
30
+ def _IntegerPredicate_number(expr, assumptions):
31
+ # helper function
32
+ try:
33
+ i = int(expr.round())
34
+ if not (expr - i).equals(0):
35
+ raise TypeError
36
+ return True
37
+ except TypeError:
38
+ return False
39
+
40
+ @IntegerPredicate.register_many(int, Integer) # type:ignore
41
+ def _(expr, assumptions):
42
+ return True
43
+
44
+ @IntegerPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
45
+ NegativeInfinity, Pi, Rational, TribonacciConstant)
46
+ def _(expr, assumptions):
47
+ return False
48
+
49
+ @IntegerPredicate.register(Expr)
50
+ def _(expr, assumptions):
51
+ ret = expr.is_integer
52
+ if ret is None:
53
+ raise MDNotImplementedError
54
+ return ret
55
+
56
+ @IntegerPredicate.register_many(Add, Pow)
57
+ def _(expr, assumptions):
58
+ """
59
+ * Integer + Integer -> Integer
60
+ * Integer + !Integer -> !Integer
61
+ * !Integer + !Integer -> ?
62
+ """
63
+ if expr.is_number:
64
+ return _IntegerPredicate_number(expr, assumptions)
65
+ return test_closed_group(expr, assumptions, Q.integer)
66
+
67
+ @IntegerPredicate.register(Mul)
68
+ def _(expr, assumptions):
69
+ """
70
+ * Integer*Integer -> Integer
71
+ * Integer*Irrational -> !Integer
72
+ * Odd/Even -> !Integer
73
+ * Integer*Rational -> ?
74
+ """
75
+ if expr.is_number:
76
+ return _IntegerPredicate_number(expr, assumptions)
77
+ _output = True
78
+ for arg in expr.args:
79
+ if not ask(Q.integer(arg), assumptions):
80
+ if arg.is_Rational:
81
+ if arg.q == 2:
82
+ return ask(Q.even(2*expr), assumptions)
83
+ if ~(arg.q & 1):
84
+ return None
85
+ elif ask(Q.irrational(arg), assumptions):
86
+ if _output:
87
+ _output = False
88
+ else:
89
+ return
90
+ else:
91
+ return
92
+
93
+ return _output
94
+
95
+ @IntegerPredicate.register(Abs)
96
+ def _(expr, assumptions):
97
+ return ask(Q.integer(expr.args[0]), assumptions)
98
+
99
+ @IntegerPredicate.register_many(Determinant, MatrixElement, Trace)
100
+ def _(expr, assumptions):
101
+ return ask(Q.integer_elements(expr.args[0]), assumptions)
102
+
103
+
104
+ # RationalPredicate
105
+
106
+ @RationalPredicate.register(Rational)
107
+ def _(expr, assumptions):
108
+ return True
109
+
110
+ @RationalPredicate.register(Float)
111
+ def _(expr, assumptions):
112
+ return None
113
+
114
+ @RationalPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
115
+ NegativeInfinity, Pi, TribonacciConstant)
116
+ def _(expr, assumptions):
117
+ return False
118
+
119
+ @RationalPredicate.register(Expr)
120
+ def _(expr, assumptions):
121
+ ret = expr.is_rational
122
+ if ret is None:
123
+ raise MDNotImplementedError
124
+ return ret
125
+
126
+ @RationalPredicate.register_many(Add, Mul)
127
+ def _(expr, assumptions):
128
+ """
129
+ * Rational + Rational -> Rational
130
+ * Rational + !Rational -> !Rational
131
+ * !Rational + !Rational -> ?
132
+ """
133
+ if expr.is_number:
134
+ if expr.as_real_imag()[1]:
135
+ return False
136
+ return test_closed_group(expr, assumptions, Q.rational)
137
+
138
+ @RationalPredicate.register(Pow)
139
+ def _(expr, assumptions):
140
+ """
141
+ * Rational ** Integer -> Rational
142
+ * Irrational ** Rational -> Irrational
143
+ * Rational ** Irrational -> ?
144
+ """
145
+ if expr.base == E:
146
+ x = expr.exp
147
+ if ask(Q.rational(x), assumptions):
148
+ return ask(~Q.nonzero(x), assumptions)
149
+ return
150
+
151
+ if ask(Q.integer(expr.exp), assumptions):
152
+ return ask(Q.rational(expr.base), assumptions)
153
+ elif ask(Q.rational(expr.exp), assumptions):
154
+ if ask(Q.prime(expr.base), assumptions):
155
+ return False
156
+
157
+ @RationalPredicate.register_many(asin, atan, cos, sin, tan)
158
+ def _(expr, assumptions):
159
+ x = expr.args[0]
160
+ if ask(Q.rational(x), assumptions):
161
+ return ask(~Q.nonzero(x), assumptions)
162
+
163
+ @RationalPredicate.register(exp)
164
+ def _(expr, assumptions):
165
+ x = expr.exp
166
+ if ask(Q.rational(x), assumptions):
167
+ return ask(~Q.nonzero(x), assumptions)
168
+
169
+ @RationalPredicate.register_many(acot, cot)
170
+ def _(expr, assumptions):
171
+ x = expr.args[0]
172
+ if ask(Q.rational(x), assumptions):
173
+ return False
174
+
175
+ @RationalPredicate.register_many(acos, log)
176
+ def _(expr, assumptions):
177
+ x = expr.args[0]
178
+ if ask(Q.rational(x), assumptions):
179
+ return ask(~Q.nonzero(x - 1), assumptions)
180
+
181
+
182
+ # IrrationalPredicate
183
+
184
+ @IrrationalPredicate.register(Expr)
185
+ def _(expr, assumptions):
186
+ ret = expr.is_irrational
187
+ if ret is None:
188
+ raise MDNotImplementedError
189
+ return ret
190
+
191
+ @IrrationalPredicate.register(Basic)
192
+ def _(expr, assumptions):
193
+ _real = ask(Q.real(expr), assumptions)
194
+ if _real:
195
+ _rational = ask(Q.rational(expr), assumptions)
196
+ if _rational is None:
197
+ return None
198
+ return not _rational
199
+ else:
200
+ return _real
201
+
202
+
203
+ # RealPredicate
204
+
205
+ def _RealPredicate_number(expr, assumptions):
206
+ # let as_real_imag() work first since the expression may
207
+ # be simpler to evaluate
208
+ i = expr.as_real_imag()[1].evalf(2)
209
+ if i._prec != 1:
210
+ return not i
211
+ # allow None to be returned if we couldn't show for sure
212
+ # that i was 0
213
+
214
+ @RealPredicate.register_many(Abs, Exp1, Float, GoldenRatio, im, Pi, Rational,
215
+ re, TribonacciConstant)
216
+ def _(expr, assumptions):
217
+ return True
218
+
219
+ @RealPredicate.register_many(ImaginaryUnit, Infinity, NegativeInfinity)
220
+ def _(expr, assumptions):
221
+ return False
222
+
223
+ @RealPredicate.register(Expr)
224
+ def _(expr, assumptions):
225
+ ret = expr.is_real
226
+ if ret is None:
227
+ raise MDNotImplementedError
228
+ return ret
229
+
230
+ @RealPredicate.register(Add)
231
+ def _(expr, assumptions):
232
+ """
233
+ * Real + Real -> Real
234
+ * Real + (Complex & !Real) -> !Real
235
+ """
236
+ if expr.is_number:
237
+ return _RealPredicate_number(expr, assumptions)
238
+ return test_closed_group(expr, assumptions, Q.real)
239
+
240
+ @RealPredicate.register(Mul)
241
+ def _(expr, assumptions):
242
+ """
243
+ * Real*Real -> Real
244
+ * Real*Imaginary -> !Real
245
+ * Imaginary*Imaginary -> Real
246
+ """
247
+ if expr.is_number:
248
+ return _RealPredicate_number(expr, assumptions)
249
+ result = True
250
+ for arg in expr.args:
251
+ if ask(Q.real(arg), assumptions):
252
+ pass
253
+ elif ask(Q.imaginary(arg), assumptions):
254
+ result = result ^ True
255
+ else:
256
+ break
257
+ else:
258
+ return result
259
+
260
+ @RealPredicate.register(Pow)
261
+ def _(expr, assumptions):
262
+ """
263
+ * Real**Integer -> Real
264
+ * Positive**Real -> Real
265
+ * Real**(Integer/Even) -> Real if base is nonnegative
266
+ * Real**(Integer/Odd) -> Real
267
+ * Imaginary**(Integer/Even) -> Real
268
+ * Imaginary**(Integer/Odd) -> not Real
269
+ * Imaginary**Real -> ? since Real could be 0 (giving real)
270
+ or 1 (giving imaginary)
271
+ * b**Imaginary -> Real if log(b) is imaginary and b != 0
272
+ and exponent != integer multiple of
273
+ I*pi/log(b)
274
+ * Real**Real -> ? e.g. sqrt(-1) is imaginary and
275
+ sqrt(2) is not
276
+ """
277
+ if expr.is_number:
278
+ return _RealPredicate_number(expr, assumptions)
279
+
280
+ if expr.base == E:
281
+ return ask(
282
+ Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
283
+ )
284
+
285
+ if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
286
+ if ask(Q.imaginary(expr.base.exp), assumptions):
287
+ if ask(Q.imaginary(expr.exp), assumptions):
288
+ return True
289
+ # If the i = (exp's arg)/(I*pi) is an integer or half-integer
290
+ # multiple of I*pi then 2*i will be an integer. In addition,
291
+ # exp(i*I*pi) = (-1)**i so the overall realness of the expr
292
+ # can be determined by replacing exp(i*I*pi) with (-1)**i.
293
+ i = expr.base.exp/I/pi
294
+ if ask(Q.integer(2*i), assumptions):
295
+ return ask(Q.real((S.NegativeOne**i)**expr.exp), assumptions)
296
+ return
297
+
298
+ if ask(Q.imaginary(expr.base), assumptions):
299
+ if ask(Q.integer(expr.exp), assumptions):
300
+ odd = ask(Q.odd(expr.exp), assumptions)
301
+ if odd is not None:
302
+ return not odd
303
+ return
304
+
305
+ if ask(Q.imaginary(expr.exp), assumptions):
306
+ imlog = ask(Q.imaginary(log(expr.base)), assumptions)
307
+ if imlog is not None:
308
+ # I**i -> real, log(I) is imag;
309
+ # (2*I)**i -> complex, log(2*I) is not imag
310
+ return imlog
311
+
312
+ if ask(Q.real(expr.base), assumptions):
313
+ if ask(Q.real(expr.exp), assumptions):
314
+ if expr.exp.is_Rational and \
315
+ ask(Q.even(expr.exp.q), assumptions):
316
+ return ask(Q.positive(expr.base), assumptions)
317
+ elif ask(Q.integer(expr.exp), assumptions):
318
+ return True
319
+ elif ask(Q.positive(expr.base), assumptions):
320
+ return True
321
+ elif ask(Q.negative(expr.base), assumptions):
322
+ return False
323
+
324
+ @RealPredicate.register_many(cos, sin)
325
+ def _(expr, assumptions):
326
+ if ask(Q.real(expr.args[0]), assumptions):
327
+ return True
328
+
329
+ @RealPredicate.register(exp)
330
+ def _(expr, assumptions):
331
+ return ask(
332
+ Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
333
+ )
334
+
335
+ @RealPredicate.register(log)
336
+ def _(expr, assumptions):
337
+ return ask(Q.positive(expr.args[0]), assumptions)
338
+
339
+ @RealPredicate.register_many(Determinant, MatrixElement, Trace)
340
+ def _(expr, assumptions):
341
+ return ask(Q.real_elements(expr.args[0]), assumptions)
342
+
343
+
344
+ # ExtendedRealPredicate
345
+
346
+ @ExtendedRealPredicate.register(object)
347
+ def _(expr, assumptions):
348
+ return ask(Q.negative_infinite(expr)
349
+ | Q.negative(expr)
350
+ | Q.zero(expr)
351
+ | Q.positive(expr)
352
+ | Q.positive_infinite(expr),
353
+ assumptions)
354
+
355
+ @ExtendedRealPredicate.register_many(Infinity, NegativeInfinity)
356
+ def _(expr, assumptions):
357
+ return True
358
+
359
+ @ExtendedRealPredicate.register_many(Add, Mul, Pow) # type:ignore
360
+ def _(expr, assumptions):
361
+ return test_closed_group(expr, assumptions, Q.extended_real)
362
+
363
+
364
+ # HermitianPredicate
365
+
366
+ @HermitianPredicate.register(object) # type:ignore
367
+ def _(expr, assumptions):
368
+ if isinstance(expr, MatrixBase):
369
+ return None
370
+ return ask(Q.real(expr), assumptions)
371
+
372
+ @HermitianPredicate.register(Add) # type:ignore
373
+ def _(expr, assumptions):
374
+ """
375
+ * Hermitian + Hermitian -> Hermitian
376
+ * Hermitian + !Hermitian -> !Hermitian
377
+ """
378
+ if expr.is_number:
379
+ raise MDNotImplementedError
380
+ return test_closed_group(expr, assumptions, Q.hermitian)
381
+
382
+ @HermitianPredicate.register(Mul) # type:ignore
383
+ def _(expr, assumptions):
384
+ """
385
+ As long as there is at most only one noncommutative term:
386
+
387
+ * Hermitian*Hermitian -> Hermitian
388
+ * Hermitian*Antihermitian -> !Hermitian
389
+ * Antihermitian*Antihermitian -> Hermitian
390
+ """
391
+ if expr.is_number:
392
+ raise MDNotImplementedError
393
+ nccount = 0
394
+ result = True
395
+ for arg in expr.args:
396
+ if ask(Q.antihermitian(arg), assumptions):
397
+ result = result ^ True
398
+ elif not ask(Q.hermitian(arg), assumptions):
399
+ break
400
+ if ask(~Q.commutative(arg), assumptions):
401
+ nccount += 1
402
+ if nccount > 1:
403
+ break
404
+ else:
405
+ return result
406
+
407
+ @HermitianPredicate.register(Pow) # type:ignore
408
+ def _(expr, assumptions):
409
+ """
410
+ * Hermitian**Integer -> Hermitian
411
+ """
412
+ if expr.is_number:
413
+ raise MDNotImplementedError
414
+ if expr.base == E:
415
+ if ask(Q.hermitian(expr.exp), assumptions):
416
+ return True
417
+ raise MDNotImplementedError
418
+ if ask(Q.hermitian(expr.base), assumptions):
419
+ if ask(Q.integer(expr.exp), assumptions):
420
+ return True
421
+ raise MDNotImplementedError
422
+
423
+ @HermitianPredicate.register_many(cos, sin) # type:ignore
424
+ def _(expr, assumptions):
425
+ if ask(Q.hermitian(expr.args[0]), assumptions):
426
+ return True
427
+ raise MDNotImplementedError
428
+
429
+ @HermitianPredicate.register(exp) # type:ignore
430
+ def _(expr, assumptions):
431
+ if ask(Q.hermitian(expr.exp), assumptions):
432
+ return True
433
+ raise MDNotImplementedError
434
+
435
+ @HermitianPredicate.register(MatrixBase) # type:ignore
436
+ def _(mat, assumptions):
437
+ rows, cols = mat.shape
438
+ ret_val = True
439
+ for i in range(rows):
440
+ for j in range(i, cols):
441
+ cond = fuzzy_bool(Eq(mat[i, j], conjugate(mat[j, i])))
442
+ if cond is None:
443
+ ret_val = None
444
+ if cond == False:
445
+ return False
446
+ if ret_val is None:
447
+ raise MDNotImplementedError
448
+ return ret_val
449
+
450
+
451
+ # ComplexPredicate
452
+
453
+ @ComplexPredicate.register_many(Abs, cos, exp, im, ImaginaryUnit, log, Number, # type:ignore
454
+ NumberSymbol, re, sin)
455
+ def _(expr, assumptions):
456
+ return True
457
+
458
+ @ComplexPredicate.register_many(Infinity, NegativeInfinity) # type:ignore
459
+ def _(expr, assumptions):
460
+ return False
461
+
462
+ @ComplexPredicate.register(Expr) # type:ignore
463
+ def _(expr, assumptions):
464
+ ret = expr.is_complex
465
+ if ret is None:
466
+ raise MDNotImplementedError
467
+ return ret
468
+
469
+ @ComplexPredicate.register_many(Add, Mul) # type:ignore
470
+ def _(expr, assumptions):
471
+ return test_closed_group(expr, assumptions, Q.complex)
472
+
473
+ @ComplexPredicate.register(Pow) # type:ignore
474
+ def _(expr, assumptions):
475
+ if expr.base == E:
476
+ return True
477
+ return test_closed_group(expr, assumptions, Q.complex)
478
+
479
+ @ComplexPredicate.register_many(Determinant, MatrixElement, Trace) # type:ignore
480
+ def _(expr, assumptions):
481
+ return ask(Q.complex_elements(expr.args[0]), assumptions)
482
+
483
+ @ComplexPredicate.register(NaN) # type:ignore
484
+ def _(expr, assumptions):
485
+ return None
486
+
487
+
488
+ # ImaginaryPredicate
489
+
490
+ def _Imaginary_number(expr, assumptions):
491
+ # let as_real_imag() work first since the expression may
492
+ # be simpler to evaluate
493
+ r = expr.as_real_imag()[0].evalf(2)
494
+ if r._prec != 1:
495
+ return not r
496
+ # allow None to be returned if we couldn't show for sure
497
+ # that r was 0
498
+
499
+ @ImaginaryPredicate.register(ImaginaryUnit) # type:ignore
500
+ def _(expr, assumptions):
501
+ return True
502
+
503
+ @ImaginaryPredicate.register(Expr) # type:ignore
504
+ def _(expr, assumptions):
505
+ ret = expr.is_imaginary
506
+ if ret is None:
507
+ raise MDNotImplementedError
508
+ return ret
509
+
510
+ @ImaginaryPredicate.register(Add) # type:ignore
511
+ def _(expr, assumptions):
512
+ """
513
+ * Imaginary + Imaginary -> Imaginary
514
+ * Imaginary + Complex -> ?
515
+ * Imaginary + Real -> !Imaginary
516
+ """
517
+ if expr.is_number:
518
+ return _Imaginary_number(expr, assumptions)
519
+
520
+ reals = 0
521
+ for arg in expr.args:
522
+ if ask(Q.imaginary(arg), assumptions):
523
+ pass
524
+ elif ask(Q.real(arg), assumptions):
525
+ reals += 1
526
+ else:
527
+ break
528
+ else:
529
+ if reals == 0:
530
+ return True
531
+ if reals in (1, len(expr.args)):
532
+ # two reals could sum 0 thus giving an imaginary
533
+ return False
534
+
535
+ @ImaginaryPredicate.register(Mul) # type:ignore
536
+ def _(expr, assumptions):
537
+ """
538
+ * Real*Imaginary -> Imaginary
539
+ * Imaginary*Imaginary -> Real
540
+ """
541
+ if expr.is_number:
542
+ return _Imaginary_number(expr, assumptions)
543
+ result = False
544
+ reals = 0
545
+ for arg in expr.args:
546
+ if ask(Q.imaginary(arg), assumptions):
547
+ result = result ^ True
548
+ elif not ask(Q.real(arg), assumptions):
549
+ break
550
+ else:
551
+ if reals == len(expr.args):
552
+ return False
553
+ return result
554
+
555
+ @ImaginaryPredicate.register(Pow) # type:ignore
556
+ def _(expr, assumptions):
557
+ """
558
+ * Imaginary**Odd -> Imaginary
559
+ * Imaginary**Even -> Real
560
+ * b**Imaginary -> !Imaginary if exponent is an integer
561
+ multiple of I*pi/log(b)
562
+ * Imaginary**Real -> ?
563
+ * Positive**Real -> Real
564
+ * Negative**Integer -> Real
565
+ * Negative**(Integer/2) -> Imaginary
566
+ * Negative**Real -> not Imaginary if exponent is not Rational
567
+ """
568
+ if expr.is_number:
569
+ return _Imaginary_number(expr, assumptions)
570
+
571
+ if expr.base == E:
572
+ a = expr.exp/I/pi
573
+ return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
574
+
575
+ if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
576
+ if ask(Q.imaginary(expr.base.exp), assumptions):
577
+ if ask(Q.imaginary(expr.exp), assumptions):
578
+ return False
579
+ i = expr.base.exp/I/pi
580
+ if ask(Q.integer(2*i), assumptions):
581
+ return ask(Q.imaginary((S.NegativeOne**i)**expr.exp), assumptions)
582
+
583
+ if ask(Q.imaginary(expr.base), assumptions):
584
+ if ask(Q.integer(expr.exp), assumptions):
585
+ odd = ask(Q.odd(expr.exp), assumptions)
586
+ if odd is not None:
587
+ return odd
588
+ return
589
+
590
+ if ask(Q.imaginary(expr.exp), assumptions):
591
+ imlog = ask(Q.imaginary(log(expr.base)), assumptions)
592
+ if imlog is not None:
593
+ # I**i -> real; (2*I)**i -> complex ==> not imaginary
594
+ return False
595
+
596
+ if ask(Q.real(expr.base) & Q.real(expr.exp), assumptions):
597
+ if ask(Q.positive(expr.base), assumptions):
598
+ return False
599
+ else:
600
+ rat = ask(Q.rational(expr.exp), assumptions)
601
+ if not rat:
602
+ return rat
603
+ if ask(Q.integer(expr.exp), assumptions):
604
+ return False
605
+ else:
606
+ half = ask(Q.integer(2*expr.exp), assumptions)
607
+ if half:
608
+ return ask(Q.negative(expr.base), assumptions)
609
+ return half
610
+
611
+ @ImaginaryPredicate.register(log) # type:ignore
612
+ def _(expr, assumptions):
613
+ if ask(Q.real(expr.args[0]), assumptions):
614
+ if ask(Q.positive(expr.args[0]), assumptions):
615
+ return False
616
+ return
617
+ # XXX it should be enough to do
618
+ # return ask(Q.nonpositive(expr.args[0]), assumptions)
619
+ # but ask(Q.nonpositive(exp(x)), Q.imaginary(x)) -> None;
620
+ # it should return True since exp(x) will be either 0 or complex
621
+ if expr.args[0].func == exp or (expr.args[0].is_Pow and expr.args[0].base == E):
622
+ if expr.args[0].exp in [I, -I]:
623
+ return True
624
+ im = ask(Q.imaginary(expr.args[0]), assumptions)
625
+ if im is False:
626
+ return False
627
+
628
+ @ImaginaryPredicate.register(exp) # type:ignore
629
+ def _(expr, assumptions):
630
+ a = expr.exp/I/pi
631
+ return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
632
+
633
+ @ImaginaryPredicate.register_many(Number, NumberSymbol) # type:ignore
634
+ def _(expr, assumptions):
635
+ return not (expr.as_real_imag()[1] == 0)
636
+
637
+ @ImaginaryPredicate.register(NaN) # type:ignore
638
+ def _(expr, assumptions):
639
+ return None
640
+
641
+
642
+ # AntihermitianPredicate
643
+
644
+ @AntihermitianPredicate.register(object) # type:ignore
645
+ def _(expr, assumptions):
646
+ if isinstance(expr, MatrixBase):
647
+ return None
648
+ if ask(Q.zero(expr), assumptions):
649
+ return True
650
+ return ask(Q.imaginary(expr), assumptions)
651
+
652
+ @AntihermitianPredicate.register(Add) # type:ignore
653
+ def _(expr, assumptions):
654
+ """
655
+ * Antihermitian + Antihermitian -> Antihermitian
656
+ * Antihermitian + !Antihermitian -> !Antihermitian
657
+ """
658
+ if expr.is_number:
659
+ raise MDNotImplementedError
660
+ return test_closed_group(expr, assumptions, Q.antihermitian)
661
+
662
+ @AntihermitianPredicate.register(Mul) # type:ignore
663
+ def _(expr, assumptions):
664
+ """
665
+ As long as there is at most only one noncommutative term:
666
+
667
+ * Hermitian*Hermitian -> !Antihermitian
668
+ * Hermitian*Antihermitian -> Antihermitian
669
+ * Antihermitian*Antihermitian -> !Antihermitian
670
+ """
671
+ if expr.is_number:
672
+ raise MDNotImplementedError
673
+ nccount = 0
674
+ result = False
675
+ for arg in expr.args:
676
+ if ask(Q.antihermitian(arg), assumptions):
677
+ result = result ^ True
678
+ elif not ask(Q.hermitian(arg), assumptions):
679
+ break
680
+ if ask(~Q.commutative(arg), assumptions):
681
+ nccount += 1
682
+ if nccount > 1:
683
+ break
684
+ else:
685
+ return result
686
+
687
+ @AntihermitianPredicate.register(Pow) # type:ignore
688
+ def _(expr, assumptions):
689
+ """
690
+ * Hermitian**Integer -> !Antihermitian
691
+ * Antihermitian**Even -> !Antihermitian
692
+ * Antihermitian**Odd -> Antihermitian
693
+ """
694
+ if expr.is_number:
695
+ raise MDNotImplementedError
696
+ if ask(Q.hermitian(expr.base), assumptions):
697
+ if ask(Q.integer(expr.exp), assumptions):
698
+ return False
699
+ elif ask(Q.antihermitian(expr.base), assumptions):
700
+ if ask(Q.even(expr.exp), assumptions):
701
+ return False
702
+ elif ask(Q.odd(expr.exp), assumptions):
703
+ return True
704
+ raise MDNotImplementedError
705
+
706
+ @AntihermitianPredicate.register(MatrixBase) # type:ignore
707
+ def _(mat, assumptions):
708
+ rows, cols = mat.shape
709
+ ret_val = True
710
+ for i in range(rows):
711
+ for j in range(i, cols):
712
+ cond = fuzzy_bool(Eq(mat[i, j], -conjugate(mat[j, i])))
713
+ if cond is None:
714
+ ret_val = None
715
+ if cond == False:
716
+ return False
717
+ if ret_val is None:
718
+ raise MDNotImplementedError
719
+ return ret_val
720
+
721
+
722
+ # AlgebraicPredicate
723
+
724
+ @AlgebraicPredicate.register_many(AlgebraicNumber, Float, GoldenRatio, # type:ignore
725
+ ImaginaryUnit, TribonacciConstant)
726
+ def _(expr, assumptions):
727
+ return True
728
+
729
+ @AlgebraicPredicate.register_many(ComplexInfinity, Exp1, Infinity, # type:ignore
730
+ NegativeInfinity, Pi)
731
+ def _(expr, assumptions):
732
+ return False
733
+
734
+ @AlgebraicPredicate.register_many(Add, Mul) # type:ignore
735
+ def _(expr, assumptions):
736
+ return test_closed_group(expr, assumptions, Q.algebraic)
737
+
738
+ @AlgebraicPredicate.register(Pow) # type:ignore
739
+ def _(expr, assumptions):
740
+ if expr.base == E:
741
+ if ask(Q.algebraic(expr.exp), assumptions):
742
+ return ask(~Q.nonzero(expr.exp), assumptions)
743
+ return
744
+ return expr.exp.is_Rational and ask(Q.algebraic(expr.base), assumptions)
745
+
746
+ @AlgebraicPredicate.register(Rational) # type:ignore
747
+ def _(expr, assumptions):
748
+ return expr.q != 0
749
+
750
+ @AlgebraicPredicate.register_many(asin, atan, cos, sin, tan) # type:ignore
751
+ def _(expr, assumptions):
752
+ x = expr.args[0]
753
+ if ask(Q.algebraic(x), assumptions):
754
+ return ask(~Q.nonzero(x), assumptions)
755
+
756
+ @AlgebraicPredicate.register(exp) # type:ignore
757
+ def _(expr, assumptions):
758
+ x = expr.exp
759
+ if ask(Q.algebraic(x), assumptions):
760
+ return ask(~Q.nonzero(x), assumptions)
761
+
762
+ @AlgebraicPredicate.register_many(acot, cot) # type:ignore
763
+ def _(expr, assumptions):
764
+ x = expr.args[0]
765
+ if ask(Q.algebraic(x), assumptions):
766
+ return False
767
+
768
+ @AlgebraicPredicate.register_many(acos, log) # type:ignore
769
+ def _(expr, assumptions):
770
+ x = expr.args[0]
771
+ if ask(Q.algebraic(x), assumptions):
772
+ return ask(~Q.nonzero(x - 1), assumptions)
lib/python3.10/site-packages/sympy/assumptions/lra_satask.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.assume import global_assumptions
2
+ from sympy.assumptions.cnf import CNF, EncodedCNF
3
+ from sympy.assumptions.ask import Q
4
+ from sympy.logic.inference import satisfiable
5
+ from sympy.logic.algorithms.lra_theory import UnhandledInput, ALLOWED_PRED
6
+ from sympy.matrices.kind import MatrixKind
7
+ from sympy.core.kind import NumberKind
8
+ from sympy.assumptions.assume import AppliedPredicate
9
+ from sympy.core.mul import Mul
10
+ from sympy.core.singleton import S
11
+
12
+
13
+ def lra_satask(proposition, assumptions=True, context=global_assumptions):
14
+ """
15
+ Function to evaluate the proposition with assumptions using SAT algorithm
16
+ in conjunction with an Linear Real Arithmetic theory solver.
17
+
18
+ Used to handle inequalities. Should eventually be depreciated and combined
19
+ into satask, but infinity handling and other things need to be implemented
20
+ before that can happen.
21
+ """
22
+ props = CNF.from_prop(proposition)
23
+ _props = CNF.from_prop(~proposition)
24
+
25
+ cnf = CNF.from_prop(assumptions)
26
+ assumptions = EncodedCNF()
27
+ assumptions.from_cnf(cnf)
28
+
29
+ context_cnf = CNF()
30
+ if context:
31
+ context_cnf = context_cnf.extend(context)
32
+
33
+ assumptions.add_from_cnf(context_cnf)
34
+
35
+ return check_satisfiability(props, _props, assumptions)
36
+
37
+ # Some predicates such as Q.prime can't be handled by lra_satask.
38
+ # For example, (x > 0) & (x < 1) & Q.prime(x) is unsat but lra_satask would think it was sat.
39
+ # WHITE_LIST is a list of predicates that can always be handled.
40
+ WHITE_LIST = ALLOWED_PRED | {Q.positive, Q.negative, Q.zero, Q.nonzero, Q.nonpositive, Q.nonnegative,
41
+ Q.extended_positive, Q.extended_negative, Q.extended_nonpositive,
42
+ Q.extended_negative, Q.extended_nonzero, Q.negative_infinite,
43
+ Q.positive_infinite}
44
+
45
+
46
+ def check_satisfiability(prop, _prop, factbase):
47
+ sat_true = factbase.copy()
48
+ sat_false = factbase.copy()
49
+ sat_true.add_from_cnf(prop)
50
+ sat_false.add_from_cnf(_prop)
51
+
52
+ all_pred, all_exprs = get_all_pred_and_expr_from_enc_cnf(sat_true)
53
+
54
+ for pred in all_pred:
55
+ if pred.function not in WHITE_LIST and pred.function != Q.ne:
56
+ raise UnhandledInput(f"LRASolver: {pred} is an unhandled predicate")
57
+ for expr in all_exprs:
58
+ if expr.kind == MatrixKind(NumberKind):
59
+ raise UnhandledInput(f"LRASolver: {expr} is of MatrixKind")
60
+ if expr == S.NaN:
61
+ raise UnhandledInput("LRASolver: nan")
62
+
63
+ # convert old assumptions into predicates and add them to sat_true and sat_false
64
+ # also check for unhandled predicates
65
+ for assm in extract_pred_from_old_assum(all_exprs):
66
+ n = len(sat_true.encoding)
67
+ if assm not in sat_true.encoding:
68
+ sat_true.encoding[assm] = n+1
69
+ sat_true.data.append([sat_true.encoding[assm]])
70
+
71
+ n = len(sat_false.encoding)
72
+ if assm not in sat_false.encoding:
73
+ sat_false.encoding[assm] = n+1
74
+ sat_false.data.append([sat_false.encoding[assm]])
75
+
76
+
77
+ sat_true = _preprocess(sat_true)
78
+ sat_false = _preprocess(sat_false)
79
+
80
+ can_be_true = satisfiable(sat_true, use_lra_theory=True) is not False
81
+ can_be_false = satisfiable(sat_false, use_lra_theory=True) is not False
82
+
83
+ if can_be_true and can_be_false:
84
+ return None
85
+
86
+ if can_be_true and not can_be_false:
87
+ return True
88
+
89
+ if not can_be_true and can_be_false:
90
+ return False
91
+
92
+ if not can_be_true and not can_be_false:
93
+ raise ValueError("Inconsistent assumptions")
94
+
95
+
96
+ def _preprocess(enc_cnf):
97
+ """
98
+ Returns an encoded cnf with only Q.eq, Q.gt, Q.lt,
99
+ Q.ge, and Q.le predicate.
100
+
101
+ Converts every unequality into a disjunction of strict
102
+ inequalities. For example, x != 3 would become
103
+ x < 3 OR x > 3.
104
+
105
+ Also converts all negated Q.ne predicates into
106
+ equalities.
107
+ """
108
+
109
+ # loops through each literal in each clause
110
+ # to construct a new, preprocessed encodedCNF
111
+
112
+ enc_cnf = enc_cnf.copy()
113
+ cur_enc = 1
114
+ rev_encoding = {value: key for key, value in enc_cnf.encoding.items()}
115
+
116
+ new_encoding = {}
117
+ new_data = []
118
+ for clause in enc_cnf.data:
119
+ new_clause = []
120
+ for lit in clause:
121
+ if lit == 0:
122
+ new_clause.append(lit)
123
+ new_encoding[lit] = False
124
+ continue
125
+ prop = rev_encoding[abs(lit)]
126
+ negated = lit < 0
127
+ sign = (lit > 0) - (lit < 0)
128
+
129
+ prop = _pred_to_binrel(prop)
130
+
131
+ if not isinstance(prop, AppliedPredicate):
132
+ if prop not in new_encoding:
133
+ new_encoding[prop] = cur_enc
134
+ cur_enc += 1
135
+ lit = new_encoding[prop]
136
+ new_clause.append(sign*lit)
137
+ continue
138
+
139
+
140
+ if negated and prop.function == Q.eq:
141
+ negated = False
142
+ prop = Q.ne(*prop.arguments)
143
+
144
+ if prop.function == Q.ne:
145
+ arg1, arg2 = prop.arguments
146
+ if negated:
147
+ new_prop = Q.eq(arg1, arg2)
148
+ if new_prop not in new_encoding:
149
+ new_encoding[new_prop] = cur_enc
150
+ cur_enc += 1
151
+
152
+ new_enc = new_encoding[new_prop]
153
+ new_clause.append(new_enc)
154
+ continue
155
+ else:
156
+ new_props = (Q.gt(arg1, arg2), Q.lt(arg1, arg2))
157
+ for new_prop in new_props:
158
+ if new_prop not in new_encoding:
159
+ new_encoding[new_prop] = cur_enc
160
+ cur_enc += 1
161
+
162
+ new_enc = new_encoding[new_prop]
163
+ new_clause.append(new_enc)
164
+ continue
165
+
166
+ if prop.function == Q.eq and negated:
167
+ assert False
168
+
169
+ if prop not in new_encoding:
170
+ new_encoding[prop] = cur_enc
171
+ cur_enc += 1
172
+ new_clause.append(new_encoding[prop]*sign)
173
+ new_data.append(new_clause)
174
+
175
+ assert len(new_encoding) >= cur_enc - 1
176
+
177
+ enc_cnf = EncodedCNF(new_data, new_encoding)
178
+ return enc_cnf
179
+
180
+
181
+ def _pred_to_binrel(pred):
182
+ if not isinstance(pred, AppliedPredicate):
183
+ return pred
184
+
185
+ if pred.function in pred_to_pos_neg_zero:
186
+ f = pred_to_pos_neg_zero[pred.function]
187
+ if f is False:
188
+ return False
189
+ pred = f(pred.arguments[0])
190
+
191
+ if pred.function == Q.positive:
192
+ pred = Q.gt(pred.arguments[0], 0)
193
+ elif pred.function == Q.negative:
194
+ pred = Q.lt(pred.arguments[0], 0)
195
+ elif pred.function == Q.zero:
196
+ pred = Q.eq(pred.arguments[0], 0)
197
+ elif pred.function == Q.nonpositive:
198
+ pred = Q.le(pred.arguments[0], 0)
199
+ elif pred.function == Q.nonnegative:
200
+ pred = Q.ge(pred.arguments[0], 0)
201
+ elif pred.function == Q.nonzero:
202
+ pred = Q.ne(pred.arguments[0], 0)
203
+
204
+ return pred
205
+
206
+ pred_to_pos_neg_zero = {
207
+ Q.extended_positive: Q.positive,
208
+ Q.extended_negative: Q.negative,
209
+ Q.extended_nonpositive: Q.nonpositive,
210
+ Q.extended_negative: Q.negative,
211
+ Q.extended_nonzero: Q.nonzero,
212
+ Q.negative_infinite: False,
213
+ Q.positive_infinite: False
214
+ }
215
+
216
+ def get_all_pred_and_expr_from_enc_cnf(enc_cnf):
217
+ all_exprs = set()
218
+ all_pred = set()
219
+ for pred in enc_cnf.encoding.keys():
220
+ if isinstance(pred, AppliedPredicate):
221
+ all_pred.add(pred)
222
+ all_exprs.update(pred.arguments)
223
+
224
+ return all_pred, all_exprs
225
+
226
+ def extract_pred_from_old_assum(all_exprs):
227
+ """
228
+ Returns a list of relevant new assumption predicate
229
+ based on any old assumptions.
230
+
231
+ Raises an UnhandledInput exception if any of the assumptions are
232
+ unhandled.
233
+
234
+ Ignored predicate:
235
+ - commutative
236
+ - complex
237
+ - algebraic
238
+ - transcendental
239
+ - extended_real
240
+ - real
241
+ - all matrix predicate
242
+ - rational
243
+ - irrational
244
+
245
+ Example
246
+ =======
247
+ >>> from sympy.assumptions.lra_satask import extract_pred_from_old_assum
248
+ >>> from sympy import symbols
249
+ >>> x, y = symbols("x y", positive=True)
250
+ >>> extract_pred_from_old_assum([x, y, 2])
251
+ [Q.positive(x), Q.positive(y)]
252
+ """
253
+ ret = []
254
+ for expr in all_exprs:
255
+ if not hasattr(expr, "free_symbols"):
256
+ continue
257
+ if len(expr.free_symbols) == 0:
258
+ continue
259
+
260
+ if expr.is_real is not True:
261
+ raise UnhandledInput(f"LRASolver: {expr} must be real")
262
+ # test for I times imaginary variable; such expressions are considered real
263
+ if isinstance(expr, Mul) and any(arg.is_real is not True for arg in expr.args):
264
+ raise UnhandledInput(f"LRASolver: {expr} must be real")
265
+
266
+ if expr.is_integer == True and expr.is_zero != True:
267
+ raise UnhandledInput(f"LRASolver: {expr} is an integer")
268
+ if expr.is_integer == False:
269
+ raise UnhandledInput(f"LRASolver: {expr} can't be an integer")
270
+ if expr.is_rational == False:
271
+ raise UnhandledInput(f"LRASolver: {expr} is irational")
272
+
273
+ if expr.is_zero:
274
+ ret.append(Q.zero(expr))
275
+ elif expr.is_positive:
276
+ ret.append(Q.positive(expr))
277
+ elif expr.is_negative:
278
+ ret.append(Q.negative(expr))
279
+ elif expr.is_nonzero:
280
+ ret.append(Q.nonzero(expr))
281
+ elif expr.is_nonpositive:
282
+ ret.append(Q.nonpositive(expr))
283
+ elif expr.is_nonnegative:
284
+ ret.append(Q.nonnegative(expr))
285
+
286
+ return ret
lib/python3.10/site-packages/sympy/assumptions/predicates/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ """
2
+ Module to implement predicate classes.
3
+
4
+ Class of every predicate registered to ``Q`` is defined here.
5
+ """
lib/python3.10/site-packages/sympy/assumptions/predicates/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (334 Bytes). View file
 
lib/python3.10/site-packages/sympy/assumptions/predicates/__pycache__/calculus.cpython-310.pyc ADDED
Binary file (2.34 kB). View file
 
lib/python3.10/site-packages/sympy/assumptions/predicates/__pycache__/common.cpython-310.pyc ADDED
Binary file (2.73 kB). View file