Shenghong commited on
Commit
0e987da
·
verified ·
1 Parent(s): e568f52

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.1/site-packages/sympy/__pycache__/__init__.cpython-310.pyc +0 -0
  2. lib/python3.1/site-packages/sympy/__pycache__/abc.cpython-310.pyc +0 -0
  3. lib/python3.1/site-packages/sympy/__pycache__/conftest.cpython-310.pyc +0 -0
  4. lib/python3.1/site-packages/sympy/__pycache__/galgebra.cpython-310.pyc +0 -0
  5. lib/python3.1/site-packages/sympy/__pycache__/release.cpython-310.pyc +0 -0
  6. lib/python3.1/site-packages/sympy/__pycache__/this.cpython-310.pyc +0 -0
  7. lib/python3.1/site-packages/sympy/algebras/__init__.py +3 -0
  8. lib/python3.1/site-packages/sympy/algebras/__pycache__/__init__.cpython-310.pyc +0 -0
  9. lib/python3.1/site-packages/sympy/algebras/__pycache__/quaternion.cpython-310.pyc +0 -0
  10. lib/python3.1/site-packages/sympy/algebras/quaternion.py +1667 -0
  11. lib/python3.1/site-packages/sympy/algebras/tests/__init__.py +0 -0
  12. lib/python3.1/site-packages/sympy/algebras/tests/__pycache__/__init__.cpython-310.pyc +0 -0
  13. lib/python3.1/site-packages/sympy/algebras/tests/__pycache__/test_quaternion.cpython-310.pyc +0 -0
  14. lib/python3.1/site-packages/sympy/algebras/tests/test_quaternion.py +428 -0
  15. lib/python3.1/site-packages/sympy/codegen/__init__.py +24 -0
  16. lib/python3.1/site-packages/sympy/codegen/__pycache__/__init__.cpython-310.pyc +0 -0
  17. lib/python3.1/site-packages/sympy/codegen/__pycache__/abstract_nodes.cpython-310.pyc +0 -0
  18. lib/python3.1/site-packages/sympy/codegen/__pycache__/algorithms.cpython-310.pyc +0 -0
  19. lib/python3.1/site-packages/sympy/codegen/__pycache__/approximations.cpython-310.pyc +0 -0
  20. lib/python3.1/site-packages/sympy/codegen/__pycache__/ast.cpython-310.pyc +0 -0
  21. lib/python3.1/site-packages/sympy/codegen/__pycache__/cfunctions.cpython-310.pyc +0 -0
  22. lib/python3.1/site-packages/sympy/codegen/__pycache__/cnodes.cpython-310.pyc +0 -0
  23. lib/python3.1/site-packages/sympy/codegen/__pycache__/cutils.cpython-310.pyc +0 -0
  24. lib/python3.1/site-packages/sympy/codegen/__pycache__/cxxnodes.cpython-310.pyc +0 -0
  25. lib/python3.1/site-packages/sympy/codegen/__pycache__/fnodes.cpython-310.pyc +0 -0
  26. lib/python3.1/site-packages/sympy/codegen/__pycache__/futils.cpython-310.pyc +0 -0
  27. lib/python3.1/site-packages/sympy/codegen/__pycache__/matrix_nodes.cpython-310.pyc +0 -0
  28. lib/python3.1/site-packages/sympy/codegen/__pycache__/numpy_nodes.cpython-310.pyc +0 -0
  29. lib/python3.1/site-packages/sympy/codegen/__pycache__/pynodes.cpython-310.pyc +0 -0
  30. lib/python3.1/site-packages/sympy/codegen/__pycache__/pyutils.cpython-310.pyc +0 -0
  31. lib/python3.1/site-packages/sympy/codegen/__pycache__/rewriting.cpython-310.pyc +0 -0
  32. lib/python3.1/site-packages/sympy/codegen/__pycache__/scipy_nodes.cpython-310.pyc +0 -0
  33. lib/python3.1/site-packages/sympy/codegen/abstract_nodes.py +18 -0
  34. lib/python3.1/site-packages/sympy/codegen/algorithms.py +180 -0
  35. lib/python3.1/site-packages/sympy/codegen/approximations.py +187 -0
  36. lib/python3.1/site-packages/sympy/codegen/ast.py +1906 -0
  37. lib/python3.1/site-packages/sympy/codegen/cfunctions.py +536 -0
  38. lib/python3.1/site-packages/sympy/codegen/cnodes.py +156 -0
  39. lib/python3.1/site-packages/sympy/codegen/cutils.py +8 -0
  40. lib/python3.1/site-packages/sympy/codegen/cxxnodes.py +14 -0
  41. lib/python3.1/site-packages/sympy/codegen/fnodes.py +657 -0
  42. lib/python3.1/site-packages/sympy/codegen/futils.py +40 -0
  43. lib/python3.1/site-packages/sympy/codegen/matrix_nodes.py +71 -0
  44. lib/python3.1/site-packages/sympy/codegen/numpy_nodes.py +110 -0
  45. lib/python3.1/site-packages/sympy/codegen/pynodes.py +11 -0
  46. lib/python3.1/site-packages/sympy/codegen/pyutils.py +24 -0
  47. lib/python3.1/site-packages/sympy/codegen/rewriting.py +357 -0
  48. lib/python3.1/site-packages/sympy/codegen/scipy_nodes.py +79 -0
  49. lib/python3.1/site-packages/sympy/codegen/tests/__init__.py +0 -0
  50. lib/python3.1/site-packages/sympy/codegen/tests/__pycache__/__init__.cpython-310.pyc +0 -0
lib/python3.1/site-packages/sympy/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (28.3 kB). View file
 
lib/python3.1/site-packages/sympy/__pycache__/abc.cpython-310.pyc ADDED
Binary file (3.56 kB). View file
 
lib/python3.1/site-packages/sympy/__pycache__/conftest.cpython-310.pyc ADDED
Binary file (2.98 kB). View file
 
lib/python3.1/site-packages/sympy/__pycache__/galgebra.cpython-310.pyc ADDED
Binary file (314 Bytes). View file
 
lib/python3.1/site-packages/sympy/__pycache__/release.cpython-310.pyc ADDED
Binary file (223 Bytes). View file
 
lib/python3.1/site-packages/sympy/__pycache__/this.cpython-310.pyc ADDED
Binary file (762 Bytes). View file
 
lib/python3.1/site-packages/sympy/algebras/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .quaternion import Quaternion
2
+
3
+ __all__ = ["Quaternion",]
lib/python3.1/site-packages/sympy/algebras/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (278 Bytes). View file
 
lib/python3.1/site-packages/sympy/algebras/__pycache__/quaternion.cpython-310.pyc ADDED
Binary file (47.2 kB). View file
 
lib/python3.1/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.1/site-packages/sympy/algebras/tests/__init__.py ADDED
File without changes
lib/python3.1/site-packages/sympy/algebras/tests/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (217 Bytes). View file
 
lib/python3.1/site-packages/sympy/algebras/tests/__pycache__/test_quaternion.cpython-310.pyc ADDED
Binary file (16 kB). View file
 
lib/python3.1/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.1/site-packages/sympy/codegen/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ The ``sympy.codegen`` module contains classes and functions for building
2
+ abstract syntax trees of algorithms. These trees may then be printed by the
3
+ code-printers in ``sympy.printing``.
4
+
5
+ There are several submodules available:
6
+ - ``sympy.codegen.ast``: AST nodes useful across multiple languages.
7
+ - ``sympy.codegen.cnodes``: AST nodes useful for the C family of languages.
8
+ - ``sympy.codegen.fnodes``: AST nodes useful for Fortran.
9
+ - ``sympy.codegen.cfunctions``: functions specific to C (C99 math functions)
10
+ - ``sympy.codegen.ffunctions``: functions specific to Fortran (e.g. ``kind``).
11
+
12
+
13
+
14
+ """
15
+ from .ast import (
16
+ Assignment, aug_assign, CodeBlock, For, Attribute, Variable, Declaration,
17
+ While, Scope, Print, FunctionPrototype, FunctionDefinition, FunctionCall
18
+ )
19
+
20
+ __all__ = [
21
+ 'Assignment', 'aug_assign', 'CodeBlock', 'For', 'Attribute', 'Variable',
22
+ 'Declaration', 'While', 'Scope', 'Print', 'FunctionPrototype',
23
+ 'FunctionDefinition', 'FunctionCall',
24
+ ]
lib/python3.1/site-packages/sympy/codegen/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (1.12 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/abstract_nodes.cpython-310.pyc ADDED
Binary file (982 Bytes). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/algorithms.cpython-310.pyc ADDED
Binary file (6.82 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/approximations.cpython-310.pyc ADDED
Binary file (6.36 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/ast.cpython-310.pyc ADDED
Binary file (60.5 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/cfunctions.cpython-310.pyc ADDED
Binary file (14.1 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/cnodes.cpython-310.pyc ADDED
Binary file (4.93 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/cutils.cpython-310.pyc ADDED
Binary file (749 Bytes). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/cxxnodes.cpython-310.pyc ADDED
Binary file (733 Bytes). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/fnodes.cpython-310.pyc ADDED
Binary file (21.3 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/futils.cpython-310.pyc ADDED
Binary file (2.05 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/matrix_nodes.cpython-310.pyc ADDED
Binary file (2.8 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/numpy_nodes.cpython-310.pyc ADDED
Binary file (3.88 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/pynodes.cpython-310.pyc ADDED
Binary file (701 Bytes). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/pyutils.cpython-310.pyc ADDED
Binary file (1.2 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/rewriting.cpython-310.pyc ADDED
Binary file (13.5 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/__pycache__/scipy_nodes.cpython-310.pyc ADDED
Binary file (3.29 kB). View file
 
lib/python3.1/site-packages/sympy/codegen/abstract_nodes.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """This module provides containers for python objects that are valid
2
+ printing targets but are not a subclass of SymPy's Printable.
3
+ """
4
+
5
+
6
+ from sympy.core.containers import Tuple
7
+
8
+
9
+ class List(Tuple):
10
+ """Represents a (frozen) (Python) list (for code printing purposes)."""
11
+ def __eq__(self, other):
12
+ if isinstance(other, list):
13
+ return self == List(*other)
14
+ else:
15
+ return self.args == other
16
+
17
+ def __hash__(self):
18
+ return super().__hash__()
lib/python3.1/site-packages/sympy/codegen/algorithms.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.containers import Tuple
2
+ from sympy.core.numbers import oo
3
+ from sympy.core.relational import (Gt, Lt)
4
+ from sympy.core.symbol import (Dummy, Symbol)
5
+ from sympy.functions.elementary.complexes import Abs
6
+ from sympy.functions.elementary.miscellaneous import Min, Max
7
+ from sympy.logic.boolalg import And
8
+ from sympy.codegen.ast import (
9
+ Assignment, AddAugmentedAssignment, break_, CodeBlock, Declaration, FunctionDefinition,
10
+ Print, Return, Scope, While, Variable, Pointer, real
11
+ )
12
+ from sympy.codegen.cfunctions import isnan
13
+
14
+ """ This module collects functions for constructing ASTs representing algorithms. """
15
+
16
+ def newtons_method(expr, wrt, atol=1e-12, delta=None, *, rtol=4e-16, debug=False,
17
+ itermax=None, counter=None, delta_fn=lambda e, x: -e/e.diff(x),
18
+ cse=False, handle_nan=None,
19
+ bounds=None):
20
+ """ Generates an AST for Newton-Raphson method (a root-finding algorithm).
21
+
22
+ Explanation
23
+ ===========
24
+
25
+ Returns an abstract syntax tree (AST) based on ``sympy.codegen.ast`` for Netwon's
26
+ method of root-finding.
27
+
28
+ Parameters
29
+ ==========
30
+
31
+ expr : expression
32
+ wrt : Symbol
33
+ With respect to, i.e. what is the variable.
34
+ atol : number or expression
35
+ Absolute tolerance (stopping criterion)
36
+ rtol : number or expression
37
+ Relative tolerance (stopping criterion)
38
+ delta : Symbol
39
+ Will be a ``Dummy`` if ``None``.
40
+ debug : bool
41
+ Whether to print convergence information during iterations
42
+ itermax : number or expr
43
+ Maximum number of iterations.
44
+ counter : Symbol
45
+ Will be a ``Dummy`` if ``None``.
46
+ delta_fn: Callable[[Expr, Symbol], Expr]
47
+ computes the step, default is newtons method. For e.g. Halley's method
48
+ use delta_fn=lambda e, x: -2*e*e.diff(x)/(2*e.diff(x)**2 - e*e.diff(x, 2))
49
+ cse: bool
50
+ Perform common sub-expression elimination on delta expression
51
+ handle_nan: Token
52
+ How to handle occurrence of not-a-number (NaN).
53
+ bounds: Optional[tuple[Expr, Expr]]
54
+ Perform optimization within bounds
55
+
56
+ Examples
57
+ ========
58
+
59
+ >>> from sympy import symbols, cos
60
+ >>> from sympy.codegen.ast import Assignment
61
+ >>> from sympy.codegen.algorithms import newtons_method
62
+ >>> x, dx, atol = symbols('x dx atol')
63
+ >>> expr = cos(x) - x**3
64
+ >>> algo = newtons_method(expr, x, atol=atol, delta=dx)
65
+ >>> algo.has(Assignment(dx, -expr/expr.diff(x)))
66
+ True
67
+
68
+ References
69
+ ==========
70
+
71
+ .. [1] https://en.wikipedia.org/wiki/Newton%27s_method
72
+
73
+ """
74
+
75
+ if delta is None:
76
+ delta = Dummy()
77
+ Wrapper = Scope
78
+ name_d = 'delta'
79
+ else:
80
+ Wrapper = lambda x: x
81
+ name_d = delta.name
82
+
83
+ delta_expr = delta_fn(expr, wrt)
84
+ if cse:
85
+ from sympy.simplify.cse_main import cse
86
+ cses, (red,) = cse([delta_expr.factor()])
87
+ whl_bdy = [Assignment(dum, sub_e) for dum, sub_e in cses]
88
+ whl_bdy += [Assignment(delta, red)]
89
+ else:
90
+ whl_bdy = [Assignment(delta, delta_expr)]
91
+ if handle_nan is not None:
92
+ whl_bdy += [While(isnan(delta), CodeBlock(handle_nan, break_))]
93
+ whl_bdy += [AddAugmentedAssignment(wrt, delta)]
94
+ if bounds is not None:
95
+ whl_bdy += [Assignment(wrt, Min(Max(wrt, bounds[0]), bounds[1]))]
96
+ if debug:
97
+ prnt = Print([wrt, delta], r"{}=%12.5g {}=%12.5g\n".format(wrt.name, name_d))
98
+ whl_bdy += [prnt]
99
+ req = Gt(Abs(delta), atol + rtol*Abs(wrt))
100
+ declars = [Declaration(Variable(delta, type=real, value=oo))]
101
+ if itermax is not None:
102
+ counter = counter or Dummy(integer=True)
103
+ v_counter = Variable.deduced(counter, 0)
104
+ declars.append(Declaration(v_counter))
105
+ whl_bdy.append(AddAugmentedAssignment(counter, 1))
106
+ req = And(req, Lt(counter, itermax))
107
+ whl = While(req, CodeBlock(*whl_bdy))
108
+ blck = declars
109
+ if debug:
110
+ blck.append(Print([wrt], r"{}=%12.5g\n".format(wrt.name)))
111
+ blck += [whl]
112
+ return Wrapper(CodeBlock(*blck))
113
+
114
+
115
+ def _symbol_of(arg):
116
+ if isinstance(arg, Declaration):
117
+ arg = arg.variable.symbol
118
+ elif isinstance(arg, Variable):
119
+ arg = arg.symbol
120
+ return arg
121
+
122
+
123
+ def newtons_method_function(expr, wrt, params=None, func_name="newton", attrs=Tuple(), *, delta=None, **kwargs):
124
+ """ Generates an AST for a function implementing the Newton-Raphson method.
125
+
126
+ Parameters
127
+ ==========
128
+
129
+ expr : expression
130
+ wrt : Symbol
131
+ With respect to, i.e. what is the variable
132
+ params : iterable of symbols
133
+ Symbols appearing in expr that are taken as constants during the iterations
134
+ (these will be accepted as parameters to the generated function).
135
+ func_name : str
136
+ Name of the generated function.
137
+ attrs : Tuple
138
+ Attribute instances passed as ``attrs`` to ``FunctionDefinition``.
139
+ \\*\\*kwargs :
140
+ Keyword arguments passed to :func:`sympy.codegen.algorithms.newtons_method`.
141
+
142
+ Examples
143
+ ========
144
+
145
+ >>> from sympy import symbols, cos
146
+ >>> from sympy.codegen.algorithms import newtons_method_function
147
+ >>> from sympy.codegen.pyutils import render_as_module
148
+ >>> x = symbols('x')
149
+ >>> expr = cos(x) - x**3
150
+ >>> func = newtons_method_function(expr, x)
151
+ >>> py_mod = render_as_module(func) # source code as string
152
+ >>> namespace = {}
153
+ >>> exec(py_mod, namespace, namespace)
154
+ >>> res = eval('newton(0.5)', namespace)
155
+ >>> abs(res - 0.865474033102) < 1e-12
156
+ True
157
+
158
+ See Also
159
+ ========
160
+
161
+ sympy.codegen.algorithms.newtons_method
162
+
163
+ """
164
+ if params is None:
165
+ params = (wrt,)
166
+ pointer_subs = {p.symbol: Symbol('(*%s)' % p.symbol.name)
167
+ for p in params if isinstance(p, Pointer)}
168
+ if delta is None:
169
+ delta = Symbol('d_' + wrt.name)
170
+ if expr.has(delta):
171
+ delta = None # will use Dummy
172
+ algo = newtons_method(expr, wrt, delta=delta, **kwargs).xreplace(pointer_subs)
173
+ if isinstance(algo, Scope):
174
+ algo = algo.body
175
+ not_in_params = expr.free_symbols.difference({_symbol_of(p) for p in params})
176
+ if not_in_params:
177
+ raise ValueError("Missing symbols in params: %s" % ', '.join(map(str, not_in_params)))
178
+ declars = tuple(Variable(p, real) for p in params)
179
+ body = CodeBlock(algo, Return(wrt))
180
+ return FunctionDefinition(real, func_name, declars, body, attrs=attrs)
lib/python3.1/site-packages/sympy/codegen/approximations.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from sympy.sets.sets import Interval
3
+ from sympy.calculus.singularities import is_increasing, is_decreasing
4
+ from sympy.codegen.rewriting import Optimization
5
+ from sympy.core.function import UndefinedFunction
6
+
7
+ """
8
+ This module collects classes useful for approimate rewriting of expressions.
9
+ This can be beneficial when generating numeric code for which performance is
10
+ of greater importance than precision (e.g. for preconditioners used in iterative
11
+ methods).
12
+ """
13
+
14
+ class SumApprox(Optimization):
15
+ """
16
+ Approximates sum by neglecting small terms.
17
+
18
+ Explanation
19
+ ===========
20
+
21
+ If terms are expressions which can be determined to be monotonic, then
22
+ bounds for those expressions are added.
23
+
24
+ Parameters
25
+ ==========
26
+
27
+ bounds : dict
28
+ Mapping expressions to length 2 tuple of bounds (low, high).
29
+ reltol : number
30
+ Threshold for when to ignore a term. Taken relative to the largest
31
+ lower bound among bounds.
32
+
33
+ Examples
34
+ ========
35
+
36
+ >>> from sympy import exp
37
+ >>> from sympy.abc import x, y, z
38
+ >>> from sympy.codegen.rewriting import optimize
39
+ >>> from sympy.codegen.approximations import SumApprox
40
+ >>> bounds = {x: (-1, 1), y: (1000, 2000), z: (-10, 3)}
41
+ >>> sum_approx3 = SumApprox(bounds, reltol=1e-3)
42
+ >>> sum_approx2 = SumApprox(bounds, reltol=1e-2)
43
+ >>> sum_approx1 = SumApprox(bounds, reltol=1e-1)
44
+ >>> expr = 3*(x + y + exp(z))
45
+ >>> optimize(expr, [sum_approx3])
46
+ 3*(x + y + exp(z))
47
+ >>> optimize(expr, [sum_approx2])
48
+ 3*y + 3*exp(z)
49
+ >>> optimize(expr, [sum_approx1])
50
+ 3*y
51
+
52
+ """
53
+
54
+ def __init__(self, bounds, reltol, **kwargs):
55
+ super().__init__(**kwargs)
56
+ self.bounds = bounds
57
+ self.reltol = reltol
58
+
59
+ def __call__(self, expr):
60
+ return expr.factor().replace(self.query, lambda arg: self.value(arg))
61
+
62
+ def query(self, expr):
63
+ return expr.is_Add
64
+
65
+ def value(self, add):
66
+ for term in add.args:
67
+ if term.is_number or term in self.bounds or len(term.free_symbols) != 1:
68
+ continue
69
+ fs, = term.free_symbols
70
+ if fs not in self.bounds:
71
+ continue
72
+ intrvl = Interval(*self.bounds[fs])
73
+ if is_increasing(term, intrvl, fs):
74
+ self.bounds[term] = (
75
+ term.subs({fs: self.bounds[fs][0]}),
76
+ term.subs({fs: self.bounds[fs][1]})
77
+ )
78
+ elif is_decreasing(term, intrvl, fs):
79
+ self.bounds[term] = (
80
+ term.subs({fs: self.bounds[fs][1]}),
81
+ term.subs({fs: self.bounds[fs][0]})
82
+ )
83
+ else:
84
+ return add
85
+
86
+ if all(term.is_number or term in self.bounds for term in add.args):
87
+ bounds = [(term, term) if term.is_number else self.bounds[term] for term in add.args]
88
+ largest_abs_guarantee = 0
89
+ for lo, hi in bounds:
90
+ if lo <= 0 <= hi:
91
+ continue
92
+ largest_abs_guarantee = max(largest_abs_guarantee,
93
+ min(abs(lo), abs(hi)))
94
+ new_terms = []
95
+ for term, (lo, hi) in zip(add.args, bounds):
96
+ if max(abs(lo), abs(hi)) >= largest_abs_guarantee*self.reltol:
97
+ new_terms.append(term)
98
+ return add.func(*new_terms)
99
+ else:
100
+ return add
101
+
102
+
103
+ class SeriesApprox(Optimization):
104
+ """ Approximates functions by expanding them as a series.
105
+
106
+ Parameters
107
+ ==========
108
+
109
+ bounds : dict
110
+ Mapping expressions to length 2 tuple of bounds (low, high).
111
+ reltol : number
112
+ Threshold for when to ignore a term. Taken relative to the largest
113
+ lower bound among bounds.
114
+ max_order : int
115
+ Largest order to include in series expansion
116
+ n_point_checks : int (even)
117
+ The validity of an expansion (with respect to reltol) is checked at
118
+ discrete points (linearly spaced over the bounds of the variable). The
119
+ number of points used in this numerical check is given by this number.
120
+
121
+ Examples
122
+ ========
123
+
124
+ >>> from sympy import sin, pi
125
+ >>> from sympy.abc import x, y
126
+ >>> from sympy.codegen.rewriting import optimize
127
+ >>> from sympy.codegen.approximations import SeriesApprox
128
+ >>> bounds = {x: (-.1, .1), y: (pi-1, pi+1)}
129
+ >>> series_approx2 = SeriesApprox(bounds, reltol=1e-2)
130
+ >>> series_approx3 = SeriesApprox(bounds, reltol=1e-3)
131
+ >>> series_approx8 = SeriesApprox(bounds, reltol=1e-8)
132
+ >>> expr = sin(x)*sin(y)
133
+ >>> optimize(expr, [series_approx2])
134
+ x*(-y + (y - pi)**3/6 + pi)
135
+ >>> optimize(expr, [series_approx3])
136
+ (-x**3/6 + x)*sin(y)
137
+ >>> optimize(expr, [series_approx8])
138
+ sin(x)*sin(y)
139
+
140
+ """
141
+ def __init__(self, bounds, reltol, max_order=4, n_point_checks=4, **kwargs):
142
+ super().__init__(**kwargs)
143
+ self.bounds = bounds
144
+ self.reltol = reltol
145
+ self.max_order = max_order
146
+ if n_point_checks % 2 == 1:
147
+ raise ValueError("Checking the solution at expansion point is not helpful")
148
+ self.n_point_checks = n_point_checks
149
+ self._prec = math.ceil(-math.log10(self.reltol))
150
+
151
+ def __call__(self, expr):
152
+ return expr.factor().replace(self.query, lambda arg: self.value(arg))
153
+
154
+ def query(self, expr):
155
+ return (expr.is_Function and not isinstance(expr, UndefinedFunction)
156
+ and len(expr.args) == 1)
157
+
158
+ def value(self, fexpr):
159
+ free_symbols = fexpr.free_symbols
160
+ if len(free_symbols) != 1:
161
+ return fexpr
162
+ symb, = free_symbols
163
+ if symb not in self.bounds:
164
+ return fexpr
165
+ lo, hi = self.bounds[symb]
166
+ x0 = (lo + hi)/2
167
+ cheapest = None
168
+ for n in range(self.max_order+1, 0, -1):
169
+ fseri = fexpr.series(symb, x0=x0, n=n).removeO()
170
+ n_ok = True
171
+ for idx in range(self.n_point_checks):
172
+ x = lo + idx*(hi - lo)/(self.n_point_checks - 1)
173
+ val = fseri.xreplace({symb: x})
174
+ ref = fexpr.xreplace({symb: x})
175
+ if abs((1 - val/ref).evalf(self._prec)) > self.reltol:
176
+ n_ok = False
177
+ break
178
+
179
+ if n_ok:
180
+ cheapest = fseri
181
+ else:
182
+ break
183
+
184
+ if cheapest is None:
185
+ return fexpr
186
+ else:
187
+ return cheapest
lib/python3.1/site-packages/sympy/codegen/ast.py ADDED
@@ -0,0 +1,1906 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Types used to represent a full function/module as an Abstract Syntax Tree.
3
+
4
+ Most types are small, and are merely used as tokens in the AST. A tree diagram
5
+ has been included below to illustrate the relationships between the AST types.
6
+
7
+
8
+ AST Type Tree
9
+ -------------
10
+ ::
11
+
12
+ *Basic*
13
+ |
14
+ |
15
+ CodegenAST
16
+ |
17
+ |--->AssignmentBase
18
+ | |--->Assignment
19
+ | |--->AugmentedAssignment
20
+ | |--->AddAugmentedAssignment
21
+ | |--->SubAugmentedAssignment
22
+ | |--->MulAugmentedAssignment
23
+ | |--->DivAugmentedAssignment
24
+ | |--->ModAugmentedAssignment
25
+ |
26
+ |--->CodeBlock
27
+ |
28
+ |
29
+ |--->Token
30
+ |--->Attribute
31
+ |--->For
32
+ |--->String
33
+ | |--->QuotedString
34
+ | |--->Comment
35
+ |--->Type
36
+ | |--->IntBaseType
37
+ | | |--->_SizedIntType
38
+ | | |--->SignedIntType
39
+ | | |--->UnsignedIntType
40
+ | |--->FloatBaseType
41
+ | |--->FloatType
42
+ | |--->ComplexBaseType
43
+ | |--->ComplexType
44
+ |--->Node
45
+ | |--->Variable
46
+ | | |---> Pointer
47
+ | |--->FunctionPrototype
48
+ | |--->FunctionDefinition
49
+ |--->Element
50
+ |--->Declaration
51
+ |--->While
52
+ |--->Scope
53
+ |--->Stream
54
+ |--->Print
55
+ |--->FunctionCall
56
+ |--->BreakToken
57
+ |--->ContinueToken
58
+ |--->NoneToken
59
+ |--->Return
60
+
61
+
62
+ Predefined types
63
+ ----------------
64
+
65
+ A number of ``Type`` instances are provided in the ``sympy.codegen.ast`` module
66
+ for convenience. Perhaps the two most common ones for code-generation (of numeric
67
+ codes) are ``float32`` and ``float64`` (known as single and double precision respectively).
68
+ There are also precision generic versions of Types (for which the codeprinters selects the
69
+ underlying data type at time of printing): ``real``, ``integer``, ``complex_``, ``bool_``.
70
+
71
+ The other ``Type`` instances defined are:
72
+
73
+ - ``intc``: Integer type used by C's "int".
74
+ - ``intp``: Integer type used by C's "unsigned".
75
+ - ``int8``, ``int16``, ``int32``, ``int64``: n-bit integers.
76
+ - ``uint8``, ``uint16``, ``uint32``, ``uint64``: n-bit unsigned integers.
77
+ - ``float80``: known as "extended precision" on modern x86/amd64 hardware.
78
+ - ``complex64``: Complex number represented by two ``float32`` numbers
79
+ - ``complex128``: Complex number represented by two ``float64`` numbers
80
+
81
+ Using the nodes
82
+ ---------------
83
+
84
+ It is possible to construct simple algorithms using the AST nodes. Let's construct a loop applying
85
+ Newton's method::
86
+
87
+ >>> from sympy import symbols, cos
88
+ >>> from sympy.codegen.ast import While, Assignment, aug_assign, Print, QuotedString
89
+ >>> t, dx, x = symbols('tol delta val')
90
+ >>> expr = cos(x) - x**3
91
+ >>> whl = While(abs(dx) > t, [
92
+ ... Assignment(dx, -expr/expr.diff(x)),
93
+ ... aug_assign(x, '+', dx),
94
+ ... Print([x])
95
+ ... ])
96
+ >>> from sympy import pycode
97
+ >>> py_str = pycode(whl)
98
+ >>> print(py_str)
99
+ while (abs(delta) > tol):
100
+ delta = (val**3 - math.cos(val))/(-3*val**2 - math.sin(val))
101
+ val += delta
102
+ print(val)
103
+ >>> import math
104
+ >>> tol, val, delta = 1e-5, 0.5, float('inf')
105
+ >>> exec(py_str)
106
+ 1.1121416371
107
+ 0.909672693737
108
+ 0.867263818209
109
+ 0.865477135298
110
+ 0.865474033111
111
+ >>> print('%3.1g' % (math.cos(val) - val**3))
112
+ -3e-11
113
+
114
+ If we want to generate Fortran code for the same while loop we simple call ``fcode``::
115
+
116
+ >>> from sympy import fcode
117
+ >>> print(fcode(whl, standard=2003, source_format='free'))
118
+ do while (abs(delta) > tol)
119
+ delta = (val**3 - cos(val))/(-3*val**2 - sin(val))
120
+ val = val + delta
121
+ print *, val
122
+ end do
123
+
124
+ There is a function constructing a loop (or a complete function) like this in
125
+ :mod:`sympy.codegen.algorithms`.
126
+
127
+ """
128
+
129
+ from __future__ import annotations
130
+ from typing import Any
131
+
132
+ from collections import defaultdict
133
+
134
+ from sympy.core.relational import (Ge, Gt, Le, Lt)
135
+ from sympy.core import Symbol, Tuple, Dummy
136
+ from sympy.core.basic import Basic
137
+ from sympy.core.expr import Expr, Atom
138
+ from sympy.core.numbers import Float, Integer, oo
139
+ from sympy.core.sympify import _sympify, sympify, SympifyError
140
+ from sympy.utilities.iterables import (iterable, topological_sort,
141
+ numbered_symbols, filter_symbols)
142
+
143
+
144
+ def _mk_Tuple(args):
145
+ """
146
+ Create a SymPy Tuple object from an iterable, converting Python strings to
147
+ AST strings.
148
+
149
+ Parameters
150
+ ==========
151
+
152
+ args: iterable
153
+ Arguments to :class:`sympy.Tuple`.
154
+
155
+ Returns
156
+ =======
157
+
158
+ sympy.Tuple
159
+ """
160
+ args = [String(arg) if isinstance(arg, str) else arg for arg in args]
161
+ return Tuple(*args)
162
+
163
+
164
+ class CodegenAST(Basic):
165
+ __slots__ = ()
166
+
167
+
168
+ class Token(CodegenAST):
169
+ """ Base class for the AST types.
170
+
171
+ Explanation
172
+ ===========
173
+
174
+ Defining fields are set in ``_fields``. Attributes (defined in _fields)
175
+ are only allowed to contain instances of Basic (unless atomic, see
176
+ ``String``). The arguments to ``__new__()`` correspond to the attributes in
177
+ the order defined in ``_fields`. The ``defaults`` class attribute is a
178
+ dictionary mapping attribute names to their default values.
179
+
180
+ Subclasses should not need to override the ``__new__()`` method. They may
181
+ define a class or static method named ``_construct_<attr>`` for each
182
+ attribute to process the value passed to ``__new__()``. Attributes listed
183
+ in the class attribute ``not_in_args`` are not passed to :class:`~.Basic`.
184
+ """
185
+
186
+ __slots__: tuple[str, ...] = ()
187
+ _fields = __slots__
188
+ defaults: dict[str, Any] = {}
189
+ not_in_args: list[str] = []
190
+ indented_args = ['body']
191
+
192
+ @property
193
+ def is_Atom(self):
194
+ return len(self._fields) == 0
195
+
196
+ @classmethod
197
+ def _get_constructor(cls, attr):
198
+ """ Get the constructor function for an attribute by name. """
199
+ return getattr(cls, '_construct_%s' % attr, lambda x: x)
200
+
201
+ @classmethod
202
+ def _construct(cls, attr, arg):
203
+ """ Construct an attribute value from argument passed to ``__new__()``. """
204
+ # arg may be ``NoneToken()``, so comparison is done using == instead of ``is`` operator
205
+ if arg == None:
206
+ return cls.defaults.get(attr, none)
207
+ else:
208
+ if isinstance(arg, Dummy): # SymPy's replace uses Dummy instances
209
+ return arg
210
+ else:
211
+ return cls._get_constructor(attr)(arg)
212
+
213
+ def __new__(cls, *args, **kwargs):
214
+ # Pass through existing instances when given as sole argument
215
+ if len(args) == 1 and not kwargs and isinstance(args[0], cls):
216
+ return args[0]
217
+
218
+ if len(args) > len(cls._fields):
219
+ raise ValueError("Too many arguments (%d), expected at most %d" % (len(args), len(cls._fields)))
220
+
221
+ attrvals = []
222
+
223
+ # Process positional arguments
224
+ for attrname, argval in zip(cls._fields, args):
225
+ if attrname in kwargs:
226
+ raise TypeError('Got multiple values for attribute %r' % attrname)
227
+
228
+ attrvals.append(cls._construct(attrname, argval))
229
+
230
+ # Process keyword arguments
231
+ for attrname in cls._fields[len(args):]:
232
+ if attrname in kwargs:
233
+ argval = kwargs.pop(attrname)
234
+
235
+ elif attrname in cls.defaults:
236
+ argval = cls.defaults[attrname]
237
+
238
+ else:
239
+ raise TypeError('No value for %r given and attribute has no default' % attrname)
240
+
241
+ attrvals.append(cls._construct(attrname, argval))
242
+
243
+ if kwargs:
244
+ raise ValueError("Unknown keyword arguments: %s" % ' '.join(kwargs))
245
+
246
+ # Parent constructor
247
+ basic_args = [
248
+ val for attr, val in zip(cls._fields, attrvals)
249
+ if attr not in cls.not_in_args
250
+ ]
251
+ obj = CodegenAST.__new__(cls, *basic_args)
252
+
253
+ # Set attributes
254
+ for attr, arg in zip(cls._fields, attrvals):
255
+ setattr(obj, attr, arg)
256
+
257
+ return obj
258
+
259
+ def __eq__(self, other):
260
+ if not isinstance(other, self.__class__):
261
+ return False
262
+ for attr in self._fields:
263
+ if getattr(self, attr) != getattr(other, attr):
264
+ return False
265
+ return True
266
+
267
+ def _hashable_content(self):
268
+ return tuple([getattr(self, attr) for attr in self._fields])
269
+
270
+ def __hash__(self):
271
+ return super().__hash__()
272
+
273
+ def _joiner(self, k, indent_level):
274
+ return (',\n' + ' '*indent_level) if k in self.indented_args else ', '
275
+
276
+ def _indented(self, printer, k, v, *args, **kwargs):
277
+ il = printer._context['indent_level']
278
+ def _print(arg):
279
+ if isinstance(arg, Token):
280
+ return printer._print(arg, *args, joiner=self._joiner(k, il), **kwargs)
281
+ else:
282
+ return printer._print(arg, *args, **kwargs)
283
+
284
+ if isinstance(v, Tuple):
285
+ joined = self._joiner(k, il).join([_print(arg) for arg in v.args])
286
+ if k in self.indented_args:
287
+ return '(\n' + ' '*il + joined + ',\n' + ' '*(il - 4) + ')'
288
+ else:
289
+ return ('({0},)' if len(v.args) == 1 else '({0})').format(joined)
290
+ else:
291
+ return _print(v)
292
+
293
+ def _sympyrepr(self, printer, *args, joiner=', ', **kwargs):
294
+ from sympy.printing.printer import printer_context
295
+ exclude = kwargs.get('exclude', ())
296
+ values = [getattr(self, k) for k in self._fields]
297
+ indent_level = printer._context.get('indent_level', 0)
298
+
299
+ arg_reprs = []
300
+
301
+ for i, (attr, value) in enumerate(zip(self._fields, values)):
302
+ if attr in exclude:
303
+ continue
304
+
305
+ # Skip attributes which have the default value
306
+ if attr in self.defaults and value == self.defaults[attr]:
307
+ continue
308
+
309
+ ilvl = indent_level + 4 if attr in self.indented_args else 0
310
+ with printer_context(printer, indent_level=ilvl):
311
+ indented = self._indented(printer, attr, value, *args, **kwargs)
312
+ arg_reprs.append(('{1}' if i == 0 else '{0}={1}').format(attr, indented.lstrip()))
313
+
314
+ return "{}({})".format(self.__class__.__name__, joiner.join(arg_reprs))
315
+
316
+ _sympystr = _sympyrepr
317
+
318
+ def __repr__(self): # sympy.core.Basic.__repr__ uses sstr
319
+ from sympy.printing import srepr
320
+ return srepr(self)
321
+
322
+ def kwargs(self, exclude=(), apply=None):
323
+ """ Get instance's attributes as dict of keyword arguments.
324
+
325
+ Parameters
326
+ ==========
327
+
328
+ exclude : collection of str
329
+ Collection of keywords to exclude.
330
+
331
+ apply : callable, optional
332
+ Function to apply to all values.
333
+ """
334
+ kwargs = {k: getattr(self, k) for k in self._fields if k not in exclude}
335
+ if apply is not None:
336
+ return {k: apply(v) for k, v in kwargs.items()}
337
+ else:
338
+ return kwargs
339
+
340
+ class BreakToken(Token):
341
+ """ Represents 'break' in C/Python ('exit' in Fortran).
342
+
343
+ Use the premade instance ``break_`` or instantiate manually.
344
+
345
+ Examples
346
+ ========
347
+
348
+ >>> from sympy import ccode, fcode
349
+ >>> from sympy.codegen.ast import break_
350
+ >>> ccode(break_)
351
+ 'break'
352
+ >>> fcode(break_, source_format='free')
353
+ 'exit'
354
+ """
355
+
356
+ break_ = BreakToken()
357
+
358
+
359
+ class ContinueToken(Token):
360
+ """ Represents 'continue' in C/Python ('cycle' in Fortran)
361
+
362
+ Use the premade instance ``continue_`` or instantiate manually.
363
+
364
+ Examples
365
+ ========
366
+
367
+ >>> from sympy import ccode, fcode
368
+ >>> from sympy.codegen.ast import continue_
369
+ >>> ccode(continue_)
370
+ 'continue'
371
+ >>> fcode(continue_, source_format='free')
372
+ 'cycle'
373
+ """
374
+
375
+ continue_ = ContinueToken()
376
+
377
+ class NoneToken(Token):
378
+ """ The AST equivalence of Python's NoneType
379
+
380
+ The corresponding instance of Python's ``None`` is ``none``.
381
+
382
+ Examples
383
+ ========
384
+
385
+ >>> from sympy.codegen.ast import none, Variable
386
+ >>> from sympy import pycode
387
+ >>> print(pycode(Variable('x').as_Declaration(value=none)))
388
+ x = None
389
+
390
+ """
391
+ def __eq__(self, other):
392
+ return other is None or isinstance(other, NoneToken)
393
+
394
+ def _hashable_content(self):
395
+ return ()
396
+
397
+ def __hash__(self):
398
+ return super().__hash__()
399
+
400
+
401
+ none = NoneToken()
402
+
403
+
404
+ class AssignmentBase(CodegenAST):
405
+ """ Abstract base class for Assignment and AugmentedAssignment.
406
+
407
+ Attributes:
408
+ ===========
409
+
410
+ op : str
411
+ Symbol for assignment operator, e.g. "=", "+=", etc.
412
+ """
413
+
414
+ def __new__(cls, lhs, rhs):
415
+ lhs = _sympify(lhs)
416
+ rhs = _sympify(rhs)
417
+
418
+ cls._check_args(lhs, rhs)
419
+
420
+ return super().__new__(cls, lhs, rhs)
421
+
422
+ @property
423
+ def lhs(self):
424
+ return self.args[0]
425
+
426
+ @property
427
+ def rhs(self):
428
+ return self.args[1]
429
+
430
+ @classmethod
431
+ def _check_args(cls, lhs, rhs):
432
+ """ Check arguments to __new__ and raise exception if any problems found.
433
+
434
+ Derived classes may wish to override this.
435
+ """
436
+ from sympy.matrices.expressions.matexpr import (
437
+ MatrixElement, MatrixSymbol)
438
+ from sympy.tensor.indexed import Indexed
439
+ from sympy.tensor.array.expressions import ArrayElement
440
+
441
+ # Tuple of things that can be on the lhs of an assignment
442
+ assignable = (Symbol, MatrixSymbol, MatrixElement, Indexed, Element, Variable,
443
+ ArrayElement)
444
+ if not isinstance(lhs, assignable):
445
+ raise TypeError("Cannot assign to lhs of type %s." % type(lhs))
446
+
447
+ # Indexed types implement shape, but don't define it until later. This
448
+ # causes issues in assignment validation. For now, matrices are defined
449
+ # as anything with a shape that is not an Indexed
450
+ lhs_is_mat = hasattr(lhs, 'shape') and not isinstance(lhs, Indexed)
451
+ rhs_is_mat = hasattr(rhs, 'shape') and not isinstance(rhs, Indexed)
452
+
453
+ # If lhs and rhs have same structure, then this assignment is ok
454
+ if lhs_is_mat:
455
+ if not rhs_is_mat:
456
+ raise ValueError("Cannot assign a scalar to a matrix.")
457
+ elif lhs.shape != rhs.shape:
458
+ raise ValueError("Dimensions of lhs and rhs do not align.")
459
+ elif rhs_is_mat and not lhs_is_mat:
460
+ raise ValueError("Cannot assign a matrix to a scalar.")
461
+
462
+
463
+ class Assignment(AssignmentBase):
464
+ """
465
+ Represents variable assignment for code generation.
466
+
467
+ Parameters
468
+ ==========
469
+
470
+ lhs : Expr
471
+ SymPy object representing the lhs of the expression. These should be
472
+ singular objects, such as one would use in writing code. Notable types
473
+ include Symbol, MatrixSymbol, MatrixElement, and Indexed. Types that
474
+ subclass these types are also supported.
475
+
476
+ rhs : Expr
477
+ SymPy object representing the rhs of the expression. This can be any
478
+ type, provided its shape corresponds to that of the lhs. For example,
479
+ a Matrix type can be assigned to MatrixSymbol, but not to Symbol, as
480
+ the dimensions will not align.
481
+
482
+ Examples
483
+ ========
484
+
485
+ >>> from sympy import symbols, MatrixSymbol, Matrix
486
+ >>> from sympy.codegen.ast import Assignment
487
+ >>> x, y, z = symbols('x, y, z')
488
+ >>> Assignment(x, y)
489
+ Assignment(x, y)
490
+ >>> Assignment(x, 0)
491
+ Assignment(x, 0)
492
+ >>> A = MatrixSymbol('A', 1, 3)
493
+ >>> mat = Matrix([x, y, z]).T
494
+ >>> Assignment(A, mat)
495
+ Assignment(A, Matrix([[x, y, z]]))
496
+ >>> Assignment(A[0, 1], x)
497
+ Assignment(A[0, 1], x)
498
+ """
499
+
500
+ op = ':='
501
+
502
+
503
+ class AugmentedAssignment(AssignmentBase):
504
+ """
505
+ Base class for augmented assignments.
506
+
507
+ Attributes:
508
+ ===========
509
+
510
+ binop : str
511
+ Symbol for binary operation being applied in the assignment, such as "+",
512
+ "*", etc.
513
+ """
514
+ binop = None # type: str
515
+
516
+ @property
517
+ def op(self):
518
+ return self.binop + '='
519
+
520
+
521
+ class AddAugmentedAssignment(AugmentedAssignment):
522
+ binop = '+'
523
+
524
+
525
+ class SubAugmentedAssignment(AugmentedAssignment):
526
+ binop = '-'
527
+
528
+
529
+ class MulAugmentedAssignment(AugmentedAssignment):
530
+ binop = '*'
531
+
532
+
533
+ class DivAugmentedAssignment(AugmentedAssignment):
534
+ binop = '/'
535
+
536
+
537
+ class ModAugmentedAssignment(AugmentedAssignment):
538
+ binop = '%'
539
+
540
+
541
+ # Mapping from binary op strings to AugmentedAssignment subclasses
542
+ augassign_classes = {
543
+ cls.binop: cls for cls in [
544
+ AddAugmentedAssignment, SubAugmentedAssignment, MulAugmentedAssignment,
545
+ DivAugmentedAssignment, ModAugmentedAssignment
546
+ ]
547
+ }
548
+
549
+
550
+ def aug_assign(lhs, op, rhs):
551
+ """
552
+ Create 'lhs op= rhs'.
553
+
554
+ Explanation
555
+ ===========
556
+
557
+ Represents augmented variable assignment for code generation. This is a
558
+ convenience function. You can also use the AugmentedAssignment classes
559
+ directly, like AddAugmentedAssignment(x, y).
560
+
561
+ Parameters
562
+ ==========
563
+
564
+ lhs : Expr
565
+ SymPy object representing the lhs of the expression. These should be
566
+ singular objects, such as one would use in writing code. Notable types
567
+ include Symbol, MatrixSymbol, MatrixElement, and Indexed. Types that
568
+ subclass these types are also supported.
569
+
570
+ op : str
571
+ Operator (+, -, /, \\*, %).
572
+
573
+ rhs : Expr
574
+ SymPy object representing the rhs of the expression. This can be any
575
+ type, provided its shape corresponds to that of the lhs. For example,
576
+ a Matrix type can be assigned to MatrixSymbol, but not to Symbol, as
577
+ the dimensions will not align.
578
+
579
+ Examples
580
+ ========
581
+
582
+ >>> from sympy import symbols
583
+ >>> from sympy.codegen.ast import aug_assign
584
+ >>> x, y = symbols('x, y')
585
+ >>> aug_assign(x, '+', y)
586
+ AddAugmentedAssignment(x, y)
587
+ """
588
+ if op not in augassign_classes:
589
+ raise ValueError("Unrecognized operator %s" % op)
590
+ return augassign_classes[op](lhs, rhs)
591
+
592
+
593
+ class CodeBlock(CodegenAST):
594
+ """
595
+ Represents a block of code.
596
+
597
+ Explanation
598
+ ===========
599
+
600
+ For now only assignments are supported. This restriction will be lifted in
601
+ the future.
602
+
603
+ Useful attributes on this object are:
604
+
605
+ ``left_hand_sides``:
606
+ Tuple of left-hand sides of assignments, in order.
607
+ ``left_hand_sides``:
608
+ Tuple of right-hand sides of assignments, in order.
609
+ ``free_symbols``: Free symbols of the expressions in the right-hand sides
610
+ which do not appear in the left-hand side of an assignment.
611
+
612
+ Useful methods on this object are:
613
+
614
+ ``topological_sort``:
615
+ Class method. Return a CodeBlock with assignments
616
+ sorted so that variables are assigned before they
617
+ are used.
618
+ ``cse``:
619
+ Return a new CodeBlock with common subexpressions eliminated and
620
+ pulled out as assignments.
621
+
622
+ Examples
623
+ ========
624
+
625
+ >>> from sympy import symbols, ccode
626
+ >>> from sympy.codegen.ast import CodeBlock, Assignment
627
+ >>> x, y = symbols('x y')
628
+ >>> c = CodeBlock(Assignment(x, 1), Assignment(y, x + 1))
629
+ >>> print(ccode(c))
630
+ x = 1;
631
+ y = x + 1;
632
+
633
+ """
634
+ def __new__(cls, *args):
635
+ left_hand_sides = []
636
+ right_hand_sides = []
637
+ for i in args:
638
+ if isinstance(i, Assignment):
639
+ lhs, rhs = i.args
640
+ left_hand_sides.append(lhs)
641
+ right_hand_sides.append(rhs)
642
+
643
+ obj = CodegenAST.__new__(cls, *args)
644
+
645
+ obj.left_hand_sides = Tuple(*left_hand_sides)
646
+ obj.right_hand_sides = Tuple(*right_hand_sides)
647
+ return obj
648
+
649
+ def __iter__(self):
650
+ return iter(self.args)
651
+
652
+ def _sympyrepr(self, printer, *args, **kwargs):
653
+ il = printer._context.get('indent_level', 0)
654
+ joiner = ',\n' + ' '*il
655
+ joined = joiner.join(map(printer._print, self.args))
656
+ return ('{}(\n'.format(' '*(il-4) + self.__class__.__name__,) +
657
+ ' '*il + joined + '\n' + ' '*(il - 4) + ')')
658
+
659
+ _sympystr = _sympyrepr
660
+
661
+ @property
662
+ def free_symbols(self):
663
+ return super().free_symbols - set(self.left_hand_sides)
664
+
665
+ @classmethod
666
+ def topological_sort(cls, assignments):
667
+ """
668
+ Return a CodeBlock with topologically sorted assignments so that
669
+ variables are assigned before they are used.
670
+
671
+ Examples
672
+ ========
673
+
674
+ The existing order of assignments is preserved as much as possible.
675
+
676
+ This function assumes that variables are assigned to only once.
677
+
678
+ This is a class constructor so that the default constructor for
679
+ CodeBlock can error when variables are used before they are assigned.
680
+
681
+ >>> from sympy import symbols
682
+ >>> from sympy.codegen.ast import CodeBlock, Assignment
683
+ >>> x, y, z = symbols('x y z')
684
+
685
+ >>> assignments = [
686
+ ... Assignment(x, y + z),
687
+ ... Assignment(y, z + 1),
688
+ ... Assignment(z, 2),
689
+ ... ]
690
+ >>> CodeBlock.topological_sort(assignments)
691
+ CodeBlock(
692
+ Assignment(z, 2),
693
+ Assignment(y, z + 1),
694
+ Assignment(x, y + z)
695
+ )
696
+
697
+ """
698
+
699
+ if not all(isinstance(i, Assignment) for i in assignments):
700
+ # Will support more things later
701
+ raise NotImplementedError("CodeBlock.topological_sort only supports Assignments")
702
+
703
+ if any(isinstance(i, AugmentedAssignment) for i in assignments):
704
+ raise NotImplementedError("CodeBlock.topological_sort does not yet work with AugmentedAssignments")
705
+
706
+ # Create a graph where the nodes are assignments and there is a directed edge
707
+ # between nodes that use a variable and nodes that assign that
708
+ # variable, like
709
+
710
+ # [(x := 1, y := x + 1), (x := 1, z := y + z), (y := x + 1, z := y + z)]
711
+
712
+ # If we then topologically sort these nodes, they will be in
713
+ # assignment order, like
714
+
715
+ # x := 1
716
+ # y := x + 1
717
+ # z := y + z
718
+
719
+ # A = The nodes
720
+ #
721
+ # enumerate keeps nodes in the same order they are already in if
722
+ # possible. It will also allow us to handle duplicate assignments to
723
+ # the same variable when those are implemented.
724
+ A = list(enumerate(assignments))
725
+
726
+ # var_map = {variable: [nodes for which this variable is assigned to]}
727
+ # like {x: [(1, x := y + z), (4, x := 2 * w)], ...}
728
+ var_map = defaultdict(list)
729
+ for node in A:
730
+ i, a = node
731
+ var_map[a.lhs].append(node)
732
+
733
+ # E = Edges in the graph
734
+ E = []
735
+ for dst_node in A:
736
+ i, a = dst_node
737
+ for s in a.rhs.free_symbols:
738
+ for src_node in var_map[s]:
739
+ E.append((src_node, dst_node))
740
+
741
+ ordered_assignments = topological_sort([A, E])
742
+
743
+ # De-enumerate the result
744
+ return cls(*[a for i, a in ordered_assignments])
745
+
746
+ def cse(self, symbols=None, optimizations=None, postprocess=None,
747
+ order='canonical'):
748
+ """
749
+ Return a new code block with common subexpressions eliminated.
750
+
751
+ Explanation
752
+ ===========
753
+
754
+ See the docstring of :func:`sympy.simplify.cse_main.cse` for more
755
+ information.
756
+
757
+ Examples
758
+ ========
759
+
760
+ >>> from sympy import symbols, sin
761
+ >>> from sympy.codegen.ast import CodeBlock, Assignment
762
+ >>> x, y, z = symbols('x y z')
763
+
764
+ >>> c = CodeBlock(
765
+ ... Assignment(x, 1),
766
+ ... Assignment(y, sin(x) + 1),
767
+ ... Assignment(z, sin(x) - 1),
768
+ ... )
769
+ ...
770
+ >>> c.cse()
771
+ CodeBlock(
772
+ Assignment(x, 1),
773
+ Assignment(x0, sin(x)),
774
+ Assignment(y, x0 + 1),
775
+ Assignment(z, x0 - 1)
776
+ )
777
+
778
+ """
779
+ from sympy.simplify.cse_main import cse
780
+
781
+ # Check that the CodeBlock only contains assignments to unique variables
782
+ if not all(isinstance(i, Assignment) for i in self.args):
783
+ # Will support more things later
784
+ raise NotImplementedError("CodeBlock.cse only supports Assignments")
785
+
786
+ if any(isinstance(i, AugmentedAssignment) for i in self.args):
787
+ raise NotImplementedError("CodeBlock.cse does not yet work with AugmentedAssignments")
788
+
789
+ for i, lhs in enumerate(self.left_hand_sides):
790
+ if lhs in self.left_hand_sides[:i]:
791
+ raise NotImplementedError("Duplicate assignments to the same "
792
+ "variable are not yet supported (%s)" % lhs)
793
+
794
+ # Ensure new symbols for subexpressions do not conflict with existing
795
+ existing_symbols = self.atoms(Symbol)
796
+ if symbols is None:
797
+ symbols = numbered_symbols()
798
+ symbols = filter_symbols(symbols, existing_symbols)
799
+
800
+ replacements, reduced_exprs = cse(list(self.right_hand_sides),
801
+ symbols=symbols, optimizations=optimizations, postprocess=postprocess,
802
+ order=order)
803
+
804
+ new_block = [Assignment(var, expr) for var, expr in
805
+ zip(self.left_hand_sides, reduced_exprs)]
806
+ new_assignments = [Assignment(var, expr) for var, expr in replacements]
807
+ return self.topological_sort(new_assignments + new_block)
808
+
809
+
810
+ class For(Token):
811
+ """Represents a 'for-loop' in the code.
812
+
813
+ Expressions are of the form:
814
+ "for target in iter:
815
+ body..."
816
+
817
+ Parameters
818
+ ==========
819
+
820
+ target : symbol
821
+ iter : iterable
822
+ body : CodeBlock or iterable
823
+ ! When passed an iterable it is used to instantiate a CodeBlock.
824
+
825
+ Examples
826
+ ========
827
+
828
+ >>> from sympy import symbols, Range
829
+ >>> from sympy.codegen.ast import aug_assign, For
830
+ >>> x, i, j, k = symbols('x i j k')
831
+ >>> for_i = For(i, Range(10), [aug_assign(x, '+', i*j*k)])
832
+ >>> for_i # doctest: -NORMALIZE_WHITESPACE
833
+ For(i, iterable=Range(0, 10, 1), body=CodeBlock(
834
+ AddAugmentedAssignment(x, i*j*k)
835
+ ))
836
+ >>> for_ji = For(j, Range(7), [for_i])
837
+ >>> for_ji # doctest: -NORMALIZE_WHITESPACE
838
+ For(j, iterable=Range(0, 7, 1), body=CodeBlock(
839
+ For(i, iterable=Range(0, 10, 1), body=CodeBlock(
840
+ AddAugmentedAssignment(x, i*j*k)
841
+ ))
842
+ ))
843
+ >>> for_kji =For(k, Range(5), [for_ji])
844
+ >>> for_kji # doctest: -NORMALIZE_WHITESPACE
845
+ For(k, iterable=Range(0, 5, 1), body=CodeBlock(
846
+ For(j, iterable=Range(0, 7, 1), body=CodeBlock(
847
+ For(i, iterable=Range(0, 10, 1), body=CodeBlock(
848
+ AddAugmentedAssignment(x, i*j*k)
849
+ ))
850
+ ))
851
+ ))
852
+ """
853
+ __slots__ = _fields = ('target', 'iterable', 'body')
854
+ _construct_target = staticmethod(_sympify)
855
+
856
+ @classmethod
857
+ def _construct_body(cls, itr):
858
+ if isinstance(itr, CodeBlock):
859
+ return itr
860
+ else:
861
+ return CodeBlock(*itr)
862
+
863
+ @classmethod
864
+ def _construct_iterable(cls, itr):
865
+ if not iterable(itr):
866
+ raise TypeError("iterable must be an iterable")
867
+ if isinstance(itr, list): # _sympify errors on lists because they are mutable
868
+ itr = tuple(itr)
869
+ return _sympify(itr)
870
+
871
+
872
+ class String(Atom, Token):
873
+ """ SymPy object representing a string.
874
+
875
+ Atomic object which is not an expression (as opposed to Symbol).
876
+
877
+ Parameters
878
+ ==========
879
+
880
+ text : str
881
+
882
+ Examples
883
+ ========
884
+
885
+ >>> from sympy.codegen.ast import String
886
+ >>> f = String('foo')
887
+ >>> f
888
+ foo
889
+ >>> str(f)
890
+ 'foo'
891
+ >>> f.text
892
+ 'foo'
893
+ >>> print(repr(f))
894
+ String('foo')
895
+
896
+ """
897
+ __slots__ = _fields = ('text',)
898
+ not_in_args = ['text']
899
+ is_Atom = True
900
+
901
+ @classmethod
902
+ def _construct_text(cls, text):
903
+ if not isinstance(text, str):
904
+ raise TypeError("Argument text is not a string type.")
905
+ return text
906
+
907
+ def _sympystr(self, printer, *args, **kwargs):
908
+ return self.text
909
+
910
+ def kwargs(self, exclude = (), apply = None):
911
+ return {}
912
+
913
+ #to be removed when Atom is given a suitable func
914
+ @property
915
+ def func(self):
916
+ return lambda: self
917
+
918
+ def _latex(self, printer):
919
+ from sympy.printing.latex import latex_escape
920
+ return r'\texttt{{"{}"}}'.format(latex_escape(self.text))
921
+
922
+ class QuotedString(String):
923
+ """ Represents a string which should be printed with quotes. """
924
+
925
+ class Comment(String):
926
+ """ Represents a comment. """
927
+
928
+ class Node(Token):
929
+ """ Subclass of Token, carrying the attribute 'attrs' (Tuple)
930
+
931
+ Examples
932
+ ========
933
+
934
+ >>> from sympy.codegen.ast import Node, value_const, pointer_const
935
+ >>> n1 = Node([value_const])
936
+ >>> n1.attr_params('value_const') # get the parameters of attribute (by name)
937
+ ()
938
+ >>> from sympy.codegen.fnodes import dimension
939
+ >>> n2 = Node([value_const, dimension(5, 3)])
940
+ >>> n2.attr_params(value_const) # get the parameters of attribute (by Attribute instance)
941
+ ()
942
+ >>> n2.attr_params('dimension') # get the parameters of attribute (by name)
943
+ (5, 3)
944
+ >>> n2.attr_params(pointer_const) is None
945
+ True
946
+
947
+ """
948
+
949
+ __slots__: tuple[str, ...] = ('attrs',)
950
+ _fields = __slots__
951
+
952
+ defaults: dict[str, Any] = {'attrs': Tuple()}
953
+
954
+ _construct_attrs = staticmethod(_mk_Tuple)
955
+
956
+ def attr_params(self, looking_for):
957
+ """ Returns the parameters of the Attribute with name ``looking_for`` in self.attrs """
958
+ for attr in self.attrs:
959
+ if str(attr.name) == str(looking_for):
960
+ return attr.parameters
961
+
962
+
963
+ class Type(Token):
964
+ """ Represents a type.
965
+
966
+ Explanation
967
+ ===========
968
+
969
+ The naming is a super-set of NumPy naming. Type has a classmethod
970
+ ``from_expr`` which offer type deduction. It also has a method
971
+ ``cast_check`` which casts the argument to its type, possibly raising an
972
+ exception if rounding error is not within tolerances, or if the value is not
973
+ representable by the underlying data type (e.g. unsigned integers).
974
+
975
+ Parameters
976
+ ==========
977
+
978
+ name : str
979
+ Name of the type, e.g. ``object``, ``int16``, ``float16`` (where the latter two
980
+ would use the ``Type`` sub-classes ``IntType`` and ``FloatType`` respectively).
981
+ If a ``Type`` instance is given, the said instance is returned.
982
+
983
+ Examples
984
+ ========
985
+
986
+ >>> from sympy.codegen.ast import Type
987
+ >>> t = Type.from_expr(42)
988
+ >>> t
989
+ integer
990
+ >>> print(repr(t))
991
+ IntBaseType(String('integer'))
992
+ >>> from sympy.codegen.ast import uint8
993
+ >>> uint8.cast_check(-1) # doctest: +ELLIPSIS
994
+ Traceback (most recent call last):
995
+ ...
996
+ ValueError: Minimum value for data type bigger than new value.
997
+ >>> from sympy.codegen.ast import float32
998
+ >>> v6 = 0.123456
999
+ >>> float32.cast_check(v6)
1000
+ 0.123456
1001
+ >>> v10 = 12345.67894
1002
+ >>> float32.cast_check(v10) # doctest: +ELLIPSIS
1003
+ Traceback (most recent call last):
1004
+ ...
1005
+ ValueError: Casting gives a significantly different value.
1006
+ >>> boost_mp50 = Type('boost::multiprecision::cpp_dec_float_50')
1007
+ >>> from sympy import cxxcode
1008
+ >>> from sympy.codegen.ast import Declaration, Variable
1009
+ >>> cxxcode(Declaration(Variable('x', type=boost_mp50)))
1010
+ 'boost::multiprecision::cpp_dec_float_50 x'
1011
+
1012
+ References
1013
+ ==========
1014
+
1015
+ .. [1] https://numpy.org/doc/stable/user/basics.types.html
1016
+
1017
+ """
1018
+ __slots__: tuple[str, ...] = ('name',)
1019
+ _fields = __slots__
1020
+
1021
+ _construct_name = String
1022
+
1023
+ def _sympystr(self, printer, *args, **kwargs):
1024
+ return str(self.name)
1025
+
1026
+ @classmethod
1027
+ def from_expr(cls, expr):
1028
+ """ Deduces type from an expression or a ``Symbol``.
1029
+
1030
+ Parameters
1031
+ ==========
1032
+
1033
+ expr : number or SymPy object
1034
+ The type will be deduced from type or properties.
1035
+
1036
+ Examples
1037
+ ========
1038
+
1039
+ >>> from sympy.codegen.ast import Type, integer, complex_
1040
+ >>> Type.from_expr(2) == integer
1041
+ True
1042
+ >>> from sympy import Symbol
1043
+ >>> Type.from_expr(Symbol('z', complex=True)) == complex_
1044
+ True
1045
+ >>> Type.from_expr(sum) # doctest: +ELLIPSIS
1046
+ Traceback (most recent call last):
1047
+ ...
1048
+ ValueError: Could not deduce type from expr.
1049
+
1050
+ Raises
1051
+ ======
1052
+
1053
+ ValueError when type deduction fails.
1054
+
1055
+ """
1056
+ if isinstance(expr, (float, Float)):
1057
+ return real
1058
+ if isinstance(expr, (int, Integer)) or getattr(expr, 'is_integer', False):
1059
+ return integer
1060
+ if getattr(expr, 'is_real', False):
1061
+ return real
1062
+ if isinstance(expr, complex) or getattr(expr, 'is_complex', False):
1063
+ return complex_
1064
+ if isinstance(expr, bool) or getattr(expr, 'is_Relational', False):
1065
+ return bool_
1066
+ else:
1067
+ raise ValueError("Could not deduce type from expr.")
1068
+
1069
+ def _check(self, value):
1070
+ pass
1071
+
1072
+ def cast_check(self, value, rtol=None, atol=0, precision_targets=None):
1073
+ """ Casts a value to the data type of the instance.
1074
+
1075
+ Parameters
1076
+ ==========
1077
+
1078
+ value : number
1079
+ rtol : floating point number
1080
+ Relative tolerance. (will be deduced if not given).
1081
+ atol : floating point number
1082
+ Absolute tolerance (in addition to ``rtol``).
1083
+ type_aliases : dict
1084
+ Maps substitutions for Type, e.g. {integer: int64, real: float32}
1085
+
1086
+ Examples
1087
+ ========
1088
+
1089
+ >>> from sympy.codegen.ast import integer, float32, int8
1090
+ >>> integer.cast_check(3.0) == 3
1091
+ True
1092
+ >>> float32.cast_check(1e-40) # doctest: +ELLIPSIS
1093
+ Traceback (most recent call last):
1094
+ ...
1095
+ ValueError: Minimum value for data type bigger than new value.
1096
+ >>> int8.cast_check(256) # doctest: +ELLIPSIS
1097
+ Traceback (most recent call last):
1098
+ ...
1099
+ ValueError: Maximum value for data type smaller than new value.
1100
+ >>> v10 = 12345.67894
1101
+ >>> float32.cast_check(v10) # doctest: +ELLIPSIS
1102
+ Traceback (most recent call last):
1103
+ ...
1104
+ ValueError: Casting gives a significantly different value.
1105
+ >>> from sympy.codegen.ast import float64
1106
+ >>> float64.cast_check(v10)
1107
+ 12345.67894
1108
+ >>> from sympy import Float
1109
+ >>> v18 = Float('0.123456789012345646')
1110
+ >>> float64.cast_check(v18)
1111
+ Traceback (most recent call last):
1112
+ ...
1113
+ ValueError: Casting gives a significantly different value.
1114
+ >>> from sympy.codegen.ast import float80
1115
+ >>> float80.cast_check(v18)
1116
+ 0.123456789012345649
1117
+
1118
+ """
1119
+ val = sympify(value)
1120
+
1121
+ ten = Integer(10)
1122
+ exp10 = getattr(self, 'decimal_dig', None)
1123
+
1124
+ if rtol is None:
1125
+ rtol = 1e-15 if exp10 is None else 2.0*ten**(-exp10)
1126
+
1127
+ def tol(num):
1128
+ return atol + rtol*abs(num)
1129
+
1130
+ new_val = self.cast_nocheck(value)
1131
+ self._check(new_val)
1132
+
1133
+ delta = new_val - val
1134
+ if abs(delta) > tol(val): # rounding, e.g. int(3.5) != 3.5
1135
+ raise ValueError("Casting gives a significantly different value.")
1136
+
1137
+ return new_val
1138
+
1139
+ def _latex(self, printer):
1140
+ from sympy.printing.latex import latex_escape
1141
+ type_name = latex_escape(self.__class__.__name__)
1142
+ name = latex_escape(self.name.text)
1143
+ return r"\text{{{}}}\left(\texttt{{{}}}\right)".format(type_name, name)
1144
+
1145
+
1146
+ class IntBaseType(Type):
1147
+ """ Integer base type, contains no size information. """
1148
+ __slots__ = ()
1149
+ cast_nocheck = lambda self, i: Integer(int(i))
1150
+
1151
+
1152
+ class _SizedIntType(IntBaseType):
1153
+ __slots__ = ('nbits',)
1154
+ _fields = Type._fields + __slots__
1155
+
1156
+ _construct_nbits = Integer
1157
+
1158
+ def _check(self, value):
1159
+ if value < self.min:
1160
+ raise ValueError("Value is too small: %d < %d" % (value, self.min))
1161
+ if value > self.max:
1162
+ raise ValueError("Value is too big: %d > %d" % (value, self.max))
1163
+
1164
+
1165
+ class SignedIntType(_SizedIntType):
1166
+ """ Represents a signed integer type. """
1167
+ __slots__ = ()
1168
+ @property
1169
+ def min(self):
1170
+ return -2**(self.nbits-1)
1171
+
1172
+ @property
1173
+ def max(self):
1174
+ return 2**(self.nbits-1) - 1
1175
+
1176
+
1177
+ class UnsignedIntType(_SizedIntType):
1178
+ """ Represents an unsigned integer type. """
1179
+ __slots__ = ()
1180
+ @property
1181
+ def min(self):
1182
+ return 0
1183
+
1184
+ @property
1185
+ def max(self):
1186
+ return 2**self.nbits - 1
1187
+
1188
+ two = Integer(2)
1189
+
1190
+ class FloatBaseType(Type):
1191
+ """ Represents a floating point number type. """
1192
+ __slots__ = ()
1193
+ cast_nocheck = Float
1194
+
1195
+ class FloatType(FloatBaseType):
1196
+ """ Represents a floating point type with fixed bit width.
1197
+
1198
+ Base 2 & one sign bit is assumed.
1199
+
1200
+ Parameters
1201
+ ==========
1202
+
1203
+ name : str
1204
+ Name of the type.
1205
+ nbits : integer
1206
+ Number of bits used (storage).
1207
+ nmant : integer
1208
+ Number of bits used to represent the mantissa.
1209
+ nexp : integer
1210
+ Number of bits used to represent the mantissa.
1211
+
1212
+ Examples
1213
+ ========
1214
+
1215
+ >>> from sympy import S
1216
+ >>> from sympy.codegen.ast import FloatType
1217
+ >>> half_precision = FloatType('f16', nbits=16, nmant=10, nexp=5)
1218
+ >>> half_precision.max
1219
+ 65504
1220
+ >>> half_precision.tiny == S(2)**-14
1221
+ True
1222
+ >>> half_precision.eps == S(2)**-10
1223
+ True
1224
+ >>> half_precision.dig == 3
1225
+ True
1226
+ >>> half_precision.decimal_dig == 5
1227
+ True
1228
+ >>> half_precision.cast_check(1.0)
1229
+ 1.0
1230
+ >>> half_precision.cast_check(1e5) # doctest: +ELLIPSIS
1231
+ Traceback (most recent call last):
1232
+ ...
1233
+ ValueError: Maximum value for data type smaller than new value.
1234
+ """
1235
+
1236
+ __slots__ = ('nbits', 'nmant', 'nexp',)
1237
+ _fields = Type._fields + __slots__
1238
+
1239
+ _construct_nbits = _construct_nmant = _construct_nexp = Integer
1240
+
1241
+
1242
+ @property
1243
+ def max_exponent(self):
1244
+ """ The largest positive number n, such that 2**(n - 1) is a representable finite value. """
1245
+ # cf. C++'s ``std::numeric_limits::max_exponent``
1246
+ return two**(self.nexp - 1)
1247
+
1248
+ @property
1249
+ def min_exponent(self):
1250
+ """ The lowest negative number n, such that 2**(n - 1) is a valid normalized number. """
1251
+ # cf. C++'s ``std::numeric_limits::min_exponent``
1252
+ return 3 - self.max_exponent
1253
+
1254
+ @property
1255
+ def max(self):
1256
+ """ Maximum value representable. """
1257
+ return (1 - two**-(self.nmant+1))*two**self.max_exponent
1258
+
1259
+ @property
1260
+ def tiny(self):
1261
+ """ The minimum positive normalized value. """
1262
+ # See C macros: FLT_MIN, DBL_MIN, LDBL_MIN
1263
+ # or C++'s ``std::numeric_limits::min``
1264
+ # or numpy.finfo(dtype).tiny
1265
+ return two**(self.min_exponent - 1)
1266
+
1267
+
1268
+ @property
1269
+ def eps(self):
1270
+ """ Difference between 1.0 and the next representable value. """
1271
+ return two**(-self.nmant)
1272
+
1273
+ @property
1274
+ def dig(self):
1275
+ """ Number of decimal digits that are guaranteed to be preserved in text.
1276
+
1277
+ When converting text -> float -> text, you are guaranteed that at least ``dig``
1278
+ number of digits are preserved with respect to rounding or overflow.
1279
+ """
1280
+ from sympy.functions import floor, log
1281
+ return floor(self.nmant * log(2)/log(10))
1282
+
1283
+ @property
1284
+ def decimal_dig(self):
1285
+ """ Number of digits needed to store & load without loss.
1286
+
1287
+ Explanation
1288
+ ===========
1289
+
1290
+ Number of decimal digits needed to guarantee that two consecutive conversions
1291
+ (float -> text -> float) to be idempotent. This is useful when one do not want
1292
+ to loose precision due to rounding errors when storing a floating point value
1293
+ as text.
1294
+ """
1295
+ from sympy.functions import ceiling, log
1296
+ return ceiling((self.nmant + 1) * log(2)/log(10) + 1)
1297
+
1298
+ def cast_nocheck(self, value):
1299
+ """ Casts without checking if out of bounds or subnormal. """
1300
+ if value == oo: # float(oo) or oo
1301
+ return float(oo)
1302
+ elif value == -oo: # float(-oo) or -oo
1303
+ return float(-oo)
1304
+ return Float(str(sympify(value).evalf(self.decimal_dig)), self.decimal_dig)
1305
+
1306
+ def _check(self, value):
1307
+ if value < -self.max:
1308
+ raise ValueError("Value is too small: %d < %d" % (value, -self.max))
1309
+ if value > self.max:
1310
+ raise ValueError("Value is too big: %d > %d" % (value, self.max))
1311
+ if abs(value) < self.tiny:
1312
+ raise ValueError("Smallest (absolute) value for data type bigger than new value.")
1313
+
1314
+ class ComplexBaseType(FloatBaseType):
1315
+
1316
+ __slots__ = ()
1317
+
1318
+ def cast_nocheck(self, value):
1319
+ """ Casts without checking if out of bounds or subnormal. """
1320
+ from sympy.functions import re, im
1321
+ return (
1322
+ super().cast_nocheck(re(value)) +
1323
+ super().cast_nocheck(im(value))*1j
1324
+ )
1325
+
1326
+ def _check(self, value):
1327
+ from sympy.functions import re, im
1328
+ super()._check(re(value))
1329
+ super()._check(im(value))
1330
+
1331
+
1332
+ class ComplexType(ComplexBaseType, FloatType):
1333
+ """ Represents a complex floating point number. """
1334
+ __slots__ = ()
1335
+
1336
+
1337
+ # NumPy types:
1338
+ intc = IntBaseType('intc')
1339
+ intp = IntBaseType('intp')
1340
+ int8 = SignedIntType('int8', 8)
1341
+ int16 = SignedIntType('int16', 16)
1342
+ int32 = SignedIntType('int32', 32)
1343
+ int64 = SignedIntType('int64', 64)
1344
+ uint8 = UnsignedIntType('uint8', 8)
1345
+ uint16 = UnsignedIntType('uint16', 16)
1346
+ uint32 = UnsignedIntType('uint32', 32)
1347
+ uint64 = UnsignedIntType('uint64', 64)
1348
+ float16 = FloatType('float16', 16, nexp=5, nmant=10) # IEEE 754 binary16, Half precision
1349
+ float32 = FloatType('float32', 32, nexp=8, nmant=23) # IEEE 754 binary32, Single precision
1350
+ float64 = FloatType('float64', 64, nexp=11, nmant=52) # IEEE 754 binary64, Double precision
1351
+ float80 = FloatType('float80', 80, nexp=15, nmant=63) # x86 extended precision (1 integer part bit), "long double"
1352
+ float128 = FloatType('float128', 128, nexp=15, nmant=112) # IEEE 754 binary128, Quadruple precision
1353
+ float256 = FloatType('float256', 256, nexp=19, nmant=236) # IEEE 754 binary256, Octuple precision
1354
+
1355
+ complex64 = ComplexType('complex64', nbits=64, **float32.kwargs(exclude=('name', 'nbits')))
1356
+ complex128 = ComplexType('complex128', nbits=128, **float64.kwargs(exclude=('name', 'nbits')))
1357
+
1358
+ # Generic types (precision may be chosen by code printers):
1359
+ untyped = Type('untyped')
1360
+ real = FloatBaseType('real')
1361
+ integer = IntBaseType('integer')
1362
+ complex_ = ComplexBaseType('complex')
1363
+ bool_ = Type('bool')
1364
+
1365
+
1366
+ class Attribute(Token):
1367
+ """ Attribute (possibly parametrized)
1368
+
1369
+ For use with :class:`sympy.codegen.ast.Node` (which takes instances of
1370
+ ``Attribute`` as ``attrs``).
1371
+
1372
+ Parameters
1373
+ ==========
1374
+
1375
+ name : str
1376
+ parameters : Tuple
1377
+
1378
+ Examples
1379
+ ========
1380
+
1381
+ >>> from sympy.codegen.ast import Attribute
1382
+ >>> volatile = Attribute('volatile')
1383
+ >>> volatile
1384
+ volatile
1385
+ >>> print(repr(volatile))
1386
+ Attribute(String('volatile'))
1387
+ >>> a = Attribute('foo', [1, 2, 3])
1388
+ >>> a
1389
+ foo(1, 2, 3)
1390
+ >>> a.parameters == (1, 2, 3)
1391
+ True
1392
+ """
1393
+ __slots__ = _fields = ('name', 'parameters')
1394
+ defaults = {'parameters': Tuple()}
1395
+
1396
+ _construct_name = String
1397
+ _construct_parameters = staticmethod(_mk_Tuple)
1398
+
1399
+ def _sympystr(self, printer, *args, **kwargs):
1400
+ result = str(self.name)
1401
+ if self.parameters:
1402
+ result += '(%s)' % ', '.join((printer._print(
1403
+ arg, *args, **kwargs) for arg in self.parameters))
1404
+ return result
1405
+
1406
+ value_const = Attribute('value_const')
1407
+ pointer_const = Attribute('pointer_const')
1408
+
1409
+
1410
+ class Variable(Node):
1411
+ """ Represents a variable.
1412
+
1413
+ Parameters
1414
+ ==========
1415
+
1416
+ symbol : Symbol
1417
+ type : Type (optional)
1418
+ Type of the variable.
1419
+ attrs : iterable of Attribute instances
1420
+ Will be stored as a Tuple.
1421
+
1422
+ Examples
1423
+ ========
1424
+
1425
+ >>> from sympy import Symbol
1426
+ >>> from sympy.codegen.ast import Variable, float32, integer
1427
+ >>> x = Symbol('x')
1428
+ >>> v = Variable(x, type=float32)
1429
+ >>> v.attrs
1430
+ ()
1431
+ >>> v == Variable('x')
1432
+ False
1433
+ >>> v == Variable('x', type=float32)
1434
+ True
1435
+ >>> v
1436
+ Variable(x, type=float32)
1437
+
1438
+ One may also construct a ``Variable`` instance with the type deduced from
1439
+ assumptions about the symbol using the ``deduced`` classmethod:
1440
+
1441
+ >>> i = Symbol('i', integer=True)
1442
+ >>> v = Variable.deduced(i)
1443
+ >>> v.type == integer
1444
+ True
1445
+ >>> v == Variable('i')
1446
+ False
1447
+ >>> from sympy.codegen.ast import value_const
1448
+ >>> value_const in v.attrs
1449
+ False
1450
+ >>> w = Variable('w', attrs=[value_const])
1451
+ >>> w
1452
+ Variable(w, attrs=(value_const,))
1453
+ >>> value_const in w.attrs
1454
+ True
1455
+ >>> w.as_Declaration(value=42)
1456
+ Declaration(Variable(w, value=42, attrs=(value_const,)))
1457
+
1458
+ """
1459
+
1460
+ __slots__ = ('symbol', 'type', 'value')
1461
+ _fields = __slots__ + Node._fields
1462
+
1463
+ defaults = Node.defaults.copy()
1464
+ defaults.update({'type': untyped, 'value': none})
1465
+
1466
+ _construct_symbol = staticmethod(sympify)
1467
+ _construct_value = staticmethod(sympify)
1468
+
1469
+ @classmethod
1470
+ def deduced(cls, symbol, value=None, attrs=Tuple(), cast_check=True):
1471
+ """ Alt. constructor with type deduction from ``Type.from_expr``.
1472
+
1473
+ Deduces type primarily from ``symbol``, secondarily from ``value``.
1474
+
1475
+ Parameters
1476
+ ==========
1477
+
1478
+ symbol : Symbol
1479
+ value : expr
1480
+ (optional) value of the variable.
1481
+ attrs : iterable of Attribute instances
1482
+ cast_check : bool
1483
+ Whether to apply ``Type.cast_check`` on ``value``.
1484
+
1485
+ Examples
1486
+ ========
1487
+
1488
+ >>> from sympy import Symbol
1489
+ >>> from sympy.codegen.ast import Variable, complex_
1490
+ >>> n = Symbol('n', integer=True)
1491
+ >>> str(Variable.deduced(n).type)
1492
+ 'integer'
1493
+ >>> x = Symbol('x', real=True)
1494
+ >>> v = Variable.deduced(x)
1495
+ >>> v.type
1496
+ real
1497
+ >>> z = Symbol('z', complex=True)
1498
+ >>> Variable.deduced(z).type == complex_
1499
+ True
1500
+
1501
+ """
1502
+ if isinstance(symbol, Variable):
1503
+ return symbol
1504
+
1505
+ try:
1506
+ type_ = Type.from_expr(symbol)
1507
+ except ValueError:
1508
+ type_ = Type.from_expr(value)
1509
+
1510
+ if value is not None and cast_check:
1511
+ value = type_.cast_check(value)
1512
+ return cls(symbol, type=type_, value=value, attrs=attrs)
1513
+
1514
+ def as_Declaration(self, **kwargs):
1515
+ """ Convenience method for creating a Declaration instance.
1516
+
1517
+ Explanation
1518
+ ===========
1519
+
1520
+ If the variable of the Declaration need to wrap a modified
1521
+ variable keyword arguments may be passed (overriding e.g.
1522
+ the ``value`` of the Variable instance).
1523
+
1524
+ Examples
1525
+ ========
1526
+
1527
+ >>> from sympy.codegen.ast import Variable, NoneToken
1528
+ >>> x = Variable('x')
1529
+ >>> decl1 = x.as_Declaration()
1530
+ >>> # value is special NoneToken() which must be tested with == operator
1531
+ >>> decl1.variable.value is None # won't work
1532
+ False
1533
+ >>> decl1.variable.value == None # not PEP-8 compliant
1534
+ True
1535
+ >>> decl1.variable.value == NoneToken() # OK
1536
+ True
1537
+ >>> decl2 = x.as_Declaration(value=42.0)
1538
+ >>> decl2.variable.value == 42.0
1539
+ True
1540
+
1541
+ """
1542
+ kw = self.kwargs()
1543
+ kw.update(kwargs)
1544
+ return Declaration(self.func(**kw))
1545
+
1546
+ def _relation(self, rhs, op):
1547
+ try:
1548
+ rhs = _sympify(rhs)
1549
+ except SympifyError:
1550
+ raise TypeError("Invalid comparison %s < %s" % (self, rhs))
1551
+ return op(self, rhs, evaluate=False)
1552
+
1553
+ __lt__ = lambda self, other: self._relation(other, Lt)
1554
+ __le__ = lambda self, other: self._relation(other, Le)
1555
+ __ge__ = lambda self, other: self._relation(other, Ge)
1556
+ __gt__ = lambda self, other: self._relation(other, Gt)
1557
+
1558
+ class Pointer(Variable):
1559
+ """ Represents a pointer. See ``Variable``.
1560
+
1561
+ Examples
1562
+ ========
1563
+
1564
+ Can create instances of ``Element``:
1565
+
1566
+ >>> from sympy import Symbol
1567
+ >>> from sympy.codegen.ast import Pointer
1568
+ >>> i = Symbol('i', integer=True)
1569
+ >>> p = Pointer('x')
1570
+ >>> p[i+1]
1571
+ Element(x, indices=(i + 1,))
1572
+
1573
+ """
1574
+ __slots__ = ()
1575
+
1576
+ def __getitem__(self, key):
1577
+ try:
1578
+ return Element(self.symbol, key)
1579
+ except TypeError:
1580
+ return Element(self.symbol, (key,))
1581
+
1582
+
1583
+ class Element(Token):
1584
+ """ Element in (a possibly N-dimensional) array.
1585
+
1586
+ Examples
1587
+ ========
1588
+
1589
+ >>> from sympy.codegen.ast import Element
1590
+ >>> elem = Element('x', 'ijk')
1591
+ >>> elem.symbol.name == 'x'
1592
+ True
1593
+ >>> elem.indices
1594
+ (i, j, k)
1595
+ >>> from sympy import ccode
1596
+ >>> ccode(elem)
1597
+ 'x[i][j][k]'
1598
+ >>> ccode(Element('x', 'ijk', strides='lmn', offset='o'))
1599
+ 'x[i*l + j*m + k*n + o]'
1600
+
1601
+ """
1602
+ __slots__ = _fields = ('symbol', 'indices', 'strides', 'offset')
1603
+ defaults = {'strides': none, 'offset': none}
1604
+ _construct_symbol = staticmethod(sympify)
1605
+ _construct_indices = staticmethod(lambda arg: Tuple(*arg))
1606
+ _construct_strides = staticmethod(lambda arg: Tuple(*arg))
1607
+ _construct_offset = staticmethod(sympify)
1608
+
1609
+
1610
+ class Declaration(Token):
1611
+ """ Represents a variable declaration
1612
+
1613
+ Parameters
1614
+ ==========
1615
+
1616
+ variable : Variable
1617
+
1618
+ Examples
1619
+ ========
1620
+
1621
+ >>> from sympy.codegen.ast import Declaration, NoneToken, untyped
1622
+ >>> z = Declaration('z')
1623
+ >>> z.variable.type == untyped
1624
+ True
1625
+ >>> # value is special NoneToken() which must be tested with == operator
1626
+ >>> z.variable.value is None # won't work
1627
+ False
1628
+ >>> z.variable.value == None # not PEP-8 compliant
1629
+ True
1630
+ >>> z.variable.value == NoneToken() # OK
1631
+ True
1632
+ """
1633
+ __slots__ = _fields = ('variable',)
1634
+ _construct_variable = Variable
1635
+
1636
+
1637
+ class While(Token):
1638
+ """ Represents a 'for-loop' in the code.
1639
+
1640
+ Expressions are of the form:
1641
+ "while condition:
1642
+ body..."
1643
+
1644
+ Parameters
1645
+ ==========
1646
+
1647
+ condition : expression convertible to Boolean
1648
+ body : CodeBlock or iterable
1649
+ When passed an iterable it is used to instantiate a CodeBlock.
1650
+
1651
+ Examples
1652
+ ========
1653
+
1654
+ >>> from sympy import symbols, Gt, Abs
1655
+ >>> from sympy.codegen import aug_assign, Assignment, While
1656
+ >>> x, dx = symbols('x dx')
1657
+ >>> expr = 1 - x**2
1658
+ >>> whl = While(Gt(Abs(dx), 1e-9), [
1659
+ ... Assignment(dx, -expr/expr.diff(x)),
1660
+ ... aug_assign(x, '+', dx)
1661
+ ... ])
1662
+
1663
+ """
1664
+ __slots__ = _fields = ('condition', 'body')
1665
+ _construct_condition = staticmethod(lambda cond: _sympify(cond))
1666
+
1667
+ @classmethod
1668
+ def _construct_body(cls, itr):
1669
+ if isinstance(itr, CodeBlock):
1670
+ return itr
1671
+ else:
1672
+ return CodeBlock(*itr)
1673
+
1674
+
1675
+ class Scope(Token):
1676
+ """ Represents a scope in the code.
1677
+
1678
+ Parameters
1679
+ ==========
1680
+
1681
+ body : CodeBlock or iterable
1682
+ When passed an iterable it is used to instantiate a CodeBlock.
1683
+
1684
+ """
1685
+ __slots__ = _fields = ('body',)
1686
+
1687
+ @classmethod
1688
+ def _construct_body(cls, itr):
1689
+ if isinstance(itr, CodeBlock):
1690
+ return itr
1691
+ else:
1692
+ return CodeBlock(*itr)
1693
+
1694
+
1695
+ class Stream(Token):
1696
+ """ Represents a stream.
1697
+
1698
+ There are two predefined Stream instances ``stdout`` & ``stderr``.
1699
+
1700
+ Parameters
1701
+ ==========
1702
+
1703
+ name : str
1704
+
1705
+ Examples
1706
+ ========
1707
+
1708
+ >>> from sympy import pycode, Symbol
1709
+ >>> from sympy.codegen.ast import Print, stderr, QuotedString
1710
+ >>> print(pycode(Print(['x'], file=stderr)))
1711
+ print(x, file=sys.stderr)
1712
+ >>> x = Symbol('x')
1713
+ >>> print(pycode(Print([QuotedString('x')], file=stderr))) # print literally "x"
1714
+ print("x", file=sys.stderr)
1715
+
1716
+ """
1717
+ __slots__ = _fields = ('name',)
1718
+ _construct_name = String
1719
+
1720
+ stdout = Stream('stdout')
1721
+ stderr = Stream('stderr')
1722
+
1723
+
1724
+ class Print(Token):
1725
+ r""" Represents print command in the code.
1726
+
1727
+ Parameters
1728
+ ==========
1729
+
1730
+ formatstring : str
1731
+ *args : Basic instances (or convertible to such through sympify)
1732
+
1733
+ Examples
1734
+ ========
1735
+
1736
+ >>> from sympy.codegen.ast import Print
1737
+ >>> from sympy import pycode
1738
+ >>> print(pycode(Print('x y'.split(), "coordinate: %12.5g %12.5g\\n")))
1739
+ print("coordinate: %12.5g %12.5g\n" % (x, y), end="")
1740
+
1741
+ """
1742
+
1743
+ __slots__ = _fields = ('print_args', 'format_string', 'file')
1744
+ defaults = {'format_string': none, 'file': none}
1745
+
1746
+ _construct_print_args = staticmethod(_mk_Tuple)
1747
+ _construct_format_string = QuotedString
1748
+ _construct_file = Stream
1749
+
1750
+
1751
+ class FunctionPrototype(Node):
1752
+ """ Represents a function prototype
1753
+
1754
+ Allows the user to generate forward declaration in e.g. C/C++.
1755
+
1756
+ Parameters
1757
+ ==========
1758
+
1759
+ return_type : Type
1760
+ name : str
1761
+ parameters: iterable of Variable instances
1762
+ attrs : iterable of Attribute instances
1763
+
1764
+ Examples
1765
+ ========
1766
+
1767
+ >>> from sympy import ccode, symbols
1768
+ >>> from sympy.codegen.ast import real, FunctionPrototype
1769
+ >>> x, y = symbols('x y', real=True)
1770
+ >>> fp = FunctionPrototype(real, 'foo', [x, y])
1771
+ >>> ccode(fp)
1772
+ 'double foo(double x, double y)'
1773
+
1774
+ """
1775
+
1776
+ __slots__ = ('return_type', 'name', 'parameters')
1777
+ _fields: tuple[str, ...] = __slots__ + Node._fields
1778
+
1779
+ _construct_return_type = Type
1780
+ _construct_name = String
1781
+
1782
+ @staticmethod
1783
+ def _construct_parameters(args):
1784
+ def _var(arg):
1785
+ if isinstance(arg, Declaration):
1786
+ return arg.variable
1787
+ elif isinstance(arg, Variable):
1788
+ return arg
1789
+ else:
1790
+ return Variable.deduced(arg)
1791
+ return Tuple(*map(_var, args))
1792
+
1793
+ @classmethod
1794
+ def from_FunctionDefinition(cls, func_def):
1795
+ if not isinstance(func_def, FunctionDefinition):
1796
+ raise TypeError("func_def is not an instance of FunctionDefinition")
1797
+ return cls(**func_def.kwargs(exclude=('body',)))
1798
+
1799
+
1800
+ class FunctionDefinition(FunctionPrototype):
1801
+ """ Represents a function definition in the code.
1802
+
1803
+ Parameters
1804
+ ==========
1805
+
1806
+ return_type : Type
1807
+ name : str
1808
+ parameters: iterable of Variable instances
1809
+ body : CodeBlock or iterable
1810
+ attrs : iterable of Attribute instances
1811
+
1812
+ Examples
1813
+ ========
1814
+
1815
+ >>> from sympy import ccode, symbols
1816
+ >>> from sympy.codegen.ast import real, FunctionPrototype
1817
+ >>> x, y = symbols('x y', real=True)
1818
+ >>> fp = FunctionPrototype(real, 'foo', [x, y])
1819
+ >>> ccode(fp)
1820
+ 'double foo(double x, double y)'
1821
+ >>> from sympy.codegen.ast import FunctionDefinition, Return
1822
+ >>> body = [Return(x*y)]
1823
+ >>> fd = FunctionDefinition.from_FunctionPrototype(fp, body)
1824
+ >>> print(ccode(fd))
1825
+ double foo(double x, double y){
1826
+ return x*y;
1827
+ }
1828
+ """
1829
+
1830
+ __slots__ = ('body', )
1831
+ _fields = FunctionPrototype._fields[:-1] + __slots__ + Node._fields
1832
+
1833
+ @classmethod
1834
+ def _construct_body(cls, itr):
1835
+ if isinstance(itr, CodeBlock):
1836
+ return itr
1837
+ else:
1838
+ return CodeBlock(*itr)
1839
+
1840
+ @classmethod
1841
+ def from_FunctionPrototype(cls, func_proto, body):
1842
+ if not isinstance(func_proto, FunctionPrototype):
1843
+ raise TypeError("func_proto is not an instance of FunctionPrototype")
1844
+ return cls(body=body, **func_proto.kwargs())
1845
+
1846
+
1847
+ class Return(Token):
1848
+ """ Represents a return command in the code.
1849
+
1850
+ Parameters
1851
+ ==========
1852
+
1853
+ return : Basic
1854
+
1855
+ Examples
1856
+ ========
1857
+
1858
+ >>> from sympy.codegen.ast import Return
1859
+ >>> from sympy.printing.pycode import pycode
1860
+ >>> from sympy import Symbol
1861
+ >>> x = Symbol('x')
1862
+ >>> print(pycode(Return(x)))
1863
+ return x
1864
+
1865
+ """
1866
+ __slots__ = _fields = ('return',)
1867
+ _construct_return=staticmethod(_sympify)
1868
+
1869
+
1870
+ class FunctionCall(Token, Expr):
1871
+ """ Represents a call to a function in the code.
1872
+
1873
+ Parameters
1874
+ ==========
1875
+
1876
+ name : str
1877
+ function_args : Tuple
1878
+
1879
+ Examples
1880
+ ========
1881
+
1882
+ >>> from sympy.codegen.ast import FunctionCall
1883
+ >>> from sympy import pycode
1884
+ >>> fcall = FunctionCall('foo', 'bar baz'.split())
1885
+ >>> print(pycode(fcall))
1886
+ foo(bar, baz)
1887
+
1888
+ """
1889
+ __slots__ = _fields = ('name', 'function_args')
1890
+
1891
+ _construct_name = String
1892
+ _construct_function_args = staticmethod(lambda args: Tuple(*args))
1893
+
1894
+
1895
+ class Raise(Token):
1896
+ """ Prints as 'raise ...' in Python, 'throw ...' in C++"""
1897
+ __slots__ = _fields = ('exception',)
1898
+
1899
+
1900
+ class RuntimeError_(Token):
1901
+ """ Represents 'std::runtime_error' in C++ and 'RuntimeError' in Python.
1902
+
1903
+ Note that the latter is uncommon, and you might want to use e.g. ValueError.
1904
+ """
1905
+ __slots__ = _fields = ('message',)
1906
+ _construct_message = String
lib/python3.1/site-packages/sympy/codegen/cfunctions.py ADDED
@@ -0,0 +1,536 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains SymPy functions mathcin corresponding to special math functions in the
3
+ C standard library (since C99, also available in C++11).
4
+
5
+ The functions defined in this module allows the user to express functions such as ``expm1``
6
+ as a SymPy function for symbolic manipulation.
7
+
8
+ """
9
+ from sympy.core.function import ArgumentIndexError, Function
10
+ from sympy.core.numbers import Rational
11
+ from sympy.core.power import Pow
12
+ from sympy.core.singleton import S
13
+ from sympy.functions.elementary.exponential import exp, log
14
+ from sympy.functions.elementary.miscellaneous import sqrt
15
+
16
+
17
+ def _expm1(x):
18
+ return exp(x) - S.One
19
+
20
+
21
+ class expm1(Function):
22
+ """
23
+ Represents the exponential function minus one.
24
+
25
+ Explanation
26
+ ===========
27
+
28
+ The benefit of using ``expm1(x)`` over ``exp(x) - 1``
29
+ is that the latter is prone to cancellation under finite precision
30
+ arithmetic when x is close to zero.
31
+
32
+ Examples
33
+ ========
34
+
35
+ >>> from sympy.abc import x
36
+ >>> from sympy.codegen.cfunctions import expm1
37
+ >>> '%.0e' % expm1(1e-99).evalf()
38
+ '1e-99'
39
+ >>> from math import exp
40
+ >>> exp(1e-99) - 1
41
+ 0.0
42
+ >>> expm1(x).diff(x)
43
+ exp(x)
44
+
45
+ See Also
46
+ ========
47
+
48
+ log1p
49
+ """
50
+ nargs = 1
51
+
52
+ def fdiff(self, argindex=1):
53
+ """
54
+ Returns the first derivative of this function.
55
+ """
56
+ if argindex == 1:
57
+ return exp(*self.args)
58
+ else:
59
+ raise ArgumentIndexError(self, argindex)
60
+
61
+ def _eval_expand_func(self, **hints):
62
+ return _expm1(*self.args)
63
+
64
+ def _eval_rewrite_as_exp(self, arg, **kwargs):
65
+ return exp(arg) - S.One
66
+
67
+ _eval_rewrite_as_tractable = _eval_rewrite_as_exp
68
+
69
+ @classmethod
70
+ def eval(cls, arg):
71
+ exp_arg = exp.eval(arg)
72
+ if exp_arg is not None:
73
+ return exp_arg - S.One
74
+
75
+ def _eval_is_real(self):
76
+ return self.args[0].is_real
77
+
78
+ def _eval_is_finite(self):
79
+ return self.args[0].is_finite
80
+
81
+
82
+ def _log1p(x):
83
+ return log(x + S.One)
84
+
85
+
86
+ class log1p(Function):
87
+ """
88
+ Represents the natural logarithm of a number plus one.
89
+
90
+ Explanation
91
+ ===========
92
+
93
+ The benefit of using ``log1p(x)`` over ``log(x + 1)``
94
+ is that the latter is prone to cancellation under finite precision
95
+ arithmetic when x is close to zero.
96
+
97
+ Examples
98
+ ========
99
+
100
+ >>> from sympy.abc import x
101
+ >>> from sympy.codegen.cfunctions import log1p
102
+ >>> from sympy import expand_log
103
+ >>> '%.0e' % expand_log(log1p(1e-99)).evalf()
104
+ '1e-99'
105
+ >>> from math import log
106
+ >>> log(1 + 1e-99)
107
+ 0.0
108
+ >>> log1p(x).diff(x)
109
+ 1/(x + 1)
110
+
111
+ See Also
112
+ ========
113
+
114
+ expm1
115
+ """
116
+ nargs = 1
117
+
118
+
119
+ def fdiff(self, argindex=1):
120
+ """
121
+ Returns the first derivative of this function.
122
+ """
123
+ if argindex == 1:
124
+ return S.One/(self.args[0] + S.One)
125
+ else:
126
+ raise ArgumentIndexError(self, argindex)
127
+
128
+
129
+ def _eval_expand_func(self, **hints):
130
+ return _log1p(*self.args)
131
+
132
+ def _eval_rewrite_as_log(self, arg, **kwargs):
133
+ return _log1p(arg)
134
+
135
+ _eval_rewrite_as_tractable = _eval_rewrite_as_log
136
+
137
+ @classmethod
138
+ def eval(cls, arg):
139
+ if arg.is_Rational:
140
+ return log(arg + S.One)
141
+ elif not arg.is_Float: # not safe to add 1 to Float
142
+ return log.eval(arg + S.One)
143
+ elif arg.is_number:
144
+ return log(Rational(arg) + S.One)
145
+
146
+ def _eval_is_real(self):
147
+ return (self.args[0] + S.One).is_nonnegative
148
+
149
+ def _eval_is_finite(self):
150
+ if (self.args[0] + S.One).is_zero:
151
+ return False
152
+ return self.args[0].is_finite
153
+
154
+ def _eval_is_positive(self):
155
+ return self.args[0].is_positive
156
+
157
+ def _eval_is_zero(self):
158
+ return self.args[0].is_zero
159
+
160
+ def _eval_is_nonnegative(self):
161
+ return self.args[0].is_nonnegative
162
+
163
+ _Two = S(2)
164
+
165
+ def _exp2(x):
166
+ return Pow(_Two, x)
167
+
168
+ class exp2(Function):
169
+ """
170
+ Represents the exponential function with base two.
171
+
172
+ Explanation
173
+ ===========
174
+
175
+ The benefit of using ``exp2(x)`` over ``2**x``
176
+ is that the latter is not as efficient under finite precision
177
+ arithmetic.
178
+
179
+ Examples
180
+ ========
181
+
182
+ >>> from sympy.abc import x
183
+ >>> from sympy.codegen.cfunctions import exp2
184
+ >>> exp2(2).evalf() == 4.0
185
+ True
186
+ >>> exp2(x).diff(x)
187
+ log(2)*exp2(x)
188
+
189
+ See Also
190
+ ========
191
+
192
+ log2
193
+ """
194
+ nargs = 1
195
+
196
+
197
+ def fdiff(self, argindex=1):
198
+ """
199
+ Returns the first derivative of this function.
200
+ """
201
+ if argindex == 1:
202
+ return self*log(_Two)
203
+ else:
204
+ raise ArgumentIndexError(self, argindex)
205
+
206
+ def _eval_rewrite_as_Pow(self, arg, **kwargs):
207
+ return _exp2(arg)
208
+
209
+ _eval_rewrite_as_tractable = _eval_rewrite_as_Pow
210
+
211
+ def _eval_expand_func(self, **hints):
212
+ return _exp2(*self.args)
213
+
214
+ @classmethod
215
+ def eval(cls, arg):
216
+ if arg.is_number:
217
+ return _exp2(arg)
218
+
219
+
220
+ def _log2(x):
221
+ return log(x)/log(_Two)
222
+
223
+
224
+ class log2(Function):
225
+ """
226
+ Represents the logarithm function with base two.
227
+
228
+ Explanation
229
+ ===========
230
+
231
+ The benefit of using ``log2(x)`` over ``log(x)/log(2)``
232
+ is that the latter is not as efficient under finite precision
233
+ arithmetic.
234
+
235
+ Examples
236
+ ========
237
+
238
+ >>> from sympy.abc import x
239
+ >>> from sympy.codegen.cfunctions import log2
240
+ >>> log2(4).evalf() == 2.0
241
+ True
242
+ >>> log2(x).diff(x)
243
+ 1/(x*log(2))
244
+
245
+ See Also
246
+ ========
247
+
248
+ exp2
249
+ log10
250
+ """
251
+ nargs = 1
252
+
253
+ def fdiff(self, argindex=1):
254
+ """
255
+ Returns the first derivative of this function.
256
+ """
257
+ if argindex == 1:
258
+ return S.One/(log(_Two)*self.args[0])
259
+ else:
260
+ raise ArgumentIndexError(self, argindex)
261
+
262
+
263
+ @classmethod
264
+ def eval(cls, arg):
265
+ if arg.is_number:
266
+ result = log.eval(arg, base=_Two)
267
+ if result.is_Atom:
268
+ return result
269
+ elif arg.is_Pow and arg.base == _Two:
270
+ return arg.exp
271
+
272
+ def _eval_evalf(self, *args, **kwargs):
273
+ return self.rewrite(log).evalf(*args, **kwargs)
274
+
275
+ def _eval_expand_func(self, **hints):
276
+ return _log2(*self.args)
277
+
278
+ def _eval_rewrite_as_log(self, arg, **kwargs):
279
+ return _log2(arg)
280
+
281
+ _eval_rewrite_as_tractable = _eval_rewrite_as_log
282
+
283
+
284
+ def _fma(x, y, z):
285
+ return x*y + z
286
+
287
+
288
+ class fma(Function):
289
+ """
290
+ Represents "fused multiply add".
291
+
292
+ Explanation
293
+ ===========
294
+
295
+ The benefit of using ``fma(x, y, z)`` over ``x*y + z``
296
+ is that, under finite precision arithmetic, the former is
297
+ supported by special instructions on some CPUs.
298
+
299
+ Examples
300
+ ========
301
+
302
+ >>> from sympy.abc import x, y, z
303
+ >>> from sympy.codegen.cfunctions import fma
304
+ >>> fma(x, y, z).diff(x)
305
+ y
306
+
307
+ """
308
+ nargs = 3
309
+
310
+ def fdiff(self, argindex=1):
311
+ """
312
+ Returns the first derivative of this function.
313
+ """
314
+ if argindex in (1, 2):
315
+ return self.args[2 - argindex]
316
+ elif argindex == 3:
317
+ return S.One
318
+ else:
319
+ raise ArgumentIndexError(self, argindex)
320
+
321
+
322
+ def _eval_expand_func(self, **hints):
323
+ return _fma(*self.args)
324
+
325
+ def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
326
+ return _fma(arg)
327
+
328
+
329
+ _Ten = S(10)
330
+
331
+
332
+ def _log10(x):
333
+ return log(x)/log(_Ten)
334
+
335
+
336
+ class log10(Function):
337
+ """
338
+ Represents the logarithm function with base ten.
339
+
340
+ Examples
341
+ ========
342
+
343
+ >>> from sympy.abc import x
344
+ >>> from sympy.codegen.cfunctions import log10
345
+ >>> log10(100).evalf() == 2.0
346
+ True
347
+ >>> log10(x).diff(x)
348
+ 1/(x*log(10))
349
+
350
+ See Also
351
+ ========
352
+
353
+ log2
354
+ """
355
+ nargs = 1
356
+
357
+ def fdiff(self, argindex=1):
358
+ """
359
+ Returns the first derivative of this function.
360
+ """
361
+ if argindex == 1:
362
+ return S.One/(log(_Ten)*self.args[0])
363
+ else:
364
+ raise ArgumentIndexError(self, argindex)
365
+
366
+
367
+ @classmethod
368
+ def eval(cls, arg):
369
+ if arg.is_number:
370
+ result = log.eval(arg, base=_Ten)
371
+ if result.is_Atom:
372
+ return result
373
+ elif arg.is_Pow and arg.base == _Ten:
374
+ return arg.exp
375
+
376
+ def _eval_expand_func(self, **hints):
377
+ return _log10(*self.args)
378
+
379
+ def _eval_rewrite_as_log(self, arg, **kwargs):
380
+ return _log10(arg)
381
+
382
+ _eval_rewrite_as_tractable = _eval_rewrite_as_log
383
+
384
+
385
+ def _Sqrt(x):
386
+ return Pow(x, S.Half)
387
+
388
+
389
+ class Sqrt(Function): # 'sqrt' already defined in sympy.functions.elementary.miscellaneous
390
+ """
391
+ Represents the square root function.
392
+
393
+ Explanation
394
+ ===========
395
+
396
+ The reason why one would use ``Sqrt(x)`` over ``sqrt(x)``
397
+ is that the latter is internally represented as ``Pow(x, S.Half)`` which
398
+ may not be what one wants when doing code-generation.
399
+
400
+ Examples
401
+ ========
402
+
403
+ >>> from sympy.abc import x
404
+ >>> from sympy.codegen.cfunctions import Sqrt
405
+ >>> Sqrt(x)
406
+ Sqrt(x)
407
+ >>> Sqrt(x).diff(x)
408
+ 1/(2*sqrt(x))
409
+
410
+ See Also
411
+ ========
412
+
413
+ Cbrt
414
+ """
415
+ nargs = 1
416
+
417
+ def fdiff(self, argindex=1):
418
+ """
419
+ Returns the first derivative of this function.
420
+ """
421
+ if argindex == 1:
422
+ return Pow(self.args[0], Rational(-1, 2))/_Two
423
+ else:
424
+ raise ArgumentIndexError(self, argindex)
425
+
426
+ def _eval_expand_func(self, **hints):
427
+ return _Sqrt(*self.args)
428
+
429
+ def _eval_rewrite_as_Pow(self, arg, **kwargs):
430
+ return _Sqrt(arg)
431
+
432
+ _eval_rewrite_as_tractable = _eval_rewrite_as_Pow
433
+
434
+
435
+ def _Cbrt(x):
436
+ return Pow(x, Rational(1, 3))
437
+
438
+
439
+ class Cbrt(Function): # 'cbrt' already defined in sympy.functions.elementary.miscellaneous
440
+ """
441
+ Represents the cube root function.
442
+
443
+ Explanation
444
+ ===========
445
+
446
+ The reason why one would use ``Cbrt(x)`` over ``cbrt(x)``
447
+ is that the latter is internally represented as ``Pow(x, Rational(1, 3))`` which
448
+ may not be what one wants when doing code-generation.
449
+
450
+ Examples
451
+ ========
452
+
453
+ >>> from sympy.abc import x
454
+ >>> from sympy.codegen.cfunctions import Cbrt
455
+ >>> Cbrt(x)
456
+ Cbrt(x)
457
+ >>> Cbrt(x).diff(x)
458
+ 1/(3*x**(2/3))
459
+
460
+ See Also
461
+ ========
462
+
463
+ Sqrt
464
+ """
465
+ nargs = 1
466
+
467
+ def fdiff(self, argindex=1):
468
+ """
469
+ Returns the first derivative of this function.
470
+ """
471
+ if argindex == 1:
472
+ return Pow(self.args[0], Rational(-_Two/3))/3
473
+ else:
474
+ raise ArgumentIndexError(self, argindex)
475
+
476
+
477
+ def _eval_expand_func(self, **hints):
478
+ return _Cbrt(*self.args)
479
+
480
+ def _eval_rewrite_as_Pow(self, arg, **kwargs):
481
+ return _Cbrt(arg)
482
+
483
+ _eval_rewrite_as_tractable = _eval_rewrite_as_Pow
484
+
485
+
486
+ def _hypot(x, y):
487
+ return sqrt(Pow(x, 2) + Pow(y, 2))
488
+
489
+
490
+ class hypot(Function):
491
+ """
492
+ Represents the hypotenuse function.
493
+
494
+ Explanation
495
+ ===========
496
+
497
+ The hypotenuse function is provided by e.g. the math library
498
+ in the C99 standard, hence one may want to represent the function
499
+ symbolically when doing code-generation.
500
+
501
+ Examples
502
+ ========
503
+
504
+ >>> from sympy.abc import x, y
505
+ >>> from sympy.codegen.cfunctions import hypot
506
+ >>> hypot(3, 4).evalf() == 5.0
507
+ True
508
+ >>> hypot(x, y)
509
+ hypot(x, y)
510
+ >>> hypot(x, y).diff(x)
511
+ x/hypot(x, y)
512
+
513
+ """
514
+ nargs = 2
515
+
516
+ def fdiff(self, argindex=1):
517
+ """
518
+ Returns the first derivative of this function.
519
+ """
520
+ if argindex in (1, 2):
521
+ return 2*self.args[argindex-1]/(_Two*self.func(*self.args))
522
+ else:
523
+ raise ArgumentIndexError(self, argindex)
524
+
525
+
526
+ def _eval_expand_func(self, **hints):
527
+ return _hypot(*self.args)
528
+
529
+ def _eval_rewrite_as_Pow(self, arg, **kwargs):
530
+ return _hypot(arg)
531
+
532
+ _eval_rewrite_as_tractable = _eval_rewrite_as_Pow
533
+
534
+
535
+ class isnan(Function):
536
+ nargs = 1
lib/python3.1/site-packages/sympy/codegen/cnodes.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ AST nodes specific to the C family of languages
3
+ """
4
+
5
+ from sympy.codegen.ast import (
6
+ Attribute, Declaration, Node, String, Token, Type, none,
7
+ FunctionCall, CodeBlock
8
+ )
9
+ from sympy.core.basic import Basic
10
+ from sympy.core.containers import Tuple
11
+ from sympy.core.sympify import sympify
12
+
13
+ void = Type('void')
14
+
15
+ restrict = Attribute('restrict') # guarantees no pointer aliasing
16
+ volatile = Attribute('volatile')
17
+ static = Attribute('static')
18
+
19
+
20
+ def alignof(arg):
21
+ """ Generate of FunctionCall instance for calling 'alignof' """
22
+ return FunctionCall('alignof', [String(arg) if isinstance(arg, str) else arg])
23
+
24
+
25
+ def sizeof(arg):
26
+ """ Generate of FunctionCall instance for calling 'sizeof'
27
+
28
+ Examples
29
+ ========
30
+
31
+ >>> from sympy.codegen.ast import real
32
+ >>> from sympy.codegen.cnodes import sizeof
33
+ >>> from sympy import ccode
34
+ >>> ccode(sizeof(real))
35
+ 'sizeof(double)'
36
+ """
37
+ return FunctionCall('sizeof', [String(arg) if isinstance(arg, str) else arg])
38
+
39
+
40
+ class CommaOperator(Basic):
41
+ """ Represents the comma operator in C """
42
+ def __new__(cls, *args):
43
+ return Basic.__new__(cls, *[sympify(arg) for arg in args])
44
+
45
+
46
+ class Label(Node):
47
+ """ Label for use with e.g. goto statement.
48
+
49
+ Examples
50
+ ========
51
+
52
+ >>> from sympy import ccode, Symbol
53
+ >>> from sympy.codegen.cnodes import Label, PreIncrement
54
+ >>> print(ccode(Label('foo')))
55
+ foo:
56
+ >>> print(ccode(Label('bar', [PreIncrement(Symbol('a'))])))
57
+ bar:
58
+ ++(a);
59
+
60
+ """
61
+ __slots__ = _fields = ('name', 'body')
62
+ defaults = {'body': none}
63
+ _construct_name = String
64
+
65
+ @classmethod
66
+ def _construct_body(cls, itr):
67
+ if isinstance(itr, CodeBlock):
68
+ return itr
69
+ else:
70
+ return CodeBlock(*itr)
71
+
72
+
73
+ class goto(Token):
74
+ """ Represents goto in C """
75
+ __slots__ = _fields = ('label',)
76
+ _construct_label = Label
77
+
78
+
79
+ class PreDecrement(Basic):
80
+ """ Represents the pre-decrement operator
81
+
82
+ Examples
83
+ ========
84
+
85
+ >>> from sympy.abc import x
86
+ >>> from sympy.codegen.cnodes import PreDecrement
87
+ >>> from sympy import ccode
88
+ >>> ccode(PreDecrement(x))
89
+ '--(x)'
90
+
91
+ """
92
+ nargs = 1
93
+
94
+
95
+ class PostDecrement(Basic):
96
+ """ Represents the post-decrement operator
97
+
98
+ Examples
99
+ ========
100
+
101
+ >>> from sympy.abc import x
102
+ >>> from sympy.codegen.cnodes import PostDecrement
103
+ >>> from sympy import ccode
104
+ >>> ccode(PostDecrement(x))
105
+ '(x)--'
106
+
107
+ """
108
+ nargs = 1
109
+
110
+
111
+ class PreIncrement(Basic):
112
+ """ Represents the pre-increment operator
113
+
114
+ Examples
115
+ ========
116
+
117
+ >>> from sympy.abc import x
118
+ >>> from sympy.codegen.cnodes import PreIncrement
119
+ >>> from sympy import ccode
120
+ >>> ccode(PreIncrement(x))
121
+ '++(x)'
122
+
123
+ """
124
+ nargs = 1
125
+
126
+
127
+ class PostIncrement(Basic):
128
+ """ Represents the post-increment operator
129
+
130
+ Examples
131
+ ========
132
+
133
+ >>> from sympy.abc import x
134
+ >>> from sympy.codegen.cnodes import PostIncrement
135
+ >>> from sympy import ccode
136
+ >>> ccode(PostIncrement(x))
137
+ '(x)++'
138
+
139
+ """
140
+ nargs = 1
141
+
142
+
143
+ class struct(Node):
144
+ """ Represents a struct in C """
145
+ __slots__ = _fields = ('name', 'declarations')
146
+ defaults = {'name': none}
147
+ _construct_name = String
148
+
149
+ @classmethod
150
+ def _construct_declarations(cls, args):
151
+ return Tuple(*[Declaration(arg) for arg in args])
152
+
153
+
154
+ class union(struct):
155
+ """ Represents a union in C """
156
+ __slots__ = ()
lib/python3.1/site-packages/sympy/codegen/cutils.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from sympy.printing.c import C99CodePrinter
2
+
3
+ def render_as_source_file(content, Printer=C99CodePrinter, settings=None):
4
+ """ Renders a C source file (with required #include statements) """
5
+ printer = Printer(settings or {})
6
+ code_str = printer.doprint(content)
7
+ includes = '\n'.join(['#include <%s>' % h for h in printer.headers])
8
+ return includes + '\n\n' + code_str
lib/python3.1/site-packages/sympy/codegen/cxxnodes.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ AST nodes specific to C++.
3
+ """
4
+
5
+ from sympy.codegen.ast import Attribute, String, Token, Type, none
6
+
7
+ class using(Token):
8
+ """ Represents a 'using' statement in C++ """
9
+ __slots__ = _fields = ('type', 'alias')
10
+ defaults = {'alias': none}
11
+ _construct_type = Type
12
+ _construct_alias = String
13
+
14
+ constexpr = Attribute('constexpr')
lib/python3.1/site-packages/sympy/codegen/fnodes.py ADDED
@@ -0,0 +1,657 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ AST nodes specific to Fortran.
3
+
4
+ The functions defined in this module allows the user to express functions such as ``dsign``
5
+ as a SymPy function for symbolic manipulation.
6
+ """
7
+
8
+ from sympy.codegen.ast import (
9
+ Attribute, CodeBlock, FunctionCall, Node, none, String,
10
+ Token, _mk_Tuple, Variable
11
+ )
12
+ from sympy.core.basic import Basic
13
+ from sympy.core.containers import Tuple
14
+ from sympy.core.expr import Expr
15
+ from sympy.core.function import Function
16
+ from sympy.core.numbers import Float, Integer
17
+ from sympy.core.symbol import Str
18
+ from sympy.core.sympify import sympify
19
+ from sympy.logic import true, false
20
+ from sympy.utilities.iterables import iterable
21
+
22
+
23
+
24
+ pure = Attribute('pure')
25
+ elemental = Attribute('elemental') # (all elemental procedures are also pure)
26
+
27
+ intent_in = Attribute('intent_in')
28
+ intent_out = Attribute('intent_out')
29
+ intent_inout = Attribute('intent_inout')
30
+
31
+ allocatable = Attribute('allocatable')
32
+
33
+ class Program(Token):
34
+ """ Represents a 'program' block in Fortran.
35
+
36
+ Examples
37
+ ========
38
+
39
+ >>> from sympy.codegen.ast import Print
40
+ >>> from sympy.codegen.fnodes import Program
41
+ >>> prog = Program('myprogram', [Print([42])])
42
+ >>> from sympy import fcode
43
+ >>> print(fcode(prog, source_format='free'))
44
+ program myprogram
45
+ print *, 42
46
+ end program
47
+
48
+ """
49
+ __slots__ = _fields = ('name', 'body')
50
+ _construct_name = String
51
+ _construct_body = staticmethod(lambda body: CodeBlock(*body))
52
+
53
+
54
+ class use_rename(Token):
55
+ """ Represents a renaming in a use statement in Fortran.
56
+
57
+ Examples
58
+ ========
59
+
60
+ >>> from sympy.codegen.fnodes import use_rename, use
61
+ >>> from sympy import fcode
62
+ >>> ren = use_rename("thingy", "convolution2d")
63
+ >>> print(fcode(ren, source_format='free'))
64
+ thingy => convolution2d
65
+ >>> full = use('signallib', only=['snr', ren])
66
+ >>> print(fcode(full, source_format='free'))
67
+ use signallib, only: snr, thingy => convolution2d
68
+
69
+ """
70
+ __slots__ = _fields = ('local', 'original')
71
+ _construct_local = String
72
+ _construct_original = String
73
+
74
+ def _name(arg):
75
+ if hasattr(arg, 'name'):
76
+ return arg.name
77
+ else:
78
+ return String(arg)
79
+
80
+ class use(Token):
81
+ """ Represents a use statement in Fortran.
82
+
83
+ Examples
84
+ ========
85
+
86
+ >>> from sympy.codegen.fnodes import use
87
+ >>> from sympy import fcode
88
+ >>> fcode(use('signallib'), source_format='free')
89
+ 'use signallib'
90
+ >>> fcode(use('signallib', [('metric', 'snr')]), source_format='free')
91
+ 'use signallib, metric => snr'
92
+ >>> fcode(use('signallib', only=['snr', 'convolution2d']), source_format='free')
93
+ 'use signallib, only: snr, convolution2d'
94
+
95
+ """
96
+ __slots__ = _fields = ('namespace', 'rename', 'only')
97
+ defaults = {'rename': none, 'only': none}
98
+ _construct_namespace = staticmethod(_name)
99
+ _construct_rename = staticmethod(lambda args: Tuple(*[arg if isinstance(arg, use_rename) else use_rename(*arg) for arg in args]))
100
+ _construct_only = staticmethod(lambda args: Tuple(*[arg if isinstance(arg, use_rename) else _name(arg) for arg in args]))
101
+
102
+
103
+ class Module(Token):
104
+ """ Represents a module in Fortran.
105
+
106
+ Examples
107
+ ========
108
+
109
+ >>> from sympy.codegen.fnodes import Module
110
+ >>> from sympy import fcode
111
+ >>> print(fcode(Module('signallib', ['implicit none'], []), source_format='free'))
112
+ module signallib
113
+ implicit none
114
+ <BLANKLINE>
115
+ contains
116
+ <BLANKLINE>
117
+ <BLANKLINE>
118
+ end module
119
+
120
+ """
121
+ __slots__ = _fields = ('name', 'declarations', 'definitions')
122
+ defaults = {'declarations': Tuple()}
123
+ _construct_name = String
124
+
125
+ @classmethod
126
+ def _construct_declarations(cls, args):
127
+ args = [Str(arg) if isinstance(arg, str) else arg for arg in args]
128
+ return CodeBlock(*args)
129
+
130
+ _construct_definitions = staticmethod(lambda arg: CodeBlock(*arg))
131
+
132
+
133
+ class Subroutine(Node):
134
+ """ Represents a subroutine in Fortran.
135
+
136
+ Examples
137
+ ========
138
+
139
+ >>> from sympy import fcode, symbols
140
+ >>> from sympy.codegen.ast import Print
141
+ >>> from sympy.codegen.fnodes import Subroutine
142
+ >>> x, y = symbols('x y', real=True)
143
+ >>> sub = Subroutine('mysub', [x, y], [Print([x**2 + y**2, x*y])])
144
+ >>> print(fcode(sub, source_format='free', standard=2003))
145
+ subroutine mysub(x, y)
146
+ real*8 :: x
147
+ real*8 :: y
148
+ print *, x**2 + y**2, x*y
149
+ end subroutine
150
+
151
+ """
152
+ __slots__ = ('name', 'parameters', 'body')
153
+ _fields = __slots__ + Node._fields
154
+ _construct_name = String
155
+ _construct_parameters = staticmethod(lambda params: Tuple(*map(Variable.deduced, params)))
156
+
157
+ @classmethod
158
+ def _construct_body(cls, itr):
159
+ if isinstance(itr, CodeBlock):
160
+ return itr
161
+ else:
162
+ return CodeBlock(*itr)
163
+
164
+ class SubroutineCall(Token):
165
+ """ Represents a call to a subroutine in Fortran.
166
+
167
+ Examples
168
+ ========
169
+
170
+ >>> from sympy.codegen.fnodes import SubroutineCall
171
+ >>> from sympy import fcode
172
+ >>> fcode(SubroutineCall('mysub', 'x y'.split()))
173
+ ' call mysub(x, y)'
174
+
175
+ """
176
+ __slots__ = _fields = ('name', 'subroutine_args')
177
+ _construct_name = staticmethod(_name)
178
+ _construct_subroutine_args = staticmethod(_mk_Tuple)
179
+
180
+
181
+ class Do(Token):
182
+ """ Represents a Do loop in in Fortran.
183
+
184
+ Examples
185
+ ========
186
+
187
+ >>> from sympy import fcode, symbols
188
+ >>> from sympy.codegen.ast import aug_assign, Print
189
+ >>> from sympy.codegen.fnodes import Do
190
+ >>> i, n = symbols('i n', integer=True)
191
+ >>> r = symbols('r', real=True)
192
+ >>> body = [aug_assign(r, '+', 1/i), Print([i, r])]
193
+ >>> do1 = Do(body, i, 1, n)
194
+ >>> print(fcode(do1, source_format='free'))
195
+ do i = 1, n
196
+ r = r + 1d0/i
197
+ print *, i, r
198
+ end do
199
+ >>> do2 = Do(body, i, 1, n, 2)
200
+ >>> print(fcode(do2, source_format='free'))
201
+ do i = 1, n, 2
202
+ r = r + 1d0/i
203
+ print *, i, r
204
+ end do
205
+
206
+ """
207
+
208
+ __slots__ = _fields = ('body', 'counter', 'first', 'last', 'step', 'concurrent')
209
+ defaults = {'step': Integer(1), 'concurrent': false}
210
+ _construct_body = staticmethod(lambda body: CodeBlock(*body))
211
+ _construct_counter = staticmethod(sympify)
212
+ _construct_first = staticmethod(sympify)
213
+ _construct_last = staticmethod(sympify)
214
+ _construct_step = staticmethod(sympify)
215
+ _construct_concurrent = staticmethod(lambda arg: true if arg else false)
216
+
217
+
218
+ class ArrayConstructor(Token):
219
+ """ Represents an array constructor.
220
+
221
+ Examples
222
+ ========
223
+
224
+ >>> from sympy import fcode
225
+ >>> from sympy.codegen.fnodes import ArrayConstructor
226
+ >>> ac = ArrayConstructor([1, 2, 3])
227
+ >>> fcode(ac, standard=95, source_format='free')
228
+ '(/1, 2, 3/)'
229
+ >>> fcode(ac, standard=2003, source_format='free')
230
+ '[1, 2, 3]'
231
+
232
+ """
233
+ __slots__ = _fields = ('elements',)
234
+ _construct_elements = staticmethod(_mk_Tuple)
235
+
236
+
237
+ class ImpliedDoLoop(Token):
238
+ """ Represents an implied do loop in Fortran.
239
+
240
+ Examples
241
+ ========
242
+
243
+ >>> from sympy import Symbol, fcode
244
+ >>> from sympy.codegen.fnodes import ImpliedDoLoop, ArrayConstructor
245
+ >>> i = Symbol('i', integer=True)
246
+ >>> idl = ImpliedDoLoop(i**3, i, -3, 3, 2) # -27, -1, 1, 27
247
+ >>> ac = ArrayConstructor([-28, idl, 28]) # -28, -27, -1, 1, 27, 28
248
+ >>> fcode(ac, standard=2003, source_format='free')
249
+ '[-28, (i**3, i = -3, 3, 2), 28]'
250
+
251
+ """
252
+ __slots__ = _fields = ('expr', 'counter', 'first', 'last', 'step')
253
+ defaults = {'step': Integer(1)}
254
+ _construct_expr = staticmethod(sympify)
255
+ _construct_counter = staticmethod(sympify)
256
+ _construct_first = staticmethod(sympify)
257
+ _construct_last = staticmethod(sympify)
258
+ _construct_step = staticmethod(sympify)
259
+
260
+
261
+ class Extent(Basic):
262
+ """ Represents a dimension extent.
263
+
264
+ Examples
265
+ ========
266
+
267
+ >>> from sympy.codegen.fnodes import Extent
268
+ >>> e = Extent(-3, 3) # -3, -2, -1, 0, 1, 2, 3
269
+ >>> from sympy import fcode
270
+ >>> fcode(e, source_format='free')
271
+ '-3:3'
272
+ >>> from sympy.codegen.ast import Variable, real
273
+ >>> from sympy.codegen.fnodes import dimension, intent_out
274
+ >>> dim = dimension(e, e)
275
+ >>> arr = Variable('x', real, attrs=[dim, intent_out])
276
+ >>> fcode(arr.as_Declaration(), source_format='free', standard=2003)
277
+ 'real*8, dimension(-3:3, -3:3), intent(out) :: x'
278
+
279
+ """
280
+ def __new__(cls, *args):
281
+ if len(args) == 2:
282
+ low, high = args
283
+ return Basic.__new__(cls, sympify(low), sympify(high))
284
+ elif len(args) == 0 or (len(args) == 1 and args[0] in (':', None)):
285
+ return Basic.__new__(cls) # assumed shape
286
+ else:
287
+ raise ValueError("Expected 0 or 2 args (or one argument == None or ':')")
288
+
289
+ def _sympystr(self, printer):
290
+ if len(self.args) == 0:
291
+ return ':'
292
+ return ":".join(str(arg) for arg in self.args)
293
+
294
+ assumed_extent = Extent() # or Extent(':'), Extent(None)
295
+
296
+
297
+ def dimension(*args):
298
+ """ Creates a 'dimension' Attribute with (up to 7) extents.
299
+
300
+ Examples
301
+ ========
302
+
303
+ >>> from sympy import fcode
304
+ >>> from sympy.codegen.fnodes import dimension, intent_in
305
+ >>> dim = dimension('2', ':') # 2 rows, runtime determined number of columns
306
+ >>> from sympy.codegen.ast import Variable, integer
307
+ >>> arr = Variable('a', integer, attrs=[dim, intent_in])
308
+ >>> fcode(arr.as_Declaration(), source_format='free', standard=2003)
309
+ 'integer*4, dimension(2, :), intent(in) :: a'
310
+
311
+ """
312
+ if len(args) > 7:
313
+ raise ValueError("Fortran only supports up to 7 dimensional arrays")
314
+ parameters = []
315
+ for arg in args:
316
+ if isinstance(arg, Extent):
317
+ parameters.append(arg)
318
+ elif isinstance(arg, str):
319
+ if arg == ':':
320
+ parameters.append(Extent())
321
+ else:
322
+ parameters.append(String(arg))
323
+ elif iterable(arg):
324
+ parameters.append(Extent(*arg))
325
+ else:
326
+ parameters.append(sympify(arg))
327
+ if len(args) == 0:
328
+ raise ValueError("Need at least one dimension")
329
+ return Attribute('dimension', parameters)
330
+
331
+
332
+ assumed_size = dimension('*')
333
+
334
+ def array(symbol, dim, intent=None, *, attrs=(), value=None, type=None):
335
+ """ Convenience function for creating a Variable instance for a Fortran array.
336
+
337
+ Parameters
338
+ ==========
339
+
340
+ symbol : symbol
341
+ dim : Attribute or iterable
342
+ If dim is an ``Attribute`` it need to have the name 'dimension'. If it is
343
+ not an ``Attribute``, then it is passed to :func:`dimension` as ``*dim``
344
+ intent : str
345
+ One of: 'in', 'out', 'inout' or None
346
+ \\*\\*kwargs:
347
+ Keyword arguments for ``Variable`` ('type' & 'value')
348
+
349
+ Examples
350
+ ========
351
+
352
+ >>> from sympy import fcode
353
+ >>> from sympy.codegen.ast import integer, real
354
+ >>> from sympy.codegen.fnodes import array
355
+ >>> arr = array('a', '*', 'in', type=integer)
356
+ >>> print(fcode(arr.as_Declaration(), source_format='free', standard=2003))
357
+ integer*4, dimension(*), intent(in) :: a
358
+ >>> x = array('x', [3, ':', ':'], intent='out', type=real)
359
+ >>> print(fcode(x.as_Declaration(value=1), source_format='free', standard=2003))
360
+ real*8, dimension(3, :, :), intent(out) :: x = 1
361
+
362
+ """
363
+ if isinstance(dim, Attribute):
364
+ if str(dim.name) != 'dimension':
365
+ raise ValueError("Got an unexpected Attribute argument as dim: %s" % str(dim))
366
+ else:
367
+ dim = dimension(*dim)
368
+
369
+ attrs = list(attrs) + [dim]
370
+ if intent is not None:
371
+ if intent not in (intent_in, intent_out, intent_inout):
372
+ intent = {'in': intent_in, 'out': intent_out, 'inout': intent_inout}[intent]
373
+ attrs.append(intent)
374
+ if type is None:
375
+ return Variable.deduced(symbol, value=value, attrs=attrs)
376
+ else:
377
+ return Variable(symbol, type, value=value, attrs=attrs)
378
+
379
+ def _printable(arg):
380
+ return String(arg) if isinstance(arg, str) else sympify(arg)
381
+
382
+
383
+ def allocated(array):
384
+ """ Creates an AST node for a function call to Fortran's "allocated(...)"
385
+
386
+ Examples
387
+ ========
388
+
389
+ >>> from sympy import fcode
390
+ >>> from sympy.codegen.fnodes import allocated
391
+ >>> alloc = allocated('x')
392
+ >>> fcode(alloc, source_format='free')
393
+ 'allocated(x)'
394
+
395
+ """
396
+ return FunctionCall('allocated', [_printable(array)])
397
+
398
+
399
+ def lbound(array, dim=None, kind=None):
400
+ """ Creates an AST node for a function call to Fortran's "lbound(...)"
401
+
402
+ Parameters
403
+ ==========
404
+
405
+ array : Symbol or String
406
+ dim : expr
407
+ kind : expr
408
+
409
+ Examples
410
+ ========
411
+
412
+ >>> from sympy import fcode
413
+ >>> from sympy.codegen.fnodes import lbound
414
+ >>> lb = lbound('arr', dim=2)
415
+ >>> fcode(lb, source_format='free')
416
+ 'lbound(arr, 2)'
417
+
418
+ """
419
+ return FunctionCall(
420
+ 'lbound',
421
+ [_printable(array)] +
422
+ ([_printable(dim)] if dim else []) +
423
+ ([_printable(kind)] if kind else [])
424
+ )
425
+
426
+
427
+ def ubound(array, dim=None, kind=None):
428
+ return FunctionCall(
429
+ 'ubound',
430
+ [_printable(array)] +
431
+ ([_printable(dim)] if dim else []) +
432
+ ([_printable(kind)] if kind else [])
433
+ )
434
+
435
+
436
+ def shape(source, kind=None):
437
+ """ Creates an AST node for a function call to Fortran's "shape(...)"
438
+
439
+ Parameters
440
+ ==========
441
+
442
+ source : Symbol or String
443
+ kind : expr
444
+
445
+ Examples
446
+ ========
447
+
448
+ >>> from sympy import fcode
449
+ >>> from sympy.codegen.fnodes import shape
450
+ >>> shp = shape('x')
451
+ >>> fcode(shp, source_format='free')
452
+ 'shape(x)'
453
+
454
+ """
455
+ return FunctionCall(
456
+ 'shape',
457
+ [_printable(source)] +
458
+ ([_printable(kind)] if kind else [])
459
+ )
460
+
461
+
462
+ def size(array, dim=None, kind=None):
463
+ """ Creates an AST node for a function call to Fortran's "size(...)"
464
+
465
+ Examples
466
+ ========
467
+
468
+ >>> from sympy import fcode, Symbol
469
+ >>> from sympy.codegen.ast import FunctionDefinition, real, Return
470
+ >>> from sympy.codegen.fnodes import array, sum_, size
471
+ >>> a = Symbol('a', real=True)
472
+ >>> body = [Return((sum_(a**2)/size(a))**.5)]
473
+ >>> arr = array(a, dim=[':'], intent='in')
474
+ >>> fd = FunctionDefinition(real, 'rms', [arr], body)
475
+ >>> print(fcode(fd, source_format='free', standard=2003))
476
+ real*8 function rms(a)
477
+ real*8, dimension(:), intent(in) :: a
478
+ rms = sqrt(sum(a**2)*1d0/size(a))
479
+ end function
480
+
481
+ """
482
+ return FunctionCall(
483
+ 'size',
484
+ [_printable(array)] +
485
+ ([_printable(dim)] if dim else []) +
486
+ ([_printable(kind)] if kind else [])
487
+ )
488
+
489
+
490
+ def reshape(source, shape, pad=None, order=None):
491
+ """ Creates an AST node for a function call to Fortran's "reshape(...)"
492
+
493
+ Parameters
494
+ ==========
495
+
496
+ source : Symbol or String
497
+ shape : ArrayExpr
498
+
499
+ """
500
+ return FunctionCall(
501
+ 'reshape',
502
+ [_printable(source), _printable(shape)] +
503
+ ([_printable(pad)] if pad else []) +
504
+ ([_printable(order)] if pad else [])
505
+ )
506
+
507
+
508
+ def bind_C(name=None):
509
+ """ Creates an Attribute ``bind_C`` with a name.
510
+
511
+ Parameters
512
+ ==========
513
+
514
+ name : str
515
+
516
+ Examples
517
+ ========
518
+
519
+ >>> from sympy import fcode, Symbol
520
+ >>> from sympy.codegen.ast import FunctionDefinition, real, Return
521
+ >>> from sympy.codegen.fnodes import array, sum_, bind_C
522
+ >>> a = Symbol('a', real=True)
523
+ >>> s = Symbol('s', integer=True)
524
+ >>> arr = array(a, dim=[s], intent='in')
525
+ >>> body = [Return((sum_(a**2)/s)**.5)]
526
+ >>> fd = FunctionDefinition(real, 'rms', [arr, s], body, attrs=[bind_C('rms')])
527
+ >>> print(fcode(fd, source_format='free', standard=2003))
528
+ real*8 function rms(a, s) bind(C, name="rms")
529
+ real*8, dimension(s), intent(in) :: a
530
+ integer*4 :: s
531
+ rms = sqrt(sum(a**2)/s)
532
+ end function
533
+
534
+ """
535
+ return Attribute('bind_C', [String(name)] if name else [])
536
+
537
+ class GoTo(Token):
538
+ """ Represents a goto statement in Fortran
539
+
540
+ Examples
541
+ ========
542
+
543
+ >>> from sympy.codegen.fnodes import GoTo
544
+ >>> go = GoTo([10, 20, 30], 'i')
545
+ >>> from sympy import fcode
546
+ >>> fcode(go, source_format='free')
547
+ 'go to (10, 20, 30), i'
548
+
549
+ """
550
+ __slots__ = _fields = ('labels', 'expr')
551
+ defaults = {'expr': none}
552
+ _construct_labels = staticmethod(_mk_Tuple)
553
+ _construct_expr = staticmethod(sympify)
554
+
555
+
556
+ class FortranReturn(Token):
557
+ """ AST node explicitly mapped to a fortran "return".
558
+
559
+ Explanation
560
+ ===========
561
+
562
+ Because a return statement in fortran is different from C, and
563
+ in order to aid reuse of our codegen ASTs the ordinary
564
+ ``.codegen.ast.Return`` is interpreted as assignment to
565
+ the result variable of the function. If one for some reason needs
566
+ to generate a fortran RETURN statement, this node should be used.
567
+
568
+ Examples
569
+ ========
570
+
571
+ >>> from sympy.codegen.fnodes import FortranReturn
572
+ >>> from sympy import fcode
573
+ >>> fcode(FortranReturn('x'))
574
+ ' return x'
575
+
576
+ """
577
+ __slots__ = _fields = ('return_value',)
578
+ defaults = {'return_value': none}
579
+ _construct_return_value = staticmethod(sympify)
580
+
581
+
582
+ class FFunction(Function):
583
+ _required_standard = 77
584
+
585
+ def _fcode(self, printer):
586
+ name = self.__class__.__name__
587
+ if printer._settings['standard'] < self._required_standard:
588
+ raise NotImplementedError("%s requires Fortran %d or newer" %
589
+ (name, self._required_standard))
590
+ return '{}({})'.format(name, ', '.join(map(printer._print, self.args)))
591
+
592
+
593
+ class F95Function(FFunction):
594
+ _required_standard = 95
595
+
596
+
597
+ class isign(FFunction):
598
+ """ Fortran sign intrinsic for integer arguments. """
599
+ nargs = 2
600
+
601
+
602
+ class dsign(FFunction):
603
+ """ Fortran sign intrinsic for double precision arguments. """
604
+ nargs = 2
605
+
606
+
607
+ class cmplx(FFunction):
608
+ """ Fortran complex conversion function. """
609
+ nargs = 2 # may be extended to (2, 3) at a later point
610
+
611
+
612
+ class kind(FFunction):
613
+ """ Fortran kind function. """
614
+ nargs = 1
615
+
616
+
617
+ class merge(F95Function):
618
+ """ Fortran merge function """
619
+ nargs = 3
620
+
621
+
622
+ class _literal(Float):
623
+ _token = None # type: str
624
+ _decimals = None # type: int
625
+
626
+ def _fcode(self, printer, *args, **kwargs):
627
+ mantissa, sgnd_ex = ('%.{}e'.format(self._decimals) % self).split('e')
628
+ mantissa = mantissa.strip('0').rstrip('.')
629
+ ex_sgn, ex_num = sgnd_ex[0], sgnd_ex[1:].lstrip('0')
630
+ ex_sgn = '' if ex_sgn == '+' else ex_sgn
631
+ return (mantissa or '0') + self._token + ex_sgn + (ex_num or '0')
632
+
633
+
634
+ class literal_sp(_literal):
635
+ """ Fortran single precision real literal """
636
+ _token = 'e'
637
+ _decimals = 9
638
+
639
+
640
+ class literal_dp(_literal):
641
+ """ Fortran double precision real literal """
642
+ _token = 'd'
643
+ _decimals = 17
644
+
645
+
646
+ class sum_(Token, Expr):
647
+ __slots__ = _fields = ('array', 'dim', 'mask')
648
+ defaults = {'dim': none, 'mask': none}
649
+ _construct_array = staticmethod(sympify)
650
+ _construct_dim = staticmethod(sympify)
651
+
652
+
653
+ class product_(Token, Expr):
654
+ __slots__ = _fields = ('array', 'dim', 'mask')
655
+ defaults = {'dim': none, 'mask': none}
656
+ _construct_array = staticmethod(sympify)
657
+ _construct_dim = staticmethod(sympify)
lib/python3.1/site-packages/sympy/codegen/futils.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import chain
2
+ from sympy.codegen.fnodes import Module
3
+ from sympy.core.symbol import Dummy
4
+ from sympy.printing.fortran import FCodePrinter
5
+
6
+ """ This module collects utilities for rendering Fortran code. """
7
+
8
+
9
+ def render_as_module(definitions, name, declarations=(), printer_settings=None):
10
+ """ Creates a ``Module`` instance and renders it as a string.
11
+
12
+ This generates Fortran source code for a module with the correct ``use`` statements.
13
+
14
+ Parameters
15
+ ==========
16
+
17
+ definitions : iterable
18
+ Passed to :class:`sympy.codegen.fnodes.Module`.
19
+ name : str
20
+ Passed to :class:`sympy.codegen.fnodes.Module`.
21
+ declarations : iterable
22
+ Passed to :class:`sympy.codegen.fnodes.Module`. It will be extended with
23
+ use statements, 'implicit none' and public list generated from ``definitions``.
24
+ printer_settings : dict
25
+ Passed to ``FCodePrinter`` (default: ``{'standard': 2003, 'source_format': 'free'}``).
26
+
27
+ """
28
+ printer_settings = printer_settings or {'standard': 2003, 'source_format': 'free'}
29
+ printer = FCodePrinter(printer_settings)
30
+ dummy = Dummy()
31
+ if isinstance(definitions, Module):
32
+ raise ValueError("This function expects to construct a module on its own.")
33
+ mod = Module(name, chain(declarations, [dummy]), definitions)
34
+ fstr = printer.doprint(mod)
35
+ module_use_str = ' %s\n' % ' \n'.join(['use %s, only: %s' % (k, ', '.join(v)) for
36
+ k, v in printer.module_uses.items()])
37
+ module_use_str += ' implicit none\n'
38
+ module_use_str += ' private\n'
39
+ module_use_str += ' public %s\n' % ', '.join([str(node.name) for node in definitions if getattr(node, 'name', None)])
40
+ return fstr.replace(printer.doprint(dummy), module_use_str)
lib/python3.1/site-packages/sympy/codegen/matrix_nodes.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Additional AST nodes for operations on matrices. The nodes in this module
3
+ are meant to represent optimization of matrix expressions within codegen's
4
+ target languages that cannot be represented by SymPy expressions.
5
+
6
+ As an example, we can use :meth:`sympy.codegen.rewriting.optimize` and the
7
+ ``matin_opt`` optimization provided in :mod:`sympy.codegen.rewriting` to
8
+ transform matrix multiplication under certain assumptions:
9
+
10
+ >>> from sympy import symbols, MatrixSymbol
11
+ >>> n = symbols('n', integer=True)
12
+ >>> A = MatrixSymbol('A', n, n)
13
+ >>> x = MatrixSymbol('x', n, 1)
14
+ >>> expr = A**(-1) * x
15
+ >>> from sympy import assuming, Q
16
+ >>> from sympy.codegen.rewriting import matinv_opt, optimize
17
+ >>> with assuming(Q.fullrank(A)):
18
+ ... optimize(expr, [matinv_opt])
19
+ MatrixSolve(A, vector=x)
20
+ """
21
+
22
+ from .ast import Token
23
+ from sympy.matrices import MatrixExpr
24
+ from sympy.core.sympify import sympify
25
+
26
+
27
+ class MatrixSolve(Token, MatrixExpr):
28
+ """Represents an operation to solve a linear matrix equation.
29
+
30
+ Parameters
31
+ ==========
32
+
33
+ matrix : MatrixSymbol
34
+
35
+ Matrix representing the coefficients of variables in the linear
36
+ equation. This matrix must be square and full-rank (i.e. all columns must
37
+ be linearly independent) for the solving operation to be valid.
38
+
39
+ vector : MatrixSymbol
40
+
41
+ One-column matrix representing the solutions to the equations
42
+ represented in ``matrix``.
43
+
44
+ Examples
45
+ ========
46
+
47
+ >>> from sympy import symbols, MatrixSymbol
48
+ >>> from sympy.codegen.matrix_nodes import MatrixSolve
49
+ >>> n = symbols('n', integer=True)
50
+ >>> A = MatrixSymbol('A', n, n)
51
+ >>> x = MatrixSymbol('x', n, 1)
52
+ >>> from sympy.printing.numpy import NumPyPrinter
53
+ >>> NumPyPrinter().doprint(MatrixSolve(A, x))
54
+ 'numpy.linalg.solve(A, x)'
55
+ >>> from sympy import octave_code
56
+ >>> octave_code(MatrixSolve(A, x))
57
+ 'A \\\\ x'
58
+
59
+ """
60
+ __slots__ = _fields = ('matrix', 'vector')
61
+
62
+ _construct_matrix = staticmethod(sympify)
63
+ _construct_vector = staticmethod(sympify)
64
+
65
+ @property
66
+ def shape(self):
67
+ return self.vector.shape
68
+
69
+ def _eval_derivative(self, x):
70
+ A, b = self.matrix, self.vector
71
+ return MatrixSolve(A, b.diff(x) - A.diff(x) * MatrixSolve(A, b))
lib/python3.1/site-packages/sympy/codegen/numpy_nodes.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.function import Add, ArgumentIndexError, Function
2
+ from sympy.core.power import Pow
3
+ from sympy.core.singleton import S
4
+ from sympy.core.sorting import default_sort_key
5
+ from sympy.functions.elementary.exponential import exp, log
6
+
7
+
8
+ def _logaddexp(x1, x2, *, evaluate=True):
9
+ return log(Add(exp(x1, evaluate=evaluate), exp(x2, evaluate=evaluate), evaluate=evaluate))
10
+
11
+
12
+ _two = S.One*2
13
+ _ln2 = log(_two)
14
+
15
+
16
+ def _lb(x, *, evaluate=True):
17
+ return log(x, evaluate=evaluate)/_ln2
18
+
19
+
20
+ def _exp2(x, *, evaluate=True):
21
+ return Pow(_two, x, evaluate=evaluate)
22
+
23
+
24
+ def _logaddexp2(x1, x2, *, evaluate=True):
25
+ return _lb(Add(_exp2(x1, evaluate=evaluate),
26
+ _exp2(x2, evaluate=evaluate), evaluate=evaluate))
27
+
28
+
29
+ class logaddexp(Function):
30
+ """ Logarithm of the sum of exponentiations of the inputs.
31
+
32
+ Helper class for use with e.g. numpy.logaddexp
33
+
34
+ See Also
35
+ ========
36
+
37
+ https://numpy.org/doc/stable/reference/generated/numpy.logaddexp.html
38
+ """
39
+ nargs = 2
40
+
41
+ def __new__(cls, *args):
42
+ return Function.__new__(cls, *sorted(args, key=default_sort_key))
43
+
44
+ def fdiff(self, argindex=1):
45
+ """
46
+ Returns the first derivative of this function.
47
+ """
48
+ if argindex == 1:
49
+ wrt, other = self.args
50
+ elif argindex == 2:
51
+ other, wrt = self.args
52
+ else:
53
+ raise ArgumentIndexError(self, argindex)
54
+ return S.One/(S.One + exp(other-wrt))
55
+
56
+ def _eval_rewrite_as_log(self, x1, x2, **kwargs):
57
+ return _logaddexp(x1, x2)
58
+
59
+ def _eval_evalf(self, *args, **kwargs):
60
+ return self.rewrite(log).evalf(*args, **kwargs)
61
+
62
+ def _eval_simplify(self, *args, **kwargs):
63
+ a, b = (x.simplify(**kwargs) for x in self.args)
64
+ candidate = _logaddexp(a, b)
65
+ if candidate != _logaddexp(a, b, evaluate=False):
66
+ return candidate
67
+ else:
68
+ return logaddexp(a, b)
69
+
70
+
71
+ class logaddexp2(Function):
72
+ """ Logarithm of the sum of exponentiations of the inputs in base-2.
73
+
74
+ Helper class for use with e.g. numpy.logaddexp2
75
+
76
+ See Also
77
+ ========
78
+
79
+ https://numpy.org/doc/stable/reference/generated/numpy.logaddexp2.html
80
+ """
81
+ nargs = 2
82
+
83
+ def __new__(cls, *args):
84
+ return Function.__new__(cls, *sorted(args, key=default_sort_key))
85
+
86
+ def fdiff(self, argindex=1):
87
+ """
88
+ Returns the first derivative of this function.
89
+ """
90
+ if argindex == 1:
91
+ wrt, other = self.args
92
+ elif argindex == 2:
93
+ other, wrt = self.args
94
+ else:
95
+ raise ArgumentIndexError(self, argindex)
96
+ return S.One/(S.One + _exp2(other-wrt))
97
+
98
+ def _eval_rewrite_as_log(self, x1, x2, **kwargs):
99
+ return _logaddexp2(x1, x2)
100
+
101
+ def _eval_evalf(self, *args, **kwargs):
102
+ return self.rewrite(log).evalf(*args, **kwargs)
103
+
104
+ def _eval_simplify(self, *args, **kwargs):
105
+ a, b = (x.simplify(**kwargs).factor() for x in self.args)
106
+ candidate = _logaddexp2(a, b)
107
+ if candidate != _logaddexp2(a, b, evaluate=False):
108
+ return candidate
109
+ else:
110
+ return logaddexp2(a, b)
lib/python3.1/site-packages/sympy/codegen/pynodes.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .abstract_nodes import List as AbstractList
2
+ from .ast import Token
3
+
4
+
5
+ class List(AbstractList):
6
+ pass
7
+
8
+
9
+ class NumExprEvaluate(Token):
10
+ """represents a call to :class:`numexpr`s :func:`evaluate`"""
11
+ __slots__ = _fields = ('expr',)
lib/python3.1/site-packages/sympy/codegen/pyutils.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.printing.pycode import PythonCodePrinter
2
+
3
+ """ This module collects utilities for rendering Python code. """
4
+
5
+
6
+ def render_as_module(content, standard='python3'):
7
+ """Renders Python code as a module (with the required imports).
8
+
9
+ Parameters
10
+ ==========
11
+
12
+ standard :
13
+ See the parameter ``standard`` in
14
+ :meth:`sympy.printing.pycode.pycode`
15
+ """
16
+
17
+ printer = PythonCodePrinter({'standard':standard})
18
+ pystr = printer.doprint(content)
19
+ if printer._settings['fully_qualified_modules']:
20
+ module_imports_str = '\n'.join('import %s' % k for k in printer.module_imports)
21
+ else:
22
+ module_imports_str = '\n'.join(['from %s import %s' % (k, ', '.join(v)) for
23
+ k, v in printer.module_imports.items()])
24
+ return module_imports_str + '\n\n' + pystr
lib/python3.1/site-packages/sympy/codegen/rewriting.py ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Classes and functions useful for rewriting expressions for optimized code
3
+ generation. Some languages (or standards thereof), e.g. C99, offer specialized
4
+ math functions for better performance and/or precision.
5
+
6
+ Using the ``optimize`` function in this module, together with a collection of
7
+ rules (represented as instances of ``Optimization``), one can rewrite the
8
+ expressions for this purpose::
9
+
10
+ >>> from sympy import Symbol, exp, log
11
+ >>> from sympy.codegen.rewriting import optimize, optims_c99
12
+ >>> x = Symbol('x')
13
+ >>> optimize(3*exp(2*x) - 3, optims_c99)
14
+ 3*expm1(2*x)
15
+ >>> optimize(exp(2*x) - 1 - exp(-33), optims_c99)
16
+ expm1(2*x) - exp(-33)
17
+ >>> optimize(log(3*x + 3), optims_c99)
18
+ log1p(x) + log(3)
19
+ >>> optimize(log(2*x + 3), optims_c99)
20
+ log(2*x + 3)
21
+
22
+ The ``optims_c99`` imported above is tuple containing the following instances
23
+ (which may be imported from ``sympy.codegen.rewriting``):
24
+
25
+ - ``expm1_opt``
26
+ - ``log1p_opt``
27
+ - ``exp2_opt``
28
+ - ``log2_opt``
29
+ - ``log2const_opt``
30
+
31
+
32
+ """
33
+ from sympy.core.function import expand_log
34
+ from sympy.core.singleton import S
35
+ from sympy.core.symbol import Wild
36
+ from sympy.functions.elementary.complexes import sign
37
+ from sympy.functions.elementary.exponential import (exp, log)
38
+ from sympy.functions.elementary.miscellaneous import (Max, Min)
39
+ from sympy.functions.elementary.trigonometric import (cos, sin, sinc)
40
+ from sympy.assumptions import Q, ask
41
+ from sympy.codegen.cfunctions import log1p, log2, exp2, expm1
42
+ from sympy.codegen.matrix_nodes import MatrixSolve
43
+ from sympy.core.expr import UnevaluatedExpr
44
+ from sympy.core.power import Pow
45
+ from sympy.codegen.numpy_nodes import logaddexp, logaddexp2
46
+ from sympy.codegen.scipy_nodes import cosm1, powm1
47
+ from sympy.core.mul import Mul
48
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
49
+ from sympy.utilities.iterables import sift
50
+
51
+
52
+ class Optimization:
53
+ """ Abstract base class for rewriting optimization.
54
+
55
+ Subclasses should implement ``__call__`` taking an expression
56
+ as argument.
57
+
58
+ Parameters
59
+ ==========
60
+ cost_function : callable returning number
61
+ priority : number
62
+
63
+ """
64
+ def __init__(self, cost_function=None, priority=1):
65
+ self.cost_function = cost_function
66
+ self.priority=priority
67
+
68
+ def cheapest(self, *args):
69
+ return min(args, key=self.cost_function)
70
+
71
+
72
+ class ReplaceOptim(Optimization):
73
+ """ Rewriting optimization calling replace on expressions.
74
+
75
+ Explanation
76
+ ===========
77
+
78
+ The instance can be used as a function on expressions for which
79
+ it will apply the ``replace`` method (see
80
+ :meth:`sympy.core.basic.Basic.replace`).
81
+
82
+ Parameters
83
+ ==========
84
+
85
+ query :
86
+ First argument passed to replace.
87
+ value :
88
+ Second argument passed to replace.
89
+
90
+ Examples
91
+ ========
92
+
93
+ >>> from sympy import Symbol
94
+ >>> from sympy.codegen.rewriting import ReplaceOptim
95
+ >>> from sympy.codegen.cfunctions import exp2
96
+ >>> x = Symbol('x')
97
+ >>> exp2_opt = ReplaceOptim(lambda p: p.is_Pow and p.base == 2,
98
+ ... lambda p: exp2(p.exp))
99
+ >>> exp2_opt(2**x)
100
+ exp2(x)
101
+
102
+ """
103
+
104
+ def __init__(self, query, value, **kwargs):
105
+ super().__init__(**kwargs)
106
+ self.query = query
107
+ self.value = value
108
+
109
+ def __call__(self, expr):
110
+ return expr.replace(self.query, self.value)
111
+
112
+
113
+ def optimize(expr, optimizations):
114
+ """ Apply optimizations to an expression.
115
+
116
+ Parameters
117
+ ==========
118
+
119
+ expr : expression
120
+ optimizations : iterable of ``Optimization`` instances
121
+ The optimizations will be sorted with respect to ``priority`` (highest first).
122
+
123
+ Examples
124
+ ========
125
+
126
+ >>> from sympy import log, Symbol
127
+ >>> from sympy.codegen.rewriting import optims_c99, optimize
128
+ >>> x = Symbol('x')
129
+ >>> optimize(log(x+3)/log(2) + log(x**2 + 1), optims_c99)
130
+ log1p(x**2) + log2(x + 3)
131
+
132
+ """
133
+
134
+ for optim in sorted(optimizations, key=lambda opt: opt.priority, reverse=True):
135
+ new_expr = optim(expr)
136
+ if optim.cost_function is None:
137
+ expr = new_expr
138
+ else:
139
+ expr = optim.cheapest(expr, new_expr)
140
+ return expr
141
+
142
+
143
+ exp2_opt = ReplaceOptim(
144
+ lambda p: p.is_Pow and p.base == 2,
145
+ lambda p: exp2(p.exp)
146
+ )
147
+
148
+
149
+ _d = Wild('d', properties=[lambda x: x.is_Dummy])
150
+ _u = Wild('u', properties=[lambda x: not x.is_number and not x.is_Add])
151
+ _v = Wild('v')
152
+ _w = Wild('w')
153
+ _n = Wild('n', properties=[lambda x: x.is_number])
154
+
155
+ sinc_opt1 = ReplaceOptim(
156
+ sin(_w)/_w, sinc(_w)
157
+ )
158
+ sinc_opt2 = ReplaceOptim(
159
+ sin(_n*_w)/_w, _n*sinc(_n*_w)
160
+ )
161
+ sinc_opts = (sinc_opt1, sinc_opt2)
162
+
163
+ log2_opt = ReplaceOptim(_v*log(_w)/log(2), _v*log2(_w), cost_function=lambda expr: expr.count(
164
+ lambda e: ( # division & eval of transcendentals are expensive floating point operations...
165
+ e.is_Pow and e.exp.is_negative # division
166
+ or (isinstance(e, (log, log2)) and not e.args[0].is_number)) # transcendental
167
+ )
168
+ )
169
+
170
+ log2const_opt = ReplaceOptim(log(2)*log2(_w), log(_w))
171
+
172
+ logsumexp_2terms_opt = ReplaceOptim(
173
+ lambda l: (isinstance(l, log)
174
+ and l.args[0].is_Add
175
+ and len(l.args[0].args) == 2
176
+ and all(isinstance(t, exp) for t in l.args[0].args)),
177
+ lambda l: (
178
+ Max(*[e.args[0] for e in l.args[0].args]) +
179
+ log1p(exp(Min(*[e.args[0] for e in l.args[0].args])))
180
+ )
181
+ )
182
+
183
+
184
+ class FuncMinusOneOptim(ReplaceOptim):
185
+ """Specialization of ReplaceOptim for functions evaluating "f(x) - 1".
186
+
187
+ Explanation
188
+ ===========
189
+
190
+ Numerical functions which go toward one as x go toward zero is often best
191
+ implemented by a dedicated function in order to avoid catastrophic
192
+ cancellation. One such example is ``expm1(x)`` in the C standard library
193
+ which evaluates ``exp(x) - 1``. Such functions preserves many more
194
+ significant digits when its argument is much smaller than one, compared
195
+ to subtracting one afterwards.
196
+
197
+ Parameters
198
+ ==========
199
+
200
+ func :
201
+ The function which is subtracted by one.
202
+ func_m_1 :
203
+ The specialized function evaluating ``func(x) - 1``.
204
+ opportunistic : bool
205
+ When ``True``, apply the transformation as long as the magnitude of the
206
+ remaining number terms decreases. When ``False``, only apply the
207
+ transformation if it completely eliminates the number term.
208
+
209
+ Examples
210
+ ========
211
+
212
+ >>> from sympy import symbols, exp
213
+ >>> from sympy.codegen.rewriting import FuncMinusOneOptim
214
+ >>> from sympy.codegen.cfunctions import expm1
215
+ >>> x, y = symbols('x y')
216
+ >>> expm1_opt = FuncMinusOneOptim(exp, expm1)
217
+ >>> expm1_opt(exp(x) + 2*exp(5*y) - 3)
218
+ expm1(x) + 2*expm1(5*y)
219
+
220
+
221
+ """
222
+
223
+ def __init__(self, func, func_m_1, opportunistic=True):
224
+ weight = 10 # <-- this is an arbitrary number (heuristic)
225
+ super().__init__(lambda e: e.is_Add, self.replace_in_Add,
226
+ cost_function=lambda expr: expr.count_ops() - weight*expr.count(func_m_1))
227
+ self.func = func
228
+ self.func_m_1 = func_m_1
229
+ self.opportunistic = opportunistic
230
+
231
+ def _group_Add_terms(self, add):
232
+ numbers, non_num = sift(add.args, lambda arg: arg.is_number, binary=True)
233
+ numsum = sum(numbers)
234
+ terms_with_func, other = sift(non_num, lambda arg: arg.has(self.func), binary=True)
235
+ return numsum, terms_with_func, other
236
+
237
+ def replace_in_Add(self, e):
238
+ """ passed as second argument to Basic.replace(...) """
239
+ numsum, terms_with_func, other_non_num_terms = self._group_Add_terms(e)
240
+ if numsum == 0:
241
+ return e
242
+ substituted, untouched = [], []
243
+ for with_func in terms_with_func:
244
+ if with_func.is_Mul:
245
+ func, coeff = sift(with_func.args, lambda arg: arg.func == self.func, binary=True)
246
+ if len(func) == 1 and len(coeff) == 1:
247
+ func, coeff = func[0], coeff[0]
248
+ else:
249
+ coeff = None
250
+ elif with_func.func == self.func:
251
+ func, coeff = with_func, S.One
252
+ else:
253
+ coeff = None
254
+
255
+ if coeff is not None and coeff.is_number and sign(coeff) == -sign(numsum):
256
+ if self.opportunistic:
257
+ do_substitute = abs(coeff+numsum) < abs(numsum)
258
+ else:
259
+ do_substitute = coeff+numsum == 0
260
+
261
+ if do_substitute: # advantageous substitution
262
+ numsum += coeff
263
+ substituted.append(coeff*self.func_m_1(*func.args))
264
+ continue
265
+ untouched.append(with_func)
266
+
267
+ return e.func(numsum, *substituted, *untouched, *other_non_num_terms)
268
+
269
+ def __call__(self, expr):
270
+ alt1 = super().__call__(expr)
271
+ alt2 = super().__call__(expr.factor())
272
+ return self.cheapest(alt1, alt2)
273
+
274
+
275
+ expm1_opt = FuncMinusOneOptim(exp, expm1)
276
+ cosm1_opt = FuncMinusOneOptim(cos, cosm1)
277
+ powm1_opt = FuncMinusOneOptim(Pow, powm1)
278
+
279
+ log1p_opt = ReplaceOptim(
280
+ lambda e: isinstance(e, log),
281
+ lambda l: expand_log(l.replace(
282
+ log, lambda arg: log(arg.factor())
283
+ )).replace(log(_u+1), log1p(_u))
284
+ )
285
+
286
+ def create_expand_pow_optimization(limit, *, base_req=lambda b: b.is_symbol):
287
+ """ Creates an instance of :class:`ReplaceOptim` for expanding ``Pow``.
288
+
289
+ Explanation
290
+ ===========
291
+
292
+ The requirements for expansions are that the base needs to be a symbol
293
+ and the exponent needs to be an Integer (and be less than or equal to
294
+ ``limit``).
295
+
296
+ Parameters
297
+ ==========
298
+
299
+ limit : int
300
+ The highest power which is expanded into multiplication.
301
+ base_req : function returning bool
302
+ Requirement on base for expansion to happen, default is to return
303
+ the ``is_symbol`` attribute of the base.
304
+
305
+ Examples
306
+ ========
307
+
308
+ >>> from sympy import Symbol, sin
309
+ >>> from sympy.codegen.rewriting import create_expand_pow_optimization
310
+ >>> x = Symbol('x')
311
+ >>> expand_opt = create_expand_pow_optimization(3)
312
+ >>> expand_opt(x**5 + x**3)
313
+ x**5 + x*x*x
314
+ >>> expand_opt(x**5 + x**3 + sin(x)**3)
315
+ x**5 + sin(x)**3 + x*x*x
316
+ >>> opt2 = create_expand_pow_optimization(3, base_req=lambda b: not b.is_Function)
317
+ >>> opt2((x+1)**2 + sin(x)**2)
318
+ sin(x)**2 + (x + 1)*(x + 1)
319
+
320
+ """
321
+ return ReplaceOptim(
322
+ lambda e: e.is_Pow and base_req(e.base) and e.exp.is_Integer and abs(e.exp) <= limit,
323
+ lambda p: (
324
+ UnevaluatedExpr(Mul(*([p.base]*+p.exp), evaluate=False)) if p.exp > 0 else
325
+ 1/UnevaluatedExpr(Mul(*([p.base]*-p.exp), evaluate=False))
326
+ ))
327
+
328
+ # Optimization procedures for turning A**(-1) * x into MatrixSolve(A, x)
329
+ def _matinv_predicate(expr):
330
+ # TODO: We should be able to support more than 2 elements
331
+ if expr.is_MatMul and len(expr.args) == 2:
332
+ left, right = expr.args
333
+ if left.is_Inverse and right.shape[1] == 1:
334
+ inv_arg = left.arg
335
+ if isinstance(inv_arg, MatrixSymbol):
336
+ return bool(ask(Q.fullrank(left.arg)))
337
+
338
+ return False
339
+
340
+ def _matinv_transform(expr):
341
+ left, right = expr.args
342
+ inv_arg = left.arg
343
+ return MatrixSolve(inv_arg, right)
344
+
345
+
346
+ matinv_opt = ReplaceOptim(_matinv_predicate, _matinv_transform)
347
+
348
+
349
+ logaddexp_opt = ReplaceOptim(log(exp(_v)+exp(_w)), logaddexp(_v, _w))
350
+ logaddexp2_opt = ReplaceOptim(log(Pow(2, _v)+Pow(2, _w)), logaddexp2(_v, _w)*log(2))
351
+
352
+ # Collections of optimizations:
353
+ optims_c99 = (expm1_opt, log1p_opt, exp2_opt, log2_opt, log2const_opt)
354
+
355
+ optims_numpy = optims_c99 + (logaddexp_opt, logaddexp2_opt,) + sinc_opts
356
+
357
+ optims_scipy = (cosm1_opt, powm1_opt)
lib/python3.1/site-packages/sympy/codegen/scipy_nodes.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.function import Add, ArgumentIndexError, Function
2
+ from sympy.core.power import Pow
3
+ from sympy.core.singleton import S
4
+ from sympy.functions.elementary.exponential import log
5
+ from sympy.functions.elementary.trigonometric import cos, sin
6
+
7
+
8
+ def _cosm1(x, *, evaluate=True):
9
+ return Add(cos(x, evaluate=evaluate), -S.One, evaluate=evaluate)
10
+
11
+
12
+ class cosm1(Function):
13
+ """ Minus one plus cosine of x, i.e. cos(x) - 1. For use when x is close to zero.
14
+
15
+ Helper class for use with e.g. scipy.special.cosm1
16
+ See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.cosm1.html
17
+ """
18
+ nargs = 1
19
+
20
+ def fdiff(self, argindex=1):
21
+ """
22
+ Returns the first derivative of this function.
23
+ """
24
+ if argindex == 1:
25
+ return -sin(*self.args)
26
+ else:
27
+ raise ArgumentIndexError(self, argindex)
28
+
29
+ def _eval_rewrite_as_cos(self, x, **kwargs):
30
+ return _cosm1(x)
31
+
32
+ def _eval_evalf(self, *args, **kwargs):
33
+ return self.rewrite(cos).evalf(*args, **kwargs)
34
+
35
+ def _eval_simplify(self, **kwargs):
36
+ x, = self.args
37
+ candidate = _cosm1(x.simplify(**kwargs))
38
+ if candidate != _cosm1(x, evaluate=False):
39
+ return candidate
40
+ else:
41
+ return cosm1(x)
42
+
43
+
44
+ def _powm1(x, y, *, evaluate=True):
45
+ return Add(Pow(x, y, evaluate=evaluate), -S.One, evaluate=evaluate)
46
+
47
+
48
+ class powm1(Function):
49
+ """ Minus one plus x to the power of y, i.e. x**y - 1. For use when x is close to one or y is close to zero.
50
+
51
+ Helper class for use with e.g. scipy.special.powm1
52
+ See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.powm1.html
53
+ """
54
+ nargs = 2
55
+
56
+ def fdiff(self, argindex=1):
57
+ """
58
+ Returns the first derivative of this function.
59
+ """
60
+ if argindex == 1:
61
+ return Pow(self.args[0], self.args[1])*self.args[1]/self.args[0]
62
+ elif argindex == 2:
63
+ return log(self.args[0])*Pow(*self.args)
64
+ else:
65
+ raise ArgumentIndexError(self, argindex)
66
+
67
+ def _eval_rewrite_as_Pow(self, x, y, **kwargs):
68
+ return _powm1(x, y)
69
+
70
+ def _eval_evalf(self, *args, **kwargs):
71
+ return self.rewrite(Pow).evalf(*args, **kwargs)
72
+
73
+ def _eval_simplify(self, **kwargs):
74
+ x, y = self.args
75
+ candidate = _powm1(x.simplify(**kwargs), y.simplify(**kwargs))
76
+ if candidate != _powm1(x, y, evaluate=False):
77
+ return candidate
78
+ else:
79
+ return powm1(x, y)
lib/python3.1/site-packages/sympy/codegen/tests/__init__.py ADDED
File without changes
lib/python3.1/site-packages/sympy/codegen/tests/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (216 Bytes). View file