Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.1/site-packages/sympy/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/__pycache__/abc.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/__pycache__/conftest.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/__pycache__/galgebra.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/__pycache__/release.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/__pycache__/this.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/algebras/__init__.py +3 -0
- lib/python3.1/site-packages/sympy/algebras/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/algebras/__pycache__/quaternion.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/algebras/quaternion.py +1667 -0
- lib/python3.1/site-packages/sympy/algebras/tests/__init__.py +0 -0
- lib/python3.1/site-packages/sympy/algebras/tests/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/algebras/tests/__pycache__/test_quaternion.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/algebras/tests/test_quaternion.py +428 -0
- lib/python3.1/site-packages/sympy/codegen/__init__.py +24 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/abstract_nodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/algorithms.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/approximations.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/ast.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/cfunctions.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/cnodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/cutils.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/cxxnodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/fnodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/futils.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/matrix_nodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/numpy_nodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/pynodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/pyutils.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/rewriting.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/__pycache__/scipy_nodes.cpython-310.pyc +0 -0
- lib/python3.1/site-packages/sympy/codegen/abstract_nodes.py +18 -0
- lib/python3.1/site-packages/sympy/codegen/algorithms.py +180 -0
- lib/python3.1/site-packages/sympy/codegen/approximations.py +187 -0
- lib/python3.1/site-packages/sympy/codegen/ast.py +1906 -0
- lib/python3.1/site-packages/sympy/codegen/cfunctions.py +536 -0
- lib/python3.1/site-packages/sympy/codegen/cnodes.py +156 -0
- lib/python3.1/site-packages/sympy/codegen/cutils.py +8 -0
- lib/python3.1/site-packages/sympy/codegen/cxxnodes.py +14 -0
- lib/python3.1/site-packages/sympy/codegen/fnodes.py +657 -0
- lib/python3.1/site-packages/sympy/codegen/futils.py +40 -0
- lib/python3.1/site-packages/sympy/codegen/matrix_nodes.py +71 -0
- lib/python3.1/site-packages/sympy/codegen/numpy_nodes.py +110 -0
- lib/python3.1/site-packages/sympy/codegen/pynodes.py +11 -0
- lib/python3.1/site-packages/sympy/codegen/pyutils.py +24 -0
- lib/python3.1/site-packages/sympy/codegen/rewriting.py +357 -0
- lib/python3.1/site-packages/sympy/codegen/scipy_nodes.py +79 -0
- lib/python3.1/site-packages/sympy/codegen/tests/__init__.py +0 -0
- 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
|
|
|