Upload folder using huggingface_hub (part 20)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/INSTALLER +1 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/METADATA +319 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/RECORD +0 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/WHEEL +5 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/entry_points.txt +2 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/licenses/AUTHORS +1379 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/licenses/LICENSE +153 -0
- venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/top_level.txt +2 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_dimensionsystem.py +95 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_prefixes.py +86 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_quantities.py +575 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_unit_system_cgs_gauss.py +55 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_unitsystem.py +86 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_util.py +178 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/unitsystem.py +204 -0
- venv/lib/python3.11/site-packages/sympy/physics/units/util.py +265 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/__init__.py +36 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/dyadic.py +545 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/fieldfunctions.py +313 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/frame.py +1575 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/functions.py +650 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/point.py +635 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/printing.py +371 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/__init__.py +0 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_dyadic.py +123 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_fieldfunctions.py +133 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_frame.py +761 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_functions.py +509 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_output.py +75 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_point.py +382 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_printing.py +353 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_vector.py +274 -0
- venv/lib/python3.11/site-packages/sympy/physics/vector/vector.py +806 -0
- venv/lib/python3.11/site-packages/sympy/physics/wigner.py +1213 -0
- venv/lib/python3.11/site-packages/sympy/plotting/__init__.py +22 -0
- venv/lib/python3.11/site-packages/sympy/plotting/backends/__init__.py +0 -0
- venv/lib/python3.11/site-packages/sympy/plotting/backends/base_backend.py +419 -0
- venv/lib/python3.11/site-packages/sympy/plotting/backends/matplotlibbackend/__init__.py +5 -0
- venv/lib/python3.11/site-packages/sympy/plotting/backends/matplotlibbackend/matplotlib.py +318 -0
- venv/lib/python3.11/site-packages/sympy/plotting/backends/textbackend/__init__.py +3 -0
- venv/lib/python3.11/site-packages/sympy/plotting/backends/textbackend/text.py +24 -0
- venv/lib/python3.11/site-packages/sympy/plotting/experimental_lambdify.py +641 -0
- venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/__init__.py +12 -0
- venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py +413 -0
- venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/interval_membership.py +78 -0
- venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/lib_interval.py +452 -0
- venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/__init__.py +0 -0
- venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/test_interval_functions.py +415 -0
- venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/test_interval_membership.py +150 -0
.gitattributes
CHANGED
|
@@ -435,3 +435,4 @@ venv/lib/python3.11/site-packages/scipy.libs/libquadmath-828275a7.so.0.0.0 filte
|
|
| 435 |
venv/lib/python3.11/site-packages/scipy.libs/libquadmath-96973f99-934c22de.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 436 |
venv/lib/python3.11/site-packages/scipy.libs/libscipy_openblas-6cdc3b4a.so filter=lfs diff=lfs merge=lfs -text
|
| 437 |
venv/lib/python3.11/site-packages/sentencepiece/_sentencepiece.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 435 |
venv/lib/python3.11/site-packages/scipy.libs/libquadmath-96973f99-934c22de.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 436 |
venv/lib/python3.11/site-packages/scipy.libs/libscipy_openblas-6cdc3b4a.so filter=lfs diff=lfs merge=lfs -text
|
| 437 |
venv/lib/python3.11/site-packages/sentencepiece/_sentencepiece.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 438 |
+
venv/lib/python3.11/site-packages/tokenizers/tokenizers.abi3.so filter=lfs diff=lfs merge=lfs -text
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: sympy
|
| 3 |
+
Version: 1.14.0
|
| 4 |
+
Summary: Computer algebra system (CAS) in Python
|
| 5 |
+
Home-page: https://sympy.org
|
| 6 |
+
Author: SymPy development team
|
| 7 |
+
Author-email: sympy@googlegroups.com
|
| 8 |
+
License: BSD
|
| 9 |
+
Project-URL: Source, https://github.com/sympy/sympy
|
| 10 |
+
Keywords: Math CAS
|
| 11 |
+
Classifier: License :: OSI Approved :: BSD License
|
| 12 |
+
Classifier: Operating System :: OS Independent
|
| 13 |
+
Classifier: Programming Language :: Python
|
| 14 |
+
Classifier: Topic :: Scientific/Engineering
|
| 15 |
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
| 16 |
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
| 17 |
+
Classifier: Programming Language :: Python :: 3
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 23 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 24 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 25 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 26 |
+
Requires-Python: >=3.9
|
| 27 |
+
Description-Content-Type: text/markdown
|
| 28 |
+
License-File: LICENSE
|
| 29 |
+
License-File: AUTHORS
|
| 30 |
+
Requires-Dist: mpmath<1.4,>=1.1.0
|
| 31 |
+
Provides-Extra: dev
|
| 32 |
+
Requires-Dist: pytest>=7.1.0; extra == "dev"
|
| 33 |
+
Requires-Dist: hypothesis>=6.70.0; extra == "dev"
|
| 34 |
+
Dynamic: author
|
| 35 |
+
Dynamic: author-email
|
| 36 |
+
Dynamic: classifier
|
| 37 |
+
Dynamic: description
|
| 38 |
+
Dynamic: description-content-type
|
| 39 |
+
Dynamic: home-page
|
| 40 |
+
Dynamic: keywords
|
| 41 |
+
Dynamic: license
|
| 42 |
+
Dynamic: license-file
|
| 43 |
+
Dynamic: project-url
|
| 44 |
+
Dynamic: provides-extra
|
| 45 |
+
Dynamic: requires-dist
|
| 46 |
+
Dynamic: requires-python
|
| 47 |
+
Dynamic: summary
|
| 48 |
+
|
| 49 |
+
# SymPy
|
| 50 |
+
|
| 51 |
+
[](https://pypi.python.org/pypi/sympy)
|
| 52 |
+
[](https://gitter.im/sympy/sympy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
| 53 |
+
[](https://zenodo.org/badge/latestdoi/18918/sympy/sympy)
|
| 54 |
+
[](https://pepy.tech/project/sympy)
|
| 55 |
+
[](https://github.com/sympy/sympy/issues)
|
| 56 |
+
[](https://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project)
|
| 57 |
+
[](https://numfocus.org)
|
| 58 |
+
[](https://github.com/sympy/sympy/releases)
|
| 59 |
+
|
| 60 |
+
[](https://sympy.org/)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
See the [AUTHORS](AUTHORS) file for the list of authors.
|
| 64 |
+
|
| 65 |
+
And many more people helped on the SymPy mailing list, reported bugs,
|
| 66 |
+
helped organize SymPy's participation in the Google Summer of Code, the
|
| 67 |
+
Google Highly Open Participation Contest, Google Code-In, wrote and
|
| 68 |
+
blogged about SymPy...
|
| 69 |
+
|
| 70 |
+
License: New BSD License (see the [LICENSE](LICENSE) file for details) covers all
|
| 71 |
+
files in the sympy repository unless stated otherwise.
|
| 72 |
+
|
| 73 |
+
Our mailing list is at
|
| 74 |
+
<https://groups.google.com/forum/?fromgroups#!forum/sympy>.
|
| 75 |
+
|
| 76 |
+
We have a community chat at [Gitter](https://gitter.im/sympy/sympy). Feel
|
| 77 |
+
free to ask us anything there. We have a very welcoming and helpful
|
| 78 |
+
community.
|
| 79 |
+
|
| 80 |
+
## Download
|
| 81 |
+
|
| 82 |
+
The recommended installation method is through Anaconda,
|
| 83 |
+
<https://www.anaconda.com/products/distribution>
|
| 84 |
+
|
| 85 |
+
You can also get the latest version of SymPy from
|
| 86 |
+
<https://pypi.python.org/pypi/sympy/>
|
| 87 |
+
|
| 88 |
+
To get the git version do
|
| 89 |
+
|
| 90 |
+
$ git clone https://github.com/sympy/sympy.git
|
| 91 |
+
|
| 92 |
+
For other options (tarballs, debs, etc.), see
|
| 93 |
+
<https://docs.sympy.org/dev/install.html>.
|
| 94 |
+
|
| 95 |
+
## Documentation and Usage
|
| 96 |
+
|
| 97 |
+
For in-depth instructions on installation and building the
|
| 98 |
+
documentation, see the [SymPy Documentation Style Guide](https://docs.sympy.org/dev/documentation-style-guide.html).
|
| 99 |
+
|
| 100 |
+
Everything is at:
|
| 101 |
+
|
| 102 |
+
<https://docs.sympy.org/>
|
| 103 |
+
|
| 104 |
+
You can generate everything at the above site in your local copy of
|
| 105 |
+
SymPy by:
|
| 106 |
+
|
| 107 |
+
$ cd doc
|
| 108 |
+
$ make html
|
| 109 |
+
|
| 110 |
+
Then the docs will be in <span class="title-ref">\_build/html</span>. If
|
| 111 |
+
you don't want to read that, here is a short usage:
|
| 112 |
+
|
| 113 |
+
From this directory, start Python and:
|
| 114 |
+
|
| 115 |
+
``` python
|
| 116 |
+
>>> from sympy import Symbol, cos
|
| 117 |
+
>>> x = Symbol('x')
|
| 118 |
+
>>> e = 1/cos(x)
|
| 119 |
+
>>> print(e.series(x, 0, 10))
|
| 120 |
+
1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + 277*x**8/8064 + O(x**10)
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
SymPy also comes with a console that is a simple wrapper around the
|
| 124 |
+
classic python console (or IPython when available) that loads the SymPy
|
| 125 |
+
namespace and executes some common commands for you.
|
| 126 |
+
|
| 127 |
+
To start it, issue:
|
| 128 |
+
|
| 129 |
+
$ bin/isympy
|
| 130 |
+
|
| 131 |
+
from this directory, if SymPy is not installed or simply:
|
| 132 |
+
|
| 133 |
+
$ isympy
|
| 134 |
+
|
| 135 |
+
if SymPy is installed.
|
| 136 |
+
|
| 137 |
+
## Installation
|
| 138 |
+
|
| 139 |
+
To install SymPy using PyPI, run the following command:
|
| 140 |
+
|
| 141 |
+
$ pip install sympy
|
| 142 |
+
|
| 143 |
+
To install SymPy using Anaconda, run the following command:
|
| 144 |
+
|
| 145 |
+
$ conda install -c anaconda sympy
|
| 146 |
+
|
| 147 |
+
To install SymPy from GitHub source, first clone SymPy using `git`:
|
| 148 |
+
|
| 149 |
+
$ git clone https://github.com/sympy/sympy.git
|
| 150 |
+
|
| 151 |
+
Then, in the `sympy` repository that you cloned, simply run:
|
| 152 |
+
|
| 153 |
+
$ pip install .
|
| 154 |
+
|
| 155 |
+
See <https://docs.sympy.org/dev/install.html> for more information.
|
| 156 |
+
|
| 157 |
+
## Contributing
|
| 158 |
+
|
| 159 |
+
We welcome contributions from anyone, even if you are new to open
|
| 160 |
+
source. Please read our [Introduction to Contributing](https://docs.sympy.org/dev/contributing/introduction-to-contributing.html)
|
| 161 |
+
page and the [SymPy Documentation Style Guide](https://docs.sympy.org/dev/documentation-style-guide.html). If you
|
| 162 |
+
are new and looking for some way to contribute, a good place to start is
|
| 163 |
+
to look at the issues tagged [Easy to Fix](https://github.com/sympy/sympy/issues?q=is%3Aopen+is%3Aissue+label%3A%22Easy+to+Fix%22).
|
| 164 |
+
|
| 165 |
+
Please note that all participants in this project are expected to follow
|
| 166 |
+
our Code of Conduct. By participating in this project you agree to abide
|
| 167 |
+
by its terms. See [CODE\_OF\_CONDUCT.md](CODE_OF_CONDUCT.md).
|
| 168 |
+
|
| 169 |
+
## Tests
|
| 170 |
+
|
| 171 |
+
To execute all tests, run:
|
| 172 |
+
|
| 173 |
+
$./setup.py test
|
| 174 |
+
|
| 175 |
+
in the current directory.
|
| 176 |
+
|
| 177 |
+
For the more fine-grained running of tests or doctests, use `bin/test`
|
| 178 |
+
or respectively `bin/doctest`. The master branch is automatically tested
|
| 179 |
+
by GitHub Actions.
|
| 180 |
+
|
| 181 |
+
To test pull requests, use
|
| 182 |
+
[sympy-bot](https://github.com/sympy/sympy-bot).
|
| 183 |
+
|
| 184 |
+
## Regenerate Experimental <span class="title-ref">LaTeX</span> Parser/Lexer
|
| 185 |
+
|
| 186 |
+
The parser and lexer were generated with the [ANTLR4](http://antlr4.org)
|
| 187 |
+
toolchain in `sympy/parsing/latex/_antlr` and checked into the repo.
|
| 188 |
+
Presently, most users should not need to regenerate these files, but
|
| 189 |
+
if you plan to work on this feature, you will need the `antlr4`
|
| 190 |
+
command-line tool (and you must ensure that it is in your `PATH`).
|
| 191 |
+
One way to get it is:
|
| 192 |
+
|
| 193 |
+
$ conda install -c conda-forge antlr=4.11.1
|
| 194 |
+
|
| 195 |
+
Alternatively, follow the instructions on the ANTLR website and download
|
| 196 |
+
the `antlr-4.11.1-complete.jar`. Then export the `CLASSPATH` as instructed
|
| 197 |
+
and instead of creating `antlr4` as an alias, make it an executable file
|
| 198 |
+
with the following contents:
|
| 199 |
+
``` bash
|
| 200 |
+
#!/bin/bash
|
| 201 |
+
java -jar /usr/local/lib/antlr-4.11.1-complete.jar "$@"
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
After making changes to `sympy/parsing/latex/LaTeX.g4`, run:
|
| 205 |
+
|
| 206 |
+
$ ./setup.py antlr
|
| 207 |
+
|
| 208 |
+
## Clean
|
| 209 |
+
|
| 210 |
+
To clean everything (thus getting the same tree as in the repository):
|
| 211 |
+
|
| 212 |
+
$ git clean -Xdf
|
| 213 |
+
|
| 214 |
+
which will clear everything ignored by `.gitignore`, and:
|
| 215 |
+
|
| 216 |
+
$ git clean -df
|
| 217 |
+
|
| 218 |
+
to clear all untracked files. You can revert the most recent changes in
|
| 219 |
+
git with:
|
| 220 |
+
|
| 221 |
+
$ git reset --hard
|
| 222 |
+
|
| 223 |
+
WARNING: The above commands will all clear changes you may have made,
|
| 224 |
+
and you will lose them forever. Be sure to check things with `git
|
| 225 |
+
status`, `git diff`, `git clean -Xn`, and `git clean -n` before doing any
|
| 226 |
+
of those.
|
| 227 |
+
|
| 228 |
+
## Bugs
|
| 229 |
+
|
| 230 |
+
Our issue tracker is at <https://github.com/sympy/sympy/issues>. Please
|
| 231 |
+
report any bugs that you find. Or, even better, fork the repository on
|
| 232 |
+
GitHub and create a pull request. We welcome all changes, big or small,
|
| 233 |
+
and we will help you make the pull request if you are new to git (just
|
| 234 |
+
ask on our mailing list or Gitter Channel). If you further have any queries, you can find answers
|
| 235 |
+
on Stack Overflow using the [sympy](https://stackoverflow.com/questions/tagged/sympy) tag.
|
| 236 |
+
|
| 237 |
+
## Brief History
|
| 238 |
+
|
| 239 |
+
SymPy was started by Ondřej Čertík in 2005, he wrote some code during
|
| 240 |
+
the summer, then he wrote some more code during summer 2006. In February
|
| 241 |
+
2007, Fabian Pedregosa joined the project and helped fix many things,
|
| 242 |
+
contributed documentation, and made it alive again. 5 students (Mateusz
|
| 243 |
+
Paprocki, Brian Jorgensen, Jason Gedge, Robert Schwarz, and Chris Wu)
|
| 244 |
+
improved SymPy incredibly during summer 2007 as part of the Google
|
| 245 |
+
Summer of Code. Pearu Peterson joined the development during the summer
|
| 246 |
+
2007 and he has made SymPy much more competitive by rewriting the core
|
| 247 |
+
from scratch, which has made it from 10x to 100x faster. Jurjen N.E. Bos
|
| 248 |
+
has contributed pretty-printing and other patches. Fredrik Johansson has
|
| 249 |
+
written mpmath and contributed a lot of patches.
|
| 250 |
+
|
| 251 |
+
SymPy has participated in every Google Summer of Code since 2007. You
|
| 252 |
+
can see <https://github.com/sympy/sympy/wiki#google-summer-of-code> for
|
| 253 |
+
full details. Each year has improved SymPy by bounds. Most of SymPy's
|
| 254 |
+
development has come from Google Summer of Code students.
|
| 255 |
+
|
| 256 |
+
In 2011, Ondřej Čertík stepped down as lead developer, with Aaron
|
| 257 |
+
Meurer, who also started as a Google Summer of Code student, taking his
|
| 258 |
+
place. Ondřej Čertík is still active in the community but is too busy
|
| 259 |
+
with work and family to play a lead development role.
|
| 260 |
+
|
| 261 |
+
Since then, a lot more people have joined the development and some
|
| 262 |
+
people have also left. You can see the full list in doc/src/aboutus.rst,
|
| 263 |
+
or online at:
|
| 264 |
+
|
| 265 |
+
<https://docs.sympy.org/dev/aboutus.html#sympy-development-team>
|
| 266 |
+
|
| 267 |
+
The git history goes back to 2007 when development moved from svn to hg.
|
| 268 |
+
To see the history before that point, look at
|
| 269 |
+
<https://github.com/sympy/sympy-old>.
|
| 270 |
+
|
| 271 |
+
You can use git to see the biggest developers. The command:
|
| 272 |
+
|
| 273 |
+
$ git shortlog -ns
|
| 274 |
+
|
| 275 |
+
will show each developer, sorted by commits to the project. The command:
|
| 276 |
+
|
| 277 |
+
$ git shortlog -ns --since="1 year"
|
| 278 |
+
|
| 279 |
+
will show the top developers from the last year.
|
| 280 |
+
|
| 281 |
+
## Citation
|
| 282 |
+
|
| 283 |
+
To cite SymPy in publications use
|
| 284 |
+
|
| 285 |
+
> Meurer A, Smith CP, Paprocki M, Čertík O, Kirpichev SB, Rocklin M,
|
| 286 |
+
> Kumar A, Ivanov S, Moore JK, Singh S, Rathnayake T, Vig S, Granger BE,
|
| 287 |
+
> Muller RP, Bonazzi F, Gupta H, Vats S, Johansson F, Pedregosa F, Curry
|
| 288 |
+
> MJ, Terrel AR, Roučka Š, Saboo A, Fernando I, Kulal S, Cimrman R,
|
| 289 |
+
> Scopatz A. (2017) SymPy: symbolic computing in Python. *PeerJ Computer
|
| 290 |
+
> Science* 3:e103 <https://doi.org/10.7717/peerj-cs.103>
|
| 291 |
+
|
| 292 |
+
A BibTeX entry for LaTeX users is
|
| 293 |
+
|
| 294 |
+
``` bibtex
|
| 295 |
+
@article{10.7717/peerj-cs.103,
|
| 296 |
+
title = {SymPy: symbolic computing in Python},
|
| 297 |
+
author = {Meurer, Aaron and Smith, Christopher P. and Paprocki, Mateusz and \v{C}ert\'{i}k, Ond\v{r}ej and Kirpichev, Sergey B. and Rocklin, Matthew and Kumar, Amit and Ivanov, Sergiu and Moore, Jason K. and Singh, Sartaj and Rathnayake, Thilina and Vig, Sean and Granger, Brian E. and Muller, Richard P. and Bonazzi, Francesco and Gupta, Harsh and Vats, Shivam and Johansson, Fredrik and Pedregosa, Fabian and Curry, Matthew J. and Terrel, Andy R. and Rou\v{c}ka, \v{S}t\v{e}p\'{a}n and Saboo, Ashutosh and Fernando, Isuru and Kulal, Sumith and Cimrman, Robert and Scopatz, Anthony},
|
| 298 |
+
year = 2017,
|
| 299 |
+
month = Jan,
|
| 300 |
+
keywords = {Python, Computer algebra system, Symbolics},
|
| 301 |
+
abstract = {
|
| 302 |
+
SymPy is an open-source computer algebra system written in pure Python. It is built with a focus on extensibility and ease of use, through both interactive and programmatic applications. These characteristics have led SymPy to become a popular symbolic library for the scientific Python ecosystem. This paper presents the architecture of SymPy, a description of its features, and a discussion of select submodules. The supplementary material provides additional examples and further outlines details of the architecture and features of SymPy.
|
| 303 |
+
},
|
| 304 |
+
volume = 3,
|
| 305 |
+
pages = {e103},
|
| 306 |
+
journal = {PeerJ Computer Science},
|
| 307 |
+
issn = {2376-5992},
|
| 308 |
+
url = {https://doi.org/10.7717/peerj-cs.103},
|
| 309 |
+
doi = {10.7717/peerj-cs.103}
|
| 310 |
+
}
|
| 311 |
+
```
|
| 312 |
+
|
| 313 |
+
SymPy is BSD licensed, so you are free to use it whatever you like, be
|
| 314 |
+
it academic, commercial, creating forks or derivatives, as long as you
|
| 315 |
+
copy the BSD statement if you redistribute it (see the LICENSE file for
|
| 316 |
+
details). That said, although not required by the SymPy license, if it
|
| 317 |
+
is convenient for you, please cite SymPy when using it in your work and
|
| 318 |
+
also consider contributing all your changes back, so that we can
|
| 319 |
+
incorporate it and all of us will benefit in the end.
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/RECORD
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (79.0.1)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
isympy = isympy:main
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/licenses/AUTHORS
ADDED
|
@@ -0,0 +1,1379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
All people who contributed to SymPy by sending at least a patch or
|
| 2 |
+
more (in the order of the date of their first contribution), except
|
| 3 |
+
those who explicitly didn't want to be mentioned. People with a * next
|
| 4 |
+
to their names are not found in the metadata of the git history. This
|
| 5 |
+
file is generated automatically by running `./bin/authors_update.py`.
|
| 6 |
+
|
| 7 |
+
There are a total of 1371 authors.
|
| 8 |
+
|
| 9 |
+
Ondřej Čertík <ondrej@certik.cz>
|
| 10 |
+
Fabian Pedregosa <fabian@fseoane.net>
|
| 11 |
+
Jurjen N.E. Bos <jnebos@gmail.com>
|
| 12 |
+
Mateusz Paprocki <mattpap@gmail.com>
|
| 13 |
+
*Marc-Etienne M.Leveille <protonyc@gmail.com>
|
| 14 |
+
Brian Jorgensen <brian.jorgensen@gmail.com>
|
| 15 |
+
Jason Gedge <inferno1386@gmail.com>
|
| 16 |
+
Robert Schwarz <lethargo@googlemail.com>
|
| 17 |
+
Pearu Peterson <pearu.peterson@gmail.com>
|
| 18 |
+
Fredrik Johansson <fredrik.johansson@gmail.com>
|
| 19 |
+
Chris Wu <chris.wu@gmail.com>
|
| 20 |
+
*Ulrich Hecht <ulrich.hecht@gmail.com>
|
| 21 |
+
Goutham Lakshminarayan <dl.goutham@gmail.com>
|
| 22 |
+
David Lawrence <dmlawrence@gmail.com>
|
| 23 |
+
Jaroslaw Tworek <dev.jrx@gmail.com>
|
| 24 |
+
David Marek <h4wk.cz@gmail.com>
|
| 25 |
+
Bernhard R. Link <brlink@debian.org>
|
| 26 |
+
Andrej Tokarčík <androsis@gmail.com>
|
| 27 |
+
Or Dvory <gidesa@gmail.com>
|
| 28 |
+
Saroj Adhikari <adh.saroj@gmail.com>
|
| 29 |
+
Pauli Virtanen <pav@iki.fi>
|
| 30 |
+
Robert Kern <robert.kern@gmail.com>
|
| 31 |
+
James Aspnes <aspnes@cs.yale.edu>
|
| 32 |
+
Nimish Telang <ntelang@gmail.com>
|
| 33 |
+
Abderrahim Kitouni <a.kitouni@gmail.com>
|
| 34 |
+
Pan Peng <pengpanster@gmail.com>
|
| 35 |
+
Friedrich Hagedorn <friedrich_h@gmx.de>
|
| 36 |
+
Elrond der Elbenfuerst <elrond+sympy.org@samba-tng.org>
|
| 37 |
+
Rizgar Mella <rizgar.mella@gmail.com>
|
| 38 |
+
Felix Kaiser <felix.kaiser@fxkr.net>
|
| 39 |
+
Roberto Nobrega <rwnobrega@gmail.com>
|
| 40 |
+
David Roberts <dvdr18@gmail.com>
|
| 41 |
+
Sebastian Krämer <basti.kr@gmail.com>
|
| 42 |
+
Vinzent Steinberg <vinzent.steinberg@gmail.com>
|
| 43 |
+
Riccardo Gori <goriccardo@gmail.com>
|
| 44 |
+
Case Van Horsen <casevh@gmail.com>
|
| 45 |
+
Stepan Roucka <stepan@roucka.eu>
|
| 46 |
+
Ali Raza Syed <arsyed@gmail.com>
|
| 47 |
+
Stefano Maggiolo <s.maggiolo@gmail.com>
|
| 48 |
+
Robert Cimrman <cimrman3@ntc.zcu.cz>
|
| 49 |
+
Bastian Weber <bastian.weber@gmx-topmail.de>
|
| 50 |
+
Sebastian Krause <sebastian.krause@gmx.de>
|
| 51 |
+
Sebastian Kreft <skreft@gmail.com>
|
| 52 |
+
*Dan <coolg49964@gmail.com>
|
| 53 |
+
Alan Bromborsky <abrombo@verizon.net>
|
| 54 |
+
Boris Timokhin <qoqenator@gmail.com>
|
| 55 |
+
Robert <average.programmer@gmail.com>
|
| 56 |
+
Andy R. Terrel <aterrel@uchicago.edu>
|
| 57 |
+
Hubert Tsang <intsangity@gmail.com>
|
| 58 |
+
Konrad Meyer <konrad.meyer@gmail.com>
|
| 59 |
+
Henrik Johansson <henjo2006@gmail.com>
|
| 60 |
+
Priit Laes <plaes@plaes.org>
|
| 61 |
+
Freddie Witherden <freddie@witherden.org>
|
| 62 |
+
Brian E. Granger <ellisonbg@gmail.com>
|
| 63 |
+
Andrew Straw <strawman@astraw.com>
|
| 64 |
+
Kaifeng Zhu <cafeeee@gmail.com>
|
| 65 |
+
Ted Horst <ted.horst@earthlink.net>
|
| 66 |
+
Andrew Docherty <andrewd@maths.usyd.edu.au>
|
| 67 |
+
Akshay Srinivasan <akshaysrinivasan@gmail.com>
|
| 68 |
+
Aaron Meurer <asmeurer@gmail.com>
|
| 69 |
+
Barry Wardell <barry.wardell@gmail.com>
|
| 70 |
+
Tomasz Buchert <thinred@gmail.com>
|
| 71 |
+
Vinay Kumar <gnulinooks@gmail.com>
|
| 72 |
+
Johann Cohen-Tanugi <johann.cohentanugi@gmail.com>
|
| 73 |
+
Jochen Voss <voss@seehuhn.de>
|
| 74 |
+
Luke Peterson <hazelnusse@gmail.com>
|
| 75 |
+
Chris Smith <smichr@gmail.com>
|
| 76 |
+
Thomas Sidoti <TSidoti@gmail.com>
|
| 77 |
+
Florian Mickler <florian@mickler.org>
|
| 78 |
+
Nicolas Pourcelot <nicolas.pourcelot@gmail.com>
|
| 79 |
+
Ben Goodrich <goodrich.ben@gmail.com>
|
| 80 |
+
Toon Verstraelen <Toon.Verstraelen@UGent.be>
|
| 81 |
+
Ronan Lamy <ronan.lamy@gmail.com>
|
| 82 |
+
James Abbatiello <abbeyj@gmail.com>
|
| 83 |
+
Ryan Krauss <ryanlists@gmail.com>
|
| 84 |
+
Bill Flynn <wflynny@gmail.com>
|
| 85 |
+
Kevin Goodsell <kevin-opensource@omegacrash.net>
|
| 86 |
+
Jorn Baayen <jorn.baayen@gmail.com>
|
| 87 |
+
Eh Tan <tan2tan2@gmail.com>
|
| 88 |
+
Renato Coutinho <renato.coutinho@gmail.com>
|
| 89 |
+
Oscar Benjamin <oscar.j.benjamin@gmail.com>
|
| 90 |
+
Øyvind Jensen <jensen.oyvind@gmail.com>
|
| 91 |
+
Julio Idichekop Filho <idichekop@yahoo.com.br>
|
| 92 |
+
Łukasz Pankowski <lukpank@o2.pl>
|
| 93 |
+
*Chu-Ching Huang <cchuang@mail.cgu.edu.tw>
|
| 94 |
+
Fernando Perez <Fernando.Perez@berkeley.edu>
|
| 95 |
+
Raffaele De Feo <alberthilbert@gmail.com>
|
| 96 |
+
Christian Muise <christian.muise@gmail.com>
|
| 97 |
+
Matt Curry <mattjcurry@gmail.com>
|
| 98 |
+
Kazuo Thow <kazuo.thow@gmail.com>
|
| 99 |
+
Christian Schubert <chr.schubert@gmx.de>
|
| 100 |
+
Jezreel Ng <jezreel@gmail.com>
|
| 101 |
+
James Pearson <xiong.chiamiov@gmail.com>
|
| 102 |
+
Matthew Brett <matthew.brett@gmail.com>
|
| 103 |
+
Addison Cugini <ajcugini@gmail.com>
|
| 104 |
+
Nicholas J.S. Kinar <n.kinar@usask.ca>
|
| 105 |
+
Harold Erbin <harold.erbin@gmail.com>
|
| 106 |
+
Thomas Dixon <thom@thomdixon.org>
|
| 107 |
+
Cristóvão Sousa <crisjss@gmail.com>
|
| 108 |
+
Andre de Fortier Smit <freevryheid@gmail.com>
|
| 109 |
+
Mark Dewing <markdewing@gmail.com>
|
| 110 |
+
Alexey U. Gudchenko <proga@goodok.ru>
|
| 111 |
+
Gary Kerr <gary.kerr@blueyonder.co.uk>
|
| 112 |
+
Sherjil Ozair <sherjilozair@gmail.com>
|
| 113 |
+
Oleksandr Gituliar <gituliar@gmail.com>
|
| 114 |
+
Sean Vig <sean.v.775@gmail.com>
|
| 115 |
+
Prafullkumar P. Tale <hector1618@gmail.com>
|
| 116 |
+
Vladimir Perić <vlada.peric@gmail.com>
|
| 117 |
+
Tom Bachmann <e_mc_h2@web.de>
|
| 118 |
+
Yuri Karadzhov <yuri.karadzhov@gmail.com>
|
| 119 |
+
Vladimir Lagunov <werehuman@gmail.com>
|
| 120 |
+
Matthew Rocklin <mrocklin@cs.uchicago.edu>
|
| 121 |
+
Saptarshi Mandal <sapta.iitkgp@gmail.com>
|
| 122 |
+
Gilbert Gede <gilbertgede@gmail.com>
|
| 123 |
+
Anatolii Koval <weralwolf@gmail.com>
|
| 124 |
+
Tomo Lazovich <lazovich@gmail.com>
|
| 125 |
+
Pavel Fedotov <fedotovp@gmail.com>
|
| 126 |
+
Jack McCaffery <jpmccaffery@gmail.com>
|
| 127 |
+
Jeremias Yehdegho <j.yehdegho@gmail.com>
|
| 128 |
+
Kibeom Kim <kk1674@nyu.edu>
|
| 129 |
+
Gregory Ksionda <ksiondag846@gmail.com>
|
| 130 |
+
Tomáš Bambas <tomas.bambas@gmail.com>
|
| 131 |
+
Raymond Wong <rayman_407@yahoo.com>
|
| 132 |
+
Luca Weihs <astronomicalcuriosity@gmail.com>
|
| 133 |
+
Shai 'Deshe' Wyborski <shaide@cs.huji.ac.il>
|
| 134 |
+
Thomas Wiecki <thomas.wiecki@gmail.com>
|
| 135 |
+
Óscar Nájera <najera.oscar@gmail.com>
|
| 136 |
+
Mario Pernici <mario.pernici@gmail.com>
|
| 137 |
+
Benjamin McDonald <mcdonald.ben@gmail.com>
|
| 138 |
+
Sam Magura <samtheman132@gmail.com>
|
| 139 |
+
Stefan Krastanov <krastanov.stefan@gmail.com>
|
| 140 |
+
Bradley Froehle <brad.froehle@gmail.com>
|
| 141 |
+
Min Ragan-Kelley <benjaminrk@gmail.com>
|
| 142 |
+
Emma Hogan <ehogan@gemini.edu>
|
| 143 |
+
Nikhil Sarda <diff.operator@gmail.com>
|
| 144 |
+
Julien Rioux <julien.rioux@gmail.com>
|
| 145 |
+
Roberto Colistete, Jr. <roberto.colistete@gmail.com>
|
| 146 |
+
Raoul Bourquin <raoulb@bluewin.ch>
|
| 147 |
+
Gert-Ludwig Ingold <gert.ingold@physik.uni-augsburg.de>
|
| 148 |
+
Srinivas Vasudevan <srvasude@gmail.com>
|
| 149 |
+
Jason Moore <moorepants@gmail.com>
|
| 150 |
+
Miha Marolt <tloramus@gmail.com>
|
| 151 |
+
Tim Lahey <tim.lahey@gmail.com>
|
| 152 |
+
Luis Garcia <ppn.online@me.com>
|
| 153 |
+
Matt Rajca <matt.rajca@me.com>
|
| 154 |
+
David Li <l33tnerd.li@gmail.com>
|
| 155 |
+
Alexandr Gudulin <alexandr.gudulin@gmail.com>
|
| 156 |
+
Bilal Akhtar <bilalakhtar@ubuntu.com>
|
| 157 |
+
Grzegorz Świrski <sognat@gmail.com>
|
| 158 |
+
Matt Habel <habelinc@gmail.com>
|
| 159 |
+
David Ju <Sgtmook314@gmail.com>
|
| 160 |
+
Nichita Utiu <nikita.utiu+github@gmail.com>
|
| 161 |
+
Nikolay Lazarov <qwerqwerqwer@abv.bg>
|
| 162 |
+
Steve Anton <anxuiz.nx@gmail.com>
|
| 163 |
+
Imran Ahmed Manzoor <imran.manzoor31@gmail.com>
|
| 164 |
+
Ljubiša Moćić <3rdslasher@gmail.com>
|
| 165 |
+
Piotr Korgul <p.korgul@gmail.com>
|
| 166 |
+
Jim Zhang <Hyriodula@gmail.com>
|
| 167 |
+
Sam Sleight <samuel.sleight@gmail.com>
|
| 168 |
+
tborisova <ts.borisova3@gmail.com>
|
| 169 |
+
Chancellor Arkantos <Chancellor_Arkantos@hotmail.co.uk>
|
| 170 |
+
Stepan Simsa <simsa.st@gmail.com>
|
| 171 |
+
Tobias Lenz <t_lenz94@web.de>
|
| 172 |
+
Siddhanathan Shanmugam <siddhanathan@gmail.com>
|
| 173 |
+
Tiffany Zhu <bubble.wubble.303@gmail.com>
|
| 174 |
+
Tristan Hume <tris.hume@gmail.com>
|
| 175 |
+
Alexey Subach <alexey.subach@gmail.com>
|
| 176 |
+
Joan Creus <joan.creus.c@gmail.com>
|
| 177 |
+
Geoffry Song <goffrie@gmail.com>
|
| 178 |
+
Puneeth Chaganti <punchagan@gmail.com>
|
| 179 |
+
Marcin Kostrzewa <>
|
| 180 |
+
Natalia Nawara <fankalemura@gmail.com>
|
| 181 |
+
vishal <vishal.panjwani15@gmail.com>
|
| 182 |
+
Shruti Mangipudi <shruti2395@gmail.com>
|
| 183 |
+
Davy Mao <e_equals_mass_speed_light_squared@hotmail.com>
|
| 184 |
+
Swapnil Agarwal <swapnilag29@gmail.com>
|
| 185 |
+
Dhia Kennouche <kendhia@gmail.com>
|
| 186 |
+
jerryma1121 <jerryma1121@gmail.com>
|
| 187 |
+
Joachim Durchholz <jo@durchholz.org>
|
| 188 |
+
Martin Povišer <martin.povik@gmail.com>
|
| 189 |
+
Siddhant Jain <getsiddhant@gmail.com>
|
| 190 |
+
Kevin Hunter <hunteke@earlham.edu>
|
| 191 |
+
Michael Mayorov <marchael@kb.csu.ru>
|
| 192 |
+
Nathan Alison <nathan.f.alison@gmail.com>
|
| 193 |
+
Christian Bühler <christian@cbuehler.de>
|
| 194 |
+
Carsten Knoll <CarstenKnoll@gmx.de>
|
| 195 |
+
Bharath M R <catchmrbharath@gmail.com>
|
| 196 |
+
Matthias Toews <mat.toews@googlemail.com>
|
| 197 |
+
Sergiu Ivanov <unlimitedscolobb@gmail.com>
|
| 198 |
+
Jorge E. Cardona <jorgeecardona@gmail.com>
|
| 199 |
+
Sanket Agarwal <sanket@sanketagarwal.com>
|
| 200 |
+
Manoj Babu K. <manoj.babu2378@gmail.com>
|
| 201 |
+
Sai Nikhil <tsnlegend@gmail.com>
|
| 202 |
+
Aleksandar Makelov <amakelov@college.harvard.edu>
|
| 203 |
+
Sachin Irukula <sachin.irukula@gmail.com>
|
| 204 |
+
Raphael Michel <webmaster@raphaelmichel.de>
|
| 205 |
+
Ashwini Oruganti <ashwini.oruganti@gmail.com>
|
| 206 |
+
Andreas Klöckner <inform@tiker.net>
|
| 207 |
+
Prateek Papriwal <papriwalprateek@gmail.com>
|
| 208 |
+
Arpit Goyal <agmps18@gmail.com>
|
| 209 |
+
Angadh Nanjangud <angadh.n@gmail.com>
|
| 210 |
+
Comer Duncan <comer.duncan@gmail.com>
|
| 211 |
+
Jens H. Nielsen <jenshnielsen@gmail.com>
|
| 212 |
+
Joseph Dougherty <Github@JWDougherty.com>
|
| 213 |
+
Elliot Marshall <Marshall2389@gmail.com>
|
| 214 |
+
Guru Devanla <grdvnl@gmail.com>
|
| 215 |
+
George Waksman <waksman@gwax.com>
|
| 216 |
+
Alexandr Popov <alexandr.s.popov@gmail.com>
|
| 217 |
+
Tarun Gaba <tarun.gaba7@gmail.com>
|
| 218 |
+
Takafumi Arakaki <aka.tkf@gmail.com>
|
| 219 |
+
Saurabh Jha <saurabh.jhaa@gmail.com>
|
| 220 |
+
Rom le Clair <jacen.guardian@gmail.com>
|
| 221 |
+
Angus Griffith <16sn6uv@gmail.com>
|
| 222 |
+
Timothy Reluga <treluga@math.psu.edu>
|
| 223 |
+
Brian Stephanik <xoedusk@gmail.com>
|
| 224 |
+
Alexander Eberspächer <alex.eberspaecher@gmail.com>
|
| 225 |
+
Sachin Joglekar <srjoglekar246@gmail.com>
|
| 226 |
+
Tyler Pirtle <teeler@gmail.com>
|
| 227 |
+
Vasily Povalyaev <vapovalyaev@gmail.com>
|
| 228 |
+
Colleen Lee <colleenclee@gmail.com>
|
| 229 |
+
Matthew Hoff <mhoff14@gmail.com>
|
| 230 |
+
Niklas Thörne <notrupertthorne@gmail.com>
|
| 231 |
+
Huijun Mai <m.maihuijun@gmail.com>
|
| 232 |
+
Marek Šuppa <mr@shu.io>
|
| 233 |
+
Ramana Venkata <idlike2dream@gmail.com>
|
| 234 |
+
Prasoon Shukla <prasoon92.iitr@gmail.com>
|
| 235 |
+
Stefen Yin <zqyin@ucdavis.edu>
|
| 236 |
+
Thomas Hisch <t.hisch@gmail.com>
|
| 237 |
+
Madeleine Ball <mpball@gmail.com>
|
| 238 |
+
Mary Clark <mary.spriteling@gmail.com>
|
| 239 |
+
Rishabh Dixit <rishabhdixit11@gmail.com>
|
| 240 |
+
Manoj Kumar <manojkumarsivaraj334@gmail.com>
|
| 241 |
+
Akshit Agarwal <akshit.jiit@gmail.com>
|
| 242 |
+
CJ Carey <perimosocordiae@gmail.com>
|
| 243 |
+
Patrick Lacasse <patrick.m.lacasse@gmail.com>
|
| 244 |
+
Ananya H <ananyaha93@gmail.com>
|
| 245 |
+
Tarang Patel <tarangrockr@gmail.com>
|
| 246 |
+
Christopher Dembia <cld72@cornell.edu>
|
| 247 |
+
Benjamin Fishbein <fishbeinb@gmail.com>
|
| 248 |
+
Sean Ge <seange727@gmail.com>
|
| 249 |
+
Amit Jamadagni <bitsjamadagni@gmail.com>
|
| 250 |
+
Ankit Agrawal <aaaagrawal@iitb.ac.in>
|
| 251 |
+
Björn Dahlgren <bjodah@gmail.com>
|
| 252 |
+
Christophe Saint-Jean <christophe.saint-jean@univ-lr.fr>
|
| 253 |
+
Demian Wassermann <demian@bwh.harvard.edu>
|
| 254 |
+
Khagesh Patel <khageshpatel93@gmail.com>
|
| 255 |
+
Stephen Loo <shikil@yahoo.com>
|
| 256 |
+
hm <hacman0@gmail.com>
|
| 257 |
+
Patrick Poitras <acebulf@gmail.com>
|
| 258 |
+
Katja Sophie Hotz <katja.sophie.hotz@student.tuwien.ac.at>
|
| 259 |
+
Varun Joshi <joshi.142@osu.edu>
|
| 260 |
+
Chetna Gupta <cheta.gup@gmail.com>
|
| 261 |
+
Thilina Rathnayake <thilinarmtb@gmail.com>
|
| 262 |
+
Max Hutchinson <maxhutch@gmail.com>
|
| 263 |
+
Shravas K Rao <shravas@gmail.com>
|
| 264 |
+
Matthew Tadd <matt.tadd@gmail.com>
|
| 265 |
+
Alexander Hirzel <alex@hirzel.us>
|
| 266 |
+
Randy Heydon <randy.heydon@clockworklab.net>
|
| 267 |
+
Oliver Lee <oliverzlee@gmail.com>
|
| 268 |
+
Seshagiri Prabhu <seshagiriprabhu@gmail.com>
|
| 269 |
+
Pradyumna <pradyu1993@gmail.com>
|
| 270 |
+
Erik Welch <erik.n.welch@gmail.com>
|
| 271 |
+
Eric Nelson <eric.the.red.XLII@gmail.com>
|
| 272 |
+
Roland Puntaier <roland.puntaier@chello.at>
|
| 273 |
+
Chris Conley <chrisconley15@gmail.com>
|
| 274 |
+
Tim Swast <tswast@gmail.com>
|
| 275 |
+
Dmitry Batkovich <batya239@gmail.com>
|
| 276 |
+
Francesco Bonazzi <franz.bonazzi@gmail.com>
|
| 277 |
+
Yuriy Demidov <iurii.demidov@gmail.com>
|
| 278 |
+
Rick Muller <rpmuller@gmail.com>
|
| 279 |
+
Manish Gill <gill.manish90@gmail.com>
|
| 280 |
+
Markus Müller <markus.mueller.1.g@googlemail.com>
|
| 281 |
+
Amit Saha <amitsaha.in@gmail.com>
|
| 282 |
+
Jeremy <twobitlogic@gmail.com>
|
| 283 |
+
QuaBoo <kisonchristian@gmail.com>
|
| 284 |
+
Stefan van der Walt <stefan@sun.ac.za>
|
| 285 |
+
David Joyner <wdjoyner@gmail.com>
|
| 286 |
+
Lars Buitinck <larsmans@gmail.com>
|
| 287 |
+
Alkiviadis G. Akritas <akritas@uth.gr>
|
| 288 |
+
Vinit Ravishankar <vinit.ravishankar@gmail.com>
|
| 289 |
+
Michael Boyle <michael.oliver.boyle@gmail.com>
|
| 290 |
+
Heiner Kirchhoffer <Heiner.Kirchhoffer@gmail.com>
|
| 291 |
+
Pablo Puente <ppuedom@gmail.com>
|
| 292 |
+
James Fiedler <jrfiedler@gmail.com>
|
| 293 |
+
Harsh Gupta <mail@hargup.in>
|
| 294 |
+
Tuomas Airaksinen <tuomas.airaksinen@gmail.com>
|
| 295 |
+
Paul Strickland <p.e.strickland@gmail.com>
|
| 296 |
+
James Goppert <james.goppert@gmail.com>
|
| 297 |
+
rathmann <rathmann.os@gmail.com>
|
| 298 |
+
Avichal Dayal <avichal.dayal@gmail.com>
|
| 299 |
+
Paul Scott <paul.scott@nicta.com.au>
|
| 300 |
+
Shipra Banga <bangashipra@gmail.com>
|
| 301 |
+
Pramod Ch <pramodch14@gmail.com>
|
| 302 |
+
Akshay <akshaynukala95@gmail.com>
|
| 303 |
+
Buck Shlegeris <buck2@bruceh15.anu.edu.au>
|
| 304 |
+
Jonathan Miller <jdmiller93@gmail.com>
|
| 305 |
+
Edward Schembor <eschemb1@jhu.edu>
|
| 306 |
+
Rajath Shashidhara <rajaths.rajaths@gmail.com>
|
| 307 |
+
Zamrath Nizam <zamiguy_ni@yahoo.com>
|
| 308 |
+
Aditya Shah <adityashah30@gmail.com>
|
| 309 |
+
Rajat Aggarwal <rajataggarwal1975@gmail.com>
|
| 310 |
+
Sambuddha Basu <sammygamer@live.com>
|
| 311 |
+
Zeel Shah <kshah215@gmail.com>
|
| 312 |
+
Abhinav Chanda <abhinavchanda01@gmail.com>
|
| 313 |
+
Jim Crist <crist042@umn.edu>
|
| 314 |
+
Sudhanshu Mishra <mrsud94@gmail.com>
|
| 315 |
+
Anurag Sharma <anurags92@gmail.com>
|
| 316 |
+
Soumya Dipta Biswas <sdb1323@gmail.com>
|
| 317 |
+
Sushant Hiray <hiraysushant@gmail.com>
|
| 318 |
+
Ben Lucato <ben.lucato@gmail.com>
|
| 319 |
+
Kunal Arora <kunalarora.135@gmail.com>
|
| 320 |
+
Henry Gebhardt <hsggebhardt@gmail.com>
|
| 321 |
+
Dammina Sahabandu <dmsahabandu@gmail.com>
|
| 322 |
+
Manish Shukla <manish.shukla393@gmail>
|
| 323 |
+
Ralph Bean <rbean@redhat.com>
|
| 324 |
+
richierichrawr <richierichrawr@users.noreply.github.com>
|
| 325 |
+
John Connor <john.theman.connor@gmail.com>
|
| 326 |
+
Juan Luis Cano Rodríguez <juanlu001@gmail.com>
|
| 327 |
+
Sahil Shekhawat <sahilshekhawat01@gmail.com>
|
| 328 |
+
Kundan Kumar <kundankumar18581@gmail.com>
|
| 329 |
+
Stas Kelvich <stanconn@gmail.com>
|
| 330 |
+
sevaader <sevaader@gmail.com>
|
| 331 |
+
Dhruvesh Vijay Parikh <parikhdhruvesh1@gmail.com>
|
| 332 |
+
Venkatesh Halli <venkatesh.fatality@gmail.com>
|
| 333 |
+
Lennart Fricke <lennart@die-frickes.eu>
|
| 334 |
+
Vlad Seghete <vlad.seghete@gmail.com>
|
| 335 |
+
Shashank Agarwal <shashank.agarwal94@gmail.com>
|
| 336 |
+
carstimon <carstimon@gmail.com>
|
| 337 |
+
Pierre Haessig <pierre.haessig@crans.org>
|
| 338 |
+
Maciej Baranski <getrox.sc@gmail.com>
|
| 339 |
+
Benjamin Gudehus <hastebrot@gmail.com>
|
| 340 |
+
Faisal Anees <faisal.iiit@gmail.com>
|
| 341 |
+
Mark Shoulson <mark@kli.org>
|
| 342 |
+
Robert Johansson <jrjohansson@gmail.com>
|
| 343 |
+
Kalevi Suominen <jksuom@gmail.com>
|
| 344 |
+
Kaushik Varanasi <kaushik.varanasi1@gmail.com>
|
| 345 |
+
Fawaz Alazemi <Mba7eth@gmail.com>
|
| 346 |
+
Ambar Mehrotra <mehrotraambar@gmail.com>
|
| 347 |
+
David P. Sanders <dpsanders@gmail.com>
|
| 348 |
+
Peter Brady <petertbrady@gmail.com>
|
| 349 |
+
John V. Siratt <jvsiratt@gmail.com>
|
| 350 |
+
Sarwar Chahal <chahal.sarwar98@gmail.com>
|
| 351 |
+
Nathan Woods <charlesnwoods@gmail.com>
|
| 352 |
+
Colin B. Macdonald <cbm@m.fsf.org>
|
| 353 |
+
Marcus Näslund <naslundx@gmail.com>
|
| 354 |
+
Clemens Novak <clemens@familie-novak.net>
|
| 355 |
+
Mridul Seth <seth.mridul@gmail.com>
|
| 356 |
+
Craig A. Stoudt <craig.stoudt@gmail.com>
|
| 357 |
+
Raj <raj454raj@gmail.com>
|
| 358 |
+
Mihai A. Ionescu <ionescu.a.mihai@gmail.com>
|
| 359 |
+
immerrr <immerrr@gmail.com>
|
| 360 |
+
Chai Wah Wu <cwwuieee@gmail.com>
|
| 361 |
+
Leonid Blouvshtein <leonidbl91@gmail.com>
|
| 362 |
+
Peleg Michaeli <freepeleg@gmail.com>
|
| 363 |
+
ck Lux <lux.r.ck@gmail.com>
|
| 364 |
+
zsc347 <zsc347@gmail.com>
|
| 365 |
+
Hamish Dickson <hamish.dickson@gmail.com>
|
| 366 |
+
Michael Gallaspy <gallaspy.michael@gmail.com>
|
| 367 |
+
Roman Inflianskas <infroma@gmail.com>
|
| 368 |
+
Duane Nykamp <nykamp@umn.edu>
|
| 369 |
+
Ted Dokos <tdokos@gmail.com>
|
| 370 |
+
Sunny Aggarwal <sunnyaggarwal1994@gmail.com>
|
| 371 |
+
Victor Brebenar <v.brebenar@gmail.com>
|
| 372 |
+
Akshat Jain <akshat.jain@students.iiit.ac.in>
|
| 373 |
+
Shivam Vats <shivamvats.iitkgp@gmail.com>
|
| 374 |
+
Longqi Wang <iqgnol@gmail.com>
|
| 375 |
+
Juan Felipe Osorio <jfosorio@gmail.com>
|
| 376 |
+
Ray Cathcart <github@cathcart.us>
|
| 377 |
+
Lukas Zorich <lukas.zorich@gmail.com>
|
| 378 |
+
Eric Miller <emiller42@gmail.com>
|
| 379 |
+
Cody Herbst <cyherbst@gmail.com>
|
| 380 |
+
Nishith Shah <nishithshah.2211@gmail.com>
|
| 381 |
+
Amit Kumar <dtu.amit@gmail.com>
|
| 382 |
+
Yury G. Kudryashov <urkud.urkud@gmail.com>
|
| 383 |
+
Guillaume Gay <contact@damcb.com>
|
| 384 |
+
Mihir Wadwekar <m.mihirw@gmail.com>
|
| 385 |
+
Tuan Manh Lai <laituan245@gmail.com>
|
| 386 |
+
Asish Panda <asishrocks95@gmail.com>
|
| 387 |
+
Darshan Chaudhary <deathbullet@gmail.com>
|
| 388 |
+
Alec Kalinin <alec.kalinin@gmail.com>
|
| 389 |
+
Ralf Stephan <ralf@ark.in-berlin.de>
|
| 390 |
+
Aaditya Nair <aadityanair6494@gmail.com>
|
| 391 |
+
Jayesh Lahori <jlahori92@gmail.com>
|
| 392 |
+
Harshil Goel <harshil158@gmail.com>
|
| 393 |
+
Luv Agarwal <agarwal.iiit@gmail.com>
|
| 394 |
+
Jason Ly <jason.ly@gmail.com>
|
| 395 |
+
Lokesh Sharma <lokeshhsharma@gmail.com>
|
| 396 |
+
Sartaj Singh <singhsartaj94@gmail.com>
|
| 397 |
+
Chris Swierczewski <cswiercz@gmail.com>
|
| 398 |
+
Konstantin Togoi <konstantin.togoi@gmail.com>
|
| 399 |
+
Param Singh <paramsingh258@gmail.com>
|
| 400 |
+
Sumith Kulal <sumith1896@gmail.com>
|
| 401 |
+
Juha Remes <jremes@outlook.com>
|
| 402 |
+
Philippe Bouafia <philippe.bouafia@ensea.fr>
|
| 403 |
+
Peter Schmidt <peter@peterjs.com>
|
| 404 |
+
Jiaxing Liang <liangjiaxing57@gmail.com>
|
| 405 |
+
Lucas Jones <lucas@lucasjones.co.uk>
|
| 406 |
+
Gregory Ashton <gash789@gmail.com>
|
| 407 |
+
Jennifer White <jcrw122@googlemail.com>
|
| 408 |
+
Renato Orsino <renato.orsino@gmail.com>
|
| 409 |
+
Alistair Lynn <arplynn@gmail.com>
|
| 410 |
+
Govind Sahai <gsiitbhu@gmail.com>
|
| 411 |
+
Adam Bloomston <adam@glitterfram.es>
|
| 412 |
+
Kyle McDaniel <mcdanie5@illinois.edu>
|
| 413 |
+
Nguyen Truong Duy <truongduy134@yahoo.com>
|
| 414 |
+
Alex Lindsay <adlinds3@ncsu.edu>
|
| 415 |
+
Mathew Chong <mathewchong.dev@gmail.com>
|
| 416 |
+
Jason Siefken <siefkenj@gmail.com>
|
| 417 |
+
Gaurav Dhingra <gauravdhingra.gxyd@gmail.com>
|
| 418 |
+
Gao, Xiang <qasdfgtyuiop@gmail.com>
|
| 419 |
+
Kevin Ventullo <kevin.ventullo@gmail.com>
|
| 420 |
+
mao8 <thisisma08@gmail.com>
|
| 421 |
+
Isuru Fernando <isuruf@gmail.com>
|
| 422 |
+
Shivam Tyagi <shivam.tyagi.apm13@itbhu.ac.in>
|
| 423 |
+
Richard Otis <richard.otis@outlook.com>
|
| 424 |
+
Rich LaSota <rjlasota@gmail.com>
|
| 425 |
+
dustyrockpyle <dustyrockpyle@gmail.com>
|
| 426 |
+
Anton Akhmerov <anton.akhmerov@gmail.com>
|
| 427 |
+
Michael Zingale <michael.zingale@stonybrook.edu>
|
| 428 |
+
Chak-Pong Chung <chakpongchung@gmail.com>
|
| 429 |
+
David T <derDavidT@users.noreply.github.com>
|
| 430 |
+
Phil Ruffwind <rf@rufflewind.com>
|
| 431 |
+
Sebastian Koslowski <koslowski@kit.edu>
|
| 432 |
+
Kumar Krishna Agrawal <kumar.1994.14@gmail.com>
|
| 433 |
+
Dustin Gadal <Dustin.Gadal@gmail.com>
|
| 434 |
+
João Moura <operte@gmail.com>
|
| 435 |
+
Yu Kobayashi <yukoba@accelart.jp>
|
| 436 |
+
Shashank Kumar <shashank.kumar.apc13@iitbhu.ac.in>
|
| 437 |
+
Timothy Cyrus <tcyrus@users.noreply.github.com>
|
| 438 |
+
Devyani Kota <devyanikota@gmail.com>
|
| 439 |
+
Keval Shah <kevalshah_96@yahoo.co.in>
|
| 440 |
+
Dzhelil Rufat <drufat@caltech.edu>
|
| 441 |
+
Pastafarianist <mr.pastafarianist@gmail.com>
|
| 442 |
+
Sourav Singh <souravsingh@users.noreply.github.com>
|
| 443 |
+
Jacob Garber <jgarber1@ualberta.ca>
|
| 444 |
+
Vinay Singh <csvinay.d@gmail.com>
|
| 445 |
+
GolimarOurHero <metalera94@hotmail.com>
|
| 446 |
+
Prashant Tyagi <prashanttyagi221295@gmail.com>
|
| 447 |
+
Matthew Davis <davisml.md@gmail.com>
|
| 448 |
+
Tschijnmo TSCHAU <tschijnmotschau@gmail.com>
|
| 449 |
+
Alexander Bentkamp <bentkamp@gmail.com>
|
| 450 |
+
Jack Kemp <metaknightdrake-git@yahoo.co.uk>
|
| 451 |
+
Kshitij Saraogi <KshitijSaraogi@gmail.com>
|
| 452 |
+
Thomas Baruchel <baruchel@gmx.com>
|
| 453 |
+
Nicolás Guarín-Zapata <nicoguarin@gmail.com>
|
| 454 |
+
Jens Jørgen Mortensen <jj@smoerhul.dk>
|
| 455 |
+
Sampad Kumar Saha <sampadsaha5@gmail.com>
|
| 456 |
+
Eva Charlotte Mayer <eva-charlotte.mayer@posteo.de>
|
| 457 |
+
Laura Domine <temigo@gmx.com>
|
| 458 |
+
Justin Blythe <jblythe29@gmail.com>
|
| 459 |
+
Meghana Madhyastha <meghana.madhyastha@gmail.com>
|
| 460 |
+
Tanu Hari Dixit <tokencolour@gmail.com>
|
| 461 |
+
Shekhar Prasad Rajak <shekharrajak@live.com>
|
| 462 |
+
Aqnouch Mohammed <aqnouch.mohammed@gmail.com>
|
| 463 |
+
Arafat Dad Khan <arafat.da.khan@gmail.com>
|
| 464 |
+
Boris Atamanovskiy <shaomoron@gmail.com>
|
| 465 |
+
Sam Tygier <sam.tygier@hep.manchester.ac.uk>
|
| 466 |
+
Jai Luthra <me@jailuthra.in>
|
| 467 |
+
Guo Xingjian <Seeker1995@gmail.com>
|
| 468 |
+
Sandeep Veethu <sandeep.veethu@gmail.com>
|
| 469 |
+
Archit Verma <architv07@gmail.com>
|
| 470 |
+
Shubham Tibra <shubh.tibra@gmail.com>
|
| 471 |
+
Ashutosh Saboo <ashutosh.saboo96@gmail.com>
|
| 472 |
+
Michael S. Hansen <michael.hansen@nih.gov>
|
| 473 |
+
Anish Shah <shah.anish07@gmail.com>
|
| 474 |
+
Guillaume Jacquenot <guillaume.jacquenot@gmail.com>
|
| 475 |
+
Bhautik Mavani <mavanibhautik@gmail.com>
|
| 476 |
+
Michał Radwański <enedil.isildur@gmail.com>
|
| 477 |
+
Jerry Li <jerry@jerryli.ca>
|
| 478 |
+
Pablo Zubieta <pabloferz@yahoo.com.mx>
|
| 479 |
+
Shivam Agarwal <knowthyself2503@gmail.com>
|
| 480 |
+
Chaitanya Sai Alaparthi <achaitanyasai@gmail.com>
|
| 481 |
+
Arihant Parsoya <parsoyaarihant@gmail.com>
|
| 482 |
+
Ruslan Pisarev <rpisarev@cloudlinux.com>
|
| 483 |
+
Akash Trehan <akash.trehan123@gmail.com>
|
| 484 |
+
Nishant Nikhil <nishantiam@gmail.com>
|
| 485 |
+
Vladimir Poluhsin <vovapolu@gmail.com>
|
| 486 |
+
Akshay Nagar <awesomeay13@yahoo.com>
|
| 487 |
+
James Brandon Milam <jmilam343@gmail.com>
|
| 488 |
+
Abhinav Agarwal <abhinavagarwal1996@gmail.com>
|
| 489 |
+
Rishabh Daal <rishabhdaal@gmail.com>
|
| 490 |
+
Sanya Khurana <sanya@monica.in>
|
| 491 |
+
Aman Deep <amandeep1024@gmail.com>
|
| 492 |
+
Aravind Reddy <aravindreddy255@gmail.com>
|
| 493 |
+
Abhishek Verma <iamvermaabhishek@gmail.com>
|
| 494 |
+
Matthew Parnell <matt@parnmatt.co.uk>
|
| 495 |
+
Thomas Hickman <Thomas.Hickman42@gmail.com>
|
| 496 |
+
Akshay Siramdas <akshaysiramdas@gmail.com>
|
| 497 |
+
YiDing Jiang <yidinggjiangg@gmail.com>
|
| 498 |
+
Jatin Yadav <jatinyadav25@gmail.com>
|
| 499 |
+
Matthew Thomas <mnmt@users.noreply.github.com>
|
| 500 |
+
Rehas Sachdeva <aquannie@gmail.com>
|
| 501 |
+
Michael Mueller <michaeldmueller7@gmail.com>
|
| 502 |
+
Srajan Garg <srajan.garg@gmail.com>
|
| 503 |
+
Prabhjot Singh <prabhjot.nith@gmail.com>
|
| 504 |
+
Haruki Moriguchi <harukimoriguchi@gmail.com>
|
| 505 |
+
Tom Gijselinck <tomgijselinck@gmail.com>
|
| 506 |
+
Nitin Chaudhary <nitinmax1000@gmail.com>
|
| 507 |
+
Alex Argunov <sajkoooo@gmail.com>
|
| 508 |
+
Nathan Musoke <nathan.musoke@gmail.com>
|
| 509 |
+
Abhishek Garg <abhishekgarg119@gmail.com>
|
| 510 |
+
Dana Jacobsen <dana@acm.org>
|
| 511 |
+
Vasiliy Dommes <vasdommes@gmail.com>
|
| 512 |
+
Phillip Berndt <phillip.berndt@googlemail.com>
|
| 513 |
+
Haimo Zhang <zh.hammer.dev@gmail.com>
|
| 514 |
+
Anthony Scopatz <scopatz@gmail.com>
|
| 515 |
+
bluebrook <perl4logic@gmail.com>
|
| 516 |
+
Leonid Kovalev <leonidvkovalev@gmail.com>
|
| 517 |
+
Josh Burkart <jburkart@gmail.com>
|
| 518 |
+
Dimitra Konomi <t8130064@dias.aueb.gr>
|
| 519 |
+
Christina Zografou <t8130048@dias.aueb.gr>
|
| 520 |
+
Fiach Antaw <fiach.antaw+github@gmail.com>
|
| 521 |
+
Langston Barrett <langston.barrett@gmail.com>
|
| 522 |
+
Krit Karan <kritkaran.b13@iiits.in>
|
| 523 |
+
G. D. McBain <gdmcbain@protonmail.com>
|
| 524 |
+
Prempal Singh <prempal.42@gmail.com>
|
| 525 |
+
Gabriel Orisaka <orisaka@gmail.com>
|
| 526 |
+
Matthias Bussonnier <bussonniermatthias@gmail.com>
|
| 527 |
+
rahuldan <rahul02013@gmail.com>
|
| 528 |
+
Colin Marquardt <github@marquardt-home.de>
|
| 529 |
+
Andrew Taber <andrew.e.taber@gmail.com>
|
| 530 |
+
Yash Reddy <write2yashreddy@gmail.com>
|
| 531 |
+
Peter Stangl <peter.stangl@ph.tum.de>
|
| 532 |
+
elvis-sik <e.sikora@grad.ufsc.br>
|
| 533 |
+
Nikos Karagiannakis <nikoskaragiannakis@gmail.com>
|
| 534 |
+
Jainul Vaghasia <jainulvaghasia@gmail.com>
|
| 535 |
+
Dennis Meckel <meckel@datenschuppen.de>
|
| 536 |
+
Harshil Meena <harshil.7535@gmail.com>
|
| 537 |
+
Micky <mickydroch@gmail.com>
|
| 538 |
+
Nick Curtis <nicholas.curtis@uconn.edu>
|
| 539 |
+
Michele Zaffalon <michele.zaffalon@gmail.com>
|
| 540 |
+
Martha Giannoudovardi <maapxa@gmail.com>
|
| 541 |
+
Devang Kulshreshtha <devang.kulshreshtha.cse14@itbhu.ac.in>
|
| 542 |
+
Steph Papanik <spapanik21@gmail.com>
|
| 543 |
+
Mohammad Sadeq Dousti <msdousti@gmail.com>
|
| 544 |
+
Arif Ahmed <arif.ahmed.5.10.1995@gmail.com>
|
| 545 |
+
Abdullah Javed Nesar <abduljaved1994@gmail.com>
|
| 546 |
+
Lakshya Agrawal <zeeshan.lakshya@gmail.com>
|
| 547 |
+
shruti <shrutishrm512@gmail.com>
|
| 548 |
+
Rohit Rango <rohit.rango@gmail.com>
|
| 549 |
+
Hong Xu <hong@topbug.net>
|
| 550 |
+
Ivan Petuhov <ivan@ostrovok.ru>
|
| 551 |
+
Alsheh <alsheh@rpi.edu>
|
| 552 |
+
Marcel Stimberg <marcel.stimberg@ens.fr>
|
| 553 |
+
Alexey Pakhocmhik <cool.Bakov@yandex.ru>
|
| 554 |
+
Tommy Olofsson <tommy.olofsson.90@gmail.com>
|
| 555 |
+
Zulfikar <zulfikar97@gmail.com>
|
| 556 |
+
Blair Azzopardi <blairuk@gmail.com>
|
| 557 |
+
Danny Hermes <daniel.j.hermes@gmail.com>
|
| 558 |
+
Sergey Pestov <pestov-sa@yandex.ru>
|
| 559 |
+
Mohit Chandra <mohit.chandra@research.iiit.ac.in>
|
| 560 |
+
Karthik Chintapalli <karthik.chintapalli@students.iiit.ac.in>
|
| 561 |
+
Marcin Briański <marcin.brianski@student.uj.edu.pl>
|
| 562 |
+
andreo <andrey.torba@gmail.com>
|
| 563 |
+
Flamy Owl <flamyowl@protonmail.ch>
|
| 564 |
+
Yicong Guo <guoyicong100@gmail.com>
|
| 565 |
+
Varun Garg <varun.garg03@gmail.com>
|
| 566 |
+
Rishabh Madan <rishabhmadan96@gmail.com>
|
| 567 |
+
Aditya Kapoor <aditya.kapoor.apm12@itbhu.ac.in>
|
| 568 |
+
Karan Sharma <karan1276@gmail.com>
|
| 569 |
+
Vedant Rathore <vedantr1998@gmail.com>
|
| 570 |
+
Johan Blåbäck <johan_bluecreek@riseup.net>
|
| 571 |
+
Pranjal Tale <pranjaltale16@gmail.com>
|
| 572 |
+
Jason Tokayer <jason.tokayer@gmail.com>
|
| 573 |
+
Raghav Jajodia <jajodia.raghav@gmail.com>
|
| 574 |
+
Rajat Thakur <rajatthakur1997@gmail.com>
|
| 575 |
+
Dhruv Bhanushali <dhruv_b@live.com>
|
| 576 |
+
Anjul Kumar Tyagi <anjul.ten@gmail.com>
|
| 577 |
+
Barun Parruck <barun.parruck@gmail.com>
|
| 578 |
+
Bao Chau <chauquocbao0907@gmail.com>
|
| 579 |
+
Tanay Agrawal <tanay_agrawal@hotmail.com>
|
| 580 |
+
Ranjith Kumar <ranjith.dakshana2015@gmail.com>
|
| 581 |
+
Shikhar Makhija <shikharmakhija2@gmail.com>
|
| 582 |
+
Yathartha Joshi <yathartha32@gmail.com>
|
| 583 |
+
Valeriia Gladkova <valeriia.gladkova@gmail.com>
|
| 584 |
+
Sagar Bharadwaj <sagarbharadwaj50@gmail.com>
|
| 585 |
+
Daniel Mahler <dmahler@gmail.com>
|
| 586 |
+
Ka Yi <chua.kayi@yahoo.com.sg>
|
| 587 |
+
Rishat Iskhakov <iskhakov@frtk.ru>
|
| 588 |
+
Szymon Mieszczak <szymon.mieszczak@gmail.com>
|
| 589 |
+
Sachin Agarwal <sachinagarwal0499@gmail.com>
|
| 590 |
+
Priyank Patel <pspbot7@gmail.com>
|
| 591 |
+
Satya Prakash Dwibedi <akash581050@gmail.com>
|
| 592 |
+
tools4origins <tools4origins@gmail.com>
|
| 593 |
+
Nico Schlömer <nico.schloemer@gmail.com>
|
| 594 |
+
Fermi Paradox <FermiParadox@users.noreply.github.com>
|
| 595 |
+
Ekansh Purohit <purohit.e15@gmail.com>
|
| 596 |
+
Vedarth Sharma <vedarth.sharma@gmail.com>
|
| 597 |
+
Peeyush Kushwaha <peeyush.p97@gmail.com>
|
| 598 |
+
Jayjayyy <vfhsln8s3l4b87t4c3@byom.de>
|
| 599 |
+
Christopher J. Wright <cjwright4242gh@gmail.com>
|
| 600 |
+
Jakub Wilk <jwilk@jwilk.net>
|
| 601 |
+
Mauro Garavello <mauro.garavello@unimib.it>
|
| 602 |
+
Chris Tefer <ctefer@gmail.com>
|
| 603 |
+
Shikhar Jaiswal <jaiswalshikhar87@gmail.com>
|
| 604 |
+
Chiu-Hsiang Hsu <wdv4758h@gmail.com>
|
| 605 |
+
Carlos Cordoba <ccordoba12@gmail.com>
|
| 606 |
+
Fabian Ball <fabian.ball@kit.edu>
|
| 607 |
+
Yerniyaz <yerniyaz.nurgabylov@nu.edu.kz>
|
| 608 |
+
Christiano Anderson <canderson@riseup.net>
|
| 609 |
+
Robin Neatherway <robin.neatherway@gmail.com>
|
| 610 |
+
Thomas Hunt <thomashunt13@gmail.com>
|
| 611 |
+
Theodore Han <theodorehan@hotmail.com>
|
| 612 |
+
Duc-Minh Phan <alephvn@gmail.com>
|
| 613 |
+
Lejla Metohajrova <l.metohajrova@gmail.com>
|
| 614 |
+
Samyak Jain <samyak.jain2016a@vitstudent.ac.in>
|
| 615 |
+
Aditya Rohan <riyuzakiiitk@gmail.com>
|
| 616 |
+
Vincent Delecroix <vincent.delecroix@labri.fr>
|
| 617 |
+
Michael Sparapany <msparapa@purdue.edu>
|
| 618 |
+
Harsh Jain <harshjniitr@gmail.com>
|
| 619 |
+
Nathan Goldbaum <ngoldbau@illinois.edu>
|
| 620 |
+
latot <felipematas@yahoo.com>
|
| 621 |
+
Kenneth Lyons <ixjlyons@gmail.com>
|
| 622 |
+
Stan Schymanski <stan.schymanski@env.ethz.ch>
|
| 623 |
+
David Daly <david.daly12@kzoo.edu>
|
| 624 |
+
Ayush Shridhar <ayush.shridhar1999@gmail.com>
|
| 625 |
+
Javed Nissar <javednissar@gmail.com>
|
| 626 |
+
Jiri Kuncar <jiri.kuncar@gmail.com>
|
| 627 |
+
vedantc98 <vedantc98@gmail.com>
|
| 628 |
+
Rupesh Harode <rupeshharode@gmail.com>
|
| 629 |
+
Rob Zinkov <rob@zinkov.com>
|
| 630 |
+
James Harrop <ebc121@gmail.com>
|
| 631 |
+
James Taylor <user234683@tutanota.com>
|
| 632 |
+
Ishan Joshi <ishanaj98@gmail.com>
|
| 633 |
+
Marco Mancini <marco.mancini@obspm.fr>
|
| 634 |
+
Boris Ettinger <ettinger.boris@gmail.com>
|
| 635 |
+
Micah Fitch <micahscopes@gmail.com>
|
| 636 |
+
Daniel Wennberg <daniel.wennberg@gmail.com>
|
| 637 |
+
ylemkimon <ylemkimon@naver.com>
|
| 638 |
+
Akash Vaish <akash.9712@gmail.com>
|
| 639 |
+
Peter Enenkel <peter.enenkel+git@gmail.com>
|
| 640 |
+
Waldir Pimenta <waldyrious@gmail.com>
|
| 641 |
+
Jithin D. George <jithindgeorge93@gmail.com>
|
| 642 |
+
Lev Chelyadinov <leva181777@gmail.com>
|
| 643 |
+
Lucas Wiman <lucas.wiman@gmail.com>
|
| 644 |
+
Rhea Parekh <rheaparekh12@gmail.com>
|
| 645 |
+
James Cotton <peabody124@gmail.com>
|
| 646 |
+
Robert Pollak <robert.pollak@posteo.net>
|
| 647 |
+
anca-mc <anca-mc@users.noreply.github.com>
|
| 648 |
+
Sourav Ghosh <souravghosh2197@gmail.com>
|
| 649 |
+
Jonathan Allan <jjallan@users.noreply.github.com>
|
| 650 |
+
Nikhil Pappu <nkhlpappu@gmail.com>
|
| 651 |
+
Ethan Ward <etkewa@gmail.com>
|
| 652 |
+
Cezary Marczak <zeddq1@gmail.com>
|
| 653 |
+
dps7ud <dps7ud@virginia.edu>
|
| 654 |
+
Nilabja Bhattacharya <nilabja10201992@gmail.com>
|
| 655 |
+
Itay4 <31018228+Itay4@users.noreply.github.com>
|
| 656 |
+
Poom Chiarawongse <eight1911@gmail.com>
|
| 657 |
+
Yang Yang <wdscxsj@gmail.com>
|
| 658 |
+
Cavendish McKay <cmckay@tachycline.com>
|
| 659 |
+
Bradley Gannon <bradley.m.gannon@gmail.com>
|
| 660 |
+
B McG <bmcg0890@gmail.com>
|
| 661 |
+
Rob Drynkin <rob.drynkin@gmail.com>
|
| 662 |
+
Seth Ebner <murgrehk@gmail.com>
|
| 663 |
+
Akash Kundu <sk.sayakkundu1997@gmail.com>
|
| 664 |
+
Mark Jeromin <mark.jeromin@sysfrog.net>
|
| 665 |
+
Roberto Díaz Pérez <r.r.1994a@gmail.com>
|
| 666 |
+
Gleb Siroki <g.shiroki@gmail.com>
|
| 667 |
+
Segev Finer <segev208@gmail.com>
|
| 668 |
+
Alex Lubbock <code@alexlubbock.com>
|
| 669 |
+
Ayodeji Ige <ayodeji18@outlook.com>
|
| 670 |
+
Matthew Wardrop <matthew.wardrop@airbnb.com>
|
| 671 |
+
Hugo van Kemenade <hugovk@users.noreply.github.com>
|
| 672 |
+
Austin Palmer <ap4000@nyu.edu>
|
| 673 |
+
der-blaue-elefant <github@kklein.de>
|
| 674 |
+
Filip Gokstorp <filip@gokstorp.se>
|
| 675 |
+
Yuki Matsuda <yuki.matsuda.w@gmail.com>
|
| 676 |
+
Aaron Miller <acmiller273@gmail.com>
|
| 677 |
+
Salil Vishnu Kapur <salilvishnukapur@gmail.com>
|
| 678 |
+
Atharva Khare <khareatharva@gmail.com>
|
| 679 |
+
Shubham Maheshwari <rmaheshwari05@gmail.com>
|
| 680 |
+
Pavel Tkachenko <paveltkachenko@email.com>
|
| 681 |
+
Ashish Kumar Gaurav <ashishkg0022@gmail.com>
|
| 682 |
+
Rajeev Singh <rajs2010@gmail.com>
|
| 683 |
+
Keno Goertz <keno@goertz-berlin.com>
|
| 684 |
+
Lucas Gallindo <lgallindo@gmail.com>
|
| 685 |
+
Himanshu <hs80941@gmail.com>
|
| 686 |
+
David Menéndez Hurtado <david.menendez.hurtado@scilifelab.se>
|
| 687 |
+
Amit Manchanda <amitdelhi1995@gmail.com>
|
| 688 |
+
Rohit Jain <rohitjain3241@gmail.com>
|
| 689 |
+
Jonathan A. Gross <jarthurgross@gmail.com>
|
| 690 |
+
Unknown <kunda@scribus.net>
|
| 691 |
+
Sayan Goswami <Sayan98@users.noreply.github.com>
|
| 692 |
+
Subhash Saurabh <subhashsaurabh419@gmail.com>
|
| 693 |
+
Rastislav Rabatin <rastislav.rabatin@gmail.com>
|
| 694 |
+
Vishal <vishalg2235@gmail.com>
|
| 695 |
+
Jeremey Gluck <jeremygluck@yahoo.com>
|
| 696 |
+
Akshat Maheshwari <akshat14714@gmail.com>
|
| 697 |
+
symbolique <symbolique@users.noreply.github.com>
|
| 698 |
+
Saloni Jain <tosalonijain@gmail.com>
|
| 699 |
+
Arighna Chakrabarty <arighna.chakrabarty100@gmail.com>
|
| 700 |
+
Abhigyan Khaund <mail@abhigyan.xyz>
|
| 701 |
+
Jashanpreet Singh <jashansingh.4398@gmail.com>
|
| 702 |
+
Saurabh Agarwal <shourabh.agarwal@gmail.com>
|
| 703 |
+
luzpaz <luzpaz@users.noreply.github.com>
|
| 704 |
+
P. Sai Prasanth <psai.prasanth.min16@itbhu.ac.in>
|
| 705 |
+
Nirmal Sarswat <nirmalsarswat400@gmail.com>
|
| 706 |
+
Cristian Di Pietrantonio <cristiandipietrantonio@gmail.com>
|
| 707 |
+
Ravi charan <ravicharan.vsp@gmail.com>
|
| 708 |
+
Nityananda Gohain <nityanandagohain@gmail.com>
|
| 709 |
+
Cédric Travelletti <cedrictravelletti@gmail.com>
|
| 710 |
+
Nicholas Bollweg <nick.bollweg@gmail.com>
|
| 711 |
+
Himanshu Ladia <hladia199811@gmail.com>
|
| 712 |
+
Adwait Baokar <adwaitbaokar18@gmail.com>
|
| 713 |
+
Mihail Tarigradschi <m.tarigradschi@gmail.com>
|
| 714 |
+
Saketh <alurusaisaketh@gmail.com>
|
| 715 |
+
rushyam <rushyamsonu@gmail.com>
|
| 716 |
+
sfoo <sfoohei@gmail.com>
|
| 717 |
+
Rahil Hastu <rahilhastu@gmail.com>
|
| 718 |
+
Zach Raines <raineszm@gmail.com>
|
| 719 |
+
Sidhant Nagpal <sidhantnagpal97@gmail.com>
|
| 720 |
+
Gagandeep Singh <singh.23@iitj.ac.in>
|
| 721 |
+
Rishav Chakraborty <annonymousxyz@outlook.com>
|
| 722 |
+
Malkhan Singh <malkhansinghrathaur@gmail.com>
|
| 723 |
+
Joaquim Monserrat <qmonserrat@mailoo.org>
|
| 724 |
+
Mayank Singh <mayank.singh081997@gmail.com>
|
| 725 |
+
Rémy Léone <rleone@online.net>
|
| 726 |
+
Maxence Mayrand <35958639+maxencemayrand@users.noreply.github.com>
|
| 727 |
+
Nikoleta Glynatsi <GlynatsiNE@cardiff.ac.uk>
|
| 728 |
+
helo9 <helo9@users.noreply.github.com>
|
| 729 |
+
Ken Wakita <wakita@is.titech.ac.jp>
|
| 730 |
+
Carl Sandrock <carl.sandrock@up.ac.za>
|
| 731 |
+
Fredrik Eriksson <freeriks@student.chalmers.se>
|
| 732 |
+
Ian Swire <oversizedpenguin@gmail.com>
|
| 733 |
+
Bulat <daianovich@mail.ru>
|
| 734 |
+
Ehren Metcalfe <ehren.m@gmail.com>
|
| 735 |
+
Dmitry Savransky <dsavransky@gmail.com>
|
| 736 |
+
Kiyohito Yamazaki <kyamaz@openql.org>
|
| 737 |
+
Caley Finn <caleyreuben@gmail.com>
|
| 738 |
+
Zhi-Qiang Zhou <zzq_890709@hotmail.com>
|
| 739 |
+
Alexander Pozdneev <pozdneev@users.noreply.github.com>
|
| 740 |
+
Wes Turner <50891+westurner@users.noreply.github.com>
|
| 741 |
+
JMSS-Unknown <31131631+JMSS-Unknown@users.noreply.github.com>
|
| 742 |
+
Arshdeep Singh <singh.arshdeep1999@gmail.com>
|
| 743 |
+
cym1 <16437732+cym1@users.noreply.github.com>
|
| 744 |
+
Stewart Wadsworth <stewart.wadsworth@gmail.com>
|
| 745 |
+
Jared Lumpe <mjlumpe@gmail.com>
|
| 746 |
+
Avi Shrivastava <shrivastavaavi123@gmail.com>
|
| 747 |
+
ramvenkat98 <ramvenkat98@gmail.com>
|
| 748 |
+
Bilal Ahmed <b.ahmed0918@gmail.com>
|
| 749 |
+
Dimas Abreu Archanjo Dutra <dimasad@ufmg.br>
|
| 750 |
+
Yatna Verma <yatnavermaa@gmail.com>
|
| 751 |
+
S.Y. Lee <sylee957@gmail.com>
|
| 752 |
+
Miro Hrončok <miro@hroncok.cz>
|
| 753 |
+
Sudarshan Kamath <sudarshan.kamath97@gmail.com>
|
| 754 |
+
Ayushman Koul <ayushmankoul4570@gmail.com>
|
| 755 |
+
Robert Dougherty-Bliss <robert.w.bliss@gmail.com>
|
| 756 |
+
Andrey Grozin <A.G.Grozin@inp.nsk.su>
|
| 757 |
+
Bavish Kulur <bavishkulur@gmail.com>
|
| 758 |
+
Arun Singh <arunsin997@gmail.com>
|
| 759 |
+
sirnicolaf <43586954+sirnicolaf@users.noreply.github.com>
|
| 760 |
+
Zachariah Etienne <zachetie@gmail.com>
|
| 761 |
+
Prayush Dawda <35144226+iamprayush@users.noreply.github.com>
|
| 762 |
+
2torus <boris.ettinger@gmail.com>
|
| 763 |
+
Faisal Riyaz <faisalriyaz011@gmail.com>
|
| 764 |
+
Martin Roelfs <u0114255@kuleuven.be>
|
| 765 |
+
SirJohnFranklin <sirjfu@googlemail.com>
|
| 766 |
+
Anthony Sottile <asottile@umich.edu>
|
| 767 |
+
ViacheslavP <public.viacheslav@gmail.com>
|
| 768 |
+
Safiya03 <safiyanesar@gmail.com>
|
| 769 |
+
Alexander Dunlap <alexander.dunlap@gmail.com>
|
| 770 |
+
Rohit Sharma <31184621+rohitx007@users.noreply.github.com>
|
| 771 |
+
Jonathan Warner <warnerjon12@gmail.com>
|
| 772 |
+
Mohit Balwani <mohitbalwani.ict17@gmail.com>
|
| 773 |
+
Marduk Bolaños <mardukbp@mac.com>
|
| 774 |
+
amsuhane <ayushsuhane99@iitkgp.ac.in>
|
| 775 |
+
Matthias Geier <Matthias.Geier@gmail.com>
|
| 776 |
+
klaasvanaarsen <44929042+klaasvanaarsen@users.noreply.github.com>
|
| 777 |
+
Shubham Kumar Jha <skjha832@gmail.com>
|
| 778 |
+
rationa-kunal <kunalgk1999@gmail.com>
|
| 779 |
+
Animesh Sinha <animeshsinha1309@gmail.com>
|
| 780 |
+
Gaurang Tandon <1gaurangtandon@gmail.com>
|
| 781 |
+
Matthew Craven <clyring@users.noreply.github.com>
|
| 782 |
+
Daniel Ingram <ingramds@appstate.edu>
|
| 783 |
+
Jogi Miglani <jmig5776@gmail.com>
|
| 784 |
+
Takumasa Nakamura <n.takumasa@gmail.com>
|
| 785 |
+
Ritu Raj Singh <RituRajSingh878@gmail.com>
|
| 786 |
+
Rajiv Ranjan Singh <rajivperfect007@gmail.com>
|
| 787 |
+
Vera Lozhkina <veralozhkina@gmail.com>
|
| 788 |
+
adhoc-king <46354827+adhoc-king@users.noreply.github.com>
|
| 789 |
+
Mikel Rouco <mikel.mrm@gmail.com>
|
| 790 |
+
Oscar Gustafsson <oscar.gustafsson@gmail.com>
|
| 791 |
+
damianos <damianos@semmle.com>
|
| 792 |
+
Supreet Agrawal <supreet11agrawal@gmail.com>
|
| 793 |
+
shiksha11 <shiksharawat01@gmail.com>
|
| 794 |
+
Martin Ueding <dev@martin-ueding.de>
|
| 795 |
+
sharma-kunal <kunalsharma6914@gmail.com>
|
| 796 |
+
Divyanshu Thakur <divyanshu@iiitmanipur.ac.in>
|
| 797 |
+
Susumu Ishizuka <susumu.ishizuka@kii.com>
|
| 798 |
+
Samnan Rahee <namanush.rsr.16@gmail.com>
|
| 799 |
+
Fredrik Andersson <fredrik.andersson@fcc.chalmers.se>
|
| 800 |
+
Bhavya Srivastava <bhavya17037@iiitd.ac.in>
|
| 801 |
+
Alpesh Jamgade <alpeshjamgade21@gmail.com>
|
| 802 |
+
Shubham Abhang <shubhamabhang77@gmail.com>
|
| 803 |
+
Vishesh Mangla <manglavishesh64@gmail.com>
|
| 804 |
+
Nicko van Someren <nicko@nicko.org>
|
| 805 |
+
dandiez <47832466+dandiez@users.noreply.github.com>
|
| 806 |
+
Frédéric Chapoton <fchapoton2@gmail.com>
|
| 807 |
+
jhanwar <f2015463@pilani.bits-pilani.ac.in>
|
| 808 |
+
Noumbissi valere Gille Geovan <noumbissivalere@gmail.com>
|
| 809 |
+
Salmista-94 <alejandrogroso@hotmail.com>
|
| 810 |
+
Shivani Kohli <shivanikohli.09@gmail.com>
|
| 811 |
+
Parker Berry <parkereberry@gmail.com>
|
| 812 |
+
Pragyan Mehrotra <pragyan18168@iiitd.ac.in>
|
| 813 |
+
Nabanita Dash <dashnabanita@gmail.com>
|
| 814 |
+
Gaetano Guerriero <x.guerriero@tin.it>
|
| 815 |
+
Ankit Raj Pandey <pandeyan@grinnell.edu>
|
| 816 |
+
Ritesh Kumar <ritesh99rakesh@gmail.com>
|
| 817 |
+
kangzhiq <709563092@qq.com>
|
| 818 |
+
Jun Lin <junlin0604@gmail.com>
|
| 819 |
+
Petr Kungurtsev <corwinat@gmail.com>
|
| 820 |
+
Anway De <anway1756@gmail.com>
|
| 821 |
+
znxftw <vishnu2101@gmail.com>
|
| 822 |
+
Denis Ivanenko <ivanenko@ucu.edu.ua>
|
| 823 |
+
Orestis Vaggelis <orestisvaggelis@mail.com>
|
| 824 |
+
Nikhil Maan <nikhilmaan22@gmail.com>
|
| 825 |
+
Abhinav Anand <abhinav.anand2807@gmail.com>
|
| 826 |
+
Qingsha Shi <googol.sqs@gmail.com>
|
| 827 |
+
Juan Barbosa <js.barbosa10@uniandes.edu.co>
|
| 828 |
+
Prionti Nasir <pdn3628@rit.edu>
|
| 829 |
+
Bharat Raghunathan <bharatraghunthan9767@gmail.com>
|
| 830 |
+
arooshiverma <av22@iitbbs.ac.in>
|
| 831 |
+
Christoph Gohle <ctg@mpq.mpg.de>
|
| 832 |
+
Charalampos Tsiagkalis <ctsiagkalis@uth.gr>
|
| 833 |
+
Daniel Sears <highpost@users.noreply.github.com>
|
| 834 |
+
Megan Ly <megan.ly@learnosity.com>
|
| 835 |
+
Sean P. Cornelius <spcornelius@gmail.com>
|
| 836 |
+
Erik R. Gomez <gomez@kth.se>
|
| 837 |
+
Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
|
| 838 |
+
Henry Metlov <genrih.metlov@gmail.com>
|
| 839 |
+
pekochun <hamburg_hamburger2000@yahoo.co.jp>
|
| 840 |
+
Bendik Samseth <b.samseth@gmail.com>
|
| 841 |
+
Vighnesh Shenoy <vighneshq@gmail.com>
|
| 842 |
+
Versus Void <versusvoid@gmail.com>
|
| 843 |
+
Denys Rybalka <rybalka.denis@gmail.com>
|
| 844 |
+
Mark Dickinson <dickinsm@gmail.com>
|
| 845 |
+
Rimi <rimibis@umich.edu>
|
| 846 |
+
rimibis <33387803+rimibis@users.noreply.github.com>
|
| 847 |
+
Steven Lee <stevenlee123@protonmail.com>
|
| 848 |
+
Gilles Schintgen <gschintgen@hambier.lu>
|
| 849 |
+
Abhi58 <abhijithbharadwaj58@gmail.com>
|
| 850 |
+
Tomasz Pytel <tompytel@gmail.com>
|
| 851 |
+
Aadit Kamat <aadit.k12@gmail.com>
|
| 852 |
+
Samesh <samesh.lakhotia@gmail.com>
|
| 853 |
+
Velibor Zeli <velibor@mech.kth.se>
|
| 854 |
+
Gabriel Bernardino <gabriel.bernardino@upf.edu>
|
| 855 |
+
Joseph Redfern <joseph@redfern.me>
|
| 856 |
+
Evelyn King <evelyn.cameron.king@gmail.com>
|
| 857 |
+
Miguel Marco <mmarco@unizar.es>
|
| 858 |
+
David Hagen <david@drhagen.com>
|
| 859 |
+
Hannah Kari <hannah.kari@marquette.edu>
|
| 860 |
+
Soniya Nayak <soniyanayak51@gmail.com>
|
| 861 |
+
Harsh Agarwal <hagarwal9200@gmail.com>
|
| 862 |
+
Enric Florit <efz1005@gmail.com>
|
| 863 |
+
Yogesh Mishra <ymishra013@gmail.com>
|
| 864 |
+
Denis Rykov <rykovd@gmail.com>
|
| 865 |
+
Ivan Tkachenko <me@ratijas.tk>
|
| 866 |
+
Kenneth Emeka Odoh <kenneth.odoh@gmail.com>
|
| 867 |
+
Stephan Seitz <stephan.seitz@fau.de>
|
| 868 |
+
Yeshwanth N <yeshsurya@gmail.com>
|
| 869 |
+
Oscar Gerardo Lazo Arjona <oscar.lazoarjona@physics.ox.ac.uk>
|
| 870 |
+
Srinivasa Arun Yeragudipati <ysarun1999@gmail.com>
|
| 871 |
+
Kirtan Mali <kirtanmali555@gmail.com>
|
| 872 |
+
TitanSnow <sweeto@live.cn>
|
| 873 |
+
Pengning Chao <8857165+PengningChao@users.noreply.github.com>
|
| 874 |
+
Louis Abraham <louis.abraham@yahoo.fr>
|
| 875 |
+
Morten Olsen Lysgaard <morten@lysgaard.no>
|
| 876 |
+
Akash Nagaraj (akasnaga) <akasnaga@cisco.com>
|
| 877 |
+
Akash Nagaraj <grassknoted@gmail.com>
|
| 878 |
+
Lauren Glattly <laurenglattly@gmail.com>
|
| 879 |
+
Hou-Rui <houruinus@gmail.com>
|
| 880 |
+
George Korepanov <gkorepanov.gk@gmail.com>
|
| 881 |
+
dranknight09 <cbhaavan@gmail.com>
|
| 882 |
+
aditisingh2362 <aditisingh2362@gmail.com>
|
| 883 |
+
Gina <Dr-G@users.noreply.github.com>
|
| 884 |
+
gregmedlock <gmedlo@gmail.com>
|
| 885 |
+
Georgios Giapitzakis Tzintanos <giorgosgiapis@mail.com>
|
| 886 |
+
Eric Wieser <wieser.eric@gmail.com>
|
| 887 |
+
Bradley Dowling <34559056+btdow@users.noreply.github.com>
|
| 888 |
+
Maria Marginean <33810762+mmargin@users.noreply.github.com>
|
| 889 |
+
Akash Agrawall <akash.wanted@gmail.com>
|
| 890 |
+
jgulian <josephdgulian@gmail.com>
|
| 891 |
+
Sourav Goyal <souravgl0@gmail.com>
|
| 892 |
+
Zlatan Vasović <zlatanvasovic@gmail.com>
|
| 893 |
+
Alex Meiburg <timeroot.alex@gmail.com>
|
| 894 |
+
Smit Lunagariya <smitlunagariya.mat18@itbhu.ac.in>
|
| 895 |
+
Naman Gera <namangera15@gmail.com>
|
| 896 |
+
Julien Palard <julien@palard.fr>
|
| 897 |
+
Dhruv Mendiratta <dhruvmendiratta6@gmail.com>
|
| 898 |
+
erdOne <36414270+erdOne@users.noreply.github.com>
|
| 899 |
+
risubaba <risubhjain1010@gmail.com>
|
| 900 |
+
abhinav28071999 <41710346+abhinav28071999@users.noreply.github.com>
|
| 901 |
+
Jisoo Song <jeesoo9595@snu.ac.kr>
|
| 902 |
+
Jaime R <38530589+Jaime02@users.noreply.github.com>
|
| 903 |
+
Vikrant Malik <vikrantmalik051@gmail.com>
|
| 904 |
+
Hardik Saini <43683678+Guardianofgotham@users.noreply.github.com>
|
| 905 |
+
Abhishek <uchiha@pop-os.localdomain>
|
| 906 |
+
Johannes Hartung <joha2@gmx.net>
|
| 907 |
+
Milan Jolly <milan.cs16@iitp.ac.in>
|
| 908 |
+
faizan2700 <syedfaizan824@gmail.com>
|
| 909 |
+
mohit <39158356+mohitacecode@users.noreply.github.com>
|
| 910 |
+
Mohit Gupta <mohitgupta@gmail.com>
|
| 911 |
+
Psycho-Pirate <prakharsaxena.civ18@iitbhu.ac.in>
|
| 912 |
+
Chanakya-Ekbote <ca10@iitbbs.ac.in>
|
| 913 |
+
Rashmi Shehana <rashmi.watagedara@syscolabs.com>
|
| 914 |
+
Jonty16117 <jonty@DESKTOP-J1O6ANP.localdomain>
|
| 915 |
+
Anubhav Gupta <anubhav.gupta.cse19@itbhu.ac.in>
|
| 916 |
+
Michal Grňo <m93a.cz@gmail.com>
|
| 917 |
+
vezeli <37907135+vezeli@users.noreply.github.com>
|
| 918 |
+
Tim Gates <tim.gates@iress.com>
|
| 919 |
+
Sandeep Murthy <smurthy@protonmail.ch>
|
| 920 |
+
Neil <mistersheik@gmail.com>
|
| 921 |
+
V1krant <46847915+V1krant@users.noreply.github.com>
|
| 922 |
+
alejandro <amartinhernan@gmail.com>
|
| 923 |
+
Riyan Dhiman <Riyandhiman14@gmail.com>
|
| 924 |
+
sbt4104 <sthorat661@gmail.com>
|
| 925 |
+
Seth Troisi <sethtroisi@google.com>
|
| 926 |
+
Bhaskar Gupta <guptabhanu1999@gmail.com>
|
| 927 |
+
Smit Gajjar <smitgajjar.gs@gmail.com>
|
| 928 |
+
rbl <rlee@grove.co>
|
| 929 |
+
Ilya Pchelintsev <ilya.pchelintsev@outlook.com>
|
| 930 |
+
Omar Wagih <o.wagih.ow@gmail.com>
|
| 931 |
+
prshnt19 <prashant.rawat216@gmail.com>
|
| 932 |
+
Johan Guzman <jguzm022@ucr.edu>
|
| 933 |
+
Vasileios Kalos <kalosbasileios@gmail.com>
|
| 934 |
+
BasileiosKal <61801875+BasileiosKal@users.noreply.github.com>
|
| 935 |
+
Shubham Thorat <37049710+sbt4104@users.noreply.github.com>
|
| 936 |
+
Arpan Chattopadhyay <f20180319@pilani.bits-pilani.ac.in>
|
| 937 |
+
Ashutosh Hathidara <ashutoshhathidara98@gmail.com>
|
| 938 |
+
Moses Paul R <iammosespaulr@gmail.com>
|
| 939 |
+
Saanidhya vats <saanidhyavats@gmail.com>
|
| 940 |
+
tnzl <you@example.com>
|
| 941 |
+
Vatsal Srivastava <alstav.trivas.sava@gmail.com>
|
| 942 |
+
Jean-Luc Herren <jlh@gmx.ch>
|
| 943 |
+
Dhruv Kothari <dhruvkothari22@gmail.com>
|
| 944 |
+
seadavis <45022599+seadavis@users.noreply.github.com>
|
| 945 |
+
kamimura <kamimura@live.jp>
|
| 946 |
+
slacker404 <pchantza@gmail.com>
|
| 947 |
+
Jaime Resano <gemailpersonal02@gmail.com>
|
| 948 |
+
Ebrahim Byagowi <ebrahim@gnu.org>
|
| 949 |
+
wuyudi <wuyudi119@163.com>
|
| 950 |
+
Akira Kyle <ak@akirakyle.com>
|
| 951 |
+
Calvin Jay Ross <calvinjayross@gmail.com>
|
| 952 |
+
Martin Thoma <info@martin-thoma.de>
|
| 953 |
+
Thomas A Caswell <tcaswell@gmail.com>
|
| 954 |
+
Lagaras Stelios <stel.lag@hotmail.com>
|
| 955 |
+
Jerry James <loganjerry@gmail.com>
|
| 956 |
+
Jan Kruse <janckruse@t-online.de>
|
| 957 |
+
Nathan Taylor <pecan.pine@gmail.com>
|
| 958 |
+
Vaishnav Damani <vaishnavdamani3496@gmail.com>
|
| 959 |
+
Mohit Shah <mohitshah3111999@gmail.com>
|
| 960 |
+
Mathias Louboutin <mathias.louboutin@gmail.com>
|
| 961 |
+
Marijan Smetko <marijansmetko123@gmail.com>
|
| 962 |
+
Dave Witte Morris <Dave.Morris@uleth.ca>
|
| 963 |
+
soumi7 <soumibardhan10@gmail.com>
|
| 964 |
+
Zhongshi <zj495@nyu.edu>
|
| 965 |
+
Wes Galbraith <galbwe92@gmail.com>
|
| 966 |
+
KaustubhDamania <kaustubh.damania@gmail.com>
|
| 967 |
+
w495 <w495@yandex-team.ru>
|
| 968 |
+
Akhil Rajput <akh1lrjput@gmail.com>
|
| 969 |
+
Markus Mohrhard <markus.mohrhard@googlemail.com>
|
| 970 |
+
Benjamin Wolba <mail@benjaminwolba.com>
|
| 971 |
+
彭于斌 <1931127624@qq.com>
|
| 972 |
+
Rudr Tiwari <rudrtiwari@gmail.com>
|
| 973 |
+
Aaryan Dewan <aaryandewan@yahoo.com>
|
| 974 |
+
Benedikt Placke <benedikt.placke@outlook.com>
|
| 975 |
+
Sneha Goddu <s.goddu@wustl.edu>
|
| 976 |
+
goddus <39923708+goddus@users.noreply.github.com>
|
| 977 |
+
Shivang Dubey <shivangdubey8@gmail.com>
|
| 978 |
+
Michael Greminger <michael.greminger@gmail.com>
|
| 979 |
+
Peter Cock <p.j.a.cock@googlemail.com>
|
| 980 |
+
Willem Melching <willem.melching@gmail.com>
|
| 981 |
+
Elias Basler <e.e.basler@protonmail.com>
|
| 982 |
+
Brandon David <brandon.david@zoho.com>
|
| 983 |
+
Abhay_Dhiman <abhaysdhimans@gmail.com>
|
| 984 |
+
Tasha Kim <jae_young_kim@brown.edu>
|
| 985 |
+
Ayush Malik <ayushmalik779@gmail.com>
|
| 986 |
+
Devesh Sawant <devesh47cool@gmail.com>
|
| 987 |
+
Wolfgang Stöcher <wolfgang@stoecher.com>
|
| 988 |
+
Sudeep Sidhu <sudeepmanilsidhu@gmail.com>
|
| 989 |
+
foice <foice.news@gmail.com>
|
| 990 |
+
Ben Payne <ben.is.located@gmail.com>
|
| 991 |
+
Muskan Kumar <31043527+muskanvk@users.noreply.github.com>
|
| 992 |
+
noam simcha finkelstein <noam.finkelstein@protonmail.com>
|
| 993 |
+
Garrett Folbe <gmfolbe@yahoo.com>
|
| 994 |
+
Islam Mansour <is3mansour@gmail.com>
|
| 995 |
+
Sayandip Halder <sayandiph4@gmail.com>
|
| 996 |
+
Shubham Agrawal <shubham.ag6845@gmail.com>
|
| 997 |
+
numbermaniac <5206120+numbermaniac@users.noreply.github.com>
|
| 998 |
+
Sakirul Alam <binarysakir@gmail.com>
|
| 999 |
+
Mohammed Bilal <r.mohammedbilal@gmail.com>
|
| 1000 |
+
Chris du Plessis <christopherjonduplessis@gmail.com>
|
| 1001 |
+
Coder-RG <rgoel1999@gmail.com>
|
| 1002 |
+
Ansh Mishra <anshmishra471@gmail.com>
|
| 1003 |
+
Alex Malins <github@alexmalins.com>
|
| 1004 |
+
Lorenzo Contento <lorenzo.contento@gmail.com>
|
| 1005 |
+
Naveen Sai <naveensaisreenivas@gmail.com>
|
| 1006 |
+
Shital Mule <shitalmule04@gmail.com>
|
| 1007 |
+
Amanda Dsouza <meezamanda@yahoo.com>
|
| 1008 |
+
Nijso Beishuizen <nijso@koolmees.numerically-related.com>
|
| 1009 |
+
Harry Zheng <harry@harryzheng.com>
|
| 1010 |
+
Felix Yan <felixonmars@archlinux.org>
|
| 1011 |
+
Constantin Mateescu <costica1234@me.com>
|
| 1012 |
+
Eva Tiwari <eva.tiwari@gmail.com>
|
| 1013 |
+
Aditya Kumar Sinha <adityakumar113141@gmail.com>
|
| 1014 |
+
Soumi Bardhan <51290447+Soumi7@users.noreply.github.com>
|
| 1015 |
+
Kaustubh Chaudhari <ckaustubhm06@gmail.com>
|
| 1016 |
+
Kristian Brünn <hello@kristianbrunn.com>
|
| 1017 |
+
Neel Gorasiya <mgorasiya1974@gmail.com>
|
| 1018 |
+
Akshat Sood <68052998+akshatsood2249@users.noreply.github.com>
|
| 1019 |
+
Jose M. Gomez <chemoki@gmail.com>
|
| 1020 |
+
Stefan Petrea <stefan@garage-coding.com>
|
| 1021 |
+
Praveen Sahu <povinsahu@gmail.com>
|
| 1022 |
+
Mark Bell <mark00bell@googlemail.com>
|
| 1023 |
+
AlexCQY <alex_chua@u.nus.edu>
|
| 1024 |
+
Fabian Froehlich <fabian@schaluck.com>
|
| 1025 |
+
Nikhil Gopalam <gopalamn@umich.edu>
|
| 1026 |
+
Kartik Sethi <kartiks31416@gmail.com>
|
| 1027 |
+
Muhammed Abdul Quadir Owais <quadirowais200@gmail.com>
|
| 1028 |
+
Harshit Yadav <harshityadav2k@gmail.com>
|
| 1029 |
+
Sidharth Mundhra <sidharthmundhra16@gmail.com>
|
| 1030 |
+
Suryam Arnav Kalra <suryamkalra35@gmail.com>
|
| 1031 |
+
Prince Gupta <codemastercpp@gmail.com>
|
| 1032 |
+
Kunal Singh <ksingh19136@gmail.com>
|
| 1033 |
+
Mayank Raj <mayank_1901cs35@iitp.ac.in>
|
| 1034 |
+
Achal Jain <2achaljain@gmail.com>
|
| 1035 |
+
Mario Maio <mario.maio@aruba.it>
|
| 1036 |
+
Aaron Stiff <69512633+AaronStiff@users.noreply.github.com>
|
| 1037 |
+
Wyatt Peak <wyattpeak@gmail.com>
|
| 1038 |
+
Bhaskar Joshi <bhaskar.joshi@research.iiit.ac.in>
|
| 1039 |
+
Aditya Jindal <jaditya8889@gmail.com>
|
| 1040 |
+
Vaibhav Bhat <vaibhav.bhat2097@gmail.com>
|
| 1041 |
+
Priyansh Rathi <techiepriyansh@gmail.com>
|
| 1042 |
+
Saket Kumar Singh <saketkumar1202@gmail.com>
|
| 1043 |
+
Yukai Chou <muzimuzhi@gmail.com>
|
| 1044 |
+
Qijia Liu <liumeo@pku.edu.cn>
|
| 1045 |
+
Paul Mandel <paulmandel@google.com>
|
| 1046 |
+
Nisarg Chaudhari <54911392+Nisarg-Chaudhari@users.noreply.github.com>
|
| 1047 |
+
Dominik Stańczak <stanczakdominik@gmail.com>
|
| 1048 |
+
Rodrigo Luger <rodluger@gmail.com>
|
| 1049 |
+
Marco Antônio Habitzreuter <mahabitzreuter@gmail.com>
|
| 1050 |
+
Ayush Bisht <bisht.ayush2001@gmail.com>
|
| 1051 |
+
Akshansh Bhatt <akshansh@tuta.io>
|
| 1052 |
+
Brandon T. Willard <brandonwillard@users.noreply.github.com>
|
| 1053 |
+
Thomas Aarholt <thomasaarholt@gmail.com>
|
| 1054 |
+
Hiren Chalodiya <hirenchalodiya99@gmail.com>
|
| 1055 |
+
Roland Dixon <rols121@gmail.com>
|
| 1056 |
+
dimasvq <dimas.vq.2020@bristol.ac.uk>
|
| 1057 |
+
Sagar231 <sagarfeb298@gmail.com>
|
| 1058 |
+
Michael Chu <michael02chu@gmail.com>
|
| 1059 |
+
Abby Ng <abigailjng@gmail.com>
|
| 1060 |
+
Angad Sandhu <55819847+angadsinghsandhu@users.noreply.github.com>
|
| 1061 |
+
Alexander Cockburn <alexander_cockburn12@hotmail.com>
|
| 1062 |
+
Yaser AlOsh <yaseralosh@outlook.com>
|
| 1063 |
+
Davide Sandonà <sandona.davide@gmail.com>
|
| 1064 |
+
Jonathan Gutow <gutow@uwosh.edu>
|
| 1065 |
+
Nihir Agarwal <f20180701@pilani.bits-pilani.ac.in>
|
| 1066 |
+
Lee Johnston <lee.johnston.100@gmail.com>
|
| 1067 |
+
Zach Carmichael <20629897+craymichael@users.noreply.github.com>
|
| 1068 |
+
Vijairam Ganesh Moorthy <vijairamg@gmail.com>
|
| 1069 |
+
Hanspeter Schmid <hanspeter.schmid@fhnw.ch>
|
| 1070 |
+
Ben Oostendorp <oostben@umich.edu>
|
| 1071 |
+
Nikita <nikita.student.cse19@itbhu.ac.in>
|
| 1072 |
+
Aman <amanmourya295@gmail.com>
|
| 1073 |
+
Shashank KS <shashankks0987@gmail.com>
|
| 1074 |
+
Aman Sharma <amansharma110603@gmail.com>
|
| 1075 |
+
Anup Parikh <parikhanupk@gmail.com>
|
| 1076 |
+
Lucy Mountain <lucymountain1@icloud.com>
|
| 1077 |
+
Miguel Torres Costa <miguelptcosta1995@gmail.com>
|
| 1078 |
+
Rikard Nordgren <rikard.nordgren@farmaci.uu.se>
|
| 1079 |
+
Arun sanganal <74652697+ArunSanganal@users.noreply.github.com>
|
| 1080 |
+
Kamlesh Joshi <72374645+kamleshjoshi8102@users.noreply.github.com>
|
| 1081 |
+
Joseph Rance <56409230+Joseph-Rance@users.noreply.github.com>
|
| 1082 |
+
Huangduirong <huangduirong@huawei.com>
|
| 1083 |
+
Nils Schulte <47043622+Schnilz@users.noreply.github.com>
|
| 1084 |
+
Matt Bogosian <matt@bogosian.net>
|
| 1085 |
+
Elisha Hollander <just4now666666@gmail.com>
|
| 1086 |
+
Aditya Ravuri <infprobscix@gmail.com>
|
| 1087 |
+
Mamidi Ratna Praneeth <praneethratna@gmail.com>
|
| 1088 |
+
Jeffrey Ryan <jeffaryan7@gmail.com>
|
| 1089 |
+
Jonathan Daniel <36337649+jond01@users.noreply.github.com>
|
| 1090 |
+
Robin Richard <raisin@ecomail.fr>
|
| 1091 |
+
Gautam Menghani <gum3ng@protonmail.com>
|
| 1092 |
+
Remco de Boer <29308176+redeboer@users.noreply.github.com>
|
| 1093 |
+
Sebastian East <sebastianeast@ymail.com>
|
| 1094 |
+
Evani Balasubramanyam <balasubramanyam.evani@gmail.com>
|
| 1095 |
+
Rahil Parikh <r.parikh@somaiya.edu>
|
| 1096 |
+
Jason Ross <jasonross1024@gmail.com>
|
| 1097 |
+
Joannah Nanjekye <joannah.nanjekye@ibm.com>
|
| 1098 |
+
Ayush Kumar <ayushk7102@gmail.com>
|
| 1099 |
+
Kshitij <kshitijparwani.mat18@itbhu.ac.in>
|
| 1100 |
+
Daniel Hyams <dhyams@gmail.com>
|
| 1101 |
+
alijosephine <alijosephine@gmail.com>
|
| 1102 |
+
Matthias Köppe <mkoeppe@math.ucdavis.edu>
|
| 1103 |
+
mohajain <mohajain99@gmail.com>
|
| 1104 |
+
Anibal M. Medina-Mardones <ammedmar@gmail.com>
|
| 1105 |
+
Travis Ens <ens.travis@gmail.com>
|
| 1106 |
+
Evgenia Karunus <lakesare@gmail.com>
|
| 1107 |
+
Risiraj Dey <risirajdey@gmail.com>
|
| 1108 |
+
lastcodestanding <rohang71604@gmail.com>
|
| 1109 |
+
Andrey Lekar <andrey_lekar@adoriasoft.com>
|
| 1110 |
+
Abbas Mohammed <42001049+iam-abbas@users.noreply.github.com>
|
| 1111 |
+
Anutosh Bhat <andersonbhat491@gmail.com>
|
| 1112 |
+
Steve Kieffer <sk@skieffer.info>
|
| 1113 |
+
Paul Spiering <paul@spiering.org>
|
| 1114 |
+
Pieter Gijsbers <p.gijsbers@tue.nl>
|
| 1115 |
+
Wang Ran (汪然) <wangr@smail.nju.edu.cn>
|
| 1116 |
+
naelsondouglas <naelson17@gmail.com>
|
| 1117 |
+
Aman Thakur <thakuraman22july@gmail.com>
|
| 1118 |
+
S. Hanko <suzy.hanko@gmail.com>
|
| 1119 |
+
Dennis Sweeney <sweeney.427@osu.edu>
|
| 1120 |
+
Gurpartap Singh <dhaliwal.gurpartap@gmail.com>
|
| 1121 |
+
Hampus Malmberg <hampus.malmberg88@gmail.com>
|
| 1122 |
+
scimax <max.kellermeier@hotmail.de>
|
| 1123 |
+
Nikhil Date <nikhil.s.date@gmail.com>
|
| 1124 |
+
Kuldeep Borkar Jr <kuldeepborkarjr765@gmail.com>
|
| 1125 |
+
AkuBrain <76952313+Franck2111@users.noreply.github.com>
|
| 1126 |
+
Leo Battle <leowbattle@gmail.com>
|
| 1127 |
+
Advait Pote <apote2050@gmail.com>
|
| 1128 |
+
Anurag Bhat <bhat.1@iitj.ac.in>
|
| 1129 |
+
Jeremy Monat <jemonat@calalum.org>
|
| 1130 |
+
Diane Tchuindjo <dtchuindjo@gmail.com>
|
| 1131 |
+
Tom Fryers <61272761+TomFryers@users.noreply.github.com>
|
| 1132 |
+
Zouhair <zouhair.mahboubi@gmail.com>
|
| 1133 |
+
zzj <29055749+zjzh@users.noreply.github.com>
|
| 1134 |
+
shubhayu09 <guptashubhayu601@gmail.com>
|
| 1135 |
+
Siddhant Jain <siddhantashoknagar@gmail.com>
|
| 1136 |
+
Tirthankar Mazumder <63574588+wermos@users.noreply.github.com>
|
| 1137 |
+
Sumit Kumar <mr.sumitkrr@gmail.com>
|
| 1138 |
+
Shivam Sagar <technoshivam12@gmail.com>
|
| 1139 |
+
Gaurav Jain <gjain369@gmail.com>
|
| 1140 |
+
Andrii Oriekhov <andriyorehov@gmail.com>
|
| 1141 |
+
Luis Talavera <luisfertalavera15@gmail.com>
|
| 1142 |
+
Arie Bovenberg <a.c.bovenberg@gmail.com>
|
| 1143 |
+
Carson McManus <carson.mcmanus1@gmail.com>
|
| 1144 |
+
Jack Schmidt <1107865+jackschmidt@users.noreply.github.com>
|
| 1145 |
+
Riley Britten <nrb1324@hotmail.com>
|
| 1146 |
+
Georges Khaznadar <georgesk@debian.org>
|
| 1147 |
+
Donald Wilson <donwilson1029@gmail.com>
|
| 1148 |
+
Timo Stienstra <timostienstra00@gmail.com>
|
| 1149 |
+
dispasha <dispasha@users.noreply.github.com>
|
| 1150 |
+
Saksham Alok <sakshamalok13@gmail.com>
|
| 1151 |
+
Varenyam Bhardwaj <varenyambhardwaj123@gmail.com>
|
| 1152 |
+
oittaa <8972248+oittaa@users.noreply.github.com>
|
| 1153 |
+
Omkaar <79257339+Pysics@users.noreply.github.com>
|
| 1154 |
+
Islem BOUZENIA <fi_bouzenia@esi.dz>
|
| 1155 |
+
extraymond <extraymond@gmail.com>
|
| 1156 |
+
Alexander Behrens <alex.git@gmx.net>
|
| 1157 |
+
user202729 <25191436+user202729@users.noreply.github.com>
|
| 1158 |
+
Pieter Eendebak <pieter.eendebak@gmail.com>
|
| 1159 |
+
Zaz Brown <zazbrown@zazbrown.com>
|
| 1160 |
+
ritikBhandari <ritikbhandari68@gmail.com>
|
| 1161 |
+
viocha <66580331+viocha@users.noreply.github.com>
|
| 1162 |
+
Arthur Ryman <arthur.ryman@gmail.com>
|
| 1163 |
+
Xiang Wu <hsiangwu@fb.com>
|
| 1164 |
+
tttc3 <T.Coxon2@lboro.ac.uk>
|
| 1165 |
+
Seth Poulsen <poulsenseth@yahoo.com>
|
| 1166 |
+
cocolato <haiizhu@outlook.com>
|
| 1167 |
+
Anton Golovanov <agolovanov256@gmail.com>
|
| 1168 |
+
Gareth Ma <grhkm21@gmail.com>
|
| 1169 |
+
Clément M.T. Robert <cr52@protonmail.com>
|
| 1170 |
+
Glenn Horton-Smith <glenn.hortonsmith@gmail.com>
|
| 1171 |
+
Karan <grgkaran03@gmail.com>
|
| 1172 |
+
Stefan Behnle <84378403+behnle@users.noreply.github.com>
|
| 1173 |
+
Shreyash Mishra <72146041+Shreyash-cyber@users.noreply.github.com>
|
| 1174 |
+
Arthur Milchior <arthur@milchior.fr>
|
| 1175 |
+
NotWearingPants <26556598+NotWearingPants@users.noreply.github.com>
|
| 1176 |
+
Ishan Pandhare <ishan9096137017@gmail.com>
|
| 1177 |
+
Carlos García Montoro <TrilceAC@gmail.com>
|
| 1178 |
+
Parcly Taxel <reddeloostw@gmail.com>
|
| 1179 |
+
Saicharan <saicharanhahaha@gmail.com>
|
| 1180 |
+
Kunal Sheth <kunal@kunalsheth.info>
|
| 1181 |
+
Biswadeep Purkayastha <98874428+metabiswadeep@users.noreply.github.com>
|
| 1182 |
+
Jyn Spring 琴春 <me@vx.st>
|
| 1183 |
+
Phil LeMaitre <phil_lemaitre@live.ca>
|
| 1184 |
+
Chris Kerr <chris.kerr@mykolab.ch>
|
| 1185 |
+
José Senart <jose.senart@gmail.com>
|
| 1186 |
+
Uwe L. Korn <uwelk@xhochy.com>
|
| 1187 |
+
ForeverHaibara <69423537+ForeverHaibara@users.noreply.github.com>
|
| 1188 |
+
Yves Tumushimire <yvestumushimire@gmail.com>
|
| 1189 |
+
wookie184 <wookie1840@gmail.com>
|
| 1190 |
+
Costor <pcs2009@web.de>
|
| 1191 |
+
Klaus Rettinghaus <klaus.rettinghaus@enote.com>
|
| 1192 |
+
Sam Brockie <sambrockie@icloud.com>
|
| 1193 |
+
Abhishek Patidar <1e9abhi1e10@gmail.com>
|
| 1194 |
+
Eric Demer <demer@mailbox.org>
|
| 1195 |
+
Pontus von Brömssen <pontus.vonbromssen+github@gmail.com>
|
| 1196 |
+
Victor Immanuel <chrollolucilfer1402@gmail.com>
|
| 1197 |
+
Evandro Bernardes <evbernardes@gmail.com>
|
| 1198 |
+
Michele Ceccacci <michelececcacci1@gmail.com>
|
| 1199 |
+
Ayush Aryan <ayush.aryan71@gmail.com>
|
| 1200 |
+
Kishore Gopalakrishnan <kishore96@gmail.com>
|
| 1201 |
+
Jan-Philipp Hoffmann <sonntagsgesicht@icloud.com>
|
| 1202 |
+
Daiki Takahashi <haru.td@gmail.com>
|
| 1203 |
+
Sayan Mitra <ee18b156@smail.iitm.ac.in>
|
| 1204 |
+
Aman Kumar Shukla <theprofessionalaman@gmail.com>
|
| 1205 |
+
Zoufiné Lauer-Baré <raszoufine@gmail.com>
|
| 1206 |
+
Charles Harris <erdos4d@gmail.com>
|
| 1207 |
+
Tejaswini Sanapathi <sastejaswini2002@gmail.com>
|
| 1208 |
+
Devansh <be19b002@smail.iitm.ac.in>
|
| 1209 |
+
Aaron Gokaslan <aaronGokaslan@gmail.com>
|
| 1210 |
+
Daan Koning (he/him) <daanolivierkoning@gmail.com>
|
| 1211 |
+
Steven Burns <royalstream@hotmail.com>
|
| 1212 |
+
Jay Patankar <patankarjays@gmail.com>
|
| 1213 |
+
Vivek Soni <sonisheela1977@gmail.com>
|
| 1214 |
+
Le Cong Minh Hieu <hieu.lecongminh@gmail.com>
|
| 1215 |
+
Sam Ritchie <sam@mentat.org>
|
| 1216 |
+
Maciej Skórski <maciej.skorski@gmail.com>
|
| 1217 |
+
Tilo Reneau-Cardoso <tiloreneau@gmail.com>
|
| 1218 |
+
Laurence Warne <laurencewarne@gmail.com>
|
| 1219 |
+
Lukas Molleman <Lukas.Molleman@gmail.com>
|
| 1220 |
+
Konstantinos Riganas <kostasriganas24@gmail.com>
|
| 1221 |
+
Grace Su <grace.duansu@gmail.com>
|
| 1222 |
+
Pedro Rosa <pedro_sxbr@usp.br>
|
| 1223 |
+
Abhinav Cillanki <abhinavcillanki@kgpian.iitkgp.ac.in>
|
| 1224 |
+
Baiyuan Qiu <1061688677@qq.com>
|
| 1225 |
+
Liwei Cai <cai.lw123@gmail.com>
|
| 1226 |
+
Daniel Weindl <daniel.weindl@helmholtz-muenchen.de>
|
| 1227 |
+
Isidora Araya <iarayaday@gmail.com>
|
| 1228 |
+
Seb Tiburzio <sebtiburzio@gmail.com>
|
| 1229 |
+
Victory Omole <vtomole2@gmail.com>
|
| 1230 |
+
Abhishek Chaudhary <ac5003@columbia.edu>
|
| 1231 |
+
Alexander Zhura <nice.zhura@list.ru>
|
| 1232 |
+
Shuai Zhou <shuaivzhou@berkeley.edu>
|
| 1233 |
+
Martin Manns <mmanns@gmx.net>
|
| 1234 |
+
John Möller <john.moller@outlook.com>
|
| 1235 |
+
zzc <1378113190@qq.com>
|
| 1236 |
+
Pablo Galindo Salgado <pablogsal@gmail.com>
|
| 1237 |
+
Johannes Kasimir <johannes.kasimir@math.lu.se>
|
| 1238 |
+
Theodore Dias <theodore.dias@hotmail.co.uk>
|
| 1239 |
+
Kaustubh <90597818+kaustubh-765@users.noreply.github.com>
|
| 1240 |
+
Idan Pazi <idan.kp@gmail.com>
|
| 1241 |
+
Bobby Palmer <bobbyp@umich.edu>
|
| 1242 |
+
Saikat Das <saikatdchhe@gmail.com>
|
| 1243 |
+
Suman mondal <smondal.qwerty@gmail.com>
|
| 1244 |
+
Taylan Sahin <info@taylansahin.net>
|
| 1245 |
+
Fabio Luporini <fabio@devitocodes.com>
|
| 1246 |
+
Oriel Malihi <orielmalihi1@gmail.com>
|
| 1247 |
+
Geetika Vadali <geetika.vadali4@gmail.com>
|
| 1248 |
+
Matthias Rettl <matthias.rettl@stud.unileoben.ac.at>
|
| 1249 |
+
Mikhail Remnev <maremnev@gmail.com>
|
| 1250 |
+
philwillnyc <56197213+philwillnyc@users.noreply.github.com>
|
| 1251 |
+
Raphael Lehner <raphael.lehner@gmail.com>
|
| 1252 |
+
Harry Mountain <harrymountain1@icloud.com>
|
| 1253 |
+
Bhavik Sachdev <b.sachdev1904@gmail.com>
|
| 1254 |
+
袁野 (Yuan Ye) <yuanyelele@tutanota.com>
|
| 1255 |
+
fazledyn-or <ataf@openrefactory.com>
|
| 1256 |
+
mohammedouahman <simofun85@gmail.com>
|
| 1257 |
+
K. Kraus <laqueray@googlemail.com>
|
| 1258 |
+
Zac Hatfield-Dodds <zac.hatfield.dodds@gmail.com>
|
| 1259 |
+
platypus <platypus.computerchip@gmail.com>
|
| 1260 |
+
codecruisader <nnisarg55@gmail.com>
|
| 1261 |
+
James Whitehead <whiteheadj@gmail.com>
|
| 1262 |
+
atharvParlikar <atharvparlikar@gmail.com>
|
| 1263 |
+
Ivan Petukhov <satels@gmail.com>
|
| 1264 |
+
Augusto Borges <borges.augustoar@gmail.com>
|
| 1265 |
+
Han Wei Ang <ang.h.w@u.nus.edu>
|
| 1266 |
+
Congxu Yang <u7189828@anu.edu.au>
|
| 1267 |
+
Saicharan <62512681+saicharan2804@users.noreply.github.com>
|
| 1268 |
+
Arnab Nandi <arnabnandi2002@gmail.com>
|
| 1269 |
+
Harrison Oates <48871176+HarrisonOates@users.noreply.github.com>
|
| 1270 |
+
Corey Cerovsek <corey@cerovsek.com>
|
| 1271 |
+
Harsh Kasat <hkasat@waymore.io>
|
| 1272 |
+
omahs <73983677+omahs@users.noreply.github.com>
|
| 1273 |
+
Pascal Gitz <pascal.gitz@hotmail.ch>
|
| 1274 |
+
Ravindu-Hirimuthugoda <ravindu.18@cse.mrt.ac.lk>
|
| 1275 |
+
Sophia Pustova <tripplezzed@gmail.com>
|
| 1276 |
+
George Pittock <gpittock4@gmail.com>
|
| 1277 |
+
Warren Jacinto <warrenjacinto@gmail.com>
|
| 1278 |
+
Sachin Singh <sachinishu02@gmail.com>
|
| 1279 |
+
Zedmat <104870914+harshkasat@users.noreply.github.com>
|
| 1280 |
+
Soumendra Ganguly <soumendraganguly@gmail.com>
|
| 1281 |
+
Samith Karunathilake <55777141+samithkavishke@users.noreply.github.com>
|
| 1282 |
+
Viraj Vekaria <virajv5593@gmail.com>
|
| 1283 |
+
Shishir Kushwaha <kushwahashishir1112@gmail.com>
|
| 1284 |
+
Ankit Kumar Singh <ankitdiswar10@gmail.com>
|
| 1285 |
+
Abhishek Kumar <abhishek.nitdelhi@gmail.com>
|
| 1286 |
+
Mohak Malviya <mohakmalviya2000@gmail.com>
|
| 1287 |
+
Matthias Liesenfeld <116307294+maliesen@users.noreply.github.com>
|
| 1288 |
+
dodo <palumbododo@gmail.com>
|
| 1289 |
+
Mohamed Rezk <mohrizq895@gmail.com>
|
| 1290 |
+
Tommaso Vaccari <05-gesto-follemente@icloud.com>
|
| 1291 |
+
Alexis Schotte <alexis.schotte@gmail.com>
|
| 1292 |
+
Lauren Yim <31467609+cherryblossom000@users.noreply.github.com>
|
| 1293 |
+
Prey Patel <patel.prey@iitgn.ac.in>
|
| 1294 |
+
Riccardo Di Girolamo <riccardodigirolamo01@gmail.com>
|
| 1295 |
+
Abhishek kumar <kumar325571@gmail.com>
|
| 1296 |
+
Sam Lubelsky <sammy56lt@gmail.com>
|
| 1297 |
+
Henrique Soares <henrique.c.soares@tecnico.ulisboa.pt>
|
| 1298 |
+
Vladimir Sereda <voffch@gmail.com>
|
| 1299 |
+
Hwayeon Kang <hwayeonniii@gmail.com>
|
| 1300 |
+
Raj Sapale <raj4sapale4@gmail.com>
|
| 1301 |
+
Gerald Teschl <gerald.teschl@univie.ac.at>
|
| 1302 |
+
Richard Samuel <98638849+samuelard7@users.noreply.github.com>
|
| 1303 |
+
HeeJae Chang <hechang@microsoft.com>
|
| 1304 |
+
Nick Harder <nharder@umich.edu>
|
| 1305 |
+
Ethan DeGuire <ethandeguire@gmail.com>
|
| 1306 |
+
Lorenz Winkler <lorenz.winkler@tuwien.ac.at>
|
| 1307 |
+
Richard Rodenbusch <rrodenbusch@gmail.com>
|
| 1308 |
+
Zhenxu Zhu <xzdlj@outlook.com>
|
| 1309 |
+
Mark van Gelder <m.j.vangelder@student.tudelft.nl>
|
| 1310 |
+
Mark van Gelder <mvgmvgmvg@live.com>
|
| 1311 |
+
Ishan Pandhare <91841626+Ishanned@users.noreply.github.com>
|
| 1312 |
+
James A. Preiss <jamesalanpreiss@gmail.com>
|
| 1313 |
+
Emile Fourcini <emile.fourcin1@gmail.com>
|
| 1314 |
+
Alberto Jiménez Ruiz <Alberto.Jimenez@uclm.es>
|
| 1315 |
+
João Bravo <joaocgbravo@tecnico.ulisboa.pt>
|
| 1316 |
+
Dean Price <dean1357price1357@gmail.com>
|
| 1317 |
+
Edward Z. Yang <ezyang@mit.edu>
|
| 1318 |
+
James Titus <titusjames299@gmail.com>
|
| 1319 |
+
Zhuoyuan Li <zy.li@stu.pku.edu.cn>
|
| 1320 |
+
Hugo Kerstens <hugo@kerstens.me>
|
| 1321 |
+
Jan Jancar <johny@neuromancer.sk>
|
| 1322 |
+
Andrew Mosson <amosson@yahoo.com>
|
| 1323 |
+
Marek Madejski <marekmadejski@yandex.com>
|
| 1324 |
+
Gonzalo Tornaría <tornaria@cmat.edu.uy>
|
| 1325 |
+
Peter Stahlecker <peter.stahlecker@gmail.com>
|
| 1326 |
+
Jean-François B <2589111+jfbu@users.noreply.github.com>
|
| 1327 |
+
Zexuan Zhou (Bruce) <zzx498636727@gmail.com>
|
| 1328 |
+
George Frolov <gosha@fro.lv>
|
| 1329 |
+
Corbet Elkins <corbet286@gmail.com>
|
| 1330 |
+
Håkon Kvernmoen <haakon.kvernmoen@gmail.com>
|
| 1331 |
+
Muhammad Maaz <mmaaz6004@gmail.com>
|
| 1332 |
+
Shishir Kushwaha <138311586+shishir-11@users.noreply.github.com>
|
| 1333 |
+
Matt Wang <mattwang44@gmail.com>
|
| 1334 |
+
bharatAmeria <21001019007@jcboseust.ac.in>
|
| 1335 |
+
Amir Ebrahimi <github@aebrahimi.com>
|
| 1336 |
+
Steven Esquea <steven.esquea@irreverente.net>
|
| 1337 |
+
Rishabh Kamboj <111004091+VectorNd@users.noreply.github.com>
|
| 1338 |
+
Aasim Ali <aasim250205@gmail.com>
|
| 1339 |
+
Ivan A. Melnikov <iv@altlinux.org>
|
| 1340 |
+
Borek Saheli <boreksaheli@gmail.com>
|
| 1341 |
+
Guido Roncarolo <guido.roncarolo@gmail.com>
|
| 1342 |
+
Quek Zi Yao <ziyaoqzy2001@gmail.com>
|
| 1343 |
+
Roelof Rietbroek <r.rietbroek@utwente.nl>
|
| 1344 |
+
MostafaGalal1 <mostafag649.mg@gmail.com>
|
| 1345 |
+
Au Huishan <huishan_au@outlook.com>
|
| 1346 |
+
Kris Katterjohn <katterjohn@gmail.com>
|
| 1347 |
+
Shiyao Guo <mivikq@gmail.com>
|
| 1348 |
+
Rushabh Mehta <mehtarushabh2005@gmail.com>
|
| 1349 |
+
Temiloluwa Yusuf ytemiloluwa@gmail.com ytemiloluwa <ytemiloluwa@gmail.com>
|
| 1350 |
+
Davi Laerte <davilae011@gmail.com>
|
| 1351 |
+
Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com>
|
| 1352 |
+
Harshit Gupta <harshithigh2001@gmail.com>
|
| 1353 |
+
Praveen Perumal <ppraveen98841@gmail.com>
|
| 1354 |
+
Kevin McWhirter <klmcw@yahoo.com>
|
| 1355 |
+
Prayag V <v.prayag2005@gmail.com>
|
| 1356 |
+
Lucas Kletzander <lucas.kletzander@gmail.com>
|
| 1357 |
+
Pratyksh Gupta <pratykshgupta9999@gmail.com>
|
| 1358 |
+
Leonardo Mangani <leomangani4@gmail.com>
|
| 1359 |
+
Karan Anand <anandkarancompsci@gmail.com>
|
| 1360 |
+
Gagan Mishra <simonsimple305@gmail.com>
|
| 1361 |
+
Krishnav Bajoria <bajoriakrishnav@gmail.com>
|
| 1362 |
+
Matt Ord <matthew.ord1@gmail.com>
|
| 1363 |
+
Jatin Bhardwaj <bhardwajjatin093@gmail.com>
|
| 1364 |
+
Prashant Tandon <tandonprashant101@gmail.com>
|
| 1365 |
+
Paramjit Singh <paramjit1071@gmail.com>
|
| 1366 |
+
João Rodrigues <abcjoao@hotmail.com>
|
| 1367 |
+
Alejandro García Prada <114813960+AlexGarciaPrada@users.noreply.github.com>
|
| 1368 |
+
Matthew Treinish <mtreinish@kortar.org>
|
| 1369 |
+
Clayton Rabideau <claytonrabideau@gmail.com>
|
| 1370 |
+
Victoria Koval <bictoriakoval16@gmail.com>
|
| 1371 |
+
Voaides Negustor Robert <134785947+voaidesr@users.noreply.github.com>
|
| 1372 |
+
Ovsk Mendov <bbb23exposed@gmail.com>
|
| 1373 |
+
David Brooks <dave@bcs.co.nz>
|
| 1374 |
+
Nicholas Laustrup <124007393+nicklaustrup@users.noreply.github.com>
|
| 1375 |
+
Harikrishna Srinivasan <harikrishnasri3@gmail.com>
|
| 1376 |
+
Mathis Cros <mathis.cros@telecom-paris.fr>
|
| 1377 |
+
Arnav Mummineni <45217840+RCoder01@users.noreply.github.com>
|
| 1378 |
+
Thangaraju Sibiraj <85477603+t-sibiraj@users.noreply.github.com>
|
| 1379 |
+
KJaybhaye <krushnajaybhaye01@gmail.com>
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2006-2023 SymPy Development Team
|
| 2 |
+
|
| 3 |
+
All rights reserved.
|
| 4 |
+
|
| 5 |
+
Redistribution and use in source and binary forms, with or without
|
| 6 |
+
modification, are permitted provided that the following conditions are met:
|
| 7 |
+
|
| 8 |
+
a. Redistributions of source code must retain the above copyright notice,
|
| 9 |
+
this list of conditions and the following disclaimer.
|
| 10 |
+
b. Redistributions in binary form must reproduce the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer in the
|
| 12 |
+
documentation and/or other materials provided with the distribution.
|
| 13 |
+
c. Neither the name of SymPy nor the names of its contributors
|
| 14 |
+
may be used to endorse or promote products derived from this software
|
| 15 |
+
without specific prior written permission.
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
| 19 |
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| 20 |
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
| 21 |
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
| 22 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
| 23 |
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
| 24 |
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 25 |
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 26 |
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
| 27 |
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
| 28 |
+
DAMAGE.
|
| 29 |
+
|
| 30 |
+
--------------------------------------------------------------------------------
|
| 31 |
+
|
| 32 |
+
Patches that were taken from the Diofant project (https://github.com/diofant/diofant)
|
| 33 |
+
are licensed as:
|
| 34 |
+
|
| 35 |
+
Copyright (c) 2006-2018 SymPy Development Team,
|
| 36 |
+
2013-2023 Sergey B Kirpichev
|
| 37 |
+
|
| 38 |
+
All rights reserved.
|
| 39 |
+
|
| 40 |
+
Redistribution and use in source and binary forms, with or without
|
| 41 |
+
modification, are permitted provided that the following conditions are met:
|
| 42 |
+
|
| 43 |
+
a. Redistributions of source code must retain the above copyright notice,
|
| 44 |
+
this list of conditions and the following disclaimer.
|
| 45 |
+
b. Redistributions in binary form must reproduce the above copyright
|
| 46 |
+
notice, this list of conditions and the following disclaimer in the
|
| 47 |
+
documentation and/or other materials provided with the distribution.
|
| 48 |
+
c. Neither the name of Diofant or SymPy nor the names of its contributors
|
| 49 |
+
may be used to endorse or promote products derived from this software
|
| 50 |
+
without specific prior written permission.
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
| 54 |
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| 55 |
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
| 56 |
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
| 57 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
| 58 |
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
| 59 |
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 60 |
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 61 |
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
| 62 |
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
| 63 |
+
DAMAGE.
|
| 64 |
+
|
| 65 |
+
--------------------------------------------------------------------------------
|
| 66 |
+
|
| 67 |
+
Submodules taken from the multipledispatch project (https://github.com/mrocklin/multipledispatch)
|
| 68 |
+
are licensed as:
|
| 69 |
+
|
| 70 |
+
Copyright (c) 2014 Matthew Rocklin
|
| 71 |
+
|
| 72 |
+
All rights reserved.
|
| 73 |
+
|
| 74 |
+
Redistribution and use in source and binary forms, with or without
|
| 75 |
+
modification, are permitted provided that the following conditions are met:
|
| 76 |
+
|
| 77 |
+
a. Redistributions of source code must retain the above copyright notice,
|
| 78 |
+
this list of conditions and the following disclaimer.
|
| 79 |
+
b. Redistributions in binary form must reproduce the above copyright
|
| 80 |
+
notice, this list of conditions and the following disclaimer in the
|
| 81 |
+
documentation and/or other materials provided with the distribution.
|
| 82 |
+
c. Neither the name of multipledispatch nor the names of its contributors
|
| 83 |
+
may be used to endorse or promote products derived from this software
|
| 84 |
+
without specific prior written permission.
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
| 88 |
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| 89 |
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
| 90 |
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
| 91 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
| 92 |
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
| 93 |
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 94 |
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 95 |
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
| 96 |
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
| 97 |
+
DAMAGE.
|
| 98 |
+
|
| 99 |
+
--------------------------------------------------------------------------------
|
| 100 |
+
|
| 101 |
+
The files under the directory sympy/parsing/autolev/tests/pydy-example-repo
|
| 102 |
+
are directly copied from PyDy project and are licensed as:
|
| 103 |
+
|
| 104 |
+
Copyright (c) 2009-2023, PyDy Authors
|
| 105 |
+
All rights reserved.
|
| 106 |
+
|
| 107 |
+
Redistribution and use in source and binary forms, with or without
|
| 108 |
+
modification, are permitted provided that the following conditions are met:
|
| 109 |
+
|
| 110 |
+
* Redistributions of source code must retain the above copyright
|
| 111 |
+
notice, this list of conditions and the following disclaimer.
|
| 112 |
+
* Redistributions in binary form must reproduce the above copyright
|
| 113 |
+
notice, this list of conditions and the following disclaimer in the
|
| 114 |
+
documentation and/or other materials provided with the distribution.
|
| 115 |
+
* Neither the name of this project nor the names of its contributors may be
|
| 116 |
+
used to endorse or promote products derived from this software without
|
| 117 |
+
specific prior written permission.
|
| 118 |
+
|
| 119 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
| 120 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 121 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 122 |
+
DISCLAIMED. IN NO EVENT SHALL PYDY AUTHORS BE LIABLE FOR ANY DIRECT,
|
| 123 |
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 124 |
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 125 |
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
| 126 |
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
| 127 |
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
| 128 |
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 129 |
+
|
| 130 |
+
--------------------------------------------------------------------------------
|
| 131 |
+
|
| 132 |
+
The files under the directory sympy/parsing/latex
|
| 133 |
+
are directly copied from latex2sympy project and are licensed as:
|
| 134 |
+
|
| 135 |
+
Copyright 2016, latex2sympy
|
| 136 |
+
|
| 137 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 138 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 139 |
+
in the Software without restriction, including without limitation the rights
|
| 140 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 141 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 142 |
+
furnished to do so, subject to the following conditions:
|
| 143 |
+
|
| 144 |
+
The above copyright notice and this permission notice shall be included in all
|
| 145 |
+
copies or substantial portions of the Software.
|
| 146 |
+
|
| 147 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 148 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 149 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 150 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 151 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 152 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 153 |
+
SOFTWARE.
|
venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
isympy
|
| 2 |
+
sympy
|
venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_dimensionsystem.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.symbol import symbols
|
| 2 |
+
from sympy.matrices.dense import (Matrix, eye)
|
| 3 |
+
from sympy.physics.units.definitions.dimension_definitions import (
|
| 4 |
+
action, current, length, mass, time,
|
| 5 |
+
velocity)
|
| 6 |
+
from sympy.physics.units.dimensions import DimensionSystem
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def test_extend():
|
| 10 |
+
ms = DimensionSystem((length, time), (velocity,))
|
| 11 |
+
|
| 12 |
+
mks = ms.extend((mass,), (action,))
|
| 13 |
+
|
| 14 |
+
res = DimensionSystem((length, time, mass), (velocity, action))
|
| 15 |
+
assert mks.base_dims == res.base_dims
|
| 16 |
+
assert mks.derived_dims == res.derived_dims
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def test_list_dims():
|
| 20 |
+
dimsys = DimensionSystem((length, time, mass))
|
| 21 |
+
|
| 22 |
+
assert dimsys.list_can_dims == (length, mass, time)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_dim_can_vector():
|
| 26 |
+
dimsys = DimensionSystem(
|
| 27 |
+
[length, mass, time],
|
| 28 |
+
[velocity, action],
|
| 29 |
+
{
|
| 30 |
+
velocity: {length: 1, time: -1}
|
| 31 |
+
}
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
assert dimsys.dim_can_vector(length) == Matrix([1, 0, 0])
|
| 35 |
+
assert dimsys.dim_can_vector(velocity) == Matrix([1, 0, -1])
|
| 36 |
+
|
| 37 |
+
dimsys = DimensionSystem(
|
| 38 |
+
(length, velocity, action),
|
| 39 |
+
(mass, time),
|
| 40 |
+
{
|
| 41 |
+
time: {length: 1, velocity: -1}
|
| 42 |
+
}
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
assert dimsys.dim_can_vector(length) == Matrix([0, 1, 0])
|
| 46 |
+
assert dimsys.dim_can_vector(velocity) == Matrix([0, 0, 1])
|
| 47 |
+
assert dimsys.dim_can_vector(time) == Matrix([0, 1, -1])
|
| 48 |
+
|
| 49 |
+
dimsys = DimensionSystem(
|
| 50 |
+
(length, mass, time),
|
| 51 |
+
(velocity, action),
|
| 52 |
+
{velocity: {length: 1, time: -1},
|
| 53 |
+
action: {mass: 1, length: 2, time: -1}})
|
| 54 |
+
|
| 55 |
+
assert dimsys.dim_vector(length) == Matrix([1, 0, 0])
|
| 56 |
+
assert dimsys.dim_vector(velocity) == Matrix([1, 0, -1])
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def test_inv_can_transf_matrix():
|
| 60 |
+
dimsys = DimensionSystem((length, mass, time))
|
| 61 |
+
assert dimsys.inv_can_transf_matrix == eye(3)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def test_can_transf_matrix():
|
| 65 |
+
dimsys = DimensionSystem((length, mass, time))
|
| 66 |
+
assert dimsys.can_transf_matrix == eye(3)
|
| 67 |
+
|
| 68 |
+
dimsys = DimensionSystem((length, velocity, action))
|
| 69 |
+
assert dimsys.can_transf_matrix == eye(3)
|
| 70 |
+
|
| 71 |
+
dimsys = DimensionSystem((length, time), (velocity,), {velocity: {length: 1, time: -1}})
|
| 72 |
+
assert dimsys.can_transf_matrix == eye(2)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_is_consistent():
|
| 76 |
+
assert DimensionSystem((length, time)).is_consistent is True
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def test_print_dim_base():
|
| 80 |
+
mksa = DimensionSystem(
|
| 81 |
+
(length, time, mass, current),
|
| 82 |
+
(action,),
|
| 83 |
+
{action: {mass: 1, length: 2, time: -1}})
|
| 84 |
+
L, M, T = symbols("L M T")
|
| 85 |
+
assert mksa.print_dim_base(action) == L**2*M/T
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def test_dim():
|
| 89 |
+
dimsys = DimensionSystem(
|
| 90 |
+
(length, mass, time),
|
| 91 |
+
(velocity, action),
|
| 92 |
+
{velocity: {length: 1, time: -1},
|
| 93 |
+
action: {mass: 1, length: 2, time: -1}}
|
| 94 |
+
)
|
| 95 |
+
assert dimsys.dim == 3
|
venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_prefixes.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.mul import Mul
|
| 2 |
+
from sympy.core.numbers import Rational
|
| 3 |
+
from sympy.core.singleton import S
|
| 4 |
+
from sympy.core.symbol import (Symbol, symbols)
|
| 5 |
+
from sympy.physics.units import Quantity, length, meter, W
|
| 6 |
+
from sympy.physics.units.prefixes import PREFIXES, Prefix, prefix_unit, kilo, \
|
| 7 |
+
kibi
|
| 8 |
+
from sympy.physics.units.systems import SI
|
| 9 |
+
|
| 10 |
+
x = Symbol('x')
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def test_prefix_operations():
|
| 14 |
+
m = PREFIXES['m']
|
| 15 |
+
k = PREFIXES['k']
|
| 16 |
+
M = PREFIXES['M']
|
| 17 |
+
|
| 18 |
+
dodeca = Prefix('dodeca', 'dd', 1, base=12)
|
| 19 |
+
|
| 20 |
+
assert m * k is S.One
|
| 21 |
+
assert m * W == W / 1000
|
| 22 |
+
assert k * k == M
|
| 23 |
+
assert 1 / m == k
|
| 24 |
+
assert k / m == M
|
| 25 |
+
|
| 26 |
+
assert dodeca * dodeca == 144
|
| 27 |
+
assert 1 / dodeca == S.One / 12
|
| 28 |
+
assert k / dodeca == S(1000) / 12
|
| 29 |
+
assert dodeca / dodeca is S.One
|
| 30 |
+
|
| 31 |
+
m = Quantity("fake_meter")
|
| 32 |
+
SI.set_quantity_dimension(m, S.One)
|
| 33 |
+
SI.set_quantity_scale_factor(m, S.One)
|
| 34 |
+
|
| 35 |
+
assert dodeca * m == 12 * m
|
| 36 |
+
assert dodeca / m == 12 / m
|
| 37 |
+
|
| 38 |
+
expr1 = kilo * 3
|
| 39 |
+
assert isinstance(expr1, Mul)
|
| 40 |
+
assert expr1.args == (3, kilo)
|
| 41 |
+
|
| 42 |
+
expr2 = kilo * x
|
| 43 |
+
assert isinstance(expr2, Mul)
|
| 44 |
+
assert expr2.args == (x, kilo)
|
| 45 |
+
|
| 46 |
+
expr3 = kilo / 3
|
| 47 |
+
assert isinstance(expr3, Mul)
|
| 48 |
+
assert expr3.args == (Rational(1, 3), kilo)
|
| 49 |
+
assert expr3.args == (S.One/3, kilo)
|
| 50 |
+
|
| 51 |
+
expr4 = kilo / x
|
| 52 |
+
assert isinstance(expr4, Mul)
|
| 53 |
+
assert expr4.args == (1/x, kilo)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def test_prefix_unit():
|
| 57 |
+
m = Quantity("fake_meter", abbrev="m")
|
| 58 |
+
m.set_global_relative_scale_factor(1, meter)
|
| 59 |
+
|
| 60 |
+
pref = {"m": PREFIXES["m"], "c": PREFIXES["c"], "d": PREFIXES["d"]}
|
| 61 |
+
|
| 62 |
+
q1 = Quantity("millifake_meter", abbrev="mm")
|
| 63 |
+
q2 = Quantity("centifake_meter", abbrev="cm")
|
| 64 |
+
q3 = Quantity("decifake_meter", abbrev="dm")
|
| 65 |
+
|
| 66 |
+
SI.set_quantity_dimension(q1, length)
|
| 67 |
+
|
| 68 |
+
SI.set_quantity_scale_factor(q1, PREFIXES["m"])
|
| 69 |
+
SI.set_quantity_scale_factor(q1, PREFIXES["c"])
|
| 70 |
+
SI.set_quantity_scale_factor(q1, PREFIXES["d"])
|
| 71 |
+
|
| 72 |
+
res = [q1, q2, q3]
|
| 73 |
+
|
| 74 |
+
prefs = prefix_unit(m, pref)
|
| 75 |
+
assert set(prefs) == set(res)
|
| 76 |
+
assert {v.abbrev for v in prefs} == set(symbols("mm,cm,dm"))
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def test_bases():
|
| 80 |
+
assert kilo.base == 10
|
| 81 |
+
assert kibi.base == 2
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def test_repr():
|
| 85 |
+
assert eval(repr(kilo)) == kilo
|
| 86 |
+
assert eval(repr(kibi)) == kibi
|
venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_quantities.py
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
|
| 3 |
+
from sympy.core.add import Add
|
| 4 |
+
from sympy.core.function import (Function, diff)
|
| 5 |
+
from sympy.core.numbers import (Number, Rational)
|
| 6 |
+
from sympy.core.singleton import S
|
| 7 |
+
from sympy.core.symbol import (Symbol, symbols)
|
| 8 |
+
from sympy.functions.elementary.complexes import Abs
|
| 9 |
+
from sympy.functions.elementary.exponential import (exp, log)
|
| 10 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 11 |
+
from sympy.functions.elementary.trigonometric import sin
|
| 12 |
+
from sympy.integrals.integrals import integrate
|
| 13 |
+
from sympy.physics.units import (amount_of_substance, area, convert_to, find_unit,
|
| 14 |
+
volume, kilometer, joule, molar_gas_constant,
|
| 15 |
+
vacuum_permittivity, elementary_charge, volt,
|
| 16 |
+
ohm)
|
| 17 |
+
from sympy.physics.units.definitions import (amu, au, centimeter, coulomb,
|
| 18 |
+
day, foot, grams, hour, inch, kg, km, m, meter, millimeter,
|
| 19 |
+
minute, quart, s, second, speed_of_light, bit,
|
| 20 |
+
byte, kibibyte, mebibyte, gibibyte, tebibyte, pebibyte, exbibyte,
|
| 21 |
+
kilogram, gravitational_constant, electron_rest_mass)
|
| 22 |
+
|
| 23 |
+
from sympy.physics.units.definitions.dimension_definitions import (
|
| 24 |
+
Dimension, charge, length, time, temperature, pressure,
|
| 25 |
+
energy, mass
|
| 26 |
+
)
|
| 27 |
+
from sympy.physics.units.prefixes import PREFIXES, kilo
|
| 28 |
+
from sympy.physics.units.quantities import PhysicalConstant, Quantity
|
| 29 |
+
from sympy.physics.units.systems import SI
|
| 30 |
+
from sympy.testing.pytest import raises
|
| 31 |
+
|
| 32 |
+
k = PREFIXES["k"]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def test_str_repr():
|
| 36 |
+
assert str(kg) == "kilogram"
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_eq():
|
| 40 |
+
# simple test
|
| 41 |
+
assert 10*m == 10*m
|
| 42 |
+
assert 10*m != 10*s
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_convert_to():
|
| 46 |
+
q = Quantity("q1")
|
| 47 |
+
q.set_global_relative_scale_factor(S(5000), meter)
|
| 48 |
+
|
| 49 |
+
assert q.convert_to(m) == 5000*m
|
| 50 |
+
|
| 51 |
+
assert speed_of_light.convert_to(m / s) == 299792458 * m / s
|
| 52 |
+
assert day.convert_to(s) == 86400*s
|
| 53 |
+
|
| 54 |
+
# Wrong dimension to convert:
|
| 55 |
+
assert q.convert_to(s) == q
|
| 56 |
+
assert speed_of_light.convert_to(m) == speed_of_light
|
| 57 |
+
|
| 58 |
+
expr = joule*second
|
| 59 |
+
conv = convert_to(expr, joule)
|
| 60 |
+
assert conv == joule*second
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def test_Quantity_definition():
|
| 64 |
+
q = Quantity("s10", abbrev="sabbr")
|
| 65 |
+
q.set_global_relative_scale_factor(10, second)
|
| 66 |
+
u = Quantity("u", abbrev="dam")
|
| 67 |
+
u.set_global_relative_scale_factor(10, meter)
|
| 68 |
+
km = Quantity("km")
|
| 69 |
+
km.set_global_relative_scale_factor(kilo, meter)
|
| 70 |
+
v = Quantity("u")
|
| 71 |
+
v.set_global_relative_scale_factor(5*kilo, meter)
|
| 72 |
+
|
| 73 |
+
assert q.scale_factor == 10
|
| 74 |
+
assert q.dimension == time
|
| 75 |
+
assert q.abbrev == Symbol("sabbr")
|
| 76 |
+
|
| 77 |
+
assert u.dimension == length
|
| 78 |
+
assert u.scale_factor == 10
|
| 79 |
+
assert u.abbrev == Symbol("dam")
|
| 80 |
+
|
| 81 |
+
assert km.scale_factor == 1000
|
| 82 |
+
assert km.func(*km.args) == km
|
| 83 |
+
assert km.func(*km.args).args == km.args
|
| 84 |
+
|
| 85 |
+
assert v.dimension == length
|
| 86 |
+
assert v.scale_factor == 5000
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def test_abbrev():
|
| 90 |
+
u = Quantity("u")
|
| 91 |
+
u.set_global_relative_scale_factor(S.One, meter)
|
| 92 |
+
|
| 93 |
+
assert u.name == Symbol("u")
|
| 94 |
+
assert u.abbrev == Symbol("u")
|
| 95 |
+
|
| 96 |
+
u = Quantity("u", abbrev="om")
|
| 97 |
+
u.set_global_relative_scale_factor(S(2), meter)
|
| 98 |
+
|
| 99 |
+
assert u.name == Symbol("u")
|
| 100 |
+
assert u.abbrev == Symbol("om")
|
| 101 |
+
assert u.scale_factor == 2
|
| 102 |
+
assert isinstance(u.scale_factor, Number)
|
| 103 |
+
|
| 104 |
+
u = Quantity("u", abbrev="ikm")
|
| 105 |
+
u.set_global_relative_scale_factor(3*kilo, meter)
|
| 106 |
+
|
| 107 |
+
assert u.abbrev == Symbol("ikm")
|
| 108 |
+
assert u.scale_factor == 3000
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def test_print():
|
| 112 |
+
u = Quantity("unitname", abbrev="dam")
|
| 113 |
+
assert repr(u) == "unitname"
|
| 114 |
+
assert str(u) == "unitname"
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def test_Quantity_eq():
|
| 118 |
+
u = Quantity("u", abbrev="dam")
|
| 119 |
+
v = Quantity("v1")
|
| 120 |
+
assert u != v
|
| 121 |
+
v = Quantity("v2", abbrev="ds")
|
| 122 |
+
assert u != v
|
| 123 |
+
v = Quantity("v3", abbrev="dm")
|
| 124 |
+
assert u != v
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def test_add_sub():
|
| 128 |
+
u = Quantity("u")
|
| 129 |
+
v = Quantity("v")
|
| 130 |
+
w = Quantity("w")
|
| 131 |
+
|
| 132 |
+
u.set_global_relative_scale_factor(S(10), meter)
|
| 133 |
+
v.set_global_relative_scale_factor(S(5), meter)
|
| 134 |
+
w.set_global_relative_scale_factor(S(2), second)
|
| 135 |
+
|
| 136 |
+
assert isinstance(u + v, Add)
|
| 137 |
+
assert (u + v.convert_to(u)) == (1 + S.Half)*u
|
| 138 |
+
assert isinstance(u - v, Add)
|
| 139 |
+
assert (u - v.convert_to(u)) == S.Half*u
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def test_quantity_abs():
|
| 143 |
+
v_w1 = Quantity('v_w1')
|
| 144 |
+
v_w2 = Quantity('v_w2')
|
| 145 |
+
v_w3 = Quantity('v_w3')
|
| 146 |
+
|
| 147 |
+
v_w1.set_global_relative_scale_factor(1, meter/second)
|
| 148 |
+
v_w2.set_global_relative_scale_factor(1, meter/second)
|
| 149 |
+
v_w3.set_global_relative_scale_factor(1, meter/second)
|
| 150 |
+
|
| 151 |
+
expr = v_w3 - Abs(v_w1 - v_w2)
|
| 152 |
+
|
| 153 |
+
assert SI.get_dimensional_expr(v_w1) == (length/time).name
|
| 154 |
+
|
| 155 |
+
Dq = Dimension(SI.get_dimensional_expr(expr))
|
| 156 |
+
|
| 157 |
+
assert SI.get_dimension_system().get_dimensional_dependencies(Dq) == {
|
| 158 |
+
length: 1,
|
| 159 |
+
time: -1,
|
| 160 |
+
}
|
| 161 |
+
assert meter == sqrt(meter**2)
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def test_check_unit_consistency():
|
| 165 |
+
u = Quantity("u")
|
| 166 |
+
v = Quantity("v")
|
| 167 |
+
w = Quantity("w")
|
| 168 |
+
|
| 169 |
+
u.set_global_relative_scale_factor(S(10), meter)
|
| 170 |
+
v.set_global_relative_scale_factor(S(5), meter)
|
| 171 |
+
w.set_global_relative_scale_factor(S(2), second)
|
| 172 |
+
|
| 173 |
+
def check_unit_consistency(expr):
|
| 174 |
+
SI._collect_factor_and_dimension(expr)
|
| 175 |
+
|
| 176 |
+
raises(ValueError, lambda: check_unit_consistency(u + w))
|
| 177 |
+
raises(ValueError, lambda: check_unit_consistency(u - w))
|
| 178 |
+
raises(ValueError, lambda: check_unit_consistency(u + 1))
|
| 179 |
+
raises(ValueError, lambda: check_unit_consistency(u - 1))
|
| 180 |
+
raises(ValueError, lambda: check_unit_consistency(1 - exp(u / w)))
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def test_mul_div():
|
| 184 |
+
u = Quantity("u")
|
| 185 |
+
v = Quantity("v")
|
| 186 |
+
t = Quantity("t")
|
| 187 |
+
ut = Quantity("ut")
|
| 188 |
+
v2 = Quantity("v")
|
| 189 |
+
|
| 190 |
+
u.set_global_relative_scale_factor(S(10), meter)
|
| 191 |
+
v.set_global_relative_scale_factor(S(5), meter)
|
| 192 |
+
t.set_global_relative_scale_factor(S(2), second)
|
| 193 |
+
ut.set_global_relative_scale_factor(S(20), meter*second)
|
| 194 |
+
v2.set_global_relative_scale_factor(S(5), meter/second)
|
| 195 |
+
|
| 196 |
+
assert 1 / u == u**(-1)
|
| 197 |
+
assert u / 1 == u
|
| 198 |
+
|
| 199 |
+
v1 = u / t
|
| 200 |
+
v2 = v
|
| 201 |
+
|
| 202 |
+
# Pow only supports structural equality:
|
| 203 |
+
assert v1 != v2
|
| 204 |
+
assert v1 == v2.convert_to(v1)
|
| 205 |
+
|
| 206 |
+
# TODO: decide whether to allow such expression in the future
|
| 207 |
+
# (requires somehow manipulating the core).
|
| 208 |
+
# assert u / Quantity('l2', dimension=length, scale_factor=2) == 5
|
| 209 |
+
|
| 210 |
+
assert u * 1 == u
|
| 211 |
+
|
| 212 |
+
ut1 = u * t
|
| 213 |
+
ut2 = ut
|
| 214 |
+
|
| 215 |
+
# Mul only supports structural equality:
|
| 216 |
+
assert ut1 != ut2
|
| 217 |
+
assert ut1 == ut2.convert_to(ut1)
|
| 218 |
+
|
| 219 |
+
# Mul only supports structural equality:
|
| 220 |
+
lp1 = Quantity("lp1")
|
| 221 |
+
lp1.set_global_relative_scale_factor(S(2), 1/meter)
|
| 222 |
+
assert u * lp1 != 20
|
| 223 |
+
|
| 224 |
+
assert u**0 == 1
|
| 225 |
+
assert u**1 == u
|
| 226 |
+
|
| 227 |
+
# TODO: Pow only support structural equality:
|
| 228 |
+
u2 = Quantity("u2")
|
| 229 |
+
u3 = Quantity("u3")
|
| 230 |
+
u2.set_global_relative_scale_factor(S(100), meter**2)
|
| 231 |
+
u3.set_global_relative_scale_factor(Rational(1, 10), 1/meter)
|
| 232 |
+
|
| 233 |
+
assert u ** 2 != u2
|
| 234 |
+
assert u ** -1 != u3
|
| 235 |
+
|
| 236 |
+
assert u ** 2 == u2.convert_to(u)
|
| 237 |
+
assert u ** -1 == u3.convert_to(u)
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def test_units():
|
| 241 |
+
assert convert_to((5*m/s * day) / km, 1) == 432
|
| 242 |
+
assert convert_to(foot / meter, meter) == Rational(3048, 10000)
|
| 243 |
+
# amu is a pure mass so mass/mass gives a number, not an amount (mol)
|
| 244 |
+
# TODO: need better simplification routine:
|
| 245 |
+
assert str(convert_to(grams/amu, grams).n(2)) == '6.0e+23'
|
| 246 |
+
|
| 247 |
+
# Light from the sun needs about 8.3 minutes to reach earth
|
| 248 |
+
t = (1*au / speed_of_light) / minute
|
| 249 |
+
# TODO: need a better way to simplify expressions containing units:
|
| 250 |
+
t = convert_to(convert_to(t, meter / minute), meter)
|
| 251 |
+
assert t.simplify() == Rational(49865956897, 5995849160)
|
| 252 |
+
|
| 253 |
+
# TODO: fix this, it should give `m` without `Abs`
|
| 254 |
+
assert sqrt(m**2) == m
|
| 255 |
+
assert (sqrt(m))**2 == m
|
| 256 |
+
|
| 257 |
+
t = Symbol('t')
|
| 258 |
+
assert integrate(t*m/s, (t, 1*s, 5*s)) == 12*m*s
|
| 259 |
+
assert (t * m/s).integrate((t, 1*s, 5*s)) == 12*m*s
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
def test_issue_quart():
|
| 263 |
+
assert convert_to(4 * quart / inch ** 3, meter) == 231
|
| 264 |
+
assert convert_to(4 * quart / inch ** 3, millimeter) == 231
|
| 265 |
+
|
| 266 |
+
def test_electron_rest_mass():
|
| 267 |
+
assert convert_to(electron_rest_mass, kilogram) == 9.1093837015e-31*kilogram
|
| 268 |
+
assert convert_to(electron_rest_mass, grams) == 9.1093837015e-28*grams
|
| 269 |
+
|
| 270 |
+
def test_issue_5565():
|
| 271 |
+
assert (m < s).is_Relational
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def test_find_unit():
|
| 275 |
+
assert find_unit('coulomb') == ['coulomb', 'coulombs', 'coulomb_constant']
|
| 276 |
+
assert find_unit(coulomb) == ['C', 'coulomb', 'coulombs', 'planck_charge', 'elementary_charge']
|
| 277 |
+
assert find_unit(charge) == ['C', 'coulomb', 'coulombs', 'planck_charge', 'elementary_charge']
|
| 278 |
+
assert find_unit(inch) == [
|
| 279 |
+
'm', 'au', 'cm', 'dm', 'ft', 'km', 'ly', 'mi', 'mm', 'nm', 'pm', 'um', 'yd',
|
| 280 |
+
'nmi', 'feet', 'foot', 'inch', 'mile', 'yard', 'meter', 'miles', 'yards',
|
| 281 |
+
'inches', 'meters', 'micron', 'microns', 'angstrom', 'angstroms', 'decimeter',
|
| 282 |
+
'kilometer', 'lightyear', 'nanometer', 'picometer', 'centimeter', 'decimeters',
|
| 283 |
+
'kilometers', 'lightyears', 'micrometer', 'millimeter', 'nanometers', 'picometers',
|
| 284 |
+
'centimeters', 'micrometers', 'millimeters', 'nautical_mile', 'planck_length',
|
| 285 |
+
'nautical_miles', 'astronomical_unit', 'astronomical_units']
|
| 286 |
+
assert find_unit(inch**-1) == ['D', 'dioptre', 'optical_power']
|
| 287 |
+
assert find_unit(length**-1) == ['D', 'dioptre', 'optical_power']
|
| 288 |
+
assert find_unit(inch ** 2) == ['ha', 'hectare', 'planck_area']
|
| 289 |
+
assert find_unit(inch ** 3) == [
|
| 290 |
+
'L', 'l', 'cL', 'cl', 'dL', 'dl', 'mL', 'ml', 'liter', 'quart', 'liters', 'quarts',
|
| 291 |
+
'deciliter', 'centiliter', 'deciliters', 'milliliter',
|
| 292 |
+
'centiliters', 'milliliters', 'planck_volume']
|
| 293 |
+
assert find_unit('voltage') == ['V', 'v', 'volt', 'volts', 'planck_voltage']
|
| 294 |
+
assert find_unit(grams) == ['g', 't', 'Da', 'kg', 'me', 'mg', 'ug', 'amu', 'mmu', 'amus',
|
| 295 |
+
'gram', 'mmus', 'grams', 'pound', 'tonne', 'dalton', 'pounds',
|
| 296 |
+
'kilogram', 'kilograms', 'microgram', 'milligram', 'metric_ton',
|
| 297 |
+
'micrograms', 'milligrams', 'planck_mass', 'milli_mass_unit', 'atomic_mass_unit',
|
| 298 |
+
'electron_rest_mass', 'atomic_mass_constant']
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def test_Quantity_derivative():
|
| 302 |
+
x = symbols("x")
|
| 303 |
+
assert diff(x*meter, x) == meter
|
| 304 |
+
assert diff(x**3*meter**2, x) == 3*x**2*meter**2
|
| 305 |
+
assert diff(meter, meter) == 1
|
| 306 |
+
assert diff(meter**2, meter) == 2*meter
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def test_quantity_postprocessing():
|
| 310 |
+
q1 = Quantity('q1')
|
| 311 |
+
q2 = Quantity('q2')
|
| 312 |
+
|
| 313 |
+
SI.set_quantity_dimension(q1, length*pressure**2*temperature/time)
|
| 314 |
+
SI.set_quantity_dimension(q2, energy*pressure*temperature/(length**2*time))
|
| 315 |
+
|
| 316 |
+
assert q1 + q2
|
| 317 |
+
q = q1 + q2
|
| 318 |
+
Dq = Dimension(SI.get_dimensional_expr(q))
|
| 319 |
+
assert SI.get_dimension_system().get_dimensional_dependencies(Dq) == {
|
| 320 |
+
length: -1,
|
| 321 |
+
mass: 2,
|
| 322 |
+
temperature: 1,
|
| 323 |
+
time: -5,
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
def test_factor_and_dimension():
|
| 328 |
+
assert (3000, Dimension(1)) == SI._collect_factor_and_dimension(3000)
|
| 329 |
+
assert (1001, length) == SI._collect_factor_and_dimension(meter + km)
|
| 330 |
+
assert (2, length/time) == SI._collect_factor_and_dimension(
|
| 331 |
+
meter/second + 36*km/(10*hour))
|
| 332 |
+
|
| 333 |
+
x, y = symbols('x y')
|
| 334 |
+
assert (x + y/100, length) == SI._collect_factor_and_dimension(
|
| 335 |
+
x*m + y*centimeter)
|
| 336 |
+
|
| 337 |
+
cH = Quantity('cH')
|
| 338 |
+
SI.set_quantity_dimension(cH, amount_of_substance/volume)
|
| 339 |
+
|
| 340 |
+
pH = -log(cH)
|
| 341 |
+
|
| 342 |
+
assert (1, volume/amount_of_substance) == SI._collect_factor_and_dimension(
|
| 343 |
+
exp(pH))
|
| 344 |
+
|
| 345 |
+
v_w1 = Quantity('v_w1')
|
| 346 |
+
v_w2 = Quantity('v_w2')
|
| 347 |
+
|
| 348 |
+
v_w1.set_global_relative_scale_factor(Rational(3, 2), meter/second)
|
| 349 |
+
v_w2.set_global_relative_scale_factor(2, meter/second)
|
| 350 |
+
|
| 351 |
+
expr = Abs(v_w1/2 - v_w2)
|
| 352 |
+
assert (Rational(5, 4), length/time) == \
|
| 353 |
+
SI._collect_factor_and_dimension(expr)
|
| 354 |
+
|
| 355 |
+
expr = Rational(5, 2)*second/meter*v_w1 - 3000
|
| 356 |
+
assert (-(2996 + Rational(1, 4)), Dimension(1)) == \
|
| 357 |
+
SI._collect_factor_and_dimension(expr)
|
| 358 |
+
|
| 359 |
+
expr = v_w1**(v_w2/v_w1)
|
| 360 |
+
assert ((Rational(3, 2))**Rational(4, 3), (length/time)**Rational(4, 3)) == \
|
| 361 |
+
SI._collect_factor_and_dimension(expr)
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def test_dimensional_expr_of_derivative():
|
| 365 |
+
l = Quantity('l')
|
| 366 |
+
t = Quantity('t')
|
| 367 |
+
t1 = Quantity('t1')
|
| 368 |
+
l.set_global_relative_scale_factor(36, km)
|
| 369 |
+
t.set_global_relative_scale_factor(1, hour)
|
| 370 |
+
t1.set_global_relative_scale_factor(1, second)
|
| 371 |
+
x = Symbol('x')
|
| 372 |
+
y = Symbol('y')
|
| 373 |
+
f = Function('f')
|
| 374 |
+
dfdx = f(x, y).diff(x, y)
|
| 375 |
+
dl_dt = dfdx.subs({f(x, y): l, x: t, y: t1})
|
| 376 |
+
assert SI.get_dimensional_expr(dl_dt) ==\
|
| 377 |
+
SI.get_dimensional_expr(l / t / t1) ==\
|
| 378 |
+
Symbol("length")/Symbol("time")**2
|
| 379 |
+
assert SI._collect_factor_and_dimension(dl_dt) ==\
|
| 380 |
+
SI._collect_factor_and_dimension(l / t / t1) ==\
|
| 381 |
+
(10, length/time**2)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def test_get_dimensional_expr_with_function():
|
| 385 |
+
v_w1 = Quantity('v_w1')
|
| 386 |
+
v_w2 = Quantity('v_w2')
|
| 387 |
+
v_w1.set_global_relative_scale_factor(1, meter/second)
|
| 388 |
+
v_w2.set_global_relative_scale_factor(1, meter/second)
|
| 389 |
+
|
| 390 |
+
assert SI.get_dimensional_expr(sin(v_w1)) == \
|
| 391 |
+
sin(SI.get_dimensional_expr(v_w1))
|
| 392 |
+
assert SI.get_dimensional_expr(sin(v_w1/v_w2)) == 1
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
def test_binary_information():
|
| 396 |
+
assert convert_to(kibibyte, byte) == 1024*byte
|
| 397 |
+
assert convert_to(mebibyte, byte) == 1024**2*byte
|
| 398 |
+
assert convert_to(gibibyte, byte) == 1024**3*byte
|
| 399 |
+
assert convert_to(tebibyte, byte) == 1024**4*byte
|
| 400 |
+
assert convert_to(pebibyte, byte) == 1024**5*byte
|
| 401 |
+
assert convert_to(exbibyte, byte) == 1024**6*byte
|
| 402 |
+
|
| 403 |
+
assert kibibyte.convert_to(bit) == 8*1024*bit
|
| 404 |
+
assert byte.convert_to(bit) == 8*bit
|
| 405 |
+
|
| 406 |
+
a = 10*kibibyte*hour
|
| 407 |
+
|
| 408 |
+
assert convert_to(a, byte) == 10240*byte*hour
|
| 409 |
+
assert convert_to(a, minute) == 600*kibibyte*minute
|
| 410 |
+
assert convert_to(a, [byte, minute]) == 614400*byte*minute
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
def test_conversion_with_2_nonstandard_dimensions():
|
| 414 |
+
good_grade = Quantity("good_grade")
|
| 415 |
+
kilo_good_grade = Quantity("kilo_good_grade")
|
| 416 |
+
centi_good_grade = Quantity("centi_good_grade")
|
| 417 |
+
|
| 418 |
+
kilo_good_grade.set_global_relative_scale_factor(1000, good_grade)
|
| 419 |
+
centi_good_grade.set_global_relative_scale_factor(S.One/10**5, kilo_good_grade)
|
| 420 |
+
|
| 421 |
+
charity_points = Quantity("charity_points")
|
| 422 |
+
milli_charity_points = Quantity("milli_charity_points")
|
| 423 |
+
missions = Quantity("missions")
|
| 424 |
+
|
| 425 |
+
milli_charity_points.set_global_relative_scale_factor(S.One/1000, charity_points)
|
| 426 |
+
missions.set_global_relative_scale_factor(251, charity_points)
|
| 427 |
+
|
| 428 |
+
assert convert_to(
|
| 429 |
+
kilo_good_grade*milli_charity_points*millimeter,
|
| 430 |
+
[centi_good_grade, missions, centimeter]
|
| 431 |
+
) == S.One * 10**5 / (251*1000) / 10 * centi_good_grade*missions*centimeter
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def test_eval_subs():
|
| 435 |
+
energy, mass, force = symbols('energy mass force')
|
| 436 |
+
expr1 = energy/mass
|
| 437 |
+
units = {energy: kilogram*meter**2/second**2, mass: kilogram}
|
| 438 |
+
assert expr1.subs(units) == meter**2/second**2
|
| 439 |
+
expr2 = force/mass
|
| 440 |
+
units = {force:gravitational_constant*kilogram**2/meter**2, mass:kilogram}
|
| 441 |
+
assert expr2.subs(units) == gravitational_constant*kilogram/meter**2
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
def test_issue_14932():
|
| 445 |
+
assert (log(inch) - log(2)).simplify() == log(inch/2)
|
| 446 |
+
assert (log(inch) - log(foot)).simplify() == -log(12)
|
| 447 |
+
p = symbols('p', positive=True)
|
| 448 |
+
assert (log(inch) - log(p)).simplify() == log(inch/p)
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
def test_issue_14547():
|
| 452 |
+
# the root issue is that an argument with dimensions should
|
| 453 |
+
# not raise an error when the `arg - 1` calculation is
|
| 454 |
+
# performed in the assumptions system
|
| 455 |
+
from sympy.physics.units import foot, inch
|
| 456 |
+
from sympy.core.relational import Eq
|
| 457 |
+
assert log(foot).is_zero is None
|
| 458 |
+
assert log(foot).is_positive is None
|
| 459 |
+
assert log(foot).is_nonnegative is None
|
| 460 |
+
assert log(foot).is_negative is None
|
| 461 |
+
assert log(foot).is_algebraic is None
|
| 462 |
+
assert log(foot).is_rational is None
|
| 463 |
+
# doesn't raise error
|
| 464 |
+
assert Eq(log(foot), log(inch)) is not None # might be False or unevaluated
|
| 465 |
+
|
| 466 |
+
x = Symbol('x')
|
| 467 |
+
e = foot + x
|
| 468 |
+
assert e.is_Add and set(e.args) == {foot, x}
|
| 469 |
+
e = foot + 1
|
| 470 |
+
assert e.is_Add and set(e.args) == {foot, 1}
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
def test_issue_22164():
|
| 474 |
+
warnings.simplefilter("error")
|
| 475 |
+
dm = Quantity("dm")
|
| 476 |
+
SI.set_quantity_dimension(dm, length)
|
| 477 |
+
SI.set_quantity_scale_factor(dm, 1)
|
| 478 |
+
|
| 479 |
+
bad_exp = Quantity("bad_exp")
|
| 480 |
+
SI.set_quantity_dimension(bad_exp, length)
|
| 481 |
+
SI.set_quantity_scale_factor(bad_exp, 1)
|
| 482 |
+
|
| 483 |
+
expr = dm ** bad_exp
|
| 484 |
+
|
| 485 |
+
# deprecation warning is not expected here
|
| 486 |
+
SI._collect_factor_and_dimension(expr)
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
def test_issue_22819():
|
| 490 |
+
from sympy.physics.units import tonne, gram, Da
|
| 491 |
+
from sympy.physics.units.systems.si import dimsys_SI
|
| 492 |
+
assert tonne.convert_to(gram) == 1000000*gram
|
| 493 |
+
assert dimsys_SI.get_dimensional_dependencies(area) == {length: 2}
|
| 494 |
+
assert Da.scale_factor == 1.66053906660000e-24
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
def test_issue_20288():
|
| 498 |
+
from sympy.core.numbers import E
|
| 499 |
+
from sympy.physics.units import energy
|
| 500 |
+
u = Quantity('u')
|
| 501 |
+
v = Quantity('v')
|
| 502 |
+
SI.set_quantity_dimension(u, energy)
|
| 503 |
+
SI.set_quantity_dimension(v, energy)
|
| 504 |
+
u.set_global_relative_scale_factor(1, joule)
|
| 505 |
+
v.set_global_relative_scale_factor(1, joule)
|
| 506 |
+
expr = 1 + exp(u**2/v**2)
|
| 507 |
+
assert SI._collect_factor_and_dimension(expr) == (1 + E, Dimension(1))
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
def test_issue_24062():
|
| 511 |
+
from sympy.core.numbers import E
|
| 512 |
+
from sympy.physics.units import impedance, capacitance, time, ohm, farad, second
|
| 513 |
+
|
| 514 |
+
R = Quantity('R')
|
| 515 |
+
C = Quantity('C')
|
| 516 |
+
T = Quantity('T')
|
| 517 |
+
SI.set_quantity_dimension(R, impedance)
|
| 518 |
+
SI.set_quantity_dimension(C, capacitance)
|
| 519 |
+
SI.set_quantity_dimension(T, time)
|
| 520 |
+
R.set_global_relative_scale_factor(1, ohm)
|
| 521 |
+
C.set_global_relative_scale_factor(1, farad)
|
| 522 |
+
T.set_global_relative_scale_factor(1, second)
|
| 523 |
+
expr = T / (R * C)
|
| 524 |
+
dim = SI._collect_factor_and_dimension(expr)[1]
|
| 525 |
+
assert SI.get_dimension_system().is_dimensionless(dim)
|
| 526 |
+
|
| 527 |
+
exp_expr = 1 + exp(expr)
|
| 528 |
+
assert SI._collect_factor_and_dimension(exp_expr) == (1 + E, Dimension(1))
|
| 529 |
+
|
| 530 |
+
def test_issue_24211():
|
| 531 |
+
from sympy.physics.units import time, velocity, acceleration, second, meter
|
| 532 |
+
V1 = Quantity('V1')
|
| 533 |
+
SI.set_quantity_dimension(V1, velocity)
|
| 534 |
+
SI.set_quantity_scale_factor(V1, 1 * meter / second)
|
| 535 |
+
A1 = Quantity('A1')
|
| 536 |
+
SI.set_quantity_dimension(A1, acceleration)
|
| 537 |
+
SI.set_quantity_scale_factor(A1, 1 * meter / second**2)
|
| 538 |
+
T1 = Quantity('T1')
|
| 539 |
+
SI.set_quantity_dimension(T1, time)
|
| 540 |
+
SI.set_quantity_scale_factor(T1, 1 * second)
|
| 541 |
+
|
| 542 |
+
expr = A1*T1 + V1
|
| 543 |
+
# should not throw ValueError here
|
| 544 |
+
SI._collect_factor_and_dimension(expr)
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
def test_prefixed_property():
|
| 548 |
+
assert not meter.is_prefixed
|
| 549 |
+
assert not joule.is_prefixed
|
| 550 |
+
assert not day.is_prefixed
|
| 551 |
+
assert not second.is_prefixed
|
| 552 |
+
assert not volt.is_prefixed
|
| 553 |
+
assert not ohm.is_prefixed
|
| 554 |
+
assert centimeter.is_prefixed
|
| 555 |
+
assert kilometer.is_prefixed
|
| 556 |
+
assert kilogram.is_prefixed
|
| 557 |
+
assert pebibyte.is_prefixed
|
| 558 |
+
|
| 559 |
+
def test_physics_constant():
|
| 560 |
+
from sympy.physics.units import definitions
|
| 561 |
+
|
| 562 |
+
for name in dir(definitions):
|
| 563 |
+
quantity = getattr(definitions, name)
|
| 564 |
+
if not isinstance(quantity, Quantity):
|
| 565 |
+
continue
|
| 566 |
+
if name.endswith('_constant'):
|
| 567 |
+
assert isinstance(quantity, PhysicalConstant), f"{quantity} must be PhysicalConstant, but is {type(quantity)}"
|
| 568 |
+
assert quantity.is_physical_constant, f"{name} is not marked as physics constant when it should be"
|
| 569 |
+
|
| 570 |
+
for const in [gravitational_constant, molar_gas_constant, vacuum_permittivity, speed_of_light, elementary_charge]:
|
| 571 |
+
assert isinstance(const, PhysicalConstant), f"{const} must be PhysicalConstant, but is {type(const)}"
|
| 572 |
+
assert const.is_physical_constant, f"{const} is not marked as physics constant when it should be"
|
| 573 |
+
|
| 574 |
+
assert not meter.is_physical_constant
|
| 575 |
+
assert not joule.is_physical_constant
|
venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_unit_system_cgs_gauss.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.concrete.tests.test_sums_products import NS
|
| 2 |
+
|
| 3 |
+
from sympy.core.singleton import S
|
| 4 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 5 |
+
from sympy.physics.units import convert_to, coulomb_constant, elementary_charge, gravitational_constant, planck
|
| 6 |
+
from sympy.physics.units.definitions.unit_definitions import angstrom, statcoulomb, coulomb, second, gram, centimeter, erg, \
|
| 7 |
+
newton, joule, dyne, speed_of_light, meter, farad, henry, statvolt, volt, ohm
|
| 8 |
+
from sympy.physics.units.systems import SI
|
| 9 |
+
from sympy.physics.units.systems.cgs import cgs_gauss
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def test_conversion_to_from_si():
|
| 13 |
+
assert convert_to(statcoulomb, coulomb, cgs_gauss) == coulomb/2997924580
|
| 14 |
+
assert convert_to(coulomb, statcoulomb, cgs_gauss) == 2997924580*statcoulomb
|
| 15 |
+
assert convert_to(statcoulomb, sqrt(gram*centimeter**3)/second, cgs_gauss) == centimeter**(S(3)/2)*sqrt(gram)/second
|
| 16 |
+
assert convert_to(coulomb, sqrt(gram*centimeter**3)/second, cgs_gauss) == 2997924580*centimeter**(S(3)/2)*sqrt(gram)/second
|
| 17 |
+
|
| 18 |
+
# SI units have an additional base unit, no conversion in case of electromagnetism:
|
| 19 |
+
assert convert_to(coulomb, statcoulomb, SI) == coulomb
|
| 20 |
+
assert convert_to(statcoulomb, coulomb, SI) == statcoulomb
|
| 21 |
+
|
| 22 |
+
# SI without electromagnetism:
|
| 23 |
+
assert convert_to(erg, joule, SI) == joule/10**7
|
| 24 |
+
assert convert_to(erg, joule, cgs_gauss) == joule/10**7
|
| 25 |
+
assert convert_to(joule, erg, SI) == 10**7*erg
|
| 26 |
+
assert convert_to(joule, erg, cgs_gauss) == 10**7*erg
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
assert convert_to(dyne, newton, SI) == newton/10**5
|
| 30 |
+
assert convert_to(dyne, newton, cgs_gauss) == newton/10**5
|
| 31 |
+
assert convert_to(newton, dyne, SI) == 10**5*dyne
|
| 32 |
+
assert convert_to(newton, dyne, cgs_gauss) == 10**5*dyne
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def test_cgs_gauss_convert_constants():
|
| 36 |
+
|
| 37 |
+
assert convert_to(speed_of_light, centimeter/second, cgs_gauss) == 29979245800*centimeter/second
|
| 38 |
+
|
| 39 |
+
assert convert_to(coulomb_constant, 1, cgs_gauss) == 1
|
| 40 |
+
assert convert_to(coulomb_constant, newton*meter**2/coulomb**2, cgs_gauss) == 22468879468420441*meter**2*newton/(2500000*coulomb**2)
|
| 41 |
+
assert convert_to(coulomb_constant, newton*meter**2/coulomb**2, SI) == 22468879468420441*meter**2*newton/(2500000*coulomb**2)
|
| 42 |
+
assert convert_to(coulomb_constant, dyne*centimeter**2/statcoulomb**2, cgs_gauss) == centimeter**2*dyne/statcoulomb**2
|
| 43 |
+
assert convert_to(coulomb_constant, 1, SI) == coulomb_constant
|
| 44 |
+
assert NS(convert_to(coulomb_constant, newton*meter**2/coulomb**2, SI)) == '8987551787.36818*meter**2*newton/coulomb**2'
|
| 45 |
+
|
| 46 |
+
assert convert_to(elementary_charge, statcoulomb, cgs_gauss)
|
| 47 |
+
assert convert_to(angstrom, centimeter, cgs_gauss) == 1*centimeter/10**8
|
| 48 |
+
assert convert_to(gravitational_constant, dyne*centimeter**2/gram**2, cgs_gauss)
|
| 49 |
+
assert NS(convert_to(planck, erg*second, cgs_gauss)) == '6.62607015e-27*erg*second'
|
| 50 |
+
|
| 51 |
+
spc = 25000*second/(22468879468420441*centimeter)
|
| 52 |
+
assert convert_to(ohm, second/centimeter, cgs_gauss) == spc
|
| 53 |
+
assert convert_to(henry, second**2/centimeter, cgs_gauss) == spc*second
|
| 54 |
+
assert convert_to(volt, statvolt, cgs_gauss) == 10**6*statvolt/299792458
|
| 55 |
+
assert convert_to(farad, centimeter, cgs_gauss) == 299792458**2*centimeter/10**5
|
venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_unitsystem.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.physics.units import DimensionSystem, joule, second, ampere
|
| 2 |
+
|
| 3 |
+
from sympy.core.numbers import Rational
|
| 4 |
+
from sympy.core.singleton import S
|
| 5 |
+
from sympy.physics.units.definitions import c, kg, m, s
|
| 6 |
+
from sympy.physics.units.definitions.dimension_definitions import length, time
|
| 7 |
+
from sympy.physics.units.quantities import Quantity
|
| 8 |
+
from sympy.physics.units.unitsystem import UnitSystem
|
| 9 |
+
from sympy.physics.units.util import convert_to
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def test_definition():
|
| 13 |
+
# want to test if the system can have several units of the same dimension
|
| 14 |
+
dm = Quantity("dm")
|
| 15 |
+
base = (m, s)
|
| 16 |
+
# base_dim = (m.dimension, s.dimension)
|
| 17 |
+
ms = UnitSystem(base, (c, dm), "MS", "MS system")
|
| 18 |
+
ms.set_quantity_dimension(dm, length)
|
| 19 |
+
ms.set_quantity_scale_factor(dm, Rational(1, 10))
|
| 20 |
+
|
| 21 |
+
assert set(ms._base_units) == set(base)
|
| 22 |
+
assert set(ms._units) == {m, s, c, dm}
|
| 23 |
+
# assert ms._units == DimensionSystem._sort_dims(base + (velocity,))
|
| 24 |
+
assert ms.name == "MS"
|
| 25 |
+
assert ms.descr == "MS system"
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def test_str_repr():
|
| 29 |
+
assert str(UnitSystem((m, s), name="MS")) == "MS"
|
| 30 |
+
assert str(UnitSystem((m, s))) == "UnitSystem((meter, second))"
|
| 31 |
+
|
| 32 |
+
assert repr(UnitSystem((m, s))) == "<UnitSystem: (%s, %s)>" % (m, s)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def test_convert_to():
|
| 36 |
+
A = Quantity("A")
|
| 37 |
+
A.set_global_relative_scale_factor(S.One, ampere)
|
| 38 |
+
|
| 39 |
+
Js = Quantity("Js")
|
| 40 |
+
Js.set_global_relative_scale_factor(S.One, joule*second)
|
| 41 |
+
|
| 42 |
+
mksa = UnitSystem((m, kg, s, A), (Js,))
|
| 43 |
+
assert convert_to(Js, mksa._base_units) == m**2*kg*s**-1/1000
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def test_extend():
|
| 47 |
+
ms = UnitSystem((m, s), (c,))
|
| 48 |
+
Js = Quantity("Js")
|
| 49 |
+
Js.set_global_relative_scale_factor(1, joule*second)
|
| 50 |
+
mks = ms.extend((kg,), (Js,))
|
| 51 |
+
|
| 52 |
+
res = UnitSystem((m, s, kg), (c, Js))
|
| 53 |
+
assert set(mks._base_units) == set(res._base_units)
|
| 54 |
+
assert set(mks._units) == set(res._units)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_dim():
|
| 58 |
+
dimsys = UnitSystem((m, kg, s), (c,))
|
| 59 |
+
assert dimsys.dim == 3
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def test_is_consistent():
|
| 63 |
+
dimension_system = DimensionSystem([length, time])
|
| 64 |
+
us = UnitSystem([m, s], dimension_system=dimension_system)
|
| 65 |
+
assert us.is_consistent == True
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def test_get_units_non_prefixed():
|
| 69 |
+
from sympy.physics.units import volt, ohm
|
| 70 |
+
unit_system = UnitSystem.get_unit_system("SI")
|
| 71 |
+
units = unit_system.get_units_non_prefixed()
|
| 72 |
+
for prefix in ["giga", "tera", "peta", "exa", "zetta", "yotta", "kilo", "hecto", "deca", "deci", "centi", "milli", "micro", "nano", "pico", "femto", "atto", "zepto", "yocto"]:
|
| 73 |
+
for unit in units:
|
| 74 |
+
assert isinstance(unit, Quantity), f"{unit} must be a Quantity, not {type(unit)}"
|
| 75 |
+
assert not unit.is_prefixed, f"{unit} is marked as prefixed"
|
| 76 |
+
assert not unit.is_physical_constant, f"{unit} is marked as physics constant"
|
| 77 |
+
assert not unit.name.name.startswith(prefix), f"Unit {unit.name} has prefix {prefix}"
|
| 78 |
+
assert volt in units
|
| 79 |
+
assert ohm in units
|
| 80 |
+
|
| 81 |
+
def test_derived_units_must_exist_in_unit_system():
|
| 82 |
+
for unit_system in UnitSystem._unit_systems.values():
|
| 83 |
+
for preferred_unit in unit_system.derived_units.values():
|
| 84 |
+
units = preferred_unit.atoms(Quantity)
|
| 85 |
+
for unit in units:
|
| 86 |
+
assert unit in unit_system._units, f"Unit {unit} is not in unit system {unit_system}"
|
venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_util.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.containers import Tuple
|
| 2 |
+
from sympy.core.numbers import pi
|
| 3 |
+
from sympy.core.power import Pow
|
| 4 |
+
from sympy.core.symbol import symbols
|
| 5 |
+
from sympy.core.sympify import sympify
|
| 6 |
+
from sympy.printing.str import sstr
|
| 7 |
+
from sympy.physics.units import (
|
| 8 |
+
G, centimeter, coulomb, day, degree, gram, hbar, hour, inch, joule, kelvin,
|
| 9 |
+
kilogram, kilometer, length, meter, mile, minute, newton, planck,
|
| 10 |
+
planck_length, planck_mass, planck_temperature, planck_time, radians,
|
| 11 |
+
second, speed_of_light, steradian, time, km)
|
| 12 |
+
from sympy.physics.units.util import convert_to, check_dimensions
|
| 13 |
+
from sympy.testing.pytest import raises
|
| 14 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def NS(e, n=15, **options):
|
| 18 |
+
return sstr(sympify(e).evalf(n, **options), full_prec=True)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
L = length
|
| 22 |
+
T = time
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_dim_simplify_add():
|
| 26 |
+
# assert Add(L, L) == L
|
| 27 |
+
assert L + L == L
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def test_dim_simplify_mul():
|
| 31 |
+
# assert Mul(L, T) == L*T
|
| 32 |
+
assert L*T == L*T
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def test_dim_simplify_pow():
|
| 36 |
+
assert Pow(L, 2) == L**2
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_dim_simplify_rec():
|
| 40 |
+
# assert Mul(Add(L, L), T) == L*T
|
| 41 |
+
assert (L + L) * T == L*T
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def test_convert_to_quantities():
|
| 45 |
+
assert convert_to(3, meter) == 3
|
| 46 |
+
|
| 47 |
+
assert convert_to(mile, kilometer) == 25146*kilometer/15625
|
| 48 |
+
assert convert_to(meter/second, speed_of_light) == speed_of_light/299792458
|
| 49 |
+
assert convert_to(299792458*meter/second, speed_of_light) == speed_of_light
|
| 50 |
+
assert convert_to(2*299792458*meter/second, speed_of_light) == 2*speed_of_light
|
| 51 |
+
assert convert_to(speed_of_light, meter/second) == 299792458*meter/second
|
| 52 |
+
assert convert_to(2*speed_of_light, meter/second) == 599584916*meter/second
|
| 53 |
+
assert convert_to(day, second) == 86400*second
|
| 54 |
+
assert convert_to(2*hour, minute) == 120*minute
|
| 55 |
+
assert convert_to(mile, meter) == 201168*meter/125
|
| 56 |
+
assert convert_to(mile/hour, kilometer/hour) == 25146*kilometer/(15625*hour)
|
| 57 |
+
assert convert_to(3*newton, meter/second) == 3*newton
|
| 58 |
+
assert convert_to(3*newton, kilogram*meter/second**2) == 3*meter*kilogram/second**2
|
| 59 |
+
assert convert_to(kilometer + mile, meter) == 326168*meter/125
|
| 60 |
+
assert convert_to(2*kilometer + 3*mile, meter) == 853504*meter/125
|
| 61 |
+
assert convert_to(inch**2, meter**2) == 16129*meter**2/25000000
|
| 62 |
+
assert convert_to(3*inch**2, meter) == 48387*meter**2/25000000
|
| 63 |
+
assert convert_to(2*kilometer/hour + 3*mile/hour, meter/second) == 53344*meter/(28125*second)
|
| 64 |
+
assert convert_to(2*kilometer/hour + 3*mile/hour, centimeter/second) == 213376*centimeter/(1125*second)
|
| 65 |
+
assert convert_to(kilometer * (mile + kilometer), meter) == 2609344 * meter ** 2
|
| 66 |
+
|
| 67 |
+
assert convert_to(steradian, coulomb) == steradian
|
| 68 |
+
assert convert_to(radians, degree) == 180*degree/pi
|
| 69 |
+
assert convert_to(radians, [meter, degree]) == 180*degree/pi
|
| 70 |
+
assert convert_to(pi*radians, degree) == 180*degree
|
| 71 |
+
assert convert_to(pi, degree) == 180*degree
|
| 72 |
+
|
| 73 |
+
# https://github.com/sympy/sympy/issues/26263
|
| 74 |
+
assert convert_to(sqrt(meter**2 + meter**2.0), meter) == sqrt(meter**2 + meter**2.0)
|
| 75 |
+
assert convert_to((meter**2 + meter**2.0)**2, meter) == (meter**2 + meter**2.0)**2
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def test_convert_to_tuples_of_quantities():
|
| 79 |
+
from sympy.core.symbol import symbols
|
| 80 |
+
|
| 81 |
+
alpha, beta = symbols('alpha beta')
|
| 82 |
+
|
| 83 |
+
assert convert_to(speed_of_light, [meter, second]) == 299792458 * meter / second
|
| 84 |
+
assert convert_to(speed_of_light, (meter, second)) == 299792458 * meter / second
|
| 85 |
+
assert convert_to(speed_of_light, Tuple(meter, second)) == 299792458 * meter / second
|
| 86 |
+
assert convert_to(joule, [meter, kilogram, second]) == kilogram*meter**2/second**2
|
| 87 |
+
assert convert_to(joule, [centimeter, gram, second]) == 10000000*centimeter**2*gram/second**2
|
| 88 |
+
assert convert_to(299792458*meter/second, [speed_of_light]) == speed_of_light
|
| 89 |
+
assert convert_to(speed_of_light / 2, [meter, second, kilogram]) == meter/second*299792458 / 2
|
| 90 |
+
# This doesn't make physically sense, but let's keep it as a conversion test:
|
| 91 |
+
assert convert_to(2 * speed_of_light, [meter, second, kilogram]) == 2 * 299792458 * meter / second
|
| 92 |
+
assert convert_to(G, [G, speed_of_light, planck]) == 1.0*G
|
| 93 |
+
|
| 94 |
+
assert NS(convert_to(meter, [G, speed_of_light, hbar]), n=7) == '6.187142e+34*gravitational_constant**0.5000000*hbar**0.5000000/speed_of_light**1.500000'
|
| 95 |
+
assert NS(convert_to(planck_mass, kilogram), n=7) == '2.176434e-8*kilogram'
|
| 96 |
+
assert NS(convert_to(planck_length, meter), n=7) == '1.616255e-35*meter'
|
| 97 |
+
assert NS(convert_to(planck_time, second), n=6) == '5.39125e-44*second'
|
| 98 |
+
assert NS(convert_to(planck_temperature, kelvin), n=7) == '1.416784e+32*kelvin'
|
| 99 |
+
assert NS(convert_to(convert_to(meter, [G, speed_of_light, planck]), meter), n=10) == '1.000000000*meter'
|
| 100 |
+
|
| 101 |
+
# similar to https://github.com/sympy/sympy/issues/26263
|
| 102 |
+
assert convert_to(sqrt(meter**2 + second**2.0), [meter, second]) == sqrt(meter**2 + second**2.0)
|
| 103 |
+
assert convert_to((meter**2 + second**2.0)**2, [meter, second]) == (meter**2 + second**2.0)**2
|
| 104 |
+
|
| 105 |
+
# similar to https://github.com/sympy/sympy/issues/21463
|
| 106 |
+
assert convert_to(1/(beta*meter + meter), 1/meter) == 1/(beta*meter + meter)
|
| 107 |
+
assert convert_to(1/(beta*meter + alpha*meter), 1/kilometer) == (1/(kilometer*beta/1000 + alpha*kilometer/1000))
|
| 108 |
+
|
| 109 |
+
def test_eval_simplify():
|
| 110 |
+
from sympy.physics.units import cm, mm, km, m, K, kilo
|
| 111 |
+
from sympy.core.symbol import symbols
|
| 112 |
+
|
| 113 |
+
x, y = symbols('x y')
|
| 114 |
+
|
| 115 |
+
assert (cm/mm).simplify() == 10
|
| 116 |
+
assert (km/m).simplify() == 1000
|
| 117 |
+
assert (km/cm).simplify() == 100000
|
| 118 |
+
assert (10*x*K*km**2/m/cm).simplify() == 1000000000*x*kelvin
|
| 119 |
+
assert (cm/km/m).simplify() == 1/(10000000*centimeter)
|
| 120 |
+
|
| 121 |
+
assert (3*kilo*meter).simplify() == 3000*meter
|
| 122 |
+
assert (4*kilo*meter/(2*kilometer)).simplify() == 2
|
| 123 |
+
assert (4*kilometer**2/(kilo*meter)**2).simplify() == 4
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def test_quantity_simplify():
|
| 127 |
+
from sympy.physics.units.util import quantity_simplify
|
| 128 |
+
from sympy.physics.units import kilo, foot
|
| 129 |
+
from sympy.core.symbol import symbols
|
| 130 |
+
|
| 131 |
+
x, y = symbols('x y')
|
| 132 |
+
|
| 133 |
+
assert quantity_simplify(x*(8*kilo*newton*meter + y)) == x*(8000*meter*newton + y)
|
| 134 |
+
assert quantity_simplify(foot*inch*(foot + inch)) == foot**2*(foot + foot/12)/12
|
| 135 |
+
assert quantity_simplify(foot*inch*(foot*foot + inch*(foot + inch))) == foot**2*(foot**2 + foot/12*(foot + foot/12))/12
|
| 136 |
+
assert quantity_simplify(2**(foot/inch*kilo/1000)*inch) == 4096*foot/12
|
| 137 |
+
assert quantity_simplify(foot**2*inch + inch**2*foot) == 13*foot**3/144
|
| 138 |
+
|
| 139 |
+
def test_quantity_simplify_across_dimensions():
|
| 140 |
+
from sympy.physics.units.util import quantity_simplify
|
| 141 |
+
from sympy.physics.units import ampere, ohm, volt, joule, pascal, farad, second, watt, siemens, henry, tesla, weber, hour, newton
|
| 142 |
+
|
| 143 |
+
assert quantity_simplify(ampere*ohm, across_dimensions=True, unit_system="SI") == volt
|
| 144 |
+
assert quantity_simplify(6*ampere*ohm, across_dimensions=True, unit_system="SI") == 6*volt
|
| 145 |
+
assert quantity_simplify(volt/ampere, across_dimensions=True, unit_system="SI") == ohm
|
| 146 |
+
assert quantity_simplify(volt/ohm, across_dimensions=True, unit_system="SI") == ampere
|
| 147 |
+
assert quantity_simplify(joule/meter**3, across_dimensions=True, unit_system="SI") == pascal
|
| 148 |
+
assert quantity_simplify(farad*ohm, across_dimensions=True, unit_system="SI") == second
|
| 149 |
+
assert quantity_simplify(joule/second, across_dimensions=True, unit_system="SI") == watt
|
| 150 |
+
assert quantity_simplify(meter**3/second, across_dimensions=True, unit_system="SI") == meter**3/second
|
| 151 |
+
assert quantity_simplify(joule/second, across_dimensions=True, unit_system="SI") == watt
|
| 152 |
+
|
| 153 |
+
assert quantity_simplify(joule/coulomb, across_dimensions=True, unit_system="SI") == volt
|
| 154 |
+
assert quantity_simplify(volt/ampere, across_dimensions=True, unit_system="SI") == ohm
|
| 155 |
+
assert quantity_simplify(ampere/volt, across_dimensions=True, unit_system="SI") == siemens
|
| 156 |
+
assert quantity_simplify(coulomb/volt, across_dimensions=True, unit_system="SI") == farad
|
| 157 |
+
assert quantity_simplify(volt*second/ampere, across_dimensions=True, unit_system="SI") == henry
|
| 158 |
+
assert quantity_simplify(volt*second/meter**2, across_dimensions=True, unit_system="SI") == tesla
|
| 159 |
+
assert quantity_simplify(joule/ampere, across_dimensions=True, unit_system="SI") == weber
|
| 160 |
+
|
| 161 |
+
assert quantity_simplify(5*kilometer/hour, across_dimensions=True, unit_system="SI") == 25*meter/(18*second)
|
| 162 |
+
assert quantity_simplify(5*kilogram*meter/second**2, across_dimensions=True, unit_system="SI") == 5*newton
|
| 163 |
+
|
| 164 |
+
def test_check_dimensions():
|
| 165 |
+
x = symbols('x')
|
| 166 |
+
assert check_dimensions(inch + x) == inch + x
|
| 167 |
+
assert check_dimensions(length + x) == length + x
|
| 168 |
+
# after subs we get 2*length; check will clear the constant
|
| 169 |
+
assert check_dimensions((length + x).subs(x, length)) == length
|
| 170 |
+
assert check_dimensions(newton*meter + joule) == joule + meter*newton
|
| 171 |
+
raises(ValueError, lambda: check_dimensions(inch + 1))
|
| 172 |
+
raises(ValueError, lambda: check_dimensions(length + 1))
|
| 173 |
+
raises(ValueError, lambda: check_dimensions(length + time))
|
| 174 |
+
raises(ValueError, lambda: check_dimensions(meter + second))
|
| 175 |
+
raises(ValueError, lambda: check_dimensions(2 * meter + second))
|
| 176 |
+
raises(ValueError, lambda: check_dimensions(2 * meter + 3 * second))
|
| 177 |
+
raises(ValueError, lambda: check_dimensions(1 / second + 1 / meter))
|
| 178 |
+
raises(ValueError, lambda: check_dimensions(2 * meter*(mile + centimeter) + km))
|
venv/lib/python3.11/site-packages/sympy/physics/units/unitsystem.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Unit system for physical quantities; include definition of constants.
|
| 3 |
+
"""
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
from sympy.core.add import Add
|
| 7 |
+
from sympy.core.function import (Derivative, Function)
|
| 8 |
+
from sympy.core.mul import Mul
|
| 9 |
+
from sympy.core.power import Pow
|
| 10 |
+
from sympy.core.singleton import S
|
| 11 |
+
from sympy.physics.units.dimensions import _QuantityMapper
|
| 12 |
+
from sympy.physics.units.quantities import Quantity
|
| 13 |
+
|
| 14 |
+
from .dimensions import Dimension
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class UnitSystem(_QuantityMapper):
|
| 18 |
+
"""
|
| 19 |
+
UnitSystem represents a coherent set of units.
|
| 20 |
+
|
| 21 |
+
A unit system is basically a dimension system with notions of scales. Many
|
| 22 |
+
of the methods are defined in the same way.
|
| 23 |
+
|
| 24 |
+
It is much better if all base units have a symbol.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
_unit_systems: dict[str, UnitSystem] = {}
|
| 28 |
+
|
| 29 |
+
def __init__(self, base_units, units=(), name="", descr="", dimension_system=None, derived_units: dict[Dimension, Quantity]={}):
|
| 30 |
+
|
| 31 |
+
UnitSystem._unit_systems[name] = self
|
| 32 |
+
|
| 33 |
+
self.name = name
|
| 34 |
+
self.descr = descr
|
| 35 |
+
|
| 36 |
+
self._base_units = base_units
|
| 37 |
+
self._dimension_system = dimension_system
|
| 38 |
+
self._units = tuple(set(base_units) | set(units))
|
| 39 |
+
self._base_units = tuple(base_units)
|
| 40 |
+
self._derived_units = derived_units
|
| 41 |
+
|
| 42 |
+
super().__init__()
|
| 43 |
+
|
| 44 |
+
def __str__(self):
|
| 45 |
+
"""
|
| 46 |
+
Return the name of the system.
|
| 47 |
+
|
| 48 |
+
If it does not exist, then it makes a list of symbols (or names) of
|
| 49 |
+
the base dimensions.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
if self.name != "":
|
| 53 |
+
return self.name
|
| 54 |
+
else:
|
| 55 |
+
return "UnitSystem((%s))" % ", ".join(
|
| 56 |
+
str(d) for d in self._base_units)
|
| 57 |
+
|
| 58 |
+
def __repr__(self):
|
| 59 |
+
return '<UnitSystem: %s>' % repr(self._base_units)
|
| 60 |
+
|
| 61 |
+
def extend(self, base, units=(), name="", description="", dimension_system=None, derived_units: dict[Dimension, Quantity]={}):
|
| 62 |
+
"""Extend the current system into a new one.
|
| 63 |
+
|
| 64 |
+
Take the base and normal units of the current system to merge
|
| 65 |
+
them to the base and normal units given in argument.
|
| 66 |
+
If not provided, name and description are overridden by empty strings.
|
| 67 |
+
"""
|
| 68 |
+
|
| 69 |
+
base = self._base_units + tuple(base)
|
| 70 |
+
units = self._units + tuple(units)
|
| 71 |
+
|
| 72 |
+
return UnitSystem(base, units, name, description, dimension_system, {**self._derived_units, **derived_units})
|
| 73 |
+
|
| 74 |
+
def get_dimension_system(self):
|
| 75 |
+
return self._dimension_system
|
| 76 |
+
|
| 77 |
+
def get_quantity_dimension(self, unit):
|
| 78 |
+
qdm = self.get_dimension_system()._quantity_dimension_map
|
| 79 |
+
if unit in qdm:
|
| 80 |
+
return qdm[unit]
|
| 81 |
+
return super().get_quantity_dimension(unit)
|
| 82 |
+
|
| 83 |
+
def get_quantity_scale_factor(self, unit):
|
| 84 |
+
qsfm = self.get_dimension_system()._quantity_scale_factors
|
| 85 |
+
if unit in qsfm:
|
| 86 |
+
return qsfm[unit]
|
| 87 |
+
return super().get_quantity_scale_factor(unit)
|
| 88 |
+
|
| 89 |
+
@staticmethod
|
| 90 |
+
def get_unit_system(unit_system):
|
| 91 |
+
if isinstance(unit_system, UnitSystem):
|
| 92 |
+
return unit_system
|
| 93 |
+
|
| 94 |
+
if unit_system not in UnitSystem._unit_systems:
|
| 95 |
+
raise ValueError(
|
| 96 |
+
"Unit system is not supported. Currently"
|
| 97 |
+
"supported unit systems are {}".format(
|
| 98 |
+
", ".join(sorted(UnitSystem._unit_systems))
|
| 99 |
+
)
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
return UnitSystem._unit_systems[unit_system]
|
| 103 |
+
|
| 104 |
+
@staticmethod
|
| 105 |
+
def get_default_unit_system():
|
| 106 |
+
return UnitSystem._unit_systems["SI"]
|
| 107 |
+
|
| 108 |
+
@property
|
| 109 |
+
def dim(self):
|
| 110 |
+
"""
|
| 111 |
+
Give the dimension of the system.
|
| 112 |
+
|
| 113 |
+
That is return the number of units forming the basis.
|
| 114 |
+
"""
|
| 115 |
+
return len(self._base_units)
|
| 116 |
+
|
| 117 |
+
@property
|
| 118 |
+
def is_consistent(self):
|
| 119 |
+
"""
|
| 120 |
+
Check if the underlying dimension system is consistent.
|
| 121 |
+
"""
|
| 122 |
+
# test is performed in DimensionSystem
|
| 123 |
+
return self.get_dimension_system().is_consistent
|
| 124 |
+
|
| 125 |
+
@property
|
| 126 |
+
def derived_units(self) -> dict[Dimension, Quantity]:
|
| 127 |
+
return self._derived_units
|
| 128 |
+
|
| 129 |
+
def get_dimensional_expr(self, expr):
|
| 130 |
+
from sympy.physics.units import Quantity
|
| 131 |
+
if isinstance(expr, Mul):
|
| 132 |
+
return Mul(*[self.get_dimensional_expr(i) for i in expr.args])
|
| 133 |
+
elif isinstance(expr, Pow):
|
| 134 |
+
return self.get_dimensional_expr(expr.base) ** expr.exp
|
| 135 |
+
elif isinstance(expr, Add):
|
| 136 |
+
return self.get_dimensional_expr(expr.args[0])
|
| 137 |
+
elif isinstance(expr, Derivative):
|
| 138 |
+
dim = self.get_dimensional_expr(expr.expr)
|
| 139 |
+
for independent, count in expr.variable_count:
|
| 140 |
+
dim /= self.get_dimensional_expr(independent)**count
|
| 141 |
+
return dim
|
| 142 |
+
elif isinstance(expr, Function):
|
| 143 |
+
args = [self.get_dimensional_expr(arg) for arg in expr.args]
|
| 144 |
+
if all(i == 1 for i in args):
|
| 145 |
+
return S.One
|
| 146 |
+
return expr.func(*args)
|
| 147 |
+
elif isinstance(expr, Quantity):
|
| 148 |
+
return self.get_quantity_dimension(expr).name
|
| 149 |
+
return S.One
|
| 150 |
+
|
| 151 |
+
def _collect_factor_and_dimension(self, expr):
|
| 152 |
+
"""
|
| 153 |
+
Return tuple with scale factor expression and dimension expression.
|
| 154 |
+
"""
|
| 155 |
+
from sympy.physics.units import Quantity
|
| 156 |
+
if isinstance(expr, Quantity):
|
| 157 |
+
return expr.scale_factor, expr.dimension
|
| 158 |
+
elif isinstance(expr, Mul):
|
| 159 |
+
factor = 1
|
| 160 |
+
dimension = Dimension(1)
|
| 161 |
+
for arg in expr.args:
|
| 162 |
+
arg_factor, arg_dim = self._collect_factor_and_dimension(arg)
|
| 163 |
+
factor *= arg_factor
|
| 164 |
+
dimension *= arg_dim
|
| 165 |
+
return factor, dimension
|
| 166 |
+
elif isinstance(expr, Pow):
|
| 167 |
+
factor, dim = self._collect_factor_and_dimension(expr.base)
|
| 168 |
+
exp_factor, exp_dim = self._collect_factor_and_dimension(expr.exp)
|
| 169 |
+
if self.get_dimension_system().is_dimensionless(exp_dim):
|
| 170 |
+
exp_dim = 1
|
| 171 |
+
return factor ** exp_factor, dim ** (exp_factor * exp_dim)
|
| 172 |
+
elif isinstance(expr, Add):
|
| 173 |
+
factor, dim = self._collect_factor_and_dimension(expr.args[0])
|
| 174 |
+
for addend in expr.args[1:]:
|
| 175 |
+
addend_factor, addend_dim = \
|
| 176 |
+
self._collect_factor_and_dimension(addend)
|
| 177 |
+
if not self.get_dimension_system().equivalent_dims(dim, addend_dim):
|
| 178 |
+
raise ValueError(
|
| 179 |
+
'Dimension of "{}" is {}, '
|
| 180 |
+
'but it should be {}'.format(
|
| 181 |
+
addend, addend_dim, dim))
|
| 182 |
+
factor += addend_factor
|
| 183 |
+
return factor, dim
|
| 184 |
+
elif isinstance(expr, Derivative):
|
| 185 |
+
factor, dim = self._collect_factor_and_dimension(expr.args[0])
|
| 186 |
+
for independent, count in expr.variable_count:
|
| 187 |
+
ifactor, idim = self._collect_factor_and_dimension(independent)
|
| 188 |
+
factor /= ifactor**count
|
| 189 |
+
dim /= idim**count
|
| 190 |
+
return factor, dim
|
| 191 |
+
elif isinstance(expr, Function):
|
| 192 |
+
fds = [self._collect_factor_and_dimension(arg) for arg in expr.args]
|
| 193 |
+
dims = [Dimension(1) if self.get_dimension_system().is_dimensionless(d[1]) else d[1] for d in fds]
|
| 194 |
+
return (expr.func(*(f[0] for f in fds)), *dims)
|
| 195 |
+
elif isinstance(expr, Dimension):
|
| 196 |
+
return S.One, expr
|
| 197 |
+
else:
|
| 198 |
+
return expr, Dimension(1)
|
| 199 |
+
|
| 200 |
+
def get_units_non_prefixed(self) -> set[Quantity]:
|
| 201 |
+
"""
|
| 202 |
+
Return the units of the system that do not have a prefix.
|
| 203 |
+
"""
|
| 204 |
+
return set(filter(lambda u: not u.is_prefixed and not u.is_physical_constant, self._units))
|
venv/lib/python3.11/site-packages/sympy/physics/units/util.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Several methods to simplify expressions involving unit objects.
|
| 3 |
+
"""
|
| 4 |
+
from functools import reduce
|
| 5 |
+
from collections.abc import Iterable
|
| 6 |
+
from typing import Optional
|
| 7 |
+
|
| 8 |
+
from sympy import default_sort_key
|
| 9 |
+
from sympy.core.add import Add
|
| 10 |
+
from sympy.core.containers import Tuple
|
| 11 |
+
from sympy.core.mul import Mul
|
| 12 |
+
from sympy.core.power import Pow
|
| 13 |
+
from sympy.core.sorting import ordered
|
| 14 |
+
from sympy.core.sympify import sympify
|
| 15 |
+
from sympy.core.function import Function
|
| 16 |
+
from sympy.matrices.exceptions import NonInvertibleMatrixError
|
| 17 |
+
from sympy.physics.units.dimensions import Dimension, DimensionSystem
|
| 18 |
+
from sympy.physics.units.prefixes import Prefix
|
| 19 |
+
from sympy.physics.units.quantities import Quantity
|
| 20 |
+
from sympy.physics.units.unitsystem import UnitSystem
|
| 21 |
+
from sympy.utilities.iterables import sift
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _get_conversion_matrix_for_expr(expr, target_units, unit_system):
|
| 25 |
+
from sympy.matrices.dense import Matrix
|
| 26 |
+
|
| 27 |
+
dimension_system = unit_system.get_dimension_system()
|
| 28 |
+
|
| 29 |
+
expr_dim = Dimension(unit_system.get_dimensional_expr(expr))
|
| 30 |
+
dim_dependencies = dimension_system.get_dimensional_dependencies(expr_dim, mark_dimensionless=True)
|
| 31 |
+
target_dims = [Dimension(unit_system.get_dimensional_expr(x)) for x in target_units]
|
| 32 |
+
canon_dim_units = [i for x in target_dims for i in dimension_system.get_dimensional_dependencies(x, mark_dimensionless=True)]
|
| 33 |
+
canon_expr_units = set(dim_dependencies)
|
| 34 |
+
|
| 35 |
+
if not canon_expr_units.issubset(set(canon_dim_units)):
|
| 36 |
+
return None
|
| 37 |
+
|
| 38 |
+
seen = set()
|
| 39 |
+
canon_dim_units = [i for i in canon_dim_units if not (i in seen or seen.add(i))]
|
| 40 |
+
|
| 41 |
+
camat = Matrix([[dimension_system.get_dimensional_dependencies(i, mark_dimensionless=True).get(j, 0) for i in target_dims] for j in canon_dim_units])
|
| 42 |
+
exprmat = Matrix([dim_dependencies.get(k, 0) for k in canon_dim_units])
|
| 43 |
+
|
| 44 |
+
try:
|
| 45 |
+
res_exponents = camat.solve(exprmat)
|
| 46 |
+
except NonInvertibleMatrixError:
|
| 47 |
+
return None
|
| 48 |
+
|
| 49 |
+
return res_exponents
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def convert_to(expr, target_units, unit_system="SI"):
|
| 53 |
+
"""
|
| 54 |
+
Convert ``expr`` to the same expression with all of its units and quantities
|
| 55 |
+
represented as factors of ``target_units``, whenever the dimension is compatible.
|
| 56 |
+
|
| 57 |
+
``target_units`` may be a single unit/quantity, or a collection of
|
| 58 |
+
units/quantities.
|
| 59 |
+
|
| 60 |
+
Examples
|
| 61 |
+
========
|
| 62 |
+
|
| 63 |
+
>>> from sympy.physics.units import speed_of_light, meter, gram, second, day
|
| 64 |
+
>>> from sympy.physics.units import mile, newton, kilogram, atomic_mass_constant
|
| 65 |
+
>>> from sympy.physics.units import kilometer, centimeter
|
| 66 |
+
>>> from sympy.physics.units import gravitational_constant, hbar
|
| 67 |
+
>>> from sympy.physics.units import convert_to
|
| 68 |
+
>>> convert_to(mile, kilometer)
|
| 69 |
+
25146*kilometer/15625
|
| 70 |
+
>>> convert_to(mile, kilometer).n()
|
| 71 |
+
1.609344*kilometer
|
| 72 |
+
>>> convert_to(speed_of_light, meter/second)
|
| 73 |
+
299792458*meter/second
|
| 74 |
+
>>> convert_to(day, second)
|
| 75 |
+
86400*second
|
| 76 |
+
>>> 3*newton
|
| 77 |
+
3*newton
|
| 78 |
+
>>> convert_to(3*newton, kilogram*meter/second**2)
|
| 79 |
+
3*kilogram*meter/second**2
|
| 80 |
+
>>> convert_to(atomic_mass_constant, gram)
|
| 81 |
+
1.660539060e-24*gram
|
| 82 |
+
|
| 83 |
+
Conversion to multiple units:
|
| 84 |
+
|
| 85 |
+
>>> convert_to(speed_of_light, [meter, second])
|
| 86 |
+
299792458*meter/second
|
| 87 |
+
>>> convert_to(3*newton, [centimeter, gram, second])
|
| 88 |
+
300000*centimeter*gram/second**2
|
| 89 |
+
|
| 90 |
+
Conversion to Planck units:
|
| 91 |
+
|
| 92 |
+
>>> convert_to(atomic_mass_constant, [gravitational_constant, speed_of_light, hbar]).n()
|
| 93 |
+
7.62963087839509e-20*hbar**0.5*speed_of_light**0.5/gravitational_constant**0.5
|
| 94 |
+
|
| 95 |
+
"""
|
| 96 |
+
from sympy.physics.units import UnitSystem
|
| 97 |
+
unit_system = UnitSystem.get_unit_system(unit_system)
|
| 98 |
+
|
| 99 |
+
if not isinstance(target_units, (Iterable, Tuple)):
|
| 100 |
+
target_units = [target_units]
|
| 101 |
+
|
| 102 |
+
def handle_Adds(expr):
|
| 103 |
+
return Add.fromiter(convert_to(i, target_units, unit_system)
|
| 104 |
+
for i in expr.args)
|
| 105 |
+
|
| 106 |
+
if isinstance(expr, Add):
|
| 107 |
+
return handle_Adds(expr)
|
| 108 |
+
elif isinstance(expr, Pow) and isinstance(expr.base, Add):
|
| 109 |
+
return handle_Adds(expr.base) ** expr.exp
|
| 110 |
+
|
| 111 |
+
expr = sympify(expr)
|
| 112 |
+
target_units = sympify(target_units)
|
| 113 |
+
|
| 114 |
+
if isinstance(expr, Function):
|
| 115 |
+
expr = expr.together()
|
| 116 |
+
|
| 117 |
+
if not isinstance(expr, Quantity) and expr.has(Quantity):
|
| 118 |
+
expr = expr.replace(lambda x: isinstance(x, Quantity),
|
| 119 |
+
lambda x: x.convert_to(target_units, unit_system))
|
| 120 |
+
|
| 121 |
+
def get_total_scale_factor(expr):
|
| 122 |
+
if isinstance(expr, Mul):
|
| 123 |
+
return reduce(lambda x, y: x * y,
|
| 124 |
+
[get_total_scale_factor(i) for i in expr.args])
|
| 125 |
+
elif isinstance(expr, Pow):
|
| 126 |
+
return get_total_scale_factor(expr.base) ** expr.exp
|
| 127 |
+
elif isinstance(expr, Quantity):
|
| 128 |
+
return unit_system.get_quantity_scale_factor(expr)
|
| 129 |
+
return expr
|
| 130 |
+
|
| 131 |
+
depmat = _get_conversion_matrix_for_expr(expr, target_units, unit_system)
|
| 132 |
+
if depmat is None:
|
| 133 |
+
return expr
|
| 134 |
+
|
| 135 |
+
expr_scale_factor = get_total_scale_factor(expr)
|
| 136 |
+
return expr_scale_factor * Mul.fromiter(
|
| 137 |
+
(1/get_total_scale_factor(u)*u)**p for u, p in
|
| 138 |
+
zip(target_units, depmat))
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def quantity_simplify(expr, across_dimensions: bool=False, unit_system=None):
|
| 142 |
+
"""Return an equivalent expression in which prefixes are replaced
|
| 143 |
+
with numerical values and all units of a given dimension are the
|
| 144 |
+
unified in a canonical manner by default. `across_dimensions` allows
|
| 145 |
+
for units of different dimensions to be simplified together.
|
| 146 |
+
|
| 147 |
+
`unit_system` must be specified if `across_dimensions` is True.
|
| 148 |
+
|
| 149 |
+
Examples
|
| 150 |
+
========
|
| 151 |
+
|
| 152 |
+
>>> from sympy.physics.units.util import quantity_simplify
|
| 153 |
+
>>> from sympy.physics.units.prefixes import kilo
|
| 154 |
+
>>> from sympy.physics.units import foot, inch, joule, coulomb
|
| 155 |
+
>>> quantity_simplify(kilo*foot*inch)
|
| 156 |
+
250*foot**2/3
|
| 157 |
+
>>> quantity_simplify(foot - 6*inch)
|
| 158 |
+
foot/2
|
| 159 |
+
>>> quantity_simplify(5*joule/coulomb, across_dimensions=True, unit_system="SI")
|
| 160 |
+
5*volt
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
if expr.is_Atom or not expr.has(Prefix, Quantity):
|
| 164 |
+
return expr
|
| 165 |
+
|
| 166 |
+
# replace all prefixes with numerical values
|
| 167 |
+
p = expr.atoms(Prefix)
|
| 168 |
+
expr = expr.xreplace({p: p.scale_factor for p in p})
|
| 169 |
+
|
| 170 |
+
# replace all quantities of given dimension with a canonical
|
| 171 |
+
# quantity, chosen from those in the expression
|
| 172 |
+
d = sift(expr.atoms(Quantity), lambda i: i.dimension)
|
| 173 |
+
for k in d:
|
| 174 |
+
if len(d[k]) == 1:
|
| 175 |
+
continue
|
| 176 |
+
v = list(ordered(d[k]))
|
| 177 |
+
ref = v[0]/v[0].scale_factor
|
| 178 |
+
expr = expr.xreplace({vi: ref*vi.scale_factor for vi in v[1:]})
|
| 179 |
+
|
| 180 |
+
if across_dimensions:
|
| 181 |
+
# combine quantities of different dimensions into a single
|
| 182 |
+
# quantity that is equivalent to the original expression
|
| 183 |
+
|
| 184 |
+
if unit_system is None:
|
| 185 |
+
raise ValueError("unit_system must be specified if across_dimensions is True")
|
| 186 |
+
|
| 187 |
+
unit_system = UnitSystem.get_unit_system(unit_system)
|
| 188 |
+
dimension_system: DimensionSystem = unit_system.get_dimension_system()
|
| 189 |
+
dim_expr = unit_system.get_dimensional_expr(expr)
|
| 190 |
+
dim_deps = dimension_system.get_dimensional_dependencies(dim_expr, mark_dimensionless=True)
|
| 191 |
+
|
| 192 |
+
target_dimension: Optional[Dimension] = None
|
| 193 |
+
for ds_dim, ds_dim_deps in dimension_system.dimensional_dependencies.items():
|
| 194 |
+
if ds_dim_deps == dim_deps:
|
| 195 |
+
target_dimension = ds_dim
|
| 196 |
+
break
|
| 197 |
+
|
| 198 |
+
if target_dimension is None:
|
| 199 |
+
# if we can't find a target dimension, we can't do anything. unsure how to handle this case.
|
| 200 |
+
return expr
|
| 201 |
+
|
| 202 |
+
target_unit = unit_system.derived_units.get(target_dimension)
|
| 203 |
+
if target_unit:
|
| 204 |
+
expr = convert_to(expr, target_unit, unit_system)
|
| 205 |
+
|
| 206 |
+
return expr
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def check_dimensions(expr, unit_system="SI"):
|
| 210 |
+
"""Return expr if units in addends have the same
|
| 211 |
+
base dimensions, else raise a ValueError."""
|
| 212 |
+
# the case of adding a number to a dimensional quantity
|
| 213 |
+
# is ignored for the sake of SymPy core routines, so this
|
| 214 |
+
# function will raise an error now if such an addend is
|
| 215 |
+
# found.
|
| 216 |
+
# Also, when doing substitutions, multiplicative constants
|
| 217 |
+
# might be introduced, so remove those now
|
| 218 |
+
|
| 219 |
+
from sympy.physics.units import UnitSystem
|
| 220 |
+
unit_system = UnitSystem.get_unit_system(unit_system)
|
| 221 |
+
|
| 222 |
+
def addDict(dict1, dict2):
|
| 223 |
+
"""Merge dictionaries by adding values of common keys and
|
| 224 |
+
removing keys with value of 0."""
|
| 225 |
+
dict3 = {**dict1, **dict2}
|
| 226 |
+
for key, value in dict3.items():
|
| 227 |
+
if key in dict1 and key in dict2:
|
| 228 |
+
dict3[key] = value + dict1[key]
|
| 229 |
+
return {key:val for key, val in dict3.items() if val != 0}
|
| 230 |
+
|
| 231 |
+
adds = expr.atoms(Add)
|
| 232 |
+
DIM_OF = unit_system.get_dimension_system().get_dimensional_dependencies
|
| 233 |
+
for a in adds:
|
| 234 |
+
deset = set()
|
| 235 |
+
for ai in a.args:
|
| 236 |
+
if ai.is_number:
|
| 237 |
+
deset.add(())
|
| 238 |
+
continue
|
| 239 |
+
dims = []
|
| 240 |
+
skip = False
|
| 241 |
+
dimdict = {}
|
| 242 |
+
for i in Mul.make_args(ai):
|
| 243 |
+
if i.has(Quantity):
|
| 244 |
+
i = Dimension(unit_system.get_dimensional_expr(i))
|
| 245 |
+
if i.has(Dimension):
|
| 246 |
+
dimdict = addDict(dimdict, DIM_OF(i))
|
| 247 |
+
elif i.free_symbols:
|
| 248 |
+
skip = True
|
| 249 |
+
break
|
| 250 |
+
dims.extend(dimdict.items())
|
| 251 |
+
if not skip:
|
| 252 |
+
deset.add(tuple(sorted(dims, key=default_sort_key)))
|
| 253 |
+
if len(deset) > 1:
|
| 254 |
+
raise ValueError(
|
| 255 |
+
"addends have incompatible dimensions: {}".format(deset))
|
| 256 |
+
|
| 257 |
+
# clear multiplicative constants on Dimensions which may be
|
| 258 |
+
# left after substitution
|
| 259 |
+
reps = {}
|
| 260 |
+
for m in expr.atoms(Mul):
|
| 261 |
+
if any(isinstance(i, Dimension) for i in m.args):
|
| 262 |
+
reps[m] = m.func(*[
|
| 263 |
+
i for i in m.args if not i.is_number])
|
| 264 |
+
|
| 265 |
+
return expr.xreplace(reps)
|
venv/lib/python3.11/site-packages/sympy/physics/vector/__init__.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__all__ = [
|
| 2 |
+
'CoordinateSym', 'ReferenceFrame',
|
| 3 |
+
|
| 4 |
+
'Dyadic',
|
| 5 |
+
|
| 6 |
+
'Vector',
|
| 7 |
+
|
| 8 |
+
'Point',
|
| 9 |
+
|
| 10 |
+
'cross', 'dot', 'express', 'time_derivative', 'outer',
|
| 11 |
+
'kinematic_equations', 'get_motion_params', 'partial_velocity',
|
| 12 |
+
'dynamicsymbols',
|
| 13 |
+
|
| 14 |
+
'vprint', 'vsstrrepr', 'vsprint', 'vpprint', 'vlatex', 'init_vprinting',
|
| 15 |
+
|
| 16 |
+
'curl', 'divergence', 'gradient', 'is_conservative', 'is_solenoidal',
|
| 17 |
+
'scalar_potential', 'scalar_potential_difference',
|
| 18 |
+
|
| 19 |
+
]
|
| 20 |
+
from .frame import CoordinateSym, ReferenceFrame
|
| 21 |
+
|
| 22 |
+
from .dyadic import Dyadic
|
| 23 |
+
|
| 24 |
+
from .vector import Vector
|
| 25 |
+
|
| 26 |
+
from .point import Point
|
| 27 |
+
|
| 28 |
+
from .functions import (cross, dot, express, time_derivative, outer,
|
| 29 |
+
kinematic_equations, get_motion_params, partial_velocity,
|
| 30 |
+
dynamicsymbols)
|
| 31 |
+
|
| 32 |
+
from .printing import (vprint, vsstrrepr, vsprint, vpprint, vlatex,
|
| 33 |
+
init_vprinting)
|
| 34 |
+
|
| 35 |
+
from .fieldfunctions import (curl, divergence, gradient, is_conservative,
|
| 36 |
+
is_solenoidal, scalar_potential, scalar_potential_difference)
|
venv/lib/python3.11/site-packages/sympy/physics/vector/dyadic.py
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy import sympify, Add, ImmutableMatrix as Matrix
|
| 2 |
+
from sympy.core.evalf import EvalfMixin
|
| 3 |
+
from sympy.printing.defaults import Printable
|
| 4 |
+
|
| 5 |
+
from mpmath.libmp.libmpf import prec_to_dps
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
__all__ = ['Dyadic']
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class Dyadic(Printable, EvalfMixin):
|
| 12 |
+
"""A Dyadic object.
|
| 13 |
+
|
| 14 |
+
See:
|
| 15 |
+
https://en.wikipedia.org/wiki/Dyadic_tensor
|
| 16 |
+
Kane, T., Levinson, D. Dynamics Theory and Applications. 1985 McGraw-Hill
|
| 17 |
+
|
| 18 |
+
A more powerful way to represent a rigid body's inertia. While it is more
|
| 19 |
+
complex, by choosing Dyadic components to be in body fixed basis vectors,
|
| 20 |
+
the resulting matrix is equivalent to the inertia tensor.
|
| 21 |
+
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
is_number = False
|
| 25 |
+
|
| 26 |
+
def __init__(self, inlist):
|
| 27 |
+
"""
|
| 28 |
+
Just like Vector's init, you should not call this unless creating a
|
| 29 |
+
zero dyadic.
|
| 30 |
+
|
| 31 |
+
zd = Dyadic(0)
|
| 32 |
+
|
| 33 |
+
Stores a Dyadic as a list of lists; the inner list has the measure
|
| 34 |
+
number and the two unit vectors; the outerlist holds each unique
|
| 35 |
+
unit vector pair.
|
| 36 |
+
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
self.args = []
|
| 40 |
+
if inlist == 0:
|
| 41 |
+
inlist = []
|
| 42 |
+
while len(inlist) != 0:
|
| 43 |
+
added = 0
|
| 44 |
+
for i, v in enumerate(self.args):
|
| 45 |
+
if ((str(inlist[0][1]) == str(self.args[i][1])) and
|
| 46 |
+
(str(inlist[0][2]) == str(self.args[i][2]))):
|
| 47 |
+
self.args[i] = (self.args[i][0] + inlist[0][0],
|
| 48 |
+
inlist[0][1], inlist[0][2])
|
| 49 |
+
inlist.remove(inlist[0])
|
| 50 |
+
added = 1
|
| 51 |
+
break
|
| 52 |
+
if added != 1:
|
| 53 |
+
self.args.append(inlist[0])
|
| 54 |
+
inlist.remove(inlist[0])
|
| 55 |
+
i = 0
|
| 56 |
+
# This code is to remove empty parts from the list
|
| 57 |
+
while i < len(self.args):
|
| 58 |
+
if ((self.args[i][0] == 0) | (self.args[i][1] == 0) |
|
| 59 |
+
(self.args[i][2] == 0)):
|
| 60 |
+
self.args.remove(self.args[i])
|
| 61 |
+
i -= 1
|
| 62 |
+
i += 1
|
| 63 |
+
|
| 64 |
+
@property
|
| 65 |
+
def func(self):
|
| 66 |
+
"""Returns the class Dyadic. """
|
| 67 |
+
return Dyadic
|
| 68 |
+
|
| 69 |
+
def __add__(self, other):
|
| 70 |
+
"""The add operator for Dyadic. """
|
| 71 |
+
other = _check_dyadic(other)
|
| 72 |
+
return Dyadic(self.args + other.args)
|
| 73 |
+
|
| 74 |
+
__radd__ = __add__
|
| 75 |
+
|
| 76 |
+
def __mul__(self, other):
|
| 77 |
+
"""Multiplies the Dyadic by a sympifyable expression.
|
| 78 |
+
|
| 79 |
+
Parameters
|
| 80 |
+
==========
|
| 81 |
+
|
| 82 |
+
other : Sympafiable
|
| 83 |
+
The scalar to multiply this Dyadic with
|
| 84 |
+
|
| 85 |
+
Examples
|
| 86 |
+
========
|
| 87 |
+
|
| 88 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer
|
| 89 |
+
>>> N = ReferenceFrame('N')
|
| 90 |
+
>>> d = outer(N.x, N.x)
|
| 91 |
+
>>> 5 * d
|
| 92 |
+
5*(N.x|N.x)
|
| 93 |
+
|
| 94 |
+
"""
|
| 95 |
+
newlist = list(self.args)
|
| 96 |
+
other = sympify(other)
|
| 97 |
+
for i in range(len(newlist)):
|
| 98 |
+
newlist[i] = (other * newlist[i][0], newlist[i][1],
|
| 99 |
+
newlist[i][2])
|
| 100 |
+
return Dyadic(newlist)
|
| 101 |
+
|
| 102 |
+
__rmul__ = __mul__
|
| 103 |
+
|
| 104 |
+
def dot(self, other):
|
| 105 |
+
"""The inner product operator for a Dyadic and a Dyadic or Vector.
|
| 106 |
+
|
| 107 |
+
Parameters
|
| 108 |
+
==========
|
| 109 |
+
|
| 110 |
+
other : Dyadic or Vector
|
| 111 |
+
The other Dyadic or Vector to take the inner product with
|
| 112 |
+
|
| 113 |
+
Examples
|
| 114 |
+
========
|
| 115 |
+
|
| 116 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer
|
| 117 |
+
>>> N = ReferenceFrame('N')
|
| 118 |
+
>>> D1 = outer(N.x, N.y)
|
| 119 |
+
>>> D2 = outer(N.y, N.y)
|
| 120 |
+
>>> D1.dot(D2)
|
| 121 |
+
(N.x|N.y)
|
| 122 |
+
>>> D1.dot(N.y)
|
| 123 |
+
N.x
|
| 124 |
+
|
| 125 |
+
"""
|
| 126 |
+
from sympy.physics.vector.vector import Vector, _check_vector
|
| 127 |
+
if isinstance(other, Dyadic):
|
| 128 |
+
other = _check_dyadic(other)
|
| 129 |
+
ol = Dyadic(0)
|
| 130 |
+
for v in self.args:
|
| 131 |
+
for v2 in other.args:
|
| 132 |
+
ol += v[0] * v2[0] * (v[2].dot(v2[1])) * (v[1].outer(v2[2]))
|
| 133 |
+
else:
|
| 134 |
+
other = _check_vector(other)
|
| 135 |
+
ol = Vector(0)
|
| 136 |
+
for v in self.args:
|
| 137 |
+
ol += v[0] * v[1] * (v[2].dot(other))
|
| 138 |
+
return ol
|
| 139 |
+
|
| 140 |
+
# NOTE : supports non-advertised Dyadic & Dyadic, Dyadic & Vector notation
|
| 141 |
+
__and__ = dot
|
| 142 |
+
|
| 143 |
+
def __truediv__(self, other):
|
| 144 |
+
"""Divides the Dyadic by a sympifyable expression. """
|
| 145 |
+
return self.__mul__(1 / other)
|
| 146 |
+
|
| 147 |
+
def __eq__(self, other):
|
| 148 |
+
"""Tests for equality.
|
| 149 |
+
|
| 150 |
+
Is currently weak; needs stronger comparison testing
|
| 151 |
+
|
| 152 |
+
"""
|
| 153 |
+
|
| 154 |
+
if other == 0:
|
| 155 |
+
other = Dyadic(0)
|
| 156 |
+
other = _check_dyadic(other)
|
| 157 |
+
if (self.args == []) and (other.args == []):
|
| 158 |
+
return True
|
| 159 |
+
elif (self.args == []) or (other.args == []):
|
| 160 |
+
return False
|
| 161 |
+
return set(self.args) == set(other.args)
|
| 162 |
+
|
| 163 |
+
def __ne__(self, other):
|
| 164 |
+
return not self == other
|
| 165 |
+
|
| 166 |
+
def __neg__(self):
|
| 167 |
+
return self * -1
|
| 168 |
+
|
| 169 |
+
def _latex(self, printer):
|
| 170 |
+
ar = self.args # just to shorten things
|
| 171 |
+
if len(ar) == 0:
|
| 172 |
+
return str(0)
|
| 173 |
+
ol = [] # output list, to be concatenated to a string
|
| 174 |
+
for v in ar:
|
| 175 |
+
# if the coef of the dyadic is 1, we skip the 1
|
| 176 |
+
if v[0] == 1:
|
| 177 |
+
ol.append(' + ' + printer._print(v[1]) + r"\otimes " +
|
| 178 |
+
printer._print(v[2]))
|
| 179 |
+
# if the coef of the dyadic is -1, we skip the 1
|
| 180 |
+
elif v[0] == -1:
|
| 181 |
+
ol.append(' - ' +
|
| 182 |
+
printer._print(v[1]) +
|
| 183 |
+
r"\otimes " +
|
| 184 |
+
printer._print(v[2]))
|
| 185 |
+
# If the coefficient of the dyadic is not 1 or -1,
|
| 186 |
+
# we might wrap it in parentheses, for readability.
|
| 187 |
+
elif v[0] != 0:
|
| 188 |
+
arg_str = printer._print(v[0])
|
| 189 |
+
if isinstance(v[0], Add):
|
| 190 |
+
arg_str = '(%s)' % arg_str
|
| 191 |
+
if arg_str.startswith('-'):
|
| 192 |
+
arg_str = arg_str[1:]
|
| 193 |
+
str_start = ' - '
|
| 194 |
+
else:
|
| 195 |
+
str_start = ' + '
|
| 196 |
+
ol.append(str_start + arg_str + printer._print(v[1]) +
|
| 197 |
+
r"\otimes " + printer._print(v[2]))
|
| 198 |
+
outstr = ''.join(ol)
|
| 199 |
+
if outstr.startswith(' + '):
|
| 200 |
+
outstr = outstr[3:]
|
| 201 |
+
elif outstr.startswith(' '):
|
| 202 |
+
outstr = outstr[1:]
|
| 203 |
+
return outstr
|
| 204 |
+
|
| 205 |
+
def _pretty(self, printer):
|
| 206 |
+
e = self
|
| 207 |
+
|
| 208 |
+
class Fake:
|
| 209 |
+
baseline = 0
|
| 210 |
+
|
| 211 |
+
def render(self, *args, **kwargs):
|
| 212 |
+
ar = e.args # just to shorten things
|
| 213 |
+
mpp = printer
|
| 214 |
+
if len(ar) == 0:
|
| 215 |
+
return str(0)
|
| 216 |
+
bar = "\N{CIRCLED TIMES}" if printer._use_unicode else "|"
|
| 217 |
+
ol = [] # output list, to be concatenated to a string
|
| 218 |
+
for v in ar:
|
| 219 |
+
# if the coef of the dyadic is 1, we skip the 1
|
| 220 |
+
if v[0] == 1:
|
| 221 |
+
ol.extend([" + ",
|
| 222 |
+
mpp.doprint(v[1]),
|
| 223 |
+
bar,
|
| 224 |
+
mpp.doprint(v[2])])
|
| 225 |
+
|
| 226 |
+
# if the coef of the dyadic is -1, we skip the 1
|
| 227 |
+
elif v[0] == -1:
|
| 228 |
+
ol.extend([" - ",
|
| 229 |
+
mpp.doprint(v[1]),
|
| 230 |
+
bar,
|
| 231 |
+
mpp.doprint(v[2])])
|
| 232 |
+
|
| 233 |
+
# If the coefficient of the dyadic is not 1 or -1,
|
| 234 |
+
# we might wrap it in parentheses, for readability.
|
| 235 |
+
elif v[0] != 0:
|
| 236 |
+
if isinstance(v[0], Add):
|
| 237 |
+
arg_str = mpp._print(
|
| 238 |
+
v[0]).parens()[0]
|
| 239 |
+
else:
|
| 240 |
+
arg_str = mpp.doprint(v[0])
|
| 241 |
+
if arg_str.startswith("-"):
|
| 242 |
+
arg_str = arg_str[1:]
|
| 243 |
+
str_start = " - "
|
| 244 |
+
else:
|
| 245 |
+
str_start = " + "
|
| 246 |
+
ol.extend([str_start, arg_str, " ",
|
| 247 |
+
mpp.doprint(v[1]),
|
| 248 |
+
bar,
|
| 249 |
+
mpp.doprint(v[2])])
|
| 250 |
+
|
| 251 |
+
outstr = "".join(ol)
|
| 252 |
+
if outstr.startswith(" + "):
|
| 253 |
+
outstr = outstr[3:]
|
| 254 |
+
elif outstr.startswith(" "):
|
| 255 |
+
outstr = outstr[1:]
|
| 256 |
+
return outstr
|
| 257 |
+
return Fake()
|
| 258 |
+
|
| 259 |
+
def __rsub__(self, other):
|
| 260 |
+
return (-1 * self) + other
|
| 261 |
+
|
| 262 |
+
def _sympystr(self, printer):
|
| 263 |
+
"""Printing method. """
|
| 264 |
+
ar = self.args # just to shorten things
|
| 265 |
+
if len(ar) == 0:
|
| 266 |
+
return printer._print(0)
|
| 267 |
+
ol = [] # output list, to be concatenated to a string
|
| 268 |
+
for v in ar:
|
| 269 |
+
# if the coef of the dyadic is 1, we skip the 1
|
| 270 |
+
if v[0] == 1:
|
| 271 |
+
ol.append(' + (' + printer._print(v[1]) + '|' +
|
| 272 |
+
printer._print(v[2]) + ')')
|
| 273 |
+
# if the coef of the dyadic is -1, we skip the 1
|
| 274 |
+
elif v[0] == -1:
|
| 275 |
+
ol.append(' - (' + printer._print(v[1]) + '|' +
|
| 276 |
+
printer._print(v[2]) + ')')
|
| 277 |
+
# If the coefficient of the dyadic is not 1 or -1,
|
| 278 |
+
# we might wrap it in parentheses, for readability.
|
| 279 |
+
elif v[0] != 0:
|
| 280 |
+
arg_str = printer._print(v[0])
|
| 281 |
+
if isinstance(v[0], Add):
|
| 282 |
+
arg_str = "(%s)" % arg_str
|
| 283 |
+
if arg_str[0] == '-':
|
| 284 |
+
arg_str = arg_str[1:]
|
| 285 |
+
str_start = ' - '
|
| 286 |
+
else:
|
| 287 |
+
str_start = ' + '
|
| 288 |
+
ol.append(str_start + arg_str + '*(' +
|
| 289 |
+
printer._print(v[1]) +
|
| 290 |
+
'|' + printer._print(v[2]) + ')')
|
| 291 |
+
outstr = ''.join(ol)
|
| 292 |
+
if outstr.startswith(' + '):
|
| 293 |
+
outstr = outstr[3:]
|
| 294 |
+
elif outstr.startswith(' '):
|
| 295 |
+
outstr = outstr[1:]
|
| 296 |
+
return outstr
|
| 297 |
+
|
| 298 |
+
def __sub__(self, other):
|
| 299 |
+
"""The subtraction operator. """
|
| 300 |
+
return self.__add__(other * -1)
|
| 301 |
+
|
| 302 |
+
def cross(self, other):
|
| 303 |
+
"""Returns the dyadic resulting from the dyadic vector cross product:
|
| 304 |
+
Dyadic x Vector.
|
| 305 |
+
|
| 306 |
+
Parameters
|
| 307 |
+
==========
|
| 308 |
+
other : Vector
|
| 309 |
+
Vector to cross with.
|
| 310 |
+
|
| 311 |
+
Examples
|
| 312 |
+
========
|
| 313 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer, cross
|
| 314 |
+
>>> N = ReferenceFrame('N')
|
| 315 |
+
>>> d = outer(N.x, N.x)
|
| 316 |
+
>>> cross(d, N.y)
|
| 317 |
+
(N.x|N.z)
|
| 318 |
+
|
| 319 |
+
"""
|
| 320 |
+
from sympy.physics.vector.vector import _check_vector
|
| 321 |
+
other = _check_vector(other)
|
| 322 |
+
ol = Dyadic(0)
|
| 323 |
+
for v in self.args:
|
| 324 |
+
ol += v[0] * (v[1].outer((v[2].cross(other))))
|
| 325 |
+
return ol
|
| 326 |
+
|
| 327 |
+
# NOTE : supports non-advertised Dyadic ^ Vector notation
|
| 328 |
+
__xor__ = cross
|
| 329 |
+
|
| 330 |
+
def express(self, frame1, frame2=None):
|
| 331 |
+
"""Expresses this Dyadic in alternate frame(s)
|
| 332 |
+
|
| 333 |
+
The first frame is the list side expression, the second frame is the
|
| 334 |
+
right side; if Dyadic is in form A.x|B.y, you can express it in two
|
| 335 |
+
different frames. If no second frame is given, the Dyadic is
|
| 336 |
+
expressed in only one frame.
|
| 337 |
+
|
| 338 |
+
Calls the global express function
|
| 339 |
+
|
| 340 |
+
Parameters
|
| 341 |
+
==========
|
| 342 |
+
|
| 343 |
+
frame1 : ReferenceFrame
|
| 344 |
+
The frame to express the left side of the Dyadic in
|
| 345 |
+
frame2 : ReferenceFrame
|
| 346 |
+
If provided, the frame to express the right side of the Dyadic in
|
| 347 |
+
|
| 348 |
+
Examples
|
| 349 |
+
========
|
| 350 |
+
|
| 351 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
|
| 352 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 353 |
+
>>> init_vprinting(pretty_print=False)
|
| 354 |
+
>>> N = ReferenceFrame('N')
|
| 355 |
+
>>> q = dynamicsymbols('q')
|
| 356 |
+
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
| 357 |
+
>>> d = outer(N.x, N.x)
|
| 358 |
+
>>> d.express(B, N)
|
| 359 |
+
cos(q)*(B.x|N.x) - sin(q)*(B.y|N.x)
|
| 360 |
+
|
| 361 |
+
"""
|
| 362 |
+
from sympy.physics.vector.functions import express
|
| 363 |
+
return express(self, frame1, frame2)
|
| 364 |
+
|
| 365 |
+
def to_matrix(self, reference_frame, second_reference_frame=None):
|
| 366 |
+
"""Returns the matrix form of the dyadic with respect to one or two
|
| 367 |
+
reference frames.
|
| 368 |
+
|
| 369 |
+
Parameters
|
| 370 |
+
----------
|
| 371 |
+
reference_frame : ReferenceFrame
|
| 372 |
+
The reference frame that the rows and columns of the matrix
|
| 373 |
+
correspond to. If a second reference frame is provided, this
|
| 374 |
+
only corresponds to the rows of the matrix.
|
| 375 |
+
second_reference_frame : ReferenceFrame, optional, default=None
|
| 376 |
+
The reference frame that the columns of the matrix correspond
|
| 377 |
+
to.
|
| 378 |
+
|
| 379 |
+
Returns
|
| 380 |
+
-------
|
| 381 |
+
matrix : ImmutableMatrix, shape(3,3)
|
| 382 |
+
The matrix that gives the 2D tensor form.
|
| 383 |
+
|
| 384 |
+
Examples
|
| 385 |
+
========
|
| 386 |
+
|
| 387 |
+
>>> from sympy import symbols, trigsimp
|
| 388 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 389 |
+
>>> from sympy.physics.mechanics import inertia
|
| 390 |
+
>>> Ixx, Iyy, Izz, Ixy, Iyz, Ixz = symbols('Ixx, Iyy, Izz, Ixy, Iyz, Ixz')
|
| 391 |
+
>>> N = ReferenceFrame('N')
|
| 392 |
+
>>> inertia_dyadic = inertia(N, Ixx, Iyy, Izz, Ixy, Iyz, Ixz)
|
| 393 |
+
>>> inertia_dyadic.to_matrix(N)
|
| 394 |
+
Matrix([
|
| 395 |
+
[Ixx, Ixy, Ixz],
|
| 396 |
+
[Ixy, Iyy, Iyz],
|
| 397 |
+
[Ixz, Iyz, Izz]])
|
| 398 |
+
>>> beta = symbols('beta')
|
| 399 |
+
>>> A = N.orientnew('A', 'Axis', (beta, N.x))
|
| 400 |
+
>>> trigsimp(inertia_dyadic.to_matrix(A))
|
| 401 |
+
Matrix([
|
| 402 |
+
[ Ixx, Ixy*cos(beta) + Ixz*sin(beta), -Ixy*sin(beta) + Ixz*cos(beta)],
|
| 403 |
+
[ Ixy*cos(beta) + Ixz*sin(beta), Iyy*cos(2*beta)/2 + Iyy/2 + Iyz*sin(2*beta) - Izz*cos(2*beta)/2 + Izz/2, -Iyy*sin(2*beta)/2 + Iyz*cos(2*beta) + Izz*sin(2*beta)/2],
|
| 404 |
+
[-Ixy*sin(beta) + Ixz*cos(beta), -Iyy*sin(2*beta)/2 + Iyz*cos(2*beta) + Izz*sin(2*beta)/2, -Iyy*cos(2*beta)/2 + Iyy/2 - Iyz*sin(2*beta) + Izz*cos(2*beta)/2 + Izz/2]])
|
| 405 |
+
|
| 406 |
+
"""
|
| 407 |
+
|
| 408 |
+
if second_reference_frame is None:
|
| 409 |
+
second_reference_frame = reference_frame
|
| 410 |
+
|
| 411 |
+
return Matrix([i.dot(self).dot(j) for i in reference_frame for j in
|
| 412 |
+
second_reference_frame]).reshape(3, 3)
|
| 413 |
+
|
| 414 |
+
def doit(self, **hints):
|
| 415 |
+
"""Calls .doit() on each term in the Dyadic"""
|
| 416 |
+
return sum([Dyadic([(v[0].doit(**hints), v[1], v[2])])
|
| 417 |
+
for v in self.args], Dyadic(0))
|
| 418 |
+
|
| 419 |
+
def dt(self, frame):
|
| 420 |
+
"""Take the time derivative of this Dyadic in a frame.
|
| 421 |
+
|
| 422 |
+
This function calls the global time_derivative method
|
| 423 |
+
|
| 424 |
+
Parameters
|
| 425 |
+
==========
|
| 426 |
+
|
| 427 |
+
frame : ReferenceFrame
|
| 428 |
+
The frame to take the time derivative in
|
| 429 |
+
|
| 430 |
+
Examples
|
| 431 |
+
========
|
| 432 |
+
|
| 433 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
|
| 434 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 435 |
+
>>> init_vprinting(pretty_print=False)
|
| 436 |
+
>>> N = ReferenceFrame('N')
|
| 437 |
+
>>> q = dynamicsymbols('q')
|
| 438 |
+
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
| 439 |
+
>>> d = outer(N.x, N.x)
|
| 440 |
+
>>> d.dt(B)
|
| 441 |
+
- q'*(N.y|N.x) - q'*(N.x|N.y)
|
| 442 |
+
|
| 443 |
+
"""
|
| 444 |
+
from sympy.physics.vector.functions import time_derivative
|
| 445 |
+
return time_derivative(self, frame)
|
| 446 |
+
|
| 447 |
+
def simplify(self):
|
| 448 |
+
"""Returns a simplified Dyadic."""
|
| 449 |
+
out = Dyadic(0)
|
| 450 |
+
for v in self.args:
|
| 451 |
+
out += Dyadic([(v[0].simplify(), v[1], v[2])])
|
| 452 |
+
return out
|
| 453 |
+
|
| 454 |
+
def subs(self, *args, **kwargs):
|
| 455 |
+
"""Substitution on the Dyadic.
|
| 456 |
+
|
| 457 |
+
Examples
|
| 458 |
+
========
|
| 459 |
+
|
| 460 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 461 |
+
>>> from sympy import Symbol
|
| 462 |
+
>>> N = ReferenceFrame('N')
|
| 463 |
+
>>> s = Symbol('s')
|
| 464 |
+
>>> a = s*(N.x|N.x)
|
| 465 |
+
>>> a.subs({s: 2})
|
| 466 |
+
2*(N.x|N.x)
|
| 467 |
+
|
| 468 |
+
"""
|
| 469 |
+
|
| 470 |
+
return sum([Dyadic([(v[0].subs(*args, **kwargs), v[1], v[2])])
|
| 471 |
+
for v in self.args], Dyadic(0))
|
| 472 |
+
|
| 473 |
+
def applyfunc(self, f):
|
| 474 |
+
"""Apply a function to each component of a Dyadic."""
|
| 475 |
+
if not callable(f):
|
| 476 |
+
raise TypeError("`f` must be callable.")
|
| 477 |
+
|
| 478 |
+
out = Dyadic(0)
|
| 479 |
+
for a, b, c in self.args:
|
| 480 |
+
out += f(a) * (b.outer(c))
|
| 481 |
+
return out
|
| 482 |
+
|
| 483 |
+
def _eval_evalf(self, prec):
|
| 484 |
+
if not self.args:
|
| 485 |
+
return self
|
| 486 |
+
new_args = []
|
| 487 |
+
dps = prec_to_dps(prec)
|
| 488 |
+
for inlist in self.args:
|
| 489 |
+
new_inlist = list(inlist)
|
| 490 |
+
new_inlist[0] = inlist[0].evalf(n=dps)
|
| 491 |
+
new_args.append(tuple(new_inlist))
|
| 492 |
+
return Dyadic(new_args)
|
| 493 |
+
|
| 494 |
+
def xreplace(self, rule):
|
| 495 |
+
"""
|
| 496 |
+
Replace occurrences of objects within the measure numbers of the
|
| 497 |
+
Dyadic.
|
| 498 |
+
|
| 499 |
+
Parameters
|
| 500 |
+
==========
|
| 501 |
+
|
| 502 |
+
rule : dict-like
|
| 503 |
+
Expresses a replacement rule.
|
| 504 |
+
|
| 505 |
+
Returns
|
| 506 |
+
=======
|
| 507 |
+
|
| 508 |
+
Dyadic
|
| 509 |
+
Result of the replacement.
|
| 510 |
+
|
| 511 |
+
Examples
|
| 512 |
+
========
|
| 513 |
+
|
| 514 |
+
>>> from sympy import symbols, pi
|
| 515 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer
|
| 516 |
+
>>> N = ReferenceFrame('N')
|
| 517 |
+
>>> D = outer(N.x, N.x)
|
| 518 |
+
>>> x, y, z = symbols('x y z')
|
| 519 |
+
>>> ((1 + x*y) * D).xreplace({x: pi})
|
| 520 |
+
(pi*y + 1)*(N.x|N.x)
|
| 521 |
+
>>> ((1 + x*y) * D).xreplace({x: pi, y: 2})
|
| 522 |
+
(1 + 2*pi)*(N.x|N.x)
|
| 523 |
+
|
| 524 |
+
Replacements occur only if an entire node in the expression tree is
|
| 525 |
+
matched:
|
| 526 |
+
|
| 527 |
+
>>> ((x*y + z) * D).xreplace({x*y: pi})
|
| 528 |
+
(z + pi)*(N.x|N.x)
|
| 529 |
+
>>> ((x*y*z) * D).xreplace({x*y: pi})
|
| 530 |
+
x*y*z*(N.x|N.x)
|
| 531 |
+
|
| 532 |
+
"""
|
| 533 |
+
|
| 534 |
+
new_args = []
|
| 535 |
+
for inlist in self.args:
|
| 536 |
+
new_inlist = list(inlist)
|
| 537 |
+
new_inlist[0] = new_inlist[0].xreplace(rule)
|
| 538 |
+
new_args.append(tuple(new_inlist))
|
| 539 |
+
return Dyadic(new_args)
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
def _check_dyadic(other):
|
| 543 |
+
if not isinstance(other, Dyadic):
|
| 544 |
+
raise TypeError('A Dyadic must be supplied')
|
| 545 |
+
return other
|
venv/lib/python3.11/site-packages/sympy/physics/vector/fieldfunctions.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.function import diff
|
| 2 |
+
from sympy.core.singleton import S
|
| 3 |
+
from sympy.integrals.integrals import integrate
|
| 4 |
+
from sympy.physics.vector import Vector, express
|
| 5 |
+
from sympy.physics.vector.frame import _check_frame
|
| 6 |
+
from sympy.physics.vector.vector import _check_vector
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
__all__ = ['curl', 'divergence', 'gradient', 'is_conservative',
|
| 10 |
+
'is_solenoidal', 'scalar_potential',
|
| 11 |
+
'scalar_potential_difference']
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def curl(vect, frame):
|
| 15 |
+
"""
|
| 16 |
+
Returns the curl of a vector field computed wrt the coordinate
|
| 17 |
+
symbols of the given frame.
|
| 18 |
+
|
| 19 |
+
Parameters
|
| 20 |
+
==========
|
| 21 |
+
|
| 22 |
+
vect : Vector
|
| 23 |
+
The vector operand
|
| 24 |
+
|
| 25 |
+
frame : ReferenceFrame
|
| 26 |
+
The reference frame to calculate the curl in
|
| 27 |
+
|
| 28 |
+
Examples
|
| 29 |
+
========
|
| 30 |
+
|
| 31 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 32 |
+
>>> from sympy.physics.vector import curl
|
| 33 |
+
>>> R = ReferenceFrame('R')
|
| 34 |
+
>>> v1 = R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z
|
| 35 |
+
>>> curl(v1, R)
|
| 36 |
+
0
|
| 37 |
+
>>> v2 = R[0]*R[1]*R[2]*R.x
|
| 38 |
+
>>> curl(v2, R)
|
| 39 |
+
R_x*R_y*R.y - R_x*R_z*R.z
|
| 40 |
+
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
_check_vector(vect)
|
| 44 |
+
if vect == 0:
|
| 45 |
+
return Vector(0)
|
| 46 |
+
vect = express(vect, frame, variables=True)
|
| 47 |
+
# A mechanical approach to avoid looping overheads
|
| 48 |
+
vectx = vect.dot(frame.x)
|
| 49 |
+
vecty = vect.dot(frame.y)
|
| 50 |
+
vectz = vect.dot(frame.z)
|
| 51 |
+
outvec = Vector(0)
|
| 52 |
+
outvec += (diff(vectz, frame[1]) - diff(vecty, frame[2])) * frame.x
|
| 53 |
+
outvec += (diff(vectx, frame[2]) - diff(vectz, frame[0])) * frame.y
|
| 54 |
+
outvec += (diff(vecty, frame[0]) - diff(vectx, frame[1])) * frame.z
|
| 55 |
+
return outvec
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def divergence(vect, frame):
|
| 59 |
+
"""
|
| 60 |
+
Returns the divergence of a vector field computed wrt the coordinate
|
| 61 |
+
symbols of the given frame.
|
| 62 |
+
|
| 63 |
+
Parameters
|
| 64 |
+
==========
|
| 65 |
+
|
| 66 |
+
vect : Vector
|
| 67 |
+
The vector operand
|
| 68 |
+
|
| 69 |
+
frame : ReferenceFrame
|
| 70 |
+
The reference frame to calculate the divergence in
|
| 71 |
+
|
| 72 |
+
Examples
|
| 73 |
+
========
|
| 74 |
+
|
| 75 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 76 |
+
>>> from sympy.physics.vector import divergence
|
| 77 |
+
>>> R = ReferenceFrame('R')
|
| 78 |
+
>>> v1 = R[0]*R[1]*R[2] * (R.x+R.y+R.z)
|
| 79 |
+
>>> divergence(v1, R)
|
| 80 |
+
R_x*R_y + R_x*R_z + R_y*R_z
|
| 81 |
+
>>> v2 = 2*R[1]*R[2]*R.y
|
| 82 |
+
>>> divergence(v2, R)
|
| 83 |
+
2*R_z
|
| 84 |
+
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
_check_vector(vect)
|
| 88 |
+
if vect == 0:
|
| 89 |
+
return S.Zero
|
| 90 |
+
vect = express(vect, frame, variables=True)
|
| 91 |
+
vectx = vect.dot(frame.x)
|
| 92 |
+
vecty = vect.dot(frame.y)
|
| 93 |
+
vectz = vect.dot(frame.z)
|
| 94 |
+
out = S.Zero
|
| 95 |
+
out += diff(vectx, frame[0])
|
| 96 |
+
out += diff(vecty, frame[1])
|
| 97 |
+
out += diff(vectz, frame[2])
|
| 98 |
+
return out
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def gradient(scalar, frame):
|
| 102 |
+
"""
|
| 103 |
+
Returns the vector gradient of a scalar field computed wrt the
|
| 104 |
+
coordinate symbols of the given frame.
|
| 105 |
+
|
| 106 |
+
Parameters
|
| 107 |
+
==========
|
| 108 |
+
|
| 109 |
+
scalar : sympifiable
|
| 110 |
+
The scalar field to take the gradient of
|
| 111 |
+
|
| 112 |
+
frame : ReferenceFrame
|
| 113 |
+
The frame to calculate the gradient in
|
| 114 |
+
|
| 115 |
+
Examples
|
| 116 |
+
========
|
| 117 |
+
|
| 118 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 119 |
+
>>> from sympy.physics.vector import gradient
|
| 120 |
+
>>> R = ReferenceFrame('R')
|
| 121 |
+
>>> s1 = R[0]*R[1]*R[2]
|
| 122 |
+
>>> gradient(s1, R)
|
| 123 |
+
R_y*R_z*R.x + R_x*R_z*R.y + R_x*R_y*R.z
|
| 124 |
+
>>> s2 = 5*R[0]**2*R[2]
|
| 125 |
+
>>> gradient(s2, R)
|
| 126 |
+
10*R_x*R_z*R.x + 5*R_x**2*R.z
|
| 127 |
+
|
| 128 |
+
"""
|
| 129 |
+
|
| 130 |
+
_check_frame(frame)
|
| 131 |
+
outvec = Vector(0)
|
| 132 |
+
scalar = express(scalar, frame, variables=True)
|
| 133 |
+
for i, x in enumerate(frame):
|
| 134 |
+
outvec += diff(scalar, frame[i]) * x # noqa: PLR1736
|
| 135 |
+
return outvec
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def is_conservative(field):
|
| 139 |
+
"""
|
| 140 |
+
Checks if a field is conservative.
|
| 141 |
+
|
| 142 |
+
Parameters
|
| 143 |
+
==========
|
| 144 |
+
|
| 145 |
+
field : Vector
|
| 146 |
+
The field to check for conservative property
|
| 147 |
+
|
| 148 |
+
Examples
|
| 149 |
+
========
|
| 150 |
+
|
| 151 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 152 |
+
>>> from sympy.physics.vector import is_conservative
|
| 153 |
+
>>> R = ReferenceFrame('R')
|
| 154 |
+
>>> is_conservative(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z)
|
| 155 |
+
True
|
| 156 |
+
>>> is_conservative(R[2] * R.y)
|
| 157 |
+
False
|
| 158 |
+
|
| 159 |
+
"""
|
| 160 |
+
|
| 161 |
+
# Field is conservative irrespective of frame
|
| 162 |
+
# Take the first frame in the result of the separate method of Vector
|
| 163 |
+
if field == Vector(0):
|
| 164 |
+
return True
|
| 165 |
+
frame = list(field.separate())[0]
|
| 166 |
+
return curl(field, frame).simplify() == Vector(0)
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
def is_solenoidal(field):
|
| 170 |
+
"""
|
| 171 |
+
Checks if a field is solenoidal.
|
| 172 |
+
|
| 173 |
+
Parameters
|
| 174 |
+
==========
|
| 175 |
+
|
| 176 |
+
field : Vector
|
| 177 |
+
The field to check for solenoidal property
|
| 178 |
+
|
| 179 |
+
Examples
|
| 180 |
+
========
|
| 181 |
+
|
| 182 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 183 |
+
>>> from sympy.physics.vector import is_solenoidal
|
| 184 |
+
>>> R = ReferenceFrame('R')
|
| 185 |
+
>>> is_solenoidal(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z)
|
| 186 |
+
True
|
| 187 |
+
>>> is_solenoidal(R[1] * R.y)
|
| 188 |
+
False
|
| 189 |
+
|
| 190 |
+
"""
|
| 191 |
+
|
| 192 |
+
# Field is solenoidal irrespective of frame
|
| 193 |
+
# Take the first frame in the result of the separate method in Vector
|
| 194 |
+
if field == Vector(0):
|
| 195 |
+
return True
|
| 196 |
+
frame = list(field.separate())[0]
|
| 197 |
+
return divergence(field, frame).simplify() is S.Zero
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def scalar_potential(field, frame):
|
| 201 |
+
"""
|
| 202 |
+
Returns the scalar potential function of a field in a given frame
|
| 203 |
+
(without the added integration constant).
|
| 204 |
+
|
| 205 |
+
Parameters
|
| 206 |
+
==========
|
| 207 |
+
|
| 208 |
+
field : Vector
|
| 209 |
+
The vector field whose scalar potential function is to be
|
| 210 |
+
calculated
|
| 211 |
+
|
| 212 |
+
frame : ReferenceFrame
|
| 213 |
+
The frame to do the calculation in
|
| 214 |
+
|
| 215 |
+
Examples
|
| 216 |
+
========
|
| 217 |
+
|
| 218 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 219 |
+
>>> from sympy.physics.vector import scalar_potential, gradient
|
| 220 |
+
>>> R = ReferenceFrame('R')
|
| 221 |
+
>>> scalar_potential(R.z, R) == R[2]
|
| 222 |
+
True
|
| 223 |
+
>>> scalar_field = 2*R[0]**2*R[1]*R[2]
|
| 224 |
+
>>> grad_field = gradient(scalar_field, R)
|
| 225 |
+
>>> scalar_potential(grad_field, R)
|
| 226 |
+
2*R_x**2*R_y*R_z
|
| 227 |
+
|
| 228 |
+
"""
|
| 229 |
+
|
| 230 |
+
# Check whether field is conservative
|
| 231 |
+
if not is_conservative(field):
|
| 232 |
+
raise ValueError("Field is not conservative")
|
| 233 |
+
if field == Vector(0):
|
| 234 |
+
return S.Zero
|
| 235 |
+
# Express the field exntirely in frame
|
| 236 |
+
# Substitute coordinate variables also
|
| 237 |
+
_check_frame(frame)
|
| 238 |
+
field = express(field, frame, variables=True)
|
| 239 |
+
# Make a list of dimensions of the frame
|
| 240 |
+
dimensions = list(frame)
|
| 241 |
+
# Calculate scalar potential function
|
| 242 |
+
temp_function = integrate(field.dot(dimensions[0]), frame[0])
|
| 243 |
+
for i, dim in enumerate(dimensions[1:]):
|
| 244 |
+
partial_diff = diff(temp_function, frame[i + 1])
|
| 245 |
+
partial_diff = field.dot(dim) - partial_diff
|
| 246 |
+
temp_function += integrate(partial_diff, frame[i + 1])
|
| 247 |
+
return temp_function
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def scalar_potential_difference(field, frame, point1, point2, origin):
|
| 251 |
+
"""
|
| 252 |
+
Returns the scalar potential difference between two points in a
|
| 253 |
+
certain frame, wrt a given field.
|
| 254 |
+
|
| 255 |
+
If a scalar field is provided, its values at the two points are
|
| 256 |
+
considered. If a conservative vector field is provided, the values
|
| 257 |
+
of its scalar potential function at the two points are used.
|
| 258 |
+
|
| 259 |
+
Returns (potential at position 2) - (potential at position 1)
|
| 260 |
+
|
| 261 |
+
Parameters
|
| 262 |
+
==========
|
| 263 |
+
|
| 264 |
+
field : Vector/sympyfiable
|
| 265 |
+
The field to calculate wrt
|
| 266 |
+
|
| 267 |
+
frame : ReferenceFrame
|
| 268 |
+
The frame to do the calculations in
|
| 269 |
+
|
| 270 |
+
point1 : Point
|
| 271 |
+
The initial Point in given frame
|
| 272 |
+
|
| 273 |
+
position2 : Point
|
| 274 |
+
The second Point in the given frame
|
| 275 |
+
|
| 276 |
+
origin : Point
|
| 277 |
+
The Point to use as reference point for position vector
|
| 278 |
+
calculation
|
| 279 |
+
|
| 280 |
+
Examples
|
| 281 |
+
========
|
| 282 |
+
|
| 283 |
+
>>> from sympy.physics.vector import ReferenceFrame, Point
|
| 284 |
+
>>> from sympy.physics.vector import scalar_potential_difference
|
| 285 |
+
>>> R = ReferenceFrame('R')
|
| 286 |
+
>>> O = Point('O')
|
| 287 |
+
>>> P = O.locatenew('P', R[0]*R.x + R[1]*R.y + R[2]*R.z)
|
| 288 |
+
>>> vectfield = 4*R[0]*R[1]*R.x + 2*R[0]**2*R.y
|
| 289 |
+
>>> scalar_potential_difference(vectfield, R, O, P, O)
|
| 290 |
+
2*R_x**2*R_y
|
| 291 |
+
>>> Q = O.locatenew('O', 3*R.x + R.y + 2*R.z)
|
| 292 |
+
>>> scalar_potential_difference(vectfield, R, P, Q, O)
|
| 293 |
+
-2*R_x**2*R_y + 18
|
| 294 |
+
|
| 295 |
+
"""
|
| 296 |
+
|
| 297 |
+
_check_frame(frame)
|
| 298 |
+
if isinstance(field, Vector):
|
| 299 |
+
# Get the scalar potential function
|
| 300 |
+
scalar_fn = scalar_potential(field, frame)
|
| 301 |
+
else:
|
| 302 |
+
# Field is a scalar
|
| 303 |
+
scalar_fn = field
|
| 304 |
+
# Express positions in required frame
|
| 305 |
+
position1 = express(point1.pos_from(origin), frame, variables=True)
|
| 306 |
+
position2 = express(point2.pos_from(origin), frame, variables=True)
|
| 307 |
+
# Get the two positions as substitution dicts for coordinate variables
|
| 308 |
+
subs_dict1 = {}
|
| 309 |
+
subs_dict2 = {}
|
| 310 |
+
for i, x in enumerate(frame):
|
| 311 |
+
subs_dict1[frame[i]] = x.dot(position1)
|
| 312 |
+
subs_dict2[frame[i]] = x.dot(position2)
|
| 313 |
+
return scalar_fn.subs(subs_dict2) - scalar_fn.subs(subs_dict1)
|
venv/lib/python3.11/site-packages/sympy/physics/vector/frame.py
ADDED
|
@@ -0,0 +1,1575 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy import (diff, expand, sin, cos, sympify, eye, zeros,
|
| 2 |
+
ImmutableMatrix as Matrix, MatrixBase)
|
| 3 |
+
from sympy.core.symbol import Symbol
|
| 4 |
+
from sympy.simplify.trigsimp import trigsimp
|
| 5 |
+
from sympy.physics.vector.vector import Vector, _check_vector
|
| 6 |
+
from sympy.utilities.misc import translate
|
| 7 |
+
|
| 8 |
+
from warnings import warn
|
| 9 |
+
|
| 10 |
+
__all__ = ['CoordinateSym', 'ReferenceFrame']
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class CoordinateSym(Symbol):
|
| 14 |
+
"""
|
| 15 |
+
A coordinate symbol/base scalar associated wrt a Reference Frame.
|
| 16 |
+
|
| 17 |
+
Ideally, users should not instantiate this class. Instances of
|
| 18 |
+
this class must only be accessed through the corresponding frame
|
| 19 |
+
as 'frame[index]'.
|
| 20 |
+
|
| 21 |
+
CoordinateSyms having the same frame and index parameters are equal
|
| 22 |
+
(even though they may be instantiated separately).
|
| 23 |
+
|
| 24 |
+
Parameters
|
| 25 |
+
==========
|
| 26 |
+
|
| 27 |
+
name : string
|
| 28 |
+
The display name of the CoordinateSym
|
| 29 |
+
|
| 30 |
+
frame : ReferenceFrame
|
| 31 |
+
The reference frame this base scalar belongs to
|
| 32 |
+
|
| 33 |
+
index : 0, 1 or 2
|
| 34 |
+
The index of the dimension denoted by this coordinate variable
|
| 35 |
+
|
| 36 |
+
Examples
|
| 37 |
+
========
|
| 38 |
+
|
| 39 |
+
>>> from sympy.physics.vector import ReferenceFrame, CoordinateSym
|
| 40 |
+
>>> A = ReferenceFrame('A')
|
| 41 |
+
>>> A[1]
|
| 42 |
+
A_y
|
| 43 |
+
>>> type(A[0])
|
| 44 |
+
<class 'sympy.physics.vector.frame.CoordinateSym'>
|
| 45 |
+
>>> a_y = CoordinateSym('a_y', A, 1)
|
| 46 |
+
>>> a_y == A[1]
|
| 47 |
+
True
|
| 48 |
+
|
| 49 |
+
"""
|
| 50 |
+
|
| 51 |
+
def __new__(cls, name, frame, index):
|
| 52 |
+
# We can't use the cached Symbol.__new__ because this class depends on
|
| 53 |
+
# frame and index, which are not passed to Symbol.__xnew__.
|
| 54 |
+
assumptions = {}
|
| 55 |
+
super()._sanitize(assumptions, cls)
|
| 56 |
+
obj = super().__xnew__(cls, name, **assumptions)
|
| 57 |
+
_check_frame(frame)
|
| 58 |
+
if index not in range(0, 3):
|
| 59 |
+
raise ValueError("Invalid index specified")
|
| 60 |
+
obj._id = (frame, index)
|
| 61 |
+
return obj
|
| 62 |
+
|
| 63 |
+
def __getnewargs_ex__(self):
|
| 64 |
+
return (self.name, *self._id), {}
|
| 65 |
+
|
| 66 |
+
@property
|
| 67 |
+
def frame(self):
|
| 68 |
+
return self._id[0]
|
| 69 |
+
|
| 70 |
+
def __eq__(self, other):
|
| 71 |
+
# Check if the other object is a CoordinateSym of the same frame and
|
| 72 |
+
# same index
|
| 73 |
+
if isinstance(other, CoordinateSym):
|
| 74 |
+
if other._id == self._id:
|
| 75 |
+
return True
|
| 76 |
+
return False
|
| 77 |
+
|
| 78 |
+
def __ne__(self, other):
|
| 79 |
+
return not self == other
|
| 80 |
+
|
| 81 |
+
def __hash__(self):
|
| 82 |
+
return (self._id[0].__hash__(), self._id[1]).__hash__()
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class ReferenceFrame:
|
| 86 |
+
"""A reference frame in classical mechanics.
|
| 87 |
+
|
| 88 |
+
ReferenceFrame is a class used to represent a reference frame in classical
|
| 89 |
+
mechanics. It has a standard basis of three unit vectors in the frame's
|
| 90 |
+
x, y, and z directions.
|
| 91 |
+
|
| 92 |
+
It also can have a rotation relative to a parent frame; this rotation is
|
| 93 |
+
defined by a direction cosine matrix relating this frame's basis vectors to
|
| 94 |
+
the parent frame's basis vectors. It can also have an angular velocity
|
| 95 |
+
vector, defined in another frame.
|
| 96 |
+
|
| 97 |
+
"""
|
| 98 |
+
_count = 0
|
| 99 |
+
|
| 100 |
+
def __init__(self, name, indices=None, latexs=None, variables=None):
|
| 101 |
+
"""ReferenceFrame initialization method.
|
| 102 |
+
|
| 103 |
+
A ReferenceFrame has a set of orthonormal basis vectors, along with
|
| 104 |
+
orientations relative to other ReferenceFrames and angular velocities
|
| 105 |
+
relative to other ReferenceFrames.
|
| 106 |
+
|
| 107 |
+
Parameters
|
| 108 |
+
==========
|
| 109 |
+
|
| 110 |
+
indices : tuple of str
|
| 111 |
+
Enables the reference frame's basis unit vectors to be accessed by
|
| 112 |
+
Python's square bracket indexing notation using the provided three
|
| 113 |
+
indice strings and alters the printing of the unit vectors to
|
| 114 |
+
reflect this choice.
|
| 115 |
+
latexs : tuple of str
|
| 116 |
+
Alters the LaTeX printing of the reference frame's basis unit
|
| 117 |
+
vectors to the provided three valid LaTeX strings.
|
| 118 |
+
|
| 119 |
+
Examples
|
| 120 |
+
========
|
| 121 |
+
|
| 122 |
+
>>> from sympy.physics.vector import ReferenceFrame, vlatex
|
| 123 |
+
>>> N = ReferenceFrame('N')
|
| 124 |
+
>>> N.x
|
| 125 |
+
N.x
|
| 126 |
+
>>> O = ReferenceFrame('O', indices=('1', '2', '3'))
|
| 127 |
+
>>> O.x
|
| 128 |
+
O['1']
|
| 129 |
+
>>> O['1']
|
| 130 |
+
O['1']
|
| 131 |
+
>>> P = ReferenceFrame('P', latexs=('A1', 'A2', 'A3'))
|
| 132 |
+
>>> vlatex(P.x)
|
| 133 |
+
'A1'
|
| 134 |
+
|
| 135 |
+
``symbols()`` can be used to create multiple Reference Frames in one
|
| 136 |
+
step, for example:
|
| 137 |
+
|
| 138 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 139 |
+
>>> from sympy import symbols
|
| 140 |
+
>>> A, B, C = symbols('A B C', cls=ReferenceFrame)
|
| 141 |
+
>>> D, E = symbols('D E', cls=ReferenceFrame, indices=('1', '2', '3'))
|
| 142 |
+
>>> A[0]
|
| 143 |
+
A_x
|
| 144 |
+
>>> D.x
|
| 145 |
+
D['1']
|
| 146 |
+
>>> E.y
|
| 147 |
+
E['2']
|
| 148 |
+
>>> type(A) == type(D)
|
| 149 |
+
True
|
| 150 |
+
|
| 151 |
+
Unit dyads for the ReferenceFrame can be accessed through the attributes ``xx``, ``xy``, etc. For example:
|
| 152 |
+
|
| 153 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 154 |
+
>>> N = ReferenceFrame('N')
|
| 155 |
+
>>> N.yz
|
| 156 |
+
(N.y|N.z)
|
| 157 |
+
>>> N.zx
|
| 158 |
+
(N.z|N.x)
|
| 159 |
+
>>> P = ReferenceFrame('P', indices=['1', '2', '3'])
|
| 160 |
+
>>> P.xx
|
| 161 |
+
(P['1']|P['1'])
|
| 162 |
+
>>> P.zy
|
| 163 |
+
(P['3']|P['2'])
|
| 164 |
+
|
| 165 |
+
Unit dyadic is also accessible via the ``u`` attribute:
|
| 166 |
+
|
| 167 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 168 |
+
>>> N = ReferenceFrame('N')
|
| 169 |
+
>>> N.u
|
| 170 |
+
(N.x|N.x) + (N.y|N.y) + (N.z|N.z)
|
| 171 |
+
>>> P = ReferenceFrame('P', indices=['1', '2', '3'])
|
| 172 |
+
>>> P.u
|
| 173 |
+
(P['1']|P['1']) + (P['2']|P['2']) + (P['3']|P['3'])
|
| 174 |
+
|
| 175 |
+
"""
|
| 176 |
+
|
| 177 |
+
if not isinstance(name, str):
|
| 178 |
+
raise TypeError('Need to supply a valid name')
|
| 179 |
+
# The if statements below are for custom printing of basis-vectors for
|
| 180 |
+
# each frame.
|
| 181 |
+
# First case, when custom indices are supplied
|
| 182 |
+
if indices is not None:
|
| 183 |
+
if not isinstance(indices, (tuple, list)):
|
| 184 |
+
raise TypeError('Supply the indices as a list')
|
| 185 |
+
if len(indices) != 3:
|
| 186 |
+
raise ValueError('Supply 3 indices')
|
| 187 |
+
for i in indices:
|
| 188 |
+
if not isinstance(i, str):
|
| 189 |
+
raise TypeError('Indices must be strings')
|
| 190 |
+
self.str_vecs = [(name + '[\'' + indices[0] + '\']'),
|
| 191 |
+
(name + '[\'' + indices[1] + '\']'),
|
| 192 |
+
(name + '[\'' + indices[2] + '\']')]
|
| 193 |
+
self.pretty_vecs = [(name.lower() + "_" + indices[0]),
|
| 194 |
+
(name.lower() + "_" + indices[1]),
|
| 195 |
+
(name.lower() + "_" + indices[2])]
|
| 196 |
+
self.latex_vecs = [(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
|
| 197 |
+
indices[0])),
|
| 198 |
+
(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
|
| 199 |
+
indices[1])),
|
| 200 |
+
(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
|
| 201 |
+
indices[2]))]
|
| 202 |
+
self.indices = indices
|
| 203 |
+
# Second case, when no custom indices are supplied
|
| 204 |
+
else:
|
| 205 |
+
self.str_vecs = [(name + '.x'), (name + '.y'), (name + '.z')]
|
| 206 |
+
self.pretty_vecs = [name.lower() + "_x",
|
| 207 |
+
name.lower() + "_y",
|
| 208 |
+
name.lower() + "_z"]
|
| 209 |
+
self.latex_vecs = [(r"\mathbf{\hat{%s}_x}" % name.lower()),
|
| 210 |
+
(r"\mathbf{\hat{%s}_y}" % name.lower()),
|
| 211 |
+
(r"\mathbf{\hat{%s}_z}" % name.lower())]
|
| 212 |
+
self.indices = ['x', 'y', 'z']
|
| 213 |
+
# Different step, for custom latex basis vectors
|
| 214 |
+
if latexs is not None:
|
| 215 |
+
if not isinstance(latexs, (tuple, list)):
|
| 216 |
+
raise TypeError('Supply the indices as a list')
|
| 217 |
+
if len(latexs) != 3:
|
| 218 |
+
raise ValueError('Supply 3 indices')
|
| 219 |
+
for i in latexs:
|
| 220 |
+
if not isinstance(i, str):
|
| 221 |
+
raise TypeError('Latex entries must be strings')
|
| 222 |
+
self.latex_vecs = latexs
|
| 223 |
+
self.name = name
|
| 224 |
+
self._var_dict = {}
|
| 225 |
+
# The _dcm_dict dictionary will only store the dcms of adjacent
|
| 226 |
+
# parent-child relationships. The _dcm_cache dictionary will store
|
| 227 |
+
# calculated dcm along with all content of _dcm_dict for faster
|
| 228 |
+
# retrieval of dcms.
|
| 229 |
+
self._dcm_dict = {}
|
| 230 |
+
self._dcm_cache = {}
|
| 231 |
+
self._ang_vel_dict = {}
|
| 232 |
+
self._ang_acc_dict = {}
|
| 233 |
+
self._dlist = [self._dcm_dict, self._ang_vel_dict, self._ang_acc_dict]
|
| 234 |
+
self._cur = 0
|
| 235 |
+
self._x = Vector([(Matrix([1, 0, 0]), self)])
|
| 236 |
+
self._y = Vector([(Matrix([0, 1, 0]), self)])
|
| 237 |
+
self._z = Vector([(Matrix([0, 0, 1]), self)])
|
| 238 |
+
# Associate coordinate symbols wrt this frame
|
| 239 |
+
if variables is not None:
|
| 240 |
+
if not isinstance(variables, (tuple, list)):
|
| 241 |
+
raise TypeError('Supply the variable names as a list/tuple')
|
| 242 |
+
if len(variables) != 3:
|
| 243 |
+
raise ValueError('Supply 3 variable names')
|
| 244 |
+
for i in variables:
|
| 245 |
+
if not isinstance(i, str):
|
| 246 |
+
raise TypeError('Variable names must be strings')
|
| 247 |
+
else:
|
| 248 |
+
variables = [name + '_x', name + '_y', name + '_z']
|
| 249 |
+
self.varlist = (CoordinateSym(variables[0], self, 0),
|
| 250 |
+
CoordinateSym(variables[1], self, 1),
|
| 251 |
+
CoordinateSym(variables[2], self, 2))
|
| 252 |
+
ReferenceFrame._count += 1
|
| 253 |
+
self.index = ReferenceFrame._count
|
| 254 |
+
|
| 255 |
+
def __getitem__(self, ind):
|
| 256 |
+
"""
|
| 257 |
+
Returns basis vector for the provided index, if the index is a string.
|
| 258 |
+
|
| 259 |
+
If the index is a number, returns the coordinate variable correspon-
|
| 260 |
+
-ding to that index.
|
| 261 |
+
"""
|
| 262 |
+
if not isinstance(ind, str):
|
| 263 |
+
if ind < 3:
|
| 264 |
+
return self.varlist[ind]
|
| 265 |
+
else:
|
| 266 |
+
raise ValueError("Invalid index provided")
|
| 267 |
+
if self.indices[0] == ind:
|
| 268 |
+
return self.x
|
| 269 |
+
if self.indices[1] == ind:
|
| 270 |
+
return self.y
|
| 271 |
+
if self.indices[2] == ind:
|
| 272 |
+
return self.z
|
| 273 |
+
else:
|
| 274 |
+
raise ValueError('Not a defined index')
|
| 275 |
+
|
| 276 |
+
def __iter__(self):
|
| 277 |
+
return iter([self.x, self.y, self.z])
|
| 278 |
+
|
| 279 |
+
def __str__(self):
|
| 280 |
+
"""Returns the name of the frame. """
|
| 281 |
+
return self.name
|
| 282 |
+
|
| 283 |
+
__repr__ = __str__
|
| 284 |
+
|
| 285 |
+
def _dict_list(self, other, num):
|
| 286 |
+
"""Returns an inclusive list of reference frames that connect this
|
| 287 |
+
reference frame to the provided reference frame.
|
| 288 |
+
|
| 289 |
+
Parameters
|
| 290 |
+
==========
|
| 291 |
+
other : ReferenceFrame
|
| 292 |
+
The other reference frame to look for a connecting relationship to.
|
| 293 |
+
num : integer
|
| 294 |
+
``0``, ``1``, and ``2`` will look for orientation, angular
|
| 295 |
+
velocity, and angular acceleration relationships between the two
|
| 296 |
+
frames, respectively.
|
| 297 |
+
|
| 298 |
+
Returns
|
| 299 |
+
=======
|
| 300 |
+
list
|
| 301 |
+
Inclusive list of reference frames that connect this reference
|
| 302 |
+
frame to the other reference frame.
|
| 303 |
+
|
| 304 |
+
Examples
|
| 305 |
+
========
|
| 306 |
+
|
| 307 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 308 |
+
>>> A = ReferenceFrame('A')
|
| 309 |
+
>>> B = ReferenceFrame('B')
|
| 310 |
+
>>> C = ReferenceFrame('C')
|
| 311 |
+
>>> D = ReferenceFrame('D')
|
| 312 |
+
>>> B.orient_axis(A, A.x, 1.0)
|
| 313 |
+
>>> C.orient_axis(B, B.x, 1.0)
|
| 314 |
+
>>> D.orient_axis(C, C.x, 1.0)
|
| 315 |
+
>>> D._dict_list(A, 0)
|
| 316 |
+
[D, C, B, A]
|
| 317 |
+
|
| 318 |
+
Raises
|
| 319 |
+
======
|
| 320 |
+
|
| 321 |
+
ValueError
|
| 322 |
+
When no path is found between the two reference frames or ``num``
|
| 323 |
+
is an incorrect value.
|
| 324 |
+
|
| 325 |
+
"""
|
| 326 |
+
|
| 327 |
+
connect_type = {0: 'orientation',
|
| 328 |
+
1: 'angular velocity',
|
| 329 |
+
2: 'angular acceleration'}
|
| 330 |
+
|
| 331 |
+
if num not in connect_type.keys():
|
| 332 |
+
raise ValueError('Valid values for num are 0, 1, or 2.')
|
| 333 |
+
|
| 334 |
+
possible_connecting_paths = [[self]]
|
| 335 |
+
oldlist = [[]]
|
| 336 |
+
while possible_connecting_paths != oldlist:
|
| 337 |
+
oldlist = possible_connecting_paths.copy()
|
| 338 |
+
for frame_list in possible_connecting_paths:
|
| 339 |
+
frames_adjacent_to_last = frame_list[-1]._dlist[num].keys()
|
| 340 |
+
for adjacent_frame in frames_adjacent_to_last:
|
| 341 |
+
if adjacent_frame not in frame_list:
|
| 342 |
+
connecting_path = frame_list + [adjacent_frame]
|
| 343 |
+
if connecting_path not in possible_connecting_paths:
|
| 344 |
+
possible_connecting_paths.append(connecting_path)
|
| 345 |
+
|
| 346 |
+
for connecting_path in oldlist:
|
| 347 |
+
if connecting_path[-1] != other:
|
| 348 |
+
possible_connecting_paths.remove(connecting_path)
|
| 349 |
+
possible_connecting_paths.sort(key=len)
|
| 350 |
+
|
| 351 |
+
if len(possible_connecting_paths) != 0:
|
| 352 |
+
return possible_connecting_paths[0] # selects the shortest path
|
| 353 |
+
|
| 354 |
+
msg = 'No connecting {} path found between {} and {}.'
|
| 355 |
+
raise ValueError(msg.format(connect_type[num], self.name, other.name))
|
| 356 |
+
|
| 357 |
+
def _w_diff_dcm(self, otherframe):
|
| 358 |
+
"""Angular velocity from time differentiating the DCM. """
|
| 359 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 360 |
+
dcm2diff = otherframe.dcm(self)
|
| 361 |
+
diffed = dcm2diff.diff(dynamicsymbols._t)
|
| 362 |
+
angvelmat = diffed * dcm2diff.T
|
| 363 |
+
w1 = trigsimp(expand(angvelmat[7]), recursive=True)
|
| 364 |
+
w2 = trigsimp(expand(angvelmat[2]), recursive=True)
|
| 365 |
+
w3 = trigsimp(expand(angvelmat[3]), recursive=True)
|
| 366 |
+
return Vector([(Matrix([w1, w2, w3]), otherframe)])
|
| 367 |
+
|
| 368 |
+
def variable_map(self, otherframe):
|
| 369 |
+
"""
|
| 370 |
+
Returns a dictionary which expresses the coordinate variables
|
| 371 |
+
of this frame in terms of the variables of otherframe.
|
| 372 |
+
|
| 373 |
+
If Vector.simp is True, returns a simplified version of the mapped
|
| 374 |
+
values. Else, returns them without simplification.
|
| 375 |
+
|
| 376 |
+
Simplification of the expressions may take time.
|
| 377 |
+
|
| 378 |
+
Parameters
|
| 379 |
+
==========
|
| 380 |
+
|
| 381 |
+
otherframe : ReferenceFrame
|
| 382 |
+
The other frame to map the variables to
|
| 383 |
+
|
| 384 |
+
Examples
|
| 385 |
+
========
|
| 386 |
+
|
| 387 |
+
>>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
|
| 388 |
+
>>> A = ReferenceFrame('A')
|
| 389 |
+
>>> q = dynamicsymbols('q')
|
| 390 |
+
>>> B = A.orientnew('B', 'Axis', [q, A.z])
|
| 391 |
+
>>> A.variable_map(B)
|
| 392 |
+
{A_x: B_x*cos(q(t)) - B_y*sin(q(t)), A_y: B_x*sin(q(t)) + B_y*cos(q(t)), A_z: B_z}
|
| 393 |
+
|
| 394 |
+
"""
|
| 395 |
+
|
| 396 |
+
_check_frame(otherframe)
|
| 397 |
+
if (otherframe, Vector.simp) in self._var_dict:
|
| 398 |
+
return self._var_dict[(otherframe, Vector.simp)]
|
| 399 |
+
else:
|
| 400 |
+
vars_matrix = self.dcm(otherframe) * Matrix(otherframe.varlist)
|
| 401 |
+
mapping = {}
|
| 402 |
+
for i, x in enumerate(self):
|
| 403 |
+
if Vector.simp:
|
| 404 |
+
mapping[self.varlist[i]] = trigsimp(vars_matrix[i],
|
| 405 |
+
method='fu')
|
| 406 |
+
else:
|
| 407 |
+
mapping[self.varlist[i]] = vars_matrix[i]
|
| 408 |
+
self._var_dict[(otherframe, Vector.simp)] = mapping
|
| 409 |
+
return mapping
|
| 410 |
+
|
| 411 |
+
def ang_acc_in(self, otherframe):
|
| 412 |
+
"""Returns the angular acceleration Vector of the ReferenceFrame.
|
| 413 |
+
|
| 414 |
+
Effectively returns the Vector:
|
| 415 |
+
|
| 416 |
+
``N_alpha_B``
|
| 417 |
+
|
| 418 |
+
which represent the angular acceleration of B in N, where B is self,
|
| 419 |
+
and N is otherframe.
|
| 420 |
+
|
| 421 |
+
Parameters
|
| 422 |
+
==========
|
| 423 |
+
|
| 424 |
+
otherframe : ReferenceFrame
|
| 425 |
+
The ReferenceFrame which the angular acceleration is returned in.
|
| 426 |
+
|
| 427 |
+
Examples
|
| 428 |
+
========
|
| 429 |
+
|
| 430 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 431 |
+
>>> N = ReferenceFrame('N')
|
| 432 |
+
>>> A = ReferenceFrame('A')
|
| 433 |
+
>>> V = 10 * N.x
|
| 434 |
+
>>> A.set_ang_acc(N, V)
|
| 435 |
+
>>> A.ang_acc_in(N)
|
| 436 |
+
10*N.x
|
| 437 |
+
|
| 438 |
+
"""
|
| 439 |
+
|
| 440 |
+
_check_frame(otherframe)
|
| 441 |
+
if otherframe in self._ang_acc_dict:
|
| 442 |
+
return self._ang_acc_dict[otherframe]
|
| 443 |
+
else:
|
| 444 |
+
return self.ang_vel_in(otherframe).dt(otherframe)
|
| 445 |
+
|
| 446 |
+
def ang_vel_in(self, otherframe):
|
| 447 |
+
"""Returns the angular velocity Vector of the ReferenceFrame.
|
| 448 |
+
|
| 449 |
+
Effectively returns the Vector:
|
| 450 |
+
|
| 451 |
+
^N omega ^B
|
| 452 |
+
|
| 453 |
+
which represent the angular velocity of B in N, where B is self, and
|
| 454 |
+
N is otherframe.
|
| 455 |
+
|
| 456 |
+
Parameters
|
| 457 |
+
==========
|
| 458 |
+
|
| 459 |
+
otherframe : ReferenceFrame
|
| 460 |
+
The ReferenceFrame which the angular velocity is returned in.
|
| 461 |
+
|
| 462 |
+
Examples
|
| 463 |
+
========
|
| 464 |
+
|
| 465 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 466 |
+
>>> N = ReferenceFrame('N')
|
| 467 |
+
>>> A = ReferenceFrame('A')
|
| 468 |
+
>>> V = 10 * N.x
|
| 469 |
+
>>> A.set_ang_vel(N, V)
|
| 470 |
+
>>> A.ang_vel_in(N)
|
| 471 |
+
10*N.x
|
| 472 |
+
|
| 473 |
+
"""
|
| 474 |
+
|
| 475 |
+
_check_frame(otherframe)
|
| 476 |
+
flist = self._dict_list(otherframe, 1)
|
| 477 |
+
outvec = Vector(0)
|
| 478 |
+
for i in range(len(flist) - 1):
|
| 479 |
+
outvec += flist[i]._ang_vel_dict[flist[i + 1]]
|
| 480 |
+
return outvec
|
| 481 |
+
|
| 482 |
+
def dcm(self, otherframe):
|
| 483 |
+
r"""Returns the direction cosine matrix of this reference frame
|
| 484 |
+
relative to the provided reference frame.
|
| 485 |
+
|
| 486 |
+
The returned matrix can be used to express the orthogonal unit vectors
|
| 487 |
+
of this frame in terms of the orthogonal unit vectors of
|
| 488 |
+
``otherframe``.
|
| 489 |
+
|
| 490 |
+
Parameters
|
| 491 |
+
==========
|
| 492 |
+
|
| 493 |
+
otherframe : ReferenceFrame
|
| 494 |
+
The reference frame which the direction cosine matrix of this frame
|
| 495 |
+
is formed relative to.
|
| 496 |
+
|
| 497 |
+
Examples
|
| 498 |
+
========
|
| 499 |
+
|
| 500 |
+
The following example rotates the reference frame A relative to N by a
|
| 501 |
+
simple rotation and then calculates the direction cosine matrix of N
|
| 502 |
+
relative to A.
|
| 503 |
+
|
| 504 |
+
>>> from sympy import symbols, sin, cos
|
| 505 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 506 |
+
>>> q1 = symbols('q1')
|
| 507 |
+
>>> N = ReferenceFrame('N')
|
| 508 |
+
>>> A = ReferenceFrame('A')
|
| 509 |
+
>>> A.orient_axis(N, q1, N.x)
|
| 510 |
+
>>> N.dcm(A)
|
| 511 |
+
Matrix([
|
| 512 |
+
[1, 0, 0],
|
| 513 |
+
[0, cos(q1), -sin(q1)],
|
| 514 |
+
[0, sin(q1), cos(q1)]])
|
| 515 |
+
|
| 516 |
+
The second row of the above direction cosine matrix represents the
|
| 517 |
+
``N.y`` unit vector in N expressed in A. Like so:
|
| 518 |
+
|
| 519 |
+
>>> Ny = 0*A.x + cos(q1)*A.y - sin(q1)*A.z
|
| 520 |
+
|
| 521 |
+
Thus, expressing ``N.y`` in A should return the same result:
|
| 522 |
+
|
| 523 |
+
>>> N.y.express(A)
|
| 524 |
+
cos(q1)*A.y - sin(q1)*A.z
|
| 525 |
+
|
| 526 |
+
Notes
|
| 527 |
+
=====
|
| 528 |
+
|
| 529 |
+
It is important to know what form of the direction cosine matrix is
|
| 530 |
+
returned. If ``B.dcm(A)`` is called, it means the "direction cosine
|
| 531 |
+
matrix of B rotated relative to A". This is the matrix
|
| 532 |
+
:math:`{}^B\mathbf{C}^A` shown in the following relationship:
|
| 533 |
+
|
| 534 |
+
.. math::
|
| 535 |
+
|
| 536 |
+
\begin{bmatrix}
|
| 537 |
+
\hat{\mathbf{b}}_1 \\
|
| 538 |
+
\hat{\mathbf{b}}_2 \\
|
| 539 |
+
\hat{\mathbf{b}}_3
|
| 540 |
+
\end{bmatrix}
|
| 541 |
+
=
|
| 542 |
+
{}^B\mathbf{C}^A
|
| 543 |
+
\begin{bmatrix}
|
| 544 |
+
\hat{\mathbf{a}}_1 \\
|
| 545 |
+
\hat{\mathbf{a}}_2 \\
|
| 546 |
+
\hat{\mathbf{a}}_3
|
| 547 |
+
\end{bmatrix}.
|
| 548 |
+
|
| 549 |
+
:math:`{}^B\mathbf{C}^A` is the matrix that expresses the B unit
|
| 550 |
+
vectors in terms of the A unit vectors.
|
| 551 |
+
|
| 552 |
+
"""
|
| 553 |
+
|
| 554 |
+
_check_frame(otherframe)
|
| 555 |
+
# Check if the dcm wrt that frame has already been calculated
|
| 556 |
+
if otherframe in self._dcm_cache:
|
| 557 |
+
return self._dcm_cache[otherframe]
|
| 558 |
+
flist = self._dict_list(otherframe, 0)
|
| 559 |
+
outdcm = eye(3)
|
| 560 |
+
for i in range(len(flist) - 1):
|
| 561 |
+
outdcm = outdcm * flist[i]._dcm_dict[flist[i + 1]]
|
| 562 |
+
# After calculation, store the dcm in dcm cache for faster future
|
| 563 |
+
# retrieval
|
| 564 |
+
self._dcm_cache[otherframe] = outdcm
|
| 565 |
+
otherframe._dcm_cache[self] = outdcm.T
|
| 566 |
+
return outdcm
|
| 567 |
+
|
| 568 |
+
def _dcm(self, parent, parent_orient):
|
| 569 |
+
# If parent.oreint(self) is already defined,then
|
| 570 |
+
# update the _dcm_dict of parent while over write
|
| 571 |
+
# all content of self._dcm_dict and self._dcm_cache
|
| 572 |
+
# with new dcm relation.
|
| 573 |
+
# Else update _dcm_cache and _dcm_dict of both
|
| 574 |
+
# self and parent.
|
| 575 |
+
frames = self._dcm_cache.keys()
|
| 576 |
+
dcm_dict_del = []
|
| 577 |
+
dcm_cache_del = []
|
| 578 |
+
if parent in frames:
|
| 579 |
+
for frame in frames:
|
| 580 |
+
if frame in self._dcm_dict:
|
| 581 |
+
dcm_dict_del += [frame]
|
| 582 |
+
dcm_cache_del += [frame]
|
| 583 |
+
# Reset the _dcm_cache of this frame, and remove it from the
|
| 584 |
+
# _dcm_caches of the frames it is linked to. Also remove it from
|
| 585 |
+
# the _dcm_dict of its parent
|
| 586 |
+
for frame in dcm_dict_del:
|
| 587 |
+
del frame._dcm_dict[self]
|
| 588 |
+
for frame in dcm_cache_del:
|
| 589 |
+
del frame._dcm_cache[self]
|
| 590 |
+
# Reset the _dcm_dict
|
| 591 |
+
self._dcm_dict = self._dlist[0] = {}
|
| 592 |
+
# Reset the _dcm_cache
|
| 593 |
+
self._dcm_cache = {}
|
| 594 |
+
|
| 595 |
+
else:
|
| 596 |
+
# Check for loops and raise warning accordingly.
|
| 597 |
+
visited = []
|
| 598 |
+
queue = list(frames)
|
| 599 |
+
cont = True # Flag to control queue loop.
|
| 600 |
+
while queue and cont:
|
| 601 |
+
node = queue.pop(0)
|
| 602 |
+
if node not in visited:
|
| 603 |
+
visited.append(node)
|
| 604 |
+
neighbors = node._dcm_dict.keys()
|
| 605 |
+
for neighbor in neighbors:
|
| 606 |
+
if neighbor == parent:
|
| 607 |
+
warn('Loops are defined among the orientation of '
|
| 608 |
+
'frames. This is likely not desired and may '
|
| 609 |
+
'cause errors in your calculations.')
|
| 610 |
+
cont = False
|
| 611 |
+
break
|
| 612 |
+
queue.append(neighbor)
|
| 613 |
+
|
| 614 |
+
# Add the dcm relationship to _dcm_dict
|
| 615 |
+
self._dcm_dict.update({parent: parent_orient.T})
|
| 616 |
+
parent._dcm_dict.update({self: parent_orient})
|
| 617 |
+
# Update the dcm cache
|
| 618 |
+
self._dcm_cache.update({parent: parent_orient.T})
|
| 619 |
+
parent._dcm_cache.update({self: parent_orient})
|
| 620 |
+
|
| 621 |
+
def orient_axis(self, parent, axis, angle):
|
| 622 |
+
"""Sets the orientation of this reference frame with respect to a
|
| 623 |
+
parent reference frame by rotating through an angle about an axis fixed
|
| 624 |
+
in the parent reference frame.
|
| 625 |
+
|
| 626 |
+
Parameters
|
| 627 |
+
==========
|
| 628 |
+
|
| 629 |
+
parent : ReferenceFrame
|
| 630 |
+
Reference frame that this reference frame will be rotated relative
|
| 631 |
+
to.
|
| 632 |
+
axis : Vector
|
| 633 |
+
Vector fixed in the parent frame about about which this frame is
|
| 634 |
+
rotated. It need not be a unit vector and the rotation follows the
|
| 635 |
+
right hand rule.
|
| 636 |
+
angle : sympifiable
|
| 637 |
+
Angle in radians by which it the frame is to be rotated.
|
| 638 |
+
|
| 639 |
+
Warns
|
| 640 |
+
======
|
| 641 |
+
|
| 642 |
+
UserWarning
|
| 643 |
+
If the orientation creates a kinematic loop.
|
| 644 |
+
|
| 645 |
+
Examples
|
| 646 |
+
========
|
| 647 |
+
|
| 648 |
+
Setup variables for the examples:
|
| 649 |
+
|
| 650 |
+
>>> from sympy import symbols
|
| 651 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 652 |
+
>>> q1 = symbols('q1')
|
| 653 |
+
>>> N = ReferenceFrame('N')
|
| 654 |
+
>>> B = ReferenceFrame('B')
|
| 655 |
+
>>> B.orient_axis(N, N.x, q1)
|
| 656 |
+
|
| 657 |
+
The ``orient_axis()`` method generates a direction cosine matrix and
|
| 658 |
+
its transpose which defines the orientation of B relative to N and vice
|
| 659 |
+
versa. Once orient is called, ``dcm()`` outputs the appropriate
|
| 660 |
+
direction cosine matrix:
|
| 661 |
+
|
| 662 |
+
>>> B.dcm(N)
|
| 663 |
+
Matrix([
|
| 664 |
+
[1, 0, 0],
|
| 665 |
+
[0, cos(q1), sin(q1)],
|
| 666 |
+
[0, -sin(q1), cos(q1)]])
|
| 667 |
+
>>> N.dcm(B)
|
| 668 |
+
Matrix([
|
| 669 |
+
[1, 0, 0],
|
| 670 |
+
[0, cos(q1), -sin(q1)],
|
| 671 |
+
[0, sin(q1), cos(q1)]])
|
| 672 |
+
|
| 673 |
+
The following two lines show that the sense of the rotation can be
|
| 674 |
+
defined by negating the vector direction or the angle. Both lines
|
| 675 |
+
produce the same result.
|
| 676 |
+
|
| 677 |
+
>>> B.orient_axis(N, -N.x, q1)
|
| 678 |
+
>>> B.orient_axis(N, N.x, -q1)
|
| 679 |
+
|
| 680 |
+
"""
|
| 681 |
+
|
| 682 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 683 |
+
_check_frame(parent)
|
| 684 |
+
|
| 685 |
+
if not isinstance(axis, Vector) and isinstance(angle, Vector):
|
| 686 |
+
axis, angle = angle, axis
|
| 687 |
+
|
| 688 |
+
axis = _check_vector(axis)
|
| 689 |
+
theta = sympify(angle)
|
| 690 |
+
|
| 691 |
+
if not axis.dt(parent) == 0:
|
| 692 |
+
raise ValueError('Axis cannot be time-varying.')
|
| 693 |
+
unit_axis = axis.express(parent).normalize()
|
| 694 |
+
unit_col = unit_axis.args[0][0]
|
| 695 |
+
parent_orient_axis = (
|
| 696 |
+
(eye(3) - unit_col * unit_col.T) * cos(theta) +
|
| 697 |
+
Matrix([[0, -unit_col[2], unit_col[1]],
|
| 698 |
+
[unit_col[2], 0, -unit_col[0]],
|
| 699 |
+
[-unit_col[1], unit_col[0], 0]]) *
|
| 700 |
+
sin(theta) + unit_col * unit_col.T)
|
| 701 |
+
|
| 702 |
+
self._dcm(parent, parent_orient_axis)
|
| 703 |
+
|
| 704 |
+
thetad = (theta).diff(dynamicsymbols._t)
|
| 705 |
+
wvec = thetad*axis.express(parent).normalize()
|
| 706 |
+
self._ang_vel_dict.update({parent: wvec})
|
| 707 |
+
parent._ang_vel_dict.update({self: -wvec})
|
| 708 |
+
self._var_dict = {}
|
| 709 |
+
|
| 710 |
+
def orient_explicit(self, parent, dcm):
|
| 711 |
+
"""Sets the orientation of this reference frame relative to another (parent) reference frame
|
| 712 |
+
using a direction cosine matrix that describes the rotation from the parent to the child.
|
| 713 |
+
|
| 714 |
+
Parameters
|
| 715 |
+
==========
|
| 716 |
+
|
| 717 |
+
parent : ReferenceFrame
|
| 718 |
+
Reference frame that this reference frame will be rotated relative
|
| 719 |
+
to.
|
| 720 |
+
dcm : Matrix, shape(3, 3)
|
| 721 |
+
Direction cosine matrix that specifies the relative rotation
|
| 722 |
+
between the two reference frames.
|
| 723 |
+
|
| 724 |
+
Warns
|
| 725 |
+
======
|
| 726 |
+
|
| 727 |
+
UserWarning
|
| 728 |
+
If the orientation creates a kinematic loop.
|
| 729 |
+
|
| 730 |
+
Examples
|
| 731 |
+
========
|
| 732 |
+
|
| 733 |
+
Setup variables for the examples:
|
| 734 |
+
|
| 735 |
+
>>> from sympy import symbols, Matrix, sin, cos
|
| 736 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 737 |
+
>>> q1 = symbols('q1')
|
| 738 |
+
>>> A = ReferenceFrame('A')
|
| 739 |
+
>>> B = ReferenceFrame('B')
|
| 740 |
+
>>> N = ReferenceFrame('N')
|
| 741 |
+
|
| 742 |
+
A simple rotation of ``A`` relative to ``N`` about ``N.x`` is defined
|
| 743 |
+
by the following direction cosine matrix:
|
| 744 |
+
|
| 745 |
+
>>> dcm = Matrix([[1, 0, 0],
|
| 746 |
+
... [0, cos(q1), -sin(q1)],
|
| 747 |
+
... [0, sin(q1), cos(q1)]])
|
| 748 |
+
>>> A.orient_explicit(N, dcm)
|
| 749 |
+
>>> A.dcm(N)
|
| 750 |
+
Matrix([
|
| 751 |
+
[1, 0, 0],
|
| 752 |
+
[0, cos(q1), sin(q1)],
|
| 753 |
+
[0, -sin(q1), cos(q1)]])
|
| 754 |
+
|
| 755 |
+
This is equivalent to using ``orient_axis()``:
|
| 756 |
+
|
| 757 |
+
>>> B.orient_axis(N, N.x, q1)
|
| 758 |
+
>>> B.dcm(N)
|
| 759 |
+
Matrix([
|
| 760 |
+
[1, 0, 0],
|
| 761 |
+
[0, cos(q1), sin(q1)],
|
| 762 |
+
[0, -sin(q1), cos(q1)]])
|
| 763 |
+
|
| 764 |
+
**Note carefully that** ``N.dcm(B)`` **(the transpose) would be passed
|
| 765 |
+
into** ``orient_explicit()`` **for** ``A.dcm(N)`` **to match**
|
| 766 |
+
``B.dcm(N)``:
|
| 767 |
+
|
| 768 |
+
>>> A.orient_explicit(N, N.dcm(B))
|
| 769 |
+
>>> A.dcm(N)
|
| 770 |
+
Matrix([
|
| 771 |
+
[1, 0, 0],
|
| 772 |
+
[0, cos(q1), sin(q1)],
|
| 773 |
+
[0, -sin(q1), cos(q1)]])
|
| 774 |
+
|
| 775 |
+
"""
|
| 776 |
+
_check_frame(parent)
|
| 777 |
+
# amounts must be a Matrix type object
|
| 778 |
+
# (e.g. sympy.matrices.dense.MutableDenseMatrix).
|
| 779 |
+
if not isinstance(dcm, MatrixBase):
|
| 780 |
+
raise TypeError("Amounts must be a SymPy Matrix type object.")
|
| 781 |
+
|
| 782 |
+
self.orient_dcm(parent, dcm.T)
|
| 783 |
+
|
| 784 |
+
def orient_dcm(self, parent, dcm):
|
| 785 |
+
"""Sets the orientation of this reference frame relative to another (parent) reference frame
|
| 786 |
+
using a direction cosine matrix that describes the rotation from the child to the parent.
|
| 787 |
+
|
| 788 |
+
Parameters
|
| 789 |
+
==========
|
| 790 |
+
|
| 791 |
+
parent : ReferenceFrame
|
| 792 |
+
Reference frame that this reference frame will be rotated relative
|
| 793 |
+
to.
|
| 794 |
+
dcm : Matrix, shape(3, 3)
|
| 795 |
+
Direction cosine matrix that specifies the relative rotation
|
| 796 |
+
between the two reference frames.
|
| 797 |
+
|
| 798 |
+
Warns
|
| 799 |
+
======
|
| 800 |
+
|
| 801 |
+
UserWarning
|
| 802 |
+
If the orientation creates a kinematic loop.
|
| 803 |
+
|
| 804 |
+
Examples
|
| 805 |
+
========
|
| 806 |
+
|
| 807 |
+
Setup variables for the examples:
|
| 808 |
+
|
| 809 |
+
>>> from sympy import symbols, Matrix, sin, cos
|
| 810 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 811 |
+
>>> q1 = symbols('q1')
|
| 812 |
+
>>> A = ReferenceFrame('A')
|
| 813 |
+
>>> B = ReferenceFrame('B')
|
| 814 |
+
>>> N = ReferenceFrame('N')
|
| 815 |
+
|
| 816 |
+
A simple rotation of ``A`` relative to ``N`` about ``N.x`` is defined
|
| 817 |
+
by the following direction cosine matrix:
|
| 818 |
+
|
| 819 |
+
>>> dcm = Matrix([[1, 0, 0],
|
| 820 |
+
... [0, cos(q1), sin(q1)],
|
| 821 |
+
... [0, -sin(q1), cos(q1)]])
|
| 822 |
+
>>> A.orient_dcm(N, dcm)
|
| 823 |
+
>>> A.dcm(N)
|
| 824 |
+
Matrix([
|
| 825 |
+
[1, 0, 0],
|
| 826 |
+
[0, cos(q1), sin(q1)],
|
| 827 |
+
[0, -sin(q1), cos(q1)]])
|
| 828 |
+
|
| 829 |
+
This is equivalent to using ``orient_axis()``:
|
| 830 |
+
|
| 831 |
+
>>> B.orient_axis(N, N.x, q1)
|
| 832 |
+
>>> B.dcm(N)
|
| 833 |
+
Matrix([
|
| 834 |
+
[1, 0, 0],
|
| 835 |
+
[0, cos(q1), sin(q1)],
|
| 836 |
+
[0, -sin(q1), cos(q1)]])
|
| 837 |
+
|
| 838 |
+
"""
|
| 839 |
+
|
| 840 |
+
_check_frame(parent)
|
| 841 |
+
# amounts must be a Matrix type object
|
| 842 |
+
# (e.g. sympy.matrices.dense.MutableDenseMatrix).
|
| 843 |
+
if not isinstance(dcm, MatrixBase):
|
| 844 |
+
raise TypeError("Amounts must be a SymPy Matrix type object.")
|
| 845 |
+
|
| 846 |
+
self._dcm(parent, dcm.T)
|
| 847 |
+
|
| 848 |
+
wvec = self._w_diff_dcm(parent)
|
| 849 |
+
self._ang_vel_dict.update({parent: wvec})
|
| 850 |
+
parent._ang_vel_dict.update({self: -wvec})
|
| 851 |
+
self._var_dict = {}
|
| 852 |
+
|
| 853 |
+
def _rot(self, axis, angle):
|
| 854 |
+
"""DCM for simple axis 1,2,or 3 rotations."""
|
| 855 |
+
if axis == 1:
|
| 856 |
+
return Matrix([[1, 0, 0],
|
| 857 |
+
[0, cos(angle), -sin(angle)],
|
| 858 |
+
[0, sin(angle), cos(angle)]])
|
| 859 |
+
elif axis == 2:
|
| 860 |
+
return Matrix([[cos(angle), 0, sin(angle)],
|
| 861 |
+
[0, 1, 0],
|
| 862 |
+
[-sin(angle), 0, cos(angle)]])
|
| 863 |
+
elif axis == 3:
|
| 864 |
+
return Matrix([[cos(angle), -sin(angle), 0],
|
| 865 |
+
[sin(angle), cos(angle), 0],
|
| 866 |
+
[0, 0, 1]])
|
| 867 |
+
|
| 868 |
+
def _parse_consecutive_rotations(self, angles, rotation_order):
|
| 869 |
+
"""Helper for orient_body_fixed and orient_space_fixed.
|
| 870 |
+
|
| 871 |
+
Parameters
|
| 872 |
+
==========
|
| 873 |
+
angles : 3-tuple of sympifiable
|
| 874 |
+
Three angles in radians used for the successive rotations.
|
| 875 |
+
rotation_order : 3 character string or 3 digit integer
|
| 876 |
+
Order of the rotations. The order can be specified by the strings
|
| 877 |
+
``'XZX'``, ``'131'``, or the integer ``131``. There are 12 unique
|
| 878 |
+
valid rotation orders.
|
| 879 |
+
|
| 880 |
+
Returns
|
| 881 |
+
=======
|
| 882 |
+
|
| 883 |
+
amounts : list
|
| 884 |
+
List of sympifiables corresponding to the rotation angles.
|
| 885 |
+
rot_order : list
|
| 886 |
+
List of integers corresponding to the axis of rotation.
|
| 887 |
+
rot_matrices : list
|
| 888 |
+
List of DCM around the given axis with corresponding magnitude.
|
| 889 |
+
|
| 890 |
+
"""
|
| 891 |
+
amounts = list(angles)
|
| 892 |
+
for i, v in enumerate(amounts):
|
| 893 |
+
if not isinstance(v, Vector):
|
| 894 |
+
amounts[i] = sympify(v)
|
| 895 |
+
|
| 896 |
+
approved_orders = ('123', '231', '312', '132', '213', '321', '121',
|
| 897 |
+
'131', '212', '232', '313', '323', '')
|
| 898 |
+
# make sure XYZ => 123
|
| 899 |
+
rot_order = translate(str(rotation_order), 'XYZxyz', '123123')
|
| 900 |
+
if rot_order not in approved_orders:
|
| 901 |
+
raise TypeError('The rotation order is not a valid order.')
|
| 902 |
+
|
| 903 |
+
rot_order = [int(r) for r in rot_order]
|
| 904 |
+
if not (len(amounts) == 3 & len(rot_order) == 3):
|
| 905 |
+
raise TypeError('Body orientation takes 3 values & 3 orders')
|
| 906 |
+
rot_matrices = [self._rot(order, amount)
|
| 907 |
+
for (order, amount) in zip(rot_order, amounts)]
|
| 908 |
+
return amounts, rot_order, rot_matrices
|
| 909 |
+
|
| 910 |
+
def orient_body_fixed(self, parent, angles, rotation_order):
|
| 911 |
+
"""Rotates this reference frame relative to the parent reference frame
|
| 912 |
+
by right hand rotating through three successive body fixed simple axis
|
| 913 |
+
rotations. Each subsequent axis of rotation is about the "body fixed"
|
| 914 |
+
unit vectors of a new intermediate reference frame. This type of
|
| 915 |
+
rotation is also referred to rotating through the `Euler and Tait-Bryan
|
| 916 |
+
Angles`_.
|
| 917 |
+
|
| 918 |
+
.. _Euler and Tait-Bryan Angles: https://en.wikipedia.org/wiki/Euler_angles
|
| 919 |
+
|
| 920 |
+
The computed angular velocity in this method is by default expressed in
|
| 921 |
+
the child's frame, so it is most preferable to use ``u1 * child.x + u2 *
|
| 922 |
+
child.y + u3 * child.z`` as generalized speeds.
|
| 923 |
+
|
| 924 |
+
Parameters
|
| 925 |
+
==========
|
| 926 |
+
|
| 927 |
+
parent : ReferenceFrame
|
| 928 |
+
Reference frame that this reference frame will be rotated relative
|
| 929 |
+
to.
|
| 930 |
+
angles : 3-tuple of sympifiable
|
| 931 |
+
Three angles in radians used for the successive rotations.
|
| 932 |
+
rotation_order : 3 character string or 3 digit integer
|
| 933 |
+
Order of the rotations about each intermediate reference frames'
|
| 934 |
+
unit vectors. The Euler rotation about the X, Z', X'' axes can be
|
| 935 |
+
specified by the strings ``'XZX'``, ``'131'``, or the integer
|
| 936 |
+
``131``. There are 12 unique valid rotation orders (6 Euler and 6
|
| 937 |
+
Tait-Bryan): zxz, xyx, yzy, zyz, xzx, yxy, xyz, yzx, zxy, xzy, zyx,
|
| 938 |
+
and yxz.
|
| 939 |
+
|
| 940 |
+
Warns
|
| 941 |
+
======
|
| 942 |
+
|
| 943 |
+
UserWarning
|
| 944 |
+
If the orientation creates a kinematic loop.
|
| 945 |
+
|
| 946 |
+
Examples
|
| 947 |
+
========
|
| 948 |
+
|
| 949 |
+
Setup variables for the examples:
|
| 950 |
+
|
| 951 |
+
>>> from sympy import symbols
|
| 952 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 953 |
+
>>> q1, q2, q3 = symbols('q1, q2, q3')
|
| 954 |
+
>>> N = ReferenceFrame('N')
|
| 955 |
+
>>> B = ReferenceFrame('B')
|
| 956 |
+
>>> B1 = ReferenceFrame('B1')
|
| 957 |
+
>>> B2 = ReferenceFrame('B2')
|
| 958 |
+
>>> B3 = ReferenceFrame('B3')
|
| 959 |
+
|
| 960 |
+
For example, a classic Euler Angle rotation can be done by:
|
| 961 |
+
|
| 962 |
+
>>> B.orient_body_fixed(N, (q1, q2, q3), 'XYX')
|
| 963 |
+
>>> B.dcm(N)
|
| 964 |
+
Matrix([
|
| 965 |
+
[ cos(q2), sin(q1)*sin(q2), -sin(q2)*cos(q1)],
|
| 966 |
+
[sin(q2)*sin(q3), -sin(q1)*sin(q3)*cos(q2) + cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q3)*cos(q1)*cos(q2)],
|
| 967 |
+
[sin(q2)*cos(q3), -sin(q1)*cos(q2)*cos(q3) - sin(q3)*cos(q1), -sin(q1)*sin(q3) + cos(q1)*cos(q2)*cos(q3)]])
|
| 968 |
+
|
| 969 |
+
This rotates reference frame B relative to reference frame N through
|
| 970 |
+
``q1`` about ``N.x``, then rotates B again through ``q2`` about
|
| 971 |
+
``B.y``, and finally through ``q3`` about ``B.x``. It is equivalent to
|
| 972 |
+
three successive ``orient_axis()`` calls:
|
| 973 |
+
|
| 974 |
+
>>> B1.orient_axis(N, N.x, q1)
|
| 975 |
+
>>> B2.orient_axis(B1, B1.y, q2)
|
| 976 |
+
>>> B3.orient_axis(B2, B2.x, q3)
|
| 977 |
+
>>> B3.dcm(N)
|
| 978 |
+
Matrix([
|
| 979 |
+
[ cos(q2), sin(q1)*sin(q2), -sin(q2)*cos(q1)],
|
| 980 |
+
[sin(q2)*sin(q3), -sin(q1)*sin(q3)*cos(q2) + cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q3)*cos(q1)*cos(q2)],
|
| 981 |
+
[sin(q2)*cos(q3), -sin(q1)*cos(q2)*cos(q3) - sin(q3)*cos(q1), -sin(q1)*sin(q3) + cos(q1)*cos(q2)*cos(q3)]])
|
| 982 |
+
|
| 983 |
+
Acceptable rotation orders are of length 3, expressed in as a string
|
| 984 |
+
``'XYZ'`` or ``'123'`` or integer ``123``. Rotations about an axis
|
| 985 |
+
twice in a row are prohibited.
|
| 986 |
+
|
| 987 |
+
>>> B.orient_body_fixed(N, (q1, q2, 0), 'ZXZ')
|
| 988 |
+
>>> B.orient_body_fixed(N, (q1, q2, 0), '121')
|
| 989 |
+
>>> B.orient_body_fixed(N, (q1, q2, q3), 123)
|
| 990 |
+
|
| 991 |
+
"""
|
| 992 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 993 |
+
|
| 994 |
+
_check_frame(parent)
|
| 995 |
+
|
| 996 |
+
amounts, rot_order, rot_matrices = self._parse_consecutive_rotations(
|
| 997 |
+
angles, rotation_order)
|
| 998 |
+
self._dcm(parent, rot_matrices[0] * rot_matrices[1] * rot_matrices[2])
|
| 999 |
+
|
| 1000 |
+
rot_vecs = [zeros(3, 1) for _ in range(3)]
|
| 1001 |
+
for i, order in enumerate(rot_order):
|
| 1002 |
+
rot_vecs[i][order - 1] = amounts[i].diff(dynamicsymbols._t)
|
| 1003 |
+
u1, u2, u3 = rot_vecs[2] + rot_matrices[2].T * (
|
| 1004 |
+
rot_vecs[1] + rot_matrices[1].T * rot_vecs[0])
|
| 1005 |
+
wvec = u1 * self.x + u2 * self.y + u3 * self.z # There is a double -
|
| 1006 |
+
self._ang_vel_dict.update({parent: wvec})
|
| 1007 |
+
parent._ang_vel_dict.update({self: -wvec})
|
| 1008 |
+
self._var_dict = {}
|
| 1009 |
+
|
| 1010 |
+
def orient_space_fixed(self, parent, angles, rotation_order):
|
| 1011 |
+
"""Rotates this reference frame relative to the parent reference frame
|
| 1012 |
+
by right hand rotating through three successive space fixed simple axis
|
| 1013 |
+
rotations. Each subsequent axis of rotation is about the "space fixed"
|
| 1014 |
+
unit vectors of the parent reference frame.
|
| 1015 |
+
|
| 1016 |
+
The computed angular velocity in this method is by default expressed in
|
| 1017 |
+
the child's frame, so it is most preferable to use ``u1 * child.x + u2 *
|
| 1018 |
+
child.y + u3 * child.z`` as generalized speeds.
|
| 1019 |
+
|
| 1020 |
+
Parameters
|
| 1021 |
+
==========
|
| 1022 |
+
parent : ReferenceFrame
|
| 1023 |
+
Reference frame that this reference frame will be rotated relative
|
| 1024 |
+
to.
|
| 1025 |
+
angles : 3-tuple of sympifiable
|
| 1026 |
+
Three angles in radians used for the successive rotations.
|
| 1027 |
+
rotation_order : 3 character string or 3 digit integer
|
| 1028 |
+
Order of the rotations about the parent reference frame's unit
|
| 1029 |
+
vectors. The order can be specified by the strings ``'XZX'``,
|
| 1030 |
+
``'131'``, or the integer ``131``. There are 12 unique valid
|
| 1031 |
+
rotation orders.
|
| 1032 |
+
|
| 1033 |
+
Warns
|
| 1034 |
+
======
|
| 1035 |
+
|
| 1036 |
+
UserWarning
|
| 1037 |
+
If the orientation creates a kinematic loop.
|
| 1038 |
+
|
| 1039 |
+
Examples
|
| 1040 |
+
========
|
| 1041 |
+
|
| 1042 |
+
Setup variables for the examples:
|
| 1043 |
+
|
| 1044 |
+
>>> from sympy import symbols
|
| 1045 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 1046 |
+
>>> q1, q2, q3 = symbols('q1, q2, q3')
|
| 1047 |
+
>>> N = ReferenceFrame('N')
|
| 1048 |
+
>>> B = ReferenceFrame('B')
|
| 1049 |
+
>>> B1 = ReferenceFrame('B1')
|
| 1050 |
+
>>> B2 = ReferenceFrame('B2')
|
| 1051 |
+
>>> B3 = ReferenceFrame('B3')
|
| 1052 |
+
|
| 1053 |
+
>>> B.orient_space_fixed(N, (q1, q2, q3), '312')
|
| 1054 |
+
>>> B.dcm(N)
|
| 1055 |
+
Matrix([
|
| 1056 |
+
[ sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1)],
|
| 1057 |
+
[-sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3)],
|
| 1058 |
+
[ sin(q3)*cos(q2), -sin(q2), cos(q2)*cos(q3)]])
|
| 1059 |
+
|
| 1060 |
+
is equivalent to:
|
| 1061 |
+
|
| 1062 |
+
>>> B1.orient_axis(N, N.z, q1)
|
| 1063 |
+
>>> B2.orient_axis(B1, N.x, q2)
|
| 1064 |
+
>>> B3.orient_axis(B2, N.y, q3)
|
| 1065 |
+
>>> B3.dcm(N).simplify()
|
| 1066 |
+
Matrix([
|
| 1067 |
+
[ sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1)],
|
| 1068 |
+
[-sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3)],
|
| 1069 |
+
[ sin(q3)*cos(q2), -sin(q2), cos(q2)*cos(q3)]])
|
| 1070 |
+
|
| 1071 |
+
It is worth noting that space-fixed and body-fixed rotations are
|
| 1072 |
+
related by the order of the rotations, i.e. the reverse order of body
|
| 1073 |
+
fixed will give space fixed and vice versa.
|
| 1074 |
+
|
| 1075 |
+
>>> B.orient_space_fixed(N, (q1, q2, q3), '231')
|
| 1076 |
+
>>> B.dcm(N)
|
| 1077 |
+
Matrix([
|
| 1078 |
+
[cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), -sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
|
| 1079 |
+
[ -sin(q2), cos(q2)*cos(q3), sin(q3)*cos(q2)],
|
| 1080 |
+
[sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3)]])
|
| 1081 |
+
|
| 1082 |
+
>>> B.orient_body_fixed(N, (q3, q2, q1), '132')
|
| 1083 |
+
>>> B.dcm(N)
|
| 1084 |
+
Matrix([
|
| 1085 |
+
[cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), -sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
|
| 1086 |
+
[ -sin(q2), cos(q2)*cos(q3), sin(q3)*cos(q2)],
|
| 1087 |
+
[sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3)]])
|
| 1088 |
+
|
| 1089 |
+
"""
|
| 1090 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 1091 |
+
|
| 1092 |
+
_check_frame(parent)
|
| 1093 |
+
|
| 1094 |
+
amounts, rot_order, rot_matrices = self._parse_consecutive_rotations(
|
| 1095 |
+
angles, rotation_order)
|
| 1096 |
+
self._dcm(parent, rot_matrices[2] * rot_matrices[1] * rot_matrices[0])
|
| 1097 |
+
|
| 1098 |
+
rot_vecs = [zeros(3, 1) for _ in range(3)]
|
| 1099 |
+
for i, order in enumerate(rot_order):
|
| 1100 |
+
rot_vecs[i][order - 1] = amounts[i].diff(dynamicsymbols._t)
|
| 1101 |
+
u1, u2, u3 = rot_vecs[0] + rot_matrices[0].T * (
|
| 1102 |
+
rot_vecs[1] + rot_matrices[1].T * rot_vecs[2])
|
| 1103 |
+
wvec = u1 * self.x + u2 * self.y + u3 * self.z # There is a double -
|
| 1104 |
+
self._ang_vel_dict.update({parent: wvec})
|
| 1105 |
+
parent._ang_vel_dict.update({self: -wvec})
|
| 1106 |
+
self._var_dict = {}
|
| 1107 |
+
|
| 1108 |
+
def orient_quaternion(self, parent, numbers):
|
| 1109 |
+
"""Sets the orientation of this reference frame relative to a parent
|
| 1110 |
+
reference frame via an orientation quaternion. An orientation
|
| 1111 |
+
quaternion is defined as a finite rotation a unit vector, ``(lambda_x,
|
| 1112 |
+
lambda_y, lambda_z)``, by an angle ``theta``. The orientation
|
| 1113 |
+
quaternion is described by four parameters:
|
| 1114 |
+
|
| 1115 |
+
- ``q0 = cos(theta/2)``
|
| 1116 |
+
- ``q1 = lambda_x*sin(theta/2)``
|
| 1117 |
+
- ``q2 = lambda_y*sin(theta/2)``
|
| 1118 |
+
- ``q3 = lambda_z*sin(theta/2)``
|
| 1119 |
+
|
| 1120 |
+
See `Quaternions and Spatial Rotation
|
| 1121 |
+
<https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation>`_ on
|
| 1122 |
+
Wikipedia for more information.
|
| 1123 |
+
|
| 1124 |
+
Parameters
|
| 1125 |
+
==========
|
| 1126 |
+
parent : ReferenceFrame
|
| 1127 |
+
Reference frame that this reference frame will be rotated relative
|
| 1128 |
+
to.
|
| 1129 |
+
numbers : 4-tuple of sympifiable
|
| 1130 |
+
The four quaternion scalar numbers as defined above: ``q0``,
|
| 1131 |
+
``q1``, ``q2``, ``q3``.
|
| 1132 |
+
|
| 1133 |
+
Warns
|
| 1134 |
+
======
|
| 1135 |
+
|
| 1136 |
+
UserWarning
|
| 1137 |
+
If the orientation creates a kinematic loop.
|
| 1138 |
+
|
| 1139 |
+
Examples
|
| 1140 |
+
========
|
| 1141 |
+
|
| 1142 |
+
Setup variables for the examples:
|
| 1143 |
+
|
| 1144 |
+
>>> from sympy import symbols
|
| 1145 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 1146 |
+
>>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
|
| 1147 |
+
>>> N = ReferenceFrame('N')
|
| 1148 |
+
>>> B = ReferenceFrame('B')
|
| 1149 |
+
|
| 1150 |
+
Set the orientation:
|
| 1151 |
+
|
| 1152 |
+
>>> B.orient_quaternion(N, (q0, q1, q2, q3))
|
| 1153 |
+
>>> B.dcm(N)
|
| 1154 |
+
Matrix([
|
| 1155 |
+
[q0**2 + q1**2 - q2**2 - q3**2, 2*q0*q3 + 2*q1*q2, -2*q0*q2 + 2*q1*q3],
|
| 1156 |
+
[ -2*q0*q3 + 2*q1*q2, q0**2 - q1**2 + q2**2 - q3**2, 2*q0*q1 + 2*q2*q3],
|
| 1157 |
+
[ 2*q0*q2 + 2*q1*q3, -2*q0*q1 + 2*q2*q3, q0**2 - q1**2 - q2**2 + q3**2]])
|
| 1158 |
+
|
| 1159 |
+
"""
|
| 1160 |
+
|
| 1161 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 1162 |
+
_check_frame(parent)
|
| 1163 |
+
|
| 1164 |
+
numbers = list(numbers)
|
| 1165 |
+
for i, v in enumerate(numbers):
|
| 1166 |
+
if not isinstance(v, Vector):
|
| 1167 |
+
numbers[i] = sympify(v)
|
| 1168 |
+
|
| 1169 |
+
if not (isinstance(numbers, (list, tuple)) & (len(numbers) == 4)):
|
| 1170 |
+
raise TypeError('Amounts are a list or tuple of length 4')
|
| 1171 |
+
q0, q1, q2, q3 = numbers
|
| 1172 |
+
parent_orient_quaternion = (
|
| 1173 |
+
Matrix([[q0**2 + q1**2 - q2**2 - q3**2,
|
| 1174 |
+
2 * (q1 * q2 - q0 * q3),
|
| 1175 |
+
2 * (q0 * q2 + q1 * q3)],
|
| 1176 |
+
[2 * (q1 * q2 + q0 * q3),
|
| 1177 |
+
q0**2 - q1**2 + q2**2 - q3**2,
|
| 1178 |
+
2 * (q2 * q3 - q0 * q1)],
|
| 1179 |
+
[2 * (q1 * q3 - q0 * q2),
|
| 1180 |
+
2 * (q0 * q1 + q2 * q3),
|
| 1181 |
+
q0**2 - q1**2 - q2**2 + q3**2]]))
|
| 1182 |
+
|
| 1183 |
+
self._dcm(parent, parent_orient_quaternion)
|
| 1184 |
+
|
| 1185 |
+
t = dynamicsymbols._t
|
| 1186 |
+
q0, q1, q2, q3 = numbers
|
| 1187 |
+
q0d = diff(q0, t)
|
| 1188 |
+
q1d = diff(q1, t)
|
| 1189 |
+
q2d = diff(q2, t)
|
| 1190 |
+
q3d = diff(q3, t)
|
| 1191 |
+
w1 = 2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1)
|
| 1192 |
+
w2 = 2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2)
|
| 1193 |
+
w3 = 2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3)
|
| 1194 |
+
wvec = Vector([(Matrix([w1, w2, w3]), self)])
|
| 1195 |
+
|
| 1196 |
+
self._ang_vel_dict.update({parent: wvec})
|
| 1197 |
+
parent._ang_vel_dict.update({self: -wvec})
|
| 1198 |
+
self._var_dict = {}
|
| 1199 |
+
|
| 1200 |
+
def orient(self, parent, rot_type, amounts, rot_order=''):
|
| 1201 |
+
"""Sets the orientation of this reference frame relative to another
|
| 1202 |
+
(parent) reference frame.
|
| 1203 |
+
|
| 1204 |
+
.. note:: It is now recommended to use the ``.orient_axis,
|
| 1205 |
+
.orient_body_fixed, .orient_space_fixed, .orient_quaternion``
|
| 1206 |
+
methods for the different rotation types.
|
| 1207 |
+
|
| 1208 |
+
Parameters
|
| 1209 |
+
==========
|
| 1210 |
+
|
| 1211 |
+
parent : ReferenceFrame
|
| 1212 |
+
Reference frame that this reference frame will be rotated relative
|
| 1213 |
+
to.
|
| 1214 |
+
rot_type : str
|
| 1215 |
+
The method used to generate the direction cosine matrix. Supported
|
| 1216 |
+
methods are:
|
| 1217 |
+
|
| 1218 |
+
- ``'Axis'``: simple rotations about a single common axis
|
| 1219 |
+
- ``'DCM'``: for setting the direction cosine matrix directly
|
| 1220 |
+
- ``'Body'``: three successive rotations about new intermediate
|
| 1221 |
+
axes, also called "Euler and Tait-Bryan angles"
|
| 1222 |
+
- ``'Space'``: three successive rotations about the parent
|
| 1223 |
+
frames' unit vectors
|
| 1224 |
+
- ``'Quaternion'``: rotations defined by four parameters which
|
| 1225 |
+
result in a singularity free direction cosine matrix
|
| 1226 |
+
|
| 1227 |
+
amounts :
|
| 1228 |
+
Expressions defining the rotation angles or direction cosine
|
| 1229 |
+
matrix. These must match the ``rot_type``. See examples below for
|
| 1230 |
+
details. The input types are:
|
| 1231 |
+
|
| 1232 |
+
- ``'Axis'``: 2-tuple (expr/sym/func, Vector)
|
| 1233 |
+
- ``'DCM'``: Matrix, shape(3,3)
|
| 1234 |
+
- ``'Body'``: 3-tuple of expressions, symbols, or functions
|
| 1235 |
+
- ``'Space'``: 3-tuple of expressions, symbols, or functions
|
| 1236 |
+
- ``'Quaternion'``: 4-tuple of expressions, symbols, or
|
| 1237 |
+
functions
|
| 1238 |
+
|
| 1239 |
+
rot_order : str or int, optional
|
| 1240 |
+
If applicable, the order of the successive of rotations. The string
|
| 1241 |
+
``'123'`` and integer ``123`` are equivalent, for example. Required
|
| 1242 |
+
for ``'Body'`` and ``'Space'``.
|
| 1243 |
+
|
| 1244 |
+
Warns
|
| 1245 |
+
======
|
| 1246 |
+
|
| 1247 |
+
UserWarning
|
| 1248 |
+
If the orientation creates a kinematic loop.
|
| 1249 |
+
|
| 1250 |
+
"""
|
| 1251 |
+
|
| 1252 |
+
_check_frame(parent)
|
| 1253 |
+
|
| 1254 |
+
approved_orders = ('123', '231', '312', '132', '213', '321', '121',
|
| 1255 |
+
'131', '212', '232', '313', '323', '')
|
| 1256 |
+
rot_order = translate(str(rot_order), 'XYZxyz', '123123')
|
| 1257 |
+
rot_type = rot_type.upper()
|
| 1258 |
+
|
| 1259 |
+
if rot_order not in approved_orders:
|
| 1260 |
+
raise TypeError('The supplied order is not an approved type')
|
| 1261 |
+
|
| 1262 |
+
if rot_type == 'AXIS':
|
| 1263 |
+
self.orient_axis(parent, amounts[1], amounts[0])
|
| 1264 |
+
|
| 1265 |
+
elif rot_type == 'DCM':
|
| 1266 |
+
self.orient_explicit(parent, amounts)
|
| 1267 |
+
|
| 1268 |
+
elif rot_type == 'BODY':
|
| 1269 |
+
self.orient_body_fixed(parent, amounts, rot_order)
|
| 1270 |
+
|
| 1271 |
+
elif rot_type == 'SPACE':
|
| 1272 |
+
self.orient_space_fixed(parent, amounts, rot_order)
|
| 1273 |
+
|
| 1274 |
+
elif rot_type == 'QUATERNION':
|
| 1275 |
+
self.orient_quaternion(parent, amounts)
|
| 1276 |
+
|
| 1277 |
+
else:
|
| 1278 |
+
raise NotImplementedError('That is not an implemented rotation')
|
| 1279 |
+
|
| 1280 |
+
def orientnew(self, newname, rot_type, amounts, rot_order='',
|
| 1281 |
+
variables=None, indices=None, latexs=None):
|
| 1282 |
+
r"""Returns a new reference frame oriented with respect to this
|
| 1283 |
+
reference frame.
|
| 1284 |
+
|
| 1285 |
+
See ``ReferenceFrame.orient()`` for detailed examples of how to orient
|
| 1286 |
+
reference frames.
|
| 1287 |
+
|
| 1288 |
+
Parameters
|
| 1289 |
+
==========
|
| 1290 |
+
|
| 1291 |
+
newname : str
|
| 1292 |
+
Name for the new reference frame.
|
| 1293 |
+
rot_type : str
|
| 1294 |
+
The method used to generate the direction cosine matrix. Supported
|
| 1295 |
+
methods are:
|
| 1296 |
+
|
| 1297 |
+
- ``'Axis'``: simple rotations about a single common axis
|
| 1298 |
+
- ``'DCM'``: for setting the direction cosine matrix directly
|
| 1299 |
+
- ``'Body'``: three successive rotations about new intermediate
|
| 1300 |
+
axes, also called "Euler and Tait-Bryan angles"
|
| 1301 |
+
- ``'Space'``: three successive rotations about the parent
|
| 1302 |
+
frames' unit vectors
|
| 1303 |
+
- ``'Quaternion'``: rotations defined by four parameters which
|
| 1304 |
+
result in a singularity free direction cosine matrix
|
| 1305 |
+
|
| 1306 |
+
amounts :
|
| 1307 |
+
Expressions defining the rotation angles or direction cosine
|
| 1308 |
+
matrix. These must match the ``rot_type``. See examples below for
|
| 1309 |
+
details. The input types are:
|
| 1310 |
+
|
| 1311 |
+
- ``'Axis'``: 2-tuple (expr/sym/func, Vector)
|
| 1312 |
+
- ``'DCM'``: Matrix, shape(3,3)
|
| 1313 |
+
- ``'Body'``: 3-tuple of expressions, symbols, or functions
|
| 1314 |
+
- ``'Space'``: 3-tuple of expressions, symbols, or functions
|
| 1315 |
+
- ``'Quaternion'``: 4-tuple of expressions, symbols, or
|
| 1316 |
+
functions
|
| 1317 |
+
|
| 1318 |
+
rot_order : str or int, optional
|
| 1319 |
+
If applicable, the order of the successive of rotations. The string
|
| 1320 |
+
``'123'`` and integer ``123`` are equivalent, for example. Required
|
| 1321 |
+
for ``'Body'`` and ``'Space'``.
|
| 1322 |
+
indices : tuple of str
|
| 1323 |
+
Enables the reference frame's basis unit vectors to be accessed by
|
| 1324 |
+
Python's square bracket indexing notation using the provided three
|
| 1325 |
+
indice strings and alters the printing of the unit vectors to
|
| 1326 |
+
reflect this choice.
|
| 1327 |
+
latexs : tuple of str
|
| 1328 |
+
Alters the LaTeX printing of the reference frame's basis unit
|
| 1329 |
+
vectors to the provided three valid LaTeX strings.
|
| 1330 |
+
|
| 1331 |
+
Examples
|
| 1332 |
+
========
|
| 1333 |
+
|
| 1334 |
+
>>> from sympy import symbols
|
| 1335 |
+
>>> from sympy.physics.vector import ReferenceFrame, vlatex
|
| 1336 |
+
>>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
|
| 1337 |
+
>>> N = ReferenceFrame('N')
|
| 1338 |
+
|
| 1339 |
+
Create a new reference frame A rotated relative to N through a simple
|
| 1340 |
+
rotation.
|
| 1341 |
+
|
| 1342 |
+
>>> A = N.orientnew('A', 'Axis', (q0, N.x))
|
| 1343 |
+
|
| 1344 |
+
Create a new reference frame B rotated relative to N through body-fixed
|
| 1345 |
+
rotations.
|
| 1346 |
+
|
| 1347 |
+
>>> B = N.orientnew('B', 'Body', (q1, q2, q3), '123')
|
| 1348 |
+
|
| 1349 |
+
Create a new reference frame C rotated relative to N through a simple
|
| 1350 |
+
rotation with unique indices and LaTeX printing.
|
| 1351 |
+
|
| 1352 |
+
>>> C = N.orientnew('C', 'Axis', (q0, N.x), indices=('1', '2', '3'),
|
| 1353 |
+
... latexs=(r'\hat{\mathbf{c}}_1',r'\hat{\mathbf{c}}_2',
|
| 1354 |
+
... r'\hat{\mathbf{c}}_3'))
|
| 1355 |
+
>>> C['1']
|
| 1356 |
+
C['1']
|
| 1357 |
+
>>> print(vlatex(C['1']))
|
| 1358 |
+
\hat{\mathbf{c}}_1
|
| 1359 |
+
|
| 1360 |
+
"""
|
| 1361 |
+
|
| 1362 |
+
newframe = self.__class__(newname, variables=variables,
|
| 1363 |
+
indices=indices, latexs=latexs)
|
| 1364 |
+
|
| 1365 |
+
approved_orders = ('123', '231', '312', '132', '213', '321', '121',
|
| 1366 |
+
'131', '212', '232', '313', '323', '')
|
| 1367 |
+
rot_order = translate(str(rot_order), 'XYZxyz', '123123')
|
| 1368 |
+
rot_type = rot_type.upper()
|
| 1369 |
+
|
| 1370 |
+
if rot_order not in approved_orders:
|
| 1371 |
+
raise TypeError('The supplied order is not an approved type')
|
| 1372 |
+
|
| 1373 |
+
if rot_type == 'AXIS':
|
| 1374 |
+
newframe.orient_axis(self, amounts[1], amounts[0])
|
| 1375 |
+
|
| 1376 |
+
elif rot_type == 'DCM':
|
| 1377 |
+
newframe.orient_explicit(self, amounts)
|
| 1378 |
+
|
| 1379 |
+
elif rot_type == 'BODY':
|
| 1380 |
+
newframe.orient_body_fixed(self, amounts, rot_order)
|
| 1381 |
+
|
| 1382 |
+
elif rot_type == 'SPACE':
|
| 1383 |
+
newframe.orient_space_fixed(self, amounts, rot_order)
|
| 1384 |
+
|
| 1385 |
+
elif rot_type == 'QUATERNION':
|
| 1386 |
+
newframe.orient_quaternion(self, amounts)
|
| 1387 |
+
|
| 1388 |
+
else:
|
| 1389 |
+
raise NotImplementedError('That is not an implemented rotation')
|
| 1390 |
+
return newframe
|
| 1391 |
+
|
| 1392 |
+
def set_ang_acc(self, otherframe, value):
|
| 1393 |
+
"""Define the angular acceleration Vector in a ReferenceFrame.
|
| 1394 |
+
|
| 1395 |
+
Defines the angular acceleration of this ReferenceFrame, in another.
|
| 1396 |
+
Angular acceleration can be defined with respect to multiple different
|
| 1397 |
+
ReferenceFrames. Care must be taken to not create loops which are
|
| 1398 |
+
inconsistent.
|
| 1399 |
+
|
| 1400 |
+
Parameters
|
| 1401 |
+
==========
|
| 1402 |
+
|
| 1403 |
+
otherframe : ReferenceFrame
|
| 1404 |
+
A ReferenceFrame to define the angular acceleration in
|
| 1405 |
+
value : Vector
|
| 1406 |
+
The Vector representing angular acceleration
|
| 1407 |
+
|
| 1408 |
+
Examples
|
| 1409 |
+
========
|
| 1410 |
+
|
| 1411 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 1412 |
+
>>> N = ReferenceFrame('N')
|
| 1413 |
+
>>> A = ReferenceFrame('A')
|
| 1414 |
+
>>> V = 10 * N.x
|
| 1415 |
+
>>> A.set_ang_acc(N, V)
|
| 1416 |
+
>>> A.ang_acc_in(N)
|
| 1417 |
+
10*N.x
|
| 1418 |
+
|
| 1419 |
+
"""
|
| 1420 |
+
|
| 1421 |
+
if value == 0:
|
| 1422 |
+
value = Vector(0)
|
| 1423 |
+
value = _check_vector(value)
|
| 1424 |
+
_check_frame(otherframe)
|
| 1425 |
+
self._ang_acc_dict.update({otherframe: value})
|
| 1426 |
+
otherframe._ang_acc_dict.update({self: -value})
|
| 1427 |
+
|
| 1428 |
+
def set_ang_vel(self, otherframe, value):
|
| 1429 |
+
"""Define the angular velocity vector in a ReferenceFrame.
|
| 1430 |
+
|
| 1431 |
+
Defines the angular velocity of this ReferenceFrame, in another.
|
| 1432 |
+
Angular velocity can be defined with respect to multiple different
|
| 1433 |
+
ReferenceFrames. Care must be taken to not create loops which are
|
| 1434 |
+
inconsistent.
|
| 1435 |
+
|
| 1436 |
+
Parameters
|
| 1437 |
+
==========
|
| 1438 |
+
|
| 1439 |
+
otherframe : ReferenceFrame
|
| 1440 |
+
A ReferenceFrame to define the angular velocity in
|
| 1441 |
+
value : Vector
|
| 1442 |
+
The Vector representing angular velocity
|
| 1443 |
+
|
| 1444 |
+
Examples
|
| 1445 |
+
========
|
| 1446 |
+
|
| 1447 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 1448 |
+
>>> N = ReferenceFrame('N')
|
| 1449 |
+
>>> A = ReferenceFrame('A')
|
| 1450 |
+
>>> V = 10 * N.x
|
| 1451 |
+
>>> A.set_ang_vel(N, V)
|
| 1452 |
+
>>> A.ang_vel_in(N)
|
| 1453 |
+
10*N.x
|
| 1454 |
+
|
| 1455 |
+
"""
|
| 1456 |
+
|
| 1457 |
+
if value == 0:
|
| 1458 |
+
value = Vector(0)
|
| 1459 |
+
value = _check_vector(value)
|
| 1460 |
+
_check_frame(otherframe)
|
| 1461 |
+
self._ang_vel_dict.update({otherframe: value})
|
| 1462 |
+
otherframe._ang_vel_dict.update({self: -value})
|
| 1463 |
+
|
| 1464 |
+
@property
|
| 1465 |
+
def x(self):
|
| 1466 |
+
"""The basis Vector for the ReferenceFrame, in the x direction. """
|
| 1467 |
+
return self._x
|
| 1468 |
+
|
| 1469 |
+
@property
|
| 1470 |
+
def y(self):
|
| 1471 |
+
"""The basis Vector for the ReferenceFrame, in the y direction. """
|
| 1472 |
+
return self._y
|
| 1473 |
+
|
| 1474 |
+
@property
|
| 1475 |
+
def z(self):
|
| 1476 |
+
"""The basis Vector for the ReferenceFrame, in the z direction. """
|
| 1477 |
+
return self._z
|
| 1478 |
+
|
| 1479 |
+
@property
|
| 1480 |
+
def xx(self):
|
| 1481 |
+
"""Unit dyad of basis Vectors x and x for the ReferenceFrame."""
|
| 1482 |
+
return Vector.outer(self.x, self.x)
|
| 1483 |
+
|
| 1484 |
+
@property
|
| 1485 |
+
def xy(self):
|
| 1486 |
+
"""Unit dyad of basis Vectors x and y for the ReferenceFrame."""
|
| 1487 |
+
return Vector.outer(self.x, self.y)
|
| 1488 |
+
|
| 1489 |
+
@property
|
| 1490 |
+
def xz(self):
|
| 1491 |
+
"""Unit dyad of basis Vectors x and z for the ReferenceFrame."""
|
| 1492 |
+
return Vector.outer(self.x, self.z)
|
| 1493 |
+
|
| 1494 |
+
@property
|
| 1495 |
+
def yx(self):
|
| 1496 |
+
"""Unit dyad of basis Vectors y and x for the ReferenceFrame."""
|
| 1497 |
+
return Vector.outer(self.y, self.x)
|
| 1498 |
+
|
| 1499 |
+
@property
|
| 1500 |
+
def yy(self):
|
| 1501 |
+
"""Unit dyad of basis Vectors y and y for the ReferenceFrame."""
|
| 1502 |
+
return Vector.outer(self.y, self.y)
|
| 1503 |
+
|
| 1504 |
+
@property
|
| 1505 |
+
def yz(self):
|
| 1506 |
+
"""Unit dyad of basis Vectors y and z for the ReferenceFrame."""
|
| 1507 |
+
return Vector.outer(self.y, self.z)
|
| 1508 |
+
|
| 1509 |
+
@property
|
| 1510 |
+
def zx(self):
|
| 1511 |
+
"""Unit dyad of basis Vectors z and x for the ReferenceFrame."""
|
| 1512 |
+
return Vector.outer(self.z, self.x)
|
| 1513 |
+
|
| 1514 |
+
@property
|
| 1515 |
+
def zy(self):
|
| 1516 |
+
"""Unit dyad of basis Vectors z and y for the ReferenceFrame."""
|
| 1517 |
+
return Vector.outer(self.z, self.y)
|
| 1518 |
+
|
| 1519 |
+
@property
|
| 1520 |
+
def zz(self):
|
| 1521 |
+
"""Unit dyad of basis Vectors z and z for the ReferenceFrame."""
|
| 1522 |
+
return Vector.outer(self.z, self.z)
|
| 1523 |
+
|
| 1524 |
+
@property
|
| 1525 |
+
def u(self):
|
| 1526 |
+
"""Unit dyadic for the ReferenceFrame."""
|
| 1527 |
+
return self.xx + self.yy + self.zz
|
| 1528 |
+
|
| 1529 |
+
def partial_velocity(self, frame, *gen_speeds):
|
| 1530 |
+
"""Returns the partial angular velocities of this frame in the given
|
| 1531 |
+
frame with respect to one or more provided generalized speeds.
|
| 1532 |
+
|
| 1533 |
+
Parameters
|
| 1534 |
+
==========
|
| 1535 |
+
frame : ReferenceFrame
|
| 1536 |
+
The frame with which the angular velocity is defined in.
|
| 1537 |
+
gen_speeds : functions of time
|
| 1538 |
+
The generalized speeds.
|
| 1539 |
+
|
| 1540 |
+
Returns
|
| 1541 |
+
=======
|
| 1542 |
+
partial_velocities : tuple of Vector
|
| 1543 |
+
The partial angular velocity vectors corresponding to the provided
|
| 1544 |
+
generalized speeds.
|
| 1545 |
+
|
| 1546 |
+
Examples
|
| 1547 |
+
========
|
| 1548 |
+
|
| 1549 |
+
>>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
|
| 1550 |
+
>>> N = ReferenceFrame('N')
|
| 1551 |
+
>>> A = ReferenceFrame('A')
|
| 1552 |
+
>>> u1, u2 = dynamicsymbols('u1, u2')
|
| 1553 |
+
>>> A.set_ang_vel(N, u1 * A.x + u2 * N.y)
|
| 1554 |
+
>>> A.partial_velocity(N, u1)
|
| 1555 |
+
A.x
|
| 1556 |
+
>>> A.partial_velocity(N, u1, u2)
|
| 1557 |
+
(A.x, N.y)
|
| 1558 |
+
|
| 1559 |
+
"""
|
| 1560 |
+
|
| 1561 |
+
from sympy.physics.vector.functions import partial_velocity
|
| 1562 |
+
|
| 1563 |
+
vel = self.ang_vel_in(frame)
|
| 1564 |
+
partials = partial_velocity([vel], gen_speeds, frame)[0]
|
| 1565 |
+
|
| 1566 |
+
if len(partials) == 1:
|
| 1567 |
+
return partials[0]
|
| 1568 |
+
else:
|
| 1569 |
+
return tuple(partials)
|
| 1570 |
+
|
| 1571 |
+
|
| 1572 |
+
def _check_frame(other):
|
| 1573 |
+
from .vector import VectorTypeError
|
| 1574 |
+
if not isinstance(other, ReferenceFrame):
|
| 1575 |
+
raise VectorTypeError(other, ReferenceFrame('A'))
|
venv/lib/python3.11/site-packages/sympy/physics/vector/functions.py
ADDED
|
@@ -0,0 +1,650 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from functools import reduce
|
| 2 |
+
|
| 3 |
+
from sympy import (sympify, diff, sin, cos, Matrix, symbols,
|
| 4 |
+
Function, S, Symbol, linear_eq_to_matrix)
|
| 5 |
+
from sympy.integrals.integrals import integrate
|
| 6 |
+
from sympy.simplify.trigsimp import trigsimp
|
| 7 |
+
from .vector import Vector, _check_vector
|
| 8 |
+
from .frame import CoordinateSym, _check_frame
|
| 9 |
+
from .dyadic import Dyadic
|
| 10 |
+
from .printing import vprint, vsprint, vpprint, vlatex, init_vprinting
|
| 11 |
+
from sympy.utilities.iterables import iterable
|
| 12 |
+
from sympy.utilities.misc import translate
|
| 13 |
+
|
| 14 |
+
__all__ = ['cross', 'dot', 'express', 'time_derivative', 'outer',
|
| 15 |
+
'kinematic_equations', 'get_motion_params', 'partial_velocity',
|
| 16 |
+
'dynamicsymbols', 'vprint', 'vsprint', 'vpprint', 'vlatex',
|
| 17 |
+
'init_vprinting']
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def cross(vec1, vec2):
|
| 21 |
+
"""Cross product convenience wrapper for Vector.cross(): \n"""
|
| 22 |
+
if not isinstance(vec1, (Vector, Dyadic)):
|
| 23 |
+
raise TypeError('Cross product is between two vectors')
|
| 24 |
+
return vec1 ^ vec2
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
cross.__doc__ += Vector.cross.__doc__ # type: ignore
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def dot(vec1, vec2):
|
| 31 |
+
"""Dot product convenience wrapper for Vector.dot(): \n"""
|
| 32 |
+
if not isinstance(vec1, (Vector, Dyadic)):
|
| 33 |
+
raise TypeError('Dot product is between two vectors')
|
| 34 |
+
return vec1 & vec2
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
dot.__doc__ += Vector.dot.__doc__ # type: ignore
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def express(expr, frame, frame2=None, variables=False):
|
| 41 |
+
"""
|
| 42 |
+
Global function for 'express' functionality.
|
| 43 |
+
|
| 44 |
+
Re-expresses a Vector, scalar(sympyfiable) or Dyadic in given frame.
|
| 45 |
+
|
| 46 |
+
Refer to the local methods of Vector and Dyadic for details.
|
| 47 |
+
If 'variables' is True, then the coordinate variables (CoordinateSym
|
| 48 |
+
instances) of other frames present in the vector/scalar field or
|
| 49 |
+
dyadic expression are also substituted in terms of the base scalars of
|
| 50 |
+
this frame.
|
| 51 |
+
|
| 52 |
+
Parameters
|
| 53 |
+
==========
|
| 54 |
+
|
| 55 |
+
expr : Vector/Dyadic/scalar(sympyfiable)
|
| 56 |
+
The expression to re-express in ReferenceFrame 'frame'
|
| 57 |
+
|
| 58 |
+
frame: ReferenceFrame
|
| 59 |
+
The reference frame to express expr in
|
| 60 |
+
|
| 61 |
+
frame2 : ReferenceFrame
|
| 62 |
+
The other frame required for re-expression(only for Dyadic expr)
|
| 63 |
+
|
| 64 |
+
variables : boolean
|
| 65 |
+
Specifies whether to substitute the coordinate variables present
|
| 66 |
+
in expr, in terms of those of frame
|
| 67 |
+
|
| 68 |
+
Examples
|
| 69 |
+
========
|
| 70 |
+
|
| 71 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
|
| 72 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 73 |
+
>>> init_vprinting(pretty_print=False)
|
| 74 |
+
>>> N = ReferenceFrame('N')
|
| 75 |
+
>>> q = dynamicsymbols('q')
|
| 76 |
+
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
| 77 |
+
>>> d = outer(N.x, N.x)
|
| 78 |
+
>>> from sympy.physics.vector import express
|
| 79 |
+
>>> express(d, B, N)
|
| 80 |
+
cos(q)*(B.x|N.x) - sin(q)*(B.y|N.x)
|
| 81 |
+
>>> express(B.x, N)
|
| 82 |
+
cos(q)*N.x + sin(q)*N.y
|
| 83 |
+
>>> express(N[0], B, variables=True)
|
| 84 |
+
B_x*cos(q) - B_y*sin(q)
|
| 85 |
+
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
_check_frame(frame)
|
| 89 |
+
|
| 90 |
+
if expr == 0:
|
| 91 |
+
return expr
|
| 92 |
+
|
| 93 |
+
if isinstance(expr, Vector):
|
| 94 |
+
# Given expr is a Vector
|
| 95 |
+
if variables:
|
| 96 |
+
# If variables attribute is True, substitute the coordinate
|
| 97 |
+
# variables in the Vector
|
| 98 |
+
frame_list = [x[-1] for x in expr.args]
|
| 99 |
+
subs_dict = {}
|
| 100 |
+
for f in frame_list:
|
| 101 |
+
subs_dict.update(f.variable_map(frame))
|
| 102 |
+
expr = expr.subs(subs_dict)
|
| 103 |
+
# Re-express in this frame
|
| 104 |
+
outvec = Vector([])
|
| 105 |
+
for v in expr.args:
|
| 106 |
+
if v[1] != frame:
|
| 107 |
+
temp = frame.dcm(v[1]) * v[0]
|
| 108 |
+
if Vector.simp:
|
| 109 |
+
temp = temp.applyfunc(lambda x:
|
| 110 |
+
trigsimp(x, method='fu'))
|
| 111 |
+
outvec += Vector([(temp, frame)])
|
| 112 |
+
else:
|
| 113 |
+
outvec += Vector([v])
|
| 114 |
+
return outvec
|
| 115 |
+
|
| 116 |
+
if isinstance(expr, Dyadic):
|
| 117 |
+
if frame2 is None:
|
| 118 |
+
frame2 = frame
|
| 119 |
+
_check_frame(frame2)
|
| 120 |
+
ol = Dyadic(0)
|
| 121 |
+
for v in expr.args:
|
| 122 |
+
ol += express(v[0], frame, variables=variables) * \
|
| 123 |
+
(express(v[1], frame, variables=variables) |
|
| 124 |
+
express(v[2], frame2, variables=variables))
|
| 125 |
+
return ol
|
| 126 |
+
|
| 127 |
+
else:
|
| 128 |
+
if variables:
|
| 129 |
+
# Given expr is a scalar field
|
| 130 |
+
frame_set = set()
|
| 131 |
+
expr = sympify(expr)
|
| 132 |
+
# Substitute all the coordinate variables
|
| 133 |
+
for x in expr.free_symbols:
|
| 134 |
+
if isinstance(x, CoordinateSym) and x.frame != frame:
|
| 135 |
+
frame_set.add(x.frame)
|
| 136 |
+
subs_dict = {}
|
| 137 |
+
for f in frame_set:
|
| 138 |
+
subs_dict.update(f.variable_map(frame))
|
| 139 |
+
return expr.subs(subs_dict)
|
| 140 |
+
return expr
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def time_derivative(expr, frame, order=1):
|
| 144 |
+
"""
|
| 145 |
+
Calculate the time derivative of a vector/scalar field function
|
| 146 |
+
or dyadic expression in given frame.
|
| 147 |
+
|
| 148 |
+
References
|
| 149 |
+
==========
|
| 150 |
+
|
| 151 |
+
https://en.wikipedia.org/wiki/Rotating_reference_frame#Time_derivatives_in_the_two_frames
|
| 152 |
+
|
| 153 |
+
Parameters
|
| 154 |
+
==========
|
| 155 |
+
|
| 156 |
+
expr : Vector/Dyadic/sympifyable
|
| 157 |
+
The expression whose time derivative is to be calculated
|
| 158 |
+
|
| 159 |
+
frame : ReferenceFrame
|
| 160 |
+
The reference frame to calculate the time derivative in
|
| 161 |
+
|
| 162 |
+
order : integer
|
| 163 |
+
The order of the derivative to be calculated
|
| 164 |
+
|
| 165 |
+
Examples
|
| 166 |
+
========
|
| 167 |
+
|
| 168 |
+
>>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
|
| 169 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 170 |
+
>>> init_vprinting(pretty_print=False)
|
| 171 |
+
>>> from sympy import Symbol
|
| 172 |
+
>>> q1 = Symbol('q1')
|
| 173 |
+
>>> u1 = dynamicsymbols('u1')
|
| 174 |
+
>>> N = ReferenceFrame('N')
|
| 175 |
+
>>> A = N.orientnew('A', 'Axis', [q1, N.x])
|
| 176 |
+
>>> v = u1 * N.x
|
| 177 |
+
>>> A.set_ang_vel(N, 10*A.x)
|
| 178 |
+
>>> from sympy.physics.vector import time_derivative
|
| 179 |
+
>>> time_derivative(v, N)
|
| 180 |
+
u1'*N.x
|
| 181 |
+
>>> time_derivative(u1*A[0], N)
|
| 182 |
+
N_x*u1'
|
| 183 |
+
>>> B = N.orientnew('B', 'Axis', [u1, N.z])
|
| 184 |
+
>>> from sympy.physics.vector import outer
|
| 185 |
+
>>> d = outer(N.x, N.x)
|
| 186 |
+
>>> time_derivative(d, B)
|
| 187 |
+
- u1'*(N.y|N.x) - u1'*(N.x|N.y)
|
| 188 |
+
|
| 189 |
+
"""
|
| 190 |
+
|
| 191 |
+
t = dynamicsymbols._t
|
| 192 |
+
_check_frame(frame)
|
| 193 |
+
|
| 194 |
+
if order == 0:
|
| 195 |
+
return expr
|
| 196 |
+
if order % 1 != 0 or order < 0:
|
| 197 |
+
raise ValueError("Unsupported value of order entered")
|
| 198 |
+
|
| 199 |
+
if isinstance(expr, Vector):
|
| 200 |
+
outlist = []
|
| 201 |
+
for v in expr.args:
|
| 202 |
+
if v[1] == frame:
|
| 203 |
+
outlist += [(express(v[0], frame, variables=True).diff(t),
|
| 204 |
+
frame)]
|
| 205 |
+
else:
|
| 206 |
+
outlist += (time_derivative(Vector([v]), v[1]) +
|
| 207 |
+
(v[1].ang_vel_in(frame) ^ Vector([v]))).args
|
| 208 |
+
outvec = Vector(outlist)
|
| 209 |
+
return time_derivative(outvec, frame, order - 1)
|
| 210 |
+
|
| 211 |
+
if isinstance(expr, Dyadic):
|
| 212 |
+
ol = Dyadic(0)
|
| 213 |
+
for v in expr.args:
|
| 214 |
+
ol += (v[0].diff(t) * (v[1] | v[2]))
|
| 215 |
+
ol += (v[0] * (time_derivative(v[1], frame) | v[2]))
|
| 216 |
+
ol += (v[0] * (v[1] | time_derivative(v[2], frame)))
|
| 217 |
+
return time_derivative(ol, frame, order - 1)
|
| 218 |
+
|
| 219 |
+
else:
|
| 220 |
+
return diff(express(expr, frame, variables=True), t, order)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def outer(vec1, vec2):
|
| 224 |
+
"""Outer product convenience wrapper for Vector.outer():\n"""
|
| 225 |
+
if not isinstance(vec1, Vector):
|
| 226 |
+
raise TypeError('Outer product is between two Vectors')
|
| 227 |
+
return vec1.outer(vec2)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
outer.__doc__ += Vector.outer.__doc__ # type: ignore
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def kinematic_equations(speeds, coords, rot_type, rot_order=''):
|
| 234 |
+
"""Gives equations relating the qdot's to u's for a rotation type.
|
| 235 |
+
|
| 236 |
+
Supply rotation type and order as in orient. Speeds are assumed to be
|
| 237 |
+
body-fixed; if we are defining the orientation of B in A using by rot_type,
|
| 238 |
+
the angular velocity of B in A is assumed to be in the form: speed[0]*B.x +
|
| 239 |
+
speed[1]*B.y + speed[2]*B.z
|
| 240 |
+
|
| 241 |
+
Parameters
|
| 242 |
+
==========
|
| 243 |
+
|
| 244 |
+
speeds : list of length 3
|
| 245 |
+
The body fixed angular velocity measure numbers.
|
| 246 |
+
coords : list of length 3 or 4
|
| 247 |
+
The coordinates used to define the orientation of the two frames.
|
| 248 |
+
rot_type : str
|
| 249 |
+
The type of rotation used to create the equations. Body, Space, or
|
| 250 |
+
Quaternion only
|
| 251 |
+
rot_order : str or int
|
| 252 |
+
If applicable, the order of a series of rotations.
|
| 253 |
+
|
| 254 |
+
Examples
|
| 255 |
+
========
|
| 256 |
+
|
| 257 |
+
>>> from sympy.physics.vector import dynamicsymbols
|
| 258 |
+
>>> from sympy.physics.vector import kinematic_equations, vprint
|
| 259 |
+
>>> u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
| 260 |
+
>>> q1, q2, q3 = dynamicsymbols('q1 q2 q3')
|
| 261 |
+
>>> vprint(kinematic_equations([u1,u2,u3], [q1,q2,q3], 'body', '313'),
|
| 262 |
+
... order=None)
|
| 263 |
+
[-(u1*sin(q3) + u2*cos(q3))/sin(q2) + q1', -u1*cos(q3) + u2*sin(q3) + q2', (u1*sin(q3) + u2*cos(q3))*cos(q2)/sin(q2) - u3 + q3']
|
| 264 |
+
|
| 265 |
+
"""
|
| 266 |
+
|
| 267 |
+
# Code below is checking and sanitizing input
|
| 268 |
+
approved_orders = ('123', '231', '312', '132', '213', '321', '121', '131',
|
| 269 |
+
'212', '232', '313', '323', '1', '2', '3', '')
|
| 270 |
+
# make sure XYZ => 123 and rot_type is in lower case
|
| 271 |
+
rot_order = translate(str(rot_order), 'XYZxyz', '123123')
|
| 272 |
+
rot_type = rot_type.lower()
|
| 273 |
+
|
| 274 |
+
if not isinstance(speeds, (list, tuple)):
|
| 275 |
+
raise TypeError('Need to supply speeds in a list')
|
| 276 |
+
if len(speeds) != 3:
|
| 277 |
+
raise TypeError('Need to supply 3 body-fixed speeds')
|
| 278 |
+
if not isinstance(coords, (list, tuple)):
|
| 279 |
+
raise TypeError('Need to supply coordinates in a list')
|
| 280 |
+
if rot_type in ['body', 'space']:
|
| 281 |
+
if rot_order not in approved_orders:
|
| 282 |
+
raise ValueError('Not an acceptable rotation order')
|
| 283 |
+
if len(coords) != 3:
|
| 284 |
+
raise ValueError('Need 3 coordinates for body or space')
|
| 285 |
+
# Actual hard-coded kinematic differential equations
|
| 286 |
+
w1, w2, w3 = speeds
|
| 287 |
+
if w1 == w2 == w3 == 0:
|
| 288 |
+
return [S.Zero]*3
|
| 289 |
+
q1, q2, q3 = coords
|
| 290 |
+
q1d, q2d, q3d = [diff(i, dynamicsymbols._t) for i in coords]
|
| 291 |
+
s1, s2, s3 = [sin(q1), sin(q2), sin(q3)]
|
| 292 |
+
c1, c2, c3 = [cos(q1), cos(q2), cos(q3)]
|
| 293 |
+
if rot_type == 'body':
|
| 294 |
+
if rot_order == '123':
|
| 295 |
+
return [q1d - (w1 * c3 - w2 * s3) / c2, q2d - w1 * s3 - w2 *
|
| 296 |
+
c3, q3d - (-w1 * c3 + w2 * s3) * s2 / c2 - w3]
|
| 297 |
+
if rot_order == '231':
|
| 298 |
+
return [q1d - (w2 * c3 - w3 * s3) / c2, q2d - w2 * s3 - w3 *
|
| 299 |
+
c3, q3d - w1 - (- w2 * c3 + w3 * s3) * s2 / c2]
|
| 300 |
+
if rot_order == '312':
|
| 301 |
+
return [q1d - (-w1 * s3 + w3 * c3) / c2, q2d - w1 * c3 - w3 *
|
| 302 |
+
s3, q3d - (w1 * s3 - w3 * c3) * s2 / c2 - w2]
|
| 303 |
+
if rot_order == '132':
|
| 304 |
+
return [q1d - (w1 * c3 + w3 * s3) / c2, q2d + w1 * s3 - w3 *
|
| 305 |
+
c3, q3d - (w1 * c3 + w3 * s3) * s2 / c2 - w2]
|
| 306 |
+
if rot_order == '213':
|
| 307 |
+
return [q1d - (w1 * s3 + w2 * c3) / c2, q2d - w1 * c3 + w2 *
|
| 308 |
+
s3, q3d - (w1 * s3 + w2 * c3) * s2 / c2 - w3]
|
| 309 |
+
if rot_order == '321':
|
| 310 |
+
return [q1d - (w2 * s3 + w3 * c3) / c2, q2d - w2 * c3 + w3 *
|
| 311 |
+
s3, q3d - w1 - (w2 * s3 + w3 * c3) * s2 / c2]
|
| 312 |
+
if rot_order == '121':
|
| 313 |
+
return [q1d - (w2 * s3 + w3 * c3) / s2, q2d - w2 * c3 + w3 *
|
| 314 |
+
s3, q3d - w1 + (w2 * s3 + w3 * c3) * c2 / s2]
|
| 315 |
+
if rot_order == '131':
|
| 316 |
+
return [q1d - (-w2 * c3 + w3 * s3) / s2, q2d - w2 * s3 - w3 *
|
| 317 |
+
c3, q3d - w1 - (w2 * c3 - w3 * s3) * c2 / s2]
|
| 318 |
+
if rot_order == '212':
|
| 319 |
+
return [q1d - (w1 * s3 - w3 * c3) / s2, q2d - w1 * c3 - w3 *
|
| 320 |
+
s3, q3d - (-w1 * s3 + w3 * c3) * c2 / s2 - w2]
|
| 321 |
+
if rot_order == '232':
|
| 322 |
+
return [q1d - (w1 * c3 + w3 * s3) / s2, q2d + w1 * s3 - w3 *
|
| 323 |
+
c3, q3d + (w1 * c3 + w3 * s3) * c2 / s2 - w2]
|
| 324 |
+
if rot_order == '313':
|
| 325 |
+
return [q1d - (w1 * s3 + w2 * c3) / s2, q2d - w1 * c3 + w2 *
|
| 326 |
+
s3, q3d + (w1 * s3 + w2 * c3) * c2 / s2 - w3]
|
| 327 |
+
if rot_order == '323':
|
| 328 |
+
return [q1d - (-w1 * c3 + w2 * s3) / s2, q2d - w1 * s3 - w2 *
|
| 329 |
+
c3, q3d - (w1 * c3 - w2 * s3) * c2 / s2 - w3]
|
| 330 |
+
if rot_type == 'space':
|
| 331 |
+
if rot_order == '123':
|
| 332 |
+
return [q1d - w1 - (w2 * s1 + w3 * c1) * s2 / c2, q2d - w2 *
|
| 333 |
+
c1 + w3 * s1, q3d - (w2 * s1 + w3 * c1) / c2]
|
| 334 |
+
if rot_order == '231':
|
| 335 |
+
return [q1d - (w1 * c1 + w3 * s1) * s2 / c2 - w2, q2d + w1 *
|
| 336 |
+
s1 - w3 * c1, q3d - (w1 * c1 + w3 * s1) / c2]
|
| 337 |
+
if rot_order == '312':
|
| 338 |
+
return [q1d - (w1 * s1 + w2 * c1) * s2 / c2 - w3, q2d - w1 *
|
| 339 |
+
c1 + w2 * s1, q3d - (w1 * s1 + w2 * c1) / c2]
|
| 340 |
+
if rot_order == '132':
|
| 341 |
+
return [q1d - w1 - (-w2 * c1 + w3 * s1) * s2 / c2, q2d - w2 *
|
| 342 |
+
s1 - w3 * c1, q3d - (w2 * c1 - w3 * s1) / c2]
|
| 343 |
+
if rot_order == '213':
|
| 344 |
+
return [q1d - (w1 * s1 - w3 * c1) * s2 / c2 - w2, q2d - w1 *
|
| 345 |
+
c1 - w3 * s1, q3d - (-w1 * s1 + w3 * c1) / c2]
|
| 346 |
+
if rot_order == '321':
|
| 347 |
+
return [q1d - (-w1 * c1 + w2 * s1) * s2 / c2 - w3, q2d - w1 *
|
| 348 |
+
s1 - w2 * c1, q3d - (w1 * c1 - w2 * s1) / c2]
|
| 349 |
+
if rot_order == '121':
|
| 350 |
+
return [q1d - w1 + (w2 * s1 + w3 * c1) * c2 / s2, q2d - w2 *
|
| 351 |
+
c1 + w3 * s1, q3d - (w2 * s1 + w3 * c1) / s2]
|
| 352 |
+
if rot_order == '131':
|
| 353 |
+
return [q1d - w1 - (w2 * c1 - w3 * s1) * c2 / s2, q2d - w2 *
|
| 354 |
+
s1 - w3 * c1, q3d - (-w2 * c1 + w3 * s1) / s2]
|
| 355 |
+
if rot_order == '212':
|
| 356 |
+
return [q1d - (-w1 * s1 + w3 * c1) * c2 / s2 - w2, q2d - w1 *
|
| 357 |
+
c1 - w3 * s1, q3d - (w1 * s1 - w3 * c1) / s2]
|
| 358 |
+
if rot_order == '232':
|
| 359 |
+
return [q1d + (w1 * c1 + w3 * s1) * c2 / s2 - w2, q2d + w1 *
|
| 360 |
+
s1 - w3 * c1, q3d - (w1 * c1 + w3 * s1) / s2]
|
| 361 |
+
if rot_order == '313':
|
| 362 |
+
return [q1d + (w1 * s1 + w2 * c1) * c2 / s2 - w3, q2d - w1 *
|
| 363 |
+
c1 + w2 * s1, q3d - (w1 * s1 + w2 * c1) / s2]
|
| 364 |
+
if rot_order == '323':
|
| 365 |
+
return [q1d - (w1 * c1 - w2 * s1) * c2 / s2 - w3, q2d - w1 *
|
| 366 |
+
s1 - w2 * c1, q3d - (-w1 * c1 + w2 * s1) / s2]
|
| 367 |
+
elif rot_type == 'quaternion':
|
| 368 |
+
if rot_order != '':
|
| 369 |
+
raise ValueError('Cannot have rotation order for quaternion')
|
| 370 |
+
if len(coords) != 4:
|
| 371 |
+
raise ValueError('Need 4 coordinates for quaternion')
|
| 372 |
+
# Actual hard-coded kinematic differential equations
|
| 373 |
+
e0, e1, e2, e3 = coords
|
| 374 |
+
w = Matrix(speeds + [0])
|
| 375 |
+
E = Matrix([[e0, -e3, e2, e1],
|
| 376 |
+
[e3, e0, -e1, e2],
|
| 377 |
+
[-e2, e1, e0, e3],
|
| 378 |
+
[-e1, -e2, -e3, e0]])
|
| 379 |
+
edots = Matrix([diff(i, dynamicsymbols._t) for i in [e1, e2, e3, e0]])
|
| 380 |
+
return list(edots.T - 0.5 * w.T * E.T)
|
| 381 |
+
else:
|
| 382 |
+
raise ValueError('Not an approved rotation type for this function')
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
def get_motion_params(frame, **kwargs):
|
| 386 |
+
"""
|
| 387 |
+
Returns the three motion parameters - (acceleration, velocity, and
|
| 388 |
+
position) as vectorial functions of time in the given frame.
|
| 389 |
+
|
| 390 |
+
If a higher order differential function is provided, the lower order
|
| 391 |
+
functions are used as boundary conditions. For example, given the
|
| 392 |
+
acceleration, the velocity and position parameters are taken as
|
| 393 |
+
boundary conditions.
|
| 394 |
+
|
| 395 |
+
The values of time at which the boundary conditions are specified
|
| 396 |
+
are taken from timevalue1(for position boundary condition) and
|
| 397 |
+
timevalue2(for velocity boundary condition).
|
| 398 |
+
|
| 399 |
+
If any of the boundary conditions are not provided, they are taken
|
| 400 |
+
to be zero by default (zero vectors, in case of vectorial inputs). If
|
| 401 |
+
the boundary conditions are also functions of time, they are converted
|
| 402 |
+
to constants by substituting the time values in the dynamicsymbols._t
|
| 403 |
+
time Symbol.
|
| 404 |
+
|
| 405 |
+
This function can also be used for calculating rotational motion
|
| 406 |
+
parameters. Have a look at the Parameters and Examples for more clarity.
|
| 407 |
+
|
| 408 |
+
Parameters
|
| 409 |
+
==========
|
| 410 |
+
|
| 411 |
+
frame : ReferenceFrame
|
| 412 |
+
The frame to express the motion parameters in
|
| 413 |
+
|
| 414 |
+
acceleration : Vector
|
| 415 |
+
Acceleration of the object/frame as a function of time
|
| 416 |
+
|
| 417 |
+
velocity : Vector
|
| 418 |
+
Velocity as function of time or as boundary condition
|
| 419 |
+
of velocity at time = timevalue1
|
| 420 |
+
|
| 421 |
+
position : Vector
|
| 422 |
+
Velocity as function of time or as boundary condition
|
| 423 |
+
of velocity at time = timevalue1
|
| 424 |
+
|
| 425 |
+
timevalue1 : sympyfiable
|
| 426 |
+
Value of time for position boundary condition
|
| 427 |
+
|
| 428 |
+
timevalue2 : sympyfiable
|
| 429 |
+
Value of time for velocity boundary condition
|
| 430 |
+
|
| 431 |
+
Examples
|
| 432 |
+
========
|
| 433 |
+
|
| 434 |
+
>>> from sympy.physics.vector import ReferenceFrame, get_motion_params, dynamicsymbols
|
| 435 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 436 |
+
>>> init_vprinting(pretty_print=False)
|
| 437 |
+
>>> from sympy import symbols
|
| 438 |
+
>>> R = ReferenceFrame('R')
|
| 439 |
+
>>> v1, v2, v3 = dynamicsymbols('v1 v2 v3')
|
| 440 |
+
>>> v = v1*R.x + v2*R.y + v3*R.z
|
| 441 |
+
>>> get_motion_params(R, position = v)
|
| 442 |
+
(v1''*R.x + v2''*R.y + v3''*R.z, v1'*R.x + v2'*R.y + v3'*R.z, v1*R.x + v2*R.y + v3*R.z)
|
| 443 |
+
>>> a, b, c = symbols('a b c')
|
| 444 |
+
>>> v = a*R.x + b*R.y + c*R.z
|
| 445 |
+
>>> get_motion_params(R, velocity = v)
|
| 446 |
+
(0, a*R.x + b*R.y + c*R.z, a*t*R.x + b*t*R.y + c*t*R.z)
|
| 447 |
+
>>> parameters = get_motion_params(R, acceleration = v)
|
| 448 |
+
>>> parameters[1]
|
| 449 |
+
a*t*R.x + b*t*R.y + c*t*R.z
|
| 450 |
+
>>> parameters[2]
|
| 451 |
+
a*t**2/2*R.x + b*t**2/2*R.y + c*t**2/2*R.z
|
| 452 |
+
|
| 453 |
+
"""
|
| 454 |
+
|
| 455 |
+
def _process_vector_differential(vectdiff, condition, variable, ordinate,
|
| 456 |
+
frame):
|
| 457 |
+
"""
|
| 458 |
+
Helper function for get_motion methods. Finds derivative of vectdiff
|
| 459 |
+
wrt variable, and its integral using the specified boundary condition
|
| 460 |
+
at value of variable = ordinate.
|
| 461 |
+
Returns a tuple of - (derivative, function and integral) wrt vectdiff
|
| 462 |
+
|
| 463 |
+
"""
|
| 464 |
+
|
| 465 |
+
# Make sure boundary condition is independent of 'variable'
|
| 466 |
+
if condition != 0:
|
| 467 |
+
condition = express(condition, frame, variables=True)
|
| 468 |
+
# Special case of vectdiff == 0
|
| 469 |
+
if vectdiff == Vector(0):
|
| 470 |
+
return (0, 0, condition)
|
| 471 |
+
# Express vectdiff completely in condition's frame to give vectdiff1
|
| 472 |
+
vectdiff1 = express(vectdiff, frame)
|
| 473 |
+
# Find derivative of vectdiff
|
| 474 |
+
vectdiff2 = time_derivative(vectdiff, frame)
|
| 475 |
+
# Integrate and use boundary condition
|
| 476 |
+
vectdiff0 = Vector(0)
|
| 477 |
+
lims = (variable, ordinate, variable)
|
| 478 |
+
for dim in frame:
|
| 479 |
+
function1 = vectdiff1.dot(dim)
|
| 480 |
+
abscissa = dim.dot(condition).subs({variable: ordinate})
|
| 481 |
+
# Indefinite integral of 'function1' wrt 'variable', using
|
| 482 |
+
# the given initial condition (ordinate, abscissa).
|
| 483 |
+
vectdiff0 += (integrate(function1, lims) + abscissa) * dim
|
| 484 |
+
# Return tuple
|
| 485 |
+
return (vectdiff2, vectdiff, vectdiff0)
|
| 486 |
+
|
| 487 |
+
_check_frame(frame)
|
| 488 |
+
# Decide mode of operation based on user's input
|
| 489 |
+
if 'acceleration' in kwargs:
|
| 490 |
+
mode = 2
|
| 491 |
+
elif 'velocity' in kwargs:
|
| 492 |
+
mode = 1
|
| 493 |
+
else:
|
| 494 |
+
mode = 0
|
| 495 |
+
# All the possible parameters in kwargs
|
| 496 |
+
# Not all are required for every case
|
| 497 |
+
# If not specified, set to default values(may or may not be used in
|
| 498 |
+
# calculations)
|
| 499 |
+
conditions = ['acceleration', 'velocity', 'position',
|
| 500 |
+
'timevalue', 'timevalue1', 'timevalue2']
|
| 501 |
+
for i, x in enumerate(conditions):
|
| 502 |
+
if x not in kwargs:
|
| 503 |
+
if i < 3:
|
| 504 |
+
kwargs[x] = Vector(0)
|
| 505 |
+
else:
|
| 506 |
+
kwargs[x] = S.Zero
|
| 507 |
+
elif i < 3:
|
| 508 |
+
_check_vector(kwargs[x])
|
| 509 |
+
else:
|
| 510 |
+
kwargs[x] = sympify(kwargs[x])
|
| 511 |
+
if mode == 2:
|
| 512 |
+
vel = _process_vector_differential(kwargs['acceleration'],
|
| 513 |
+
kwargs['velocity'],
|
| 514 |
+
dynamicsymbols._t,
|
| 515 |
+
kwargs['timevalue2'], frame)[2]
|
| 516 |
+
pos = _process_vector_differential(vel, kwargs['position'],
|
| 517 |
+
dynamicsymbols._t,
|
| 518 |
+
kwargs['timevalue1'], frame)[2]
|
| 519 |
+
return (kwargs['acceleration'], vel, pos)
|
| 520 |
+
elif mode == 1:
|
| 521 |
+
return _process_vector_differential(kwargs['velocity'],
|
| 522 |
+
kwargs['position'],
|
| 523 |
+
dynamicsymbols._t,
|
| 524 |
+
kwargs['timevalue1'], frame)
|
| 525 |
+
else:
|
| 526 |
+
vel = time_derivative(kwargs['position'], frame)
|
| 527 |
+
acc = time_derivative(vel, frame)
|
| 528 |
+
return (acc, vel, kwargs['position'])
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
def partial_velocity(vel_vecs, gen_speeds, frame):
|
| 532 |
+
"""Returns a list of partial velocities with respect to the provided
|
| 533 |
+
generalized speeds in the given reference frame for each of the supplied
|
| 534 |
+
velocity vectors.
|
| 535 |
+
|
| 536 |
+
The output is a list of lists. The outer list has a number of elements
|
| 537 |
+
equal to the number of supplied velocity vectors. The inner lists are, for
|
| 538 |
+
each velocity vector, the partial derivatives of that velocity vector with
|
| 539 |
+
respect to the generalized speeds supplied.
|
| 540 |
+
|
| 541 |
+
Parameters
|
| 542 |
+
==========
|
| 543 |
+
|
| 544 |
+
vel_vecs : iterable
|
| 545 |
+
An iterable of velocity vectors (angular or linear).
|
| 546 |
+
gen_speeds : iterable
|
| 547 |
+
An iterable of generalized speeds.
|
| 548 |
+
frame : ReferenceFrame
|
| 549 |
+
The reference frame that the partial derivatives are going to be taken
|
| 550 |
+
in.
|
| 551 |
+
|
| 552 |
+
Examples
|
| 553 |
+
========
|
| 554 |
+
|
| 555 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 556 |
+
>>> from sympy.physics.vector import dynamicsymbols
|
| 557 |
+
>>> from sympy.physics.vector import partial_velocity
|
| 558 |
+
>>> u = dynamicsymbols('u')
|
| 559 |
+
>>> N = ReferenceFrame('N')
|
| 560 |
+
>>> P = Point('P')
|
| 561 |
+
>>> P.set_vel(N, u * N.x)
|
| 562 |
+
>>> vel_vecs = [P.vel(N)]
|
| 563 |
+
>>> gen_speeds = [u]
|
| 564 |
+
>>> partial_velocity(vel_vecs, gen_speeds, N)
|
| 565 |
+
[[N.x]]
|
| 566 |
+
|
| 567 |
+
"""
|
| 568 |
+
|
| 569 |
+
if not iterable(vel_vecs):
|
| 570 |
+
raise TypeError('Velocity vectors must be contained in an iterable.')
|
| 571 |
+
|
| 572 |
+
if not iterable(gen_speeds):
|
| 573 |
+
raise TypeError('Generalized speeds must be contained in an iterable')
|
| 574 |
+
|
| 575 |
+
vec_partials = []
|
| 576 |
+
gen_speeds = list(gen_speeds)
|
| 577 |
+
for vel in vel_vecs:
|
| 578 |
+
partials = [Vector(0) for _ in gen_speeds]
|
| 579 |
+
for components, ref in vel.args:
|
| 580 |
+
mat, _ = linear_eq_to_matrix(components, gen_speeds)
|
| 581 |
+
for i in range(len(gen_speeds)):
|
| 582 |
+
for dim, direction in enumerate(ref):
|
| 583 |
+
if mat[dim, i] != 0:
|
| 584 |
+
partials[i] += direction * mat[dim, i]
|
| 585 |
+
|
| 586 |
+
vec_partials.append(partials)
|
| 587 |
+
|
| 588 |
+
return vec_partials
|
| 589 |
+
|
| 590 |
+
|
| 591 |
+
def dynamicsymbols(names, level=0, **assumptions):
|
| 592 |
+
"""Uses symbols and Function for functions of time.
|
| 593 |
+
|
| 594 |
+
Creates a SymPy UndefinedFunction, which is then initialized as a function
|
| 595 |
+
of a variable, the default being Symbol('t').
|
| 596 |
+
|
| 597 |
+
Parameters
|
| 598 |
+
==========
|
| 599 |
+
|
| 600 |
+
names : str
|
| 601 |
+
Names of the dynamic symbols you want to create; works the same way as
|
| 602 |
+
inputs to symbols
|
| 603 |
+
level : int
|
| 604 |
+
Level of differentiation of the returned function; d/dt once of t,
|
| 605 |
+
twice of t, etc.
|
| 606 |
+
assumptions :
|
| 607 |
+
- real(bool) : This is used to set the dynamicsymbol as real,
|
| 608 |
+
by default is False.
|
| 609 |
+
- positive(bool) : This is used to set the dynamicsymbol as positive,
|
| 610 |
+
by default is False.
|
| 611 |
+
- commutative(bool) : This is used to set the commutative property of
|
| 612 |
+
a dynamicsymbol, by default is True.
|
| 613 |
+
- integer(bool) : This is used to set the dynamicsymbol as integer,
|
| 614 |
+
by default is False.
|
| 615 |
+
|
| 616 |
+
Examples
|
| 617 |
+
========
|
| 618 |
+
|
| 619 |
+
>>> from sympy.physics.vector import dynamicsymbols
|
| 620 |
+
>>> from sympy import diff, Symbol
|
| 621 |
+
>>> q1 = dynamicsymbols('q1')
|
| 622 |
+
>>> q1
|
| 623 |
+
q1(t)
|
| 624 |
+
>>> q2 = dynamicsymbols('q2', real=True)
|
| 625 |
+
>>> q2.is_real
|
| 626 |
+
True
|
| 627 |
+
>>> q3 = dynamicsymbols('q3', positive=True)
|
| 628 |
+
>>> q3.is_positive
|
| 629 |
+
True
|
| 630 |
+
>>> q4, q5 = dynamicsymbols('q4,q5', commutative=False)
|
| 631 |
+
>>> bool(q4*q5 != q5*q4)
|
| 632 |
+
True
|
| 633 |
+
>>> q6 = dynamicsymbols('q6', integer=True)
|
| 634 |
+
>>> q6.is_integer
|
| 635 |
+
True
|
| 636 |
+
>>> diff(q1, Symbol('t'))
|
| 637 |
+
Derivative(q1(t), t)
|
| 638 |
+
|
| 639 |
+
"""
|
| 640 |
+
esses = symbols(names, cls=Function, **assumptions)
|
| 641 |
+
t = dynamicsymbols._t
|
| 642 |
+
if iterable(esses):
|
| 643 |
+
esses = [reduce(diff, [t] * level, e(t)) for e in esses]
|
| 644 |
+
return esses
|
| 645 |
+
else:
|
| 646 |
+
return reduce(diff, [t] * level, esses(t))
|
| 647 |
+
|
| 648 |
+
|
| 649 |
+
dynamicsymbols._t = Symbol('t') # type: ignore
|
| 650 |
+
dynamicsymbols._str = '\'' # type: ignore
|
venv/lib/python3.11/site-packages/sympy/physics/vector/point.py
ADDED
|
@@ -0,0 +1,635 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .vector import Vector, _check_vector
|
| 2 |
+
from .frame import _check_frame
|
| 3 |
+
from warnings import warn
|
| 4 |
+
from sympy.utilities.misc import filldedent
|
| 5 |
+
|
| 6 |
+
__all__ = ['Point']
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Point:
|
| 10 |
+
"""This object represents a point in a dynamic system.
|
| 11 |
+
|
| 12 |
+
It stores the: position, velocity, and acceleration of a point.
|
| 13 |
+
The position is a vector defined as the vector distance from a parent
|
| 14 |
+
point to this point.
|
| 15 |
+
|
| 16 |
+
Parameters
|
| 17 |
+
==========
|
| 18 |
+
|
| 19 |
+
name : string
|
| 20 |
+
The display name of the Point
|
| 21 |
+
|
| 22 |
+
Examples
|
| 23 |
+
========
|
| 24 |
+
|
| 25 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
| 26 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 27 |
+
>>> init_vprinting(pretty_print=False)
|
| 28 |
+
>>> N = ReferenceFrame('N')
|
| 29 |
+
>>> O = Point('O')
|
| 30 |
+
>>> P = Point('P')
|
| 31 |
+
>>> u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
| 32 |
+
>>> O.set_vel(N, u1 * N.x + u2 * N.y + u3 * N.z)
|
| 33 |
+
>>> O.acc(N)
|
| 34 |
+
u1'*N.x + u2'*N.y + u3'*N.z
|
| 35 |
+
|
| 36 |
+
``symbols()`` can be used to create multiple Points in a single step, for
|
| 37 |
+
example:
|
| 38 |
+
|
| 39 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
| 40 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 41 |
+
>>> init_vprinting(pretty_print=False)
|
| 42 |
+
>>> from sympy import symbols
|
| 43 |
+
>>> N = ReferenceFrame('N')
|
| 44 |
+
>>> u1, u2 = dynamicsymbols('u1 u2')
|
| 45 |
+
>>> A, B = symbols('A B', cls=Point)
|
| 46 |
+
>>> type(A)
|
| 47 |
+
<class 'sympy.physics.vector.point.Point'>
|
| 48 |
+
>>> A.set_vel(N, u1 * N.x + u2 * N.y)
|
| 49 |
+
>>> B.set_vel(N, u2 * N.x + u1 * N.y)
|
| 50 |
+
>>> A.acc(N) - B.acc(N)
|
| 51 |
+
(u1' - u2')*N.x + (-u1' + u2')*N.y
|
| 52 |
+
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
def __init__(self, name):
|
| 56 |
+
"""Initialization of a Point object. """
|
| 57 |
+
self.name = name
|
| 58 |
+
self._pos_dict = {}
|
| 59 |
+
self._vel_dict = {}
|
| 60 |
+
self._acc_dict = {}
|
| 61 |
+
self._pdlist = [self._pos_dict, self._vel_dict, self._acc_dict]
|
| 62 |
+
|
| 63 |
+
def __str__(self):
|
| 64 |
+
return self.name
|
| 65 |
+
|
| 66 |
+
__repr__ = __str__
|
| 67 |
+
|
| 68 |
+
def _check_point(self, other):
|
| 69 |
+
if not isinstance(other, Point):
|
| 70 |
+
raise TypeError('A Point must be supplied')
|
| 71 |
+
|
| 72 |
+
def _pdict_list(self, other, num):
|
| 73 |
+
"""Returns a list of points that gives the shortest path with respect
|
| 74 |
+
to position, velocity, or acceleration from this point to the provided
|
| 75 |
+
point.
|
| 76 |
+
|
| 77 |
+
Parameters
|
| 78 |
+
==========
|
| 79 |
+
other : Point
|
| 80 |
+
A point that may be related to this point by position, velocity, or
|
| 81 |
+
acceleration.
|
| 82 |
+
num : integer
|
| 83 |
+
0 for searching the position tree, 1 for searching the velocity
|
| 84 |
+
tree, and 2 for searching the acceleration tree.
|
| 85 |
+
|
| 86 |
+
Returns
|
| 87 |
+
=======
|
| 88 |
+
list of Points
|
| 89 |
+
A sequence of points from self to other.
|
| 90 |
+
|
| 91 |
+
Notes
|
| 92 |
+
=====
|
| 93 |
+
|
| 94 |
+
It is not clear if num = 1 or num = 2 actually works because the keys
|
| 95 |
+
to ``_vel_dict`` and ``_acc_dict`` are :class:`ReferenceFrame` objects
|
| 96 |
+
which do not have the ``_pdlist`` attribute.
|
| 97 |
+
|
| 98 |
+
"""
|
| 99 |
+
outlist = [[self]]
|
| 100 |
+
oldlist = [[]]
|
| 101 |
+
while outlist != oldlist:
|
| 102 |
+
oldlist = outlist.copy()
|
| 103 |
+
for v in outlist:
|
| 104 |
+
templist = v[-1]._pdlist[num].keys()
|
| 105 |
+
for v2 in templist:
|
| 106 |
+
if not v.__contains__(v2):
|
| 107 |
+
littletemplist = v + [v2]
|
| 108 |
+
if not outlist.__contains__(littletemplist):
|
| 109 |
+
outlist.append(littletemplist)
|
| 110 |
+
for v in oldlist:
|
| 111 |
+
if v[-1] != other:
|
| 112 |
+
outlist.remove(v)
|
| 113 |
+
outlist.sort(key=len)
|
| 114 |
+
if len(outlist) != 0:
|
| 115 |
+
return outlist[0]
|
| 116 |
+
raise ValueError('No Connecting Path found between ' + other.name +
|
| 117 |
+
' and ' + self.name)
|
| 118 |
+
|
| 119 |
+
def a1pt_theory(self, otherpoint, outframe, interframe):
|
| 120 |
+
"""Sets the acceleration of this point with the 1-point theory.
|
| 121 |
+
|
| 122 |
+
The 1-point theory for point acceleration looks like this:
|
| 123 |
+
|
| 124 |
+
^N a^P = ^B a^P + ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B
|
| 125 |
+
x r^OP) + 2 ^N omega^B x ^B v^P
|
| 126 |
+
|
| 127 |
+
where O is a point fixed in B, P is a point moving in B, and B is
|
| 128 |
+
rotating in frame N.
|
| 129 |
+
|
| 130 |
+
Parameters
|
| 131 |
+
==========
|
| 132 |
+
|
| 133 |
+
otherpoint : Point
|
| 134 |
+
The first point of the 1-point theory (O)
|
| 135 |
+
outframe : ReferenceFrame
|
| 136 |
+
The frame we want this point's acceleration defined in (N)
|
| 137 |
+
fixedframe : ReferenceFrame
|
| 138 |
+
The intermediate frame in this calculation (B)
|
| 139 |
+
|
| 140 |
+
Examples
|
| 141 |
+
========
|
| 142 |
+
|
| 143 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 144 |
+
>>> from sympy.physics.vector import dynamicsymbols
|
| 145 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 146 |
+
>>> init_vprinting(pretty_print=False)
|
| 147 |
+
>>> q = dynamicsymbols('q')
|
| 148 |
+
>>> q2 = dynamicsymbols('q2')
|
| 149 |
+
>>> qd = dynamicsymbols('q', 1)
|
| 150 |
+
>>> q2d = dynamicsymbols('q2', 1)
|
| 151 |
+
>>> N = ReferenceFrame('N')
|
| 152 |
+
>>> B = ReferenceFrame('B')
|
| 153 |
+
>>> B.set_ang_vel(N, 5 * B.y)
|
| 154 |
+
>>> O = Point('O')
|
| 155 |
+
>>> P = O.locatenew('P', q * B.x + q2 * B.y)
|
| 156 |
+
>>> P.set_vel(B, qd * B.x + q2d * B.y)
|
| 157 |
+
>>> O.set_vel(N, 0)
|
| 158 |
+
>>> P.a1pt_theory(O, N, B)
|
| 159 |
+
(-25*q + q'')*B.x + q2''*B.y - 10*q'*B.z
|
| 160 |
+
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
_check_frame(outframe)
|
| 164 |
+
_check_frame(interframe)
|
| 165 |
+
self._check_point(otherpoint)
|
| 166 |
+
dist = self.pos_from(otherpoint)
|
| 167 |
+
v = self.vel(interframe)
|
| 168 |
+
a1 = otherpoint.acc(outframe)
|
| 169 |
+
a2 = self.acc(interframe)
|
| 170 |
+
omega = interframe.ang_vel_in(outframe)
|
| 171 |
+
alpha = interframe.ang_acc_in(outframe)
|
| 172 |
+
self.set_acc(outframe, a2 + 2 * (omega.cross(v)) + a1 +
|
| 173 |
+
(alpha.cross(dist)) + (omega.cross(omega.cross(dist))))
|
| 174 |
+
return self.acc(outframe)
|
| 175 |
+
|
| 176 |
+
def a2pt_theory(self, otherpoint, outframe, fixedframe):
|
| 177 |
+
"""Sets the acceleration of this point with the 2-point theory.
|
| 178 |
+
|
| 179 |
+
The 2-point theory for point acceleration looks like this:
|
| 180 |
+
|
| 181 |
+
^N a^P = ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B x r^OP)
|
| 182 |
+
|
| 183 |
+
where O and P are both points fixed in frame B, which is rotating in
|
| 184 |
+
frame N.
|
| 185 |
+
|
| 186 |
+
Parameters
|
| 187 |
+
==========
|
| 188 |
+
|
| 189 |
+
otherpoint : Point
|
| 190 |
+
The first point of the 2-point theory (O)
|
| 191 |
+
outframe : ReferenceFrame
|
| 192 |
+
The frame we want this point's acceleration defined in (N)
|
| 193 |
+
fixedframe : ReferenceFrame
|
| 194 |
+
The frame in which both points are fixed (B)
|
| 195 |
+
|
| 196 |
+
Examples
|
| 197 |
+
========
|
| 198 |
+
|
| 199 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
| 200 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 201 |
+
>>> init_vprinting(pretty_print=False)
|
| 202 |
+
>>> q = dynamicsymbols('q')
|
| 203 |
+
>>> qd = dynamicsymbols('q', 1)
|
| 204 |
+
>>> N = ReferenceFrame('N')
|
| 205 |
+
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
| 206 |
+
>>> O = Point('O')
|
| 207 |
+
>>> P = O.locatenew('P', 10 * B.x)
|
| 208 |
+
>>> O.set_vel(N, 5 * N.x)
|
| 209 |
+
>>> P.a2pt_theory(O, N, B)
|
| 210 |
+
- 10*q'**2*B.x + 10*q''*B.y
|
| 211 |
+
|
| 212 |
+
"""
|
| 213 |
+
|
| 214 |
+
_check_frame(outframe)
|
| 215 |
+
_check_frame(fixedframe)
|
| 216 |
+
self._check_point(otherpoint)
|
| 217 |
+
dist = self.pos_from(otherpoint)
|
| 218 |
+
a = otherpoint.acc(outframe)
|
| 219 |
+
omega = fixedframe.ang_vel_in(outframe)
|
| 220 |
+
alpha = fixedframe.ang_acc_in(outframe)
|
| 221 |
+
self.set_acc(outframe, a + (alpha.cross(dist)) +
|
| 222 |
+
(omega.cross(omega.cross(dist))))
|
| 223 |
+
return self.acc(outframe)
|
| 224 |
+
|
| 225 |
+
def acc(self, frame):
|
| 226 |
+
"""The acceleration Vector of this Point in a ReferenceFrame.
|
| 227 |
+
|
| 228 |
+
Parameters
|
| 229 |
+
==========
|
| 230 |
+
|
| 231 |
+
frame : ReferenceFrame
|
| 232 |
+
The frame in which the returned acceleration vector will be defined
|
| 233 |
+
in.
|
| 234 |
+
|
| 235 |
+
Examples
|
| 236 |
+
========
|
| 237 |
+
|
| 238 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 239 |
+
>>> N = ReferenceFrame('N')
|
| 240 |
+
>>> p1 = Point('p1')
|
| 241 |
+
>>> p1.set_acc(N, 10 * N.x)
|
| 242 |
+
>>> p1.acc(N)
|
| 243 |
+
10*N.x
|
| 244 |
+
|
| 245 |
+
"""
|
| 246 |
+
|
| 247 |
+
_check_frame(frame)
|
| 248 |
+
if not (frame in self._acc_dict):
|
| 249 |
+
if self.vel(frame) != 0:
|
| 250 |
+
return (self._vel_dict[frame]).dt(frame)
|
| 251 |
+
else:
|
| 252 |
+
return Vector(0)
|
| 253 |
+
return self._acc_dict[frame]
|
| 254 |
+
|
| 255 |
+
def locatenew(self, name, value):
|
| 256 |
+
"""Creates a new point with a position defined from this point.
|
| 257 |
+
|
| 258 |
+
Parameters
|
| 259 |
+
==========
|
| 260 |
+
|
| 261 |
+
name : str
|
| 262 |
+
The name for the new point
|
| 263 |
+
value : Vector
|
| 264 |
+
The position of the new point relative to this point
|
| 265 |
+
|
| 266 |
+
Examples
|
| 267 |
+
========
|
| 268 |
+
|
| 269 |
+
>>> from sympy.physics.vector import ReferenceFrame, Point
|
| 270 |
+
>>> N = ReferenceFrame('N')
|
| 271 |
+
>>> P1 = Point('P1')
|
| 272 |
+
>>> P2 = P1.locatenew('P2', 10 * N.x)
|
| 273 |
+
|
| 274 |
+
"""
|
| 275 |
+
|
| 276 |
+
if not isinstance(name, str):
|
| 277 |
+
raise TypeError('Must supply a valid name')
|
| 278 |
+
if value == 0:
|
| 279 |
+
value = Vector(0)
|
| 280 |
+
value = _check_vector(value)
|
| 281 |
+
p = Point(name)
|
| 282 |
+
p.set_pos(self, value)
|
| 283 |
+
self.set_pos(p, -value)
|
| 284 |
+
return p
|
| 285 |
+
|
| 286 |
+
def pos_from(self, otherpoint):
|
| 287 |
+
"""Returns a Vector distance between this Point and the other Point.
|
| 288 |
+
|
| 289 |
+
Parameters
|
| 290 |
+
==========
|
| 291 |
+
|
| 292 |
+
otherpoint : Point
|
| 293 |
+
The otherpoint we are locating this one relative to
|
| 294 |
+
|
| 295 |
+
Examples
|
| 296 |
+
========
|
| 297 |
+
|
| 298 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 299 |
+
>>> N = ReferenceFrame('N')
|
| 300 |
+
>>> p1 = Point('p1')
|
| 301 |
+
>>> p2 = Point('p2')
|
| 302 |
+
>>> p1.set_pos(p2, 10 * N.x)
|
| 303 |
+
>>> p1.pos_from(p2)
|
| 304 |
+
10*N.x
|
| 305 |
+
|
| 306 |
+
"""
|
| 307 |
+
|
| 308 |
+
outvec = Vector(0)
|
| 309 |
+
plist = self._pdict_list(otherpoint, 0)
|
| 310 |
+
for i in range(len(plist) - 1):
|
| 311 |
+
outvec += plist[i]._pos_dict[plist[i + 1]]
|
| 312 |
+
return outvec
|
| 313 |
+
|
| 314 |
+
def set_acc(self, frame, value):
|
| 315 |
+
"""Used to set the acceleration of this Point in a ReferenceFrame.
|
| 316 |
+
|
| 317 |
+
Parameters
|
| 318 |
+
==========
|
| 319 |
+
|
| 320 |
+
frame : ReferenceFrame
|
| 321 |
+
The frame in which this point's acceleration is defined
|
| 322 |
+
value : Vector
|
| 323 |
+
The vector value of this point's acceleration in the frame
|
| 324 |
+
|
| 325 |
+
Examples
|
| 326 |
+
========
|
| 327 |
+
|
| 328 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 329 |
+
>>> N = ReferenceFrame('N')
|
| 330 |
+
>>> p1 = Point('p1')
|
| 331 |
+
>>> p1.set_acc(N, 10 * N.x)
|
| 332 |
+
>>> p1.acc(N)
|
| 333 |
+
10*N.x
|
| 334 |
+
|
| 335 |
+
"""
|
| 336 |
+
|
| 337 |
+
if value == 0:
|
| 338 |
+
value = Vector(0)
|
| 339 |
+
value = _check_vector(value)
|
| 340 |
+
_check_frame(frame)
|
| 341 |
+
self._acc_dict.update({frame: value})
|
| 342 |
+
|
| 343 |
+
def set_pos(self, otherpoint, value):
|
| 344 |
+
"""Used to set the position of this point w.r.t. another point.
|
| 345 |
+
|
| 346 |
+
Parameters
|
| 347 |
+
==========
|
| 348 |
+
|
| 349 |
+
otherpoint : Point
|
| 350 |
+
The other point which this point's location is defined relative to
|
| 351 |
+
value : Vector
|
| 352 |
+
The vector which defines the location of this point
|
| 353 |
+
|
| 354 |
+
Examples
|
| 355 |
+
========
|
| 356 |
+
|
| 357 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 358 |
+
>>> N = ReferenceFrame('N')
|
| 359 |
+
>>> p1 = Point('p1')
|
| 360 |
+
>>> p2 = Point('p2')
|
| 361 |
+
>>> p1.set_pos(p2, 10 * N.x)
|
| 362 |
+
>>> p1.pos_from(p2)
|
| 363 |
+
10*N.x
|
| 364 |
+
|
| 365 |
+
"""
|
| 366 |
+
|
| 367 |
+
if value == 0:
|
| 368 |
+
value = Vector(0)
|
| 369 |
+
value = _check_vector(value)
|
| 370 |
+
self._check_point(otherpoint)
|
| 371 |
+
self._pos_dict.update({otherpoint: value})
|
| 372 |
+
otherpoint._pos_dict.update({self: -value})
|
| 373 |
+
|
| 374 |
+
def set_vel(self, frame, value):
|
| 375 |
+
"""Sets the velocity Vector of this Point in a ReferenceFrame.
|
| 376 |
+
|
| 377 |
+
Parameters
|
| 378 |
+
==========
|
| 379 |
+
|
| 380 |
+
frame : ReferenceFrame
|
| 381 |
+
The frame in which this point's velocity is defined
|
| 382 |
+
value : Vector
|
| 383 |
+
The vector value of this point's velocity in the frame
|
| 384 |
+
|
| 385 |
+
Examples
|
| 386 |
+
========
|
| 387 |
+
|
| 388 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 389 |
+
>>> N = ReferenceFrame('N')
|
| 390 |
+
>>> p1 = Point('p1')
|
| 391 |
+
>>> p1.set_vel(N, 10 * N.x)
|
| 392 |
+
>>> p1.vel(N)
|
| 393 |
+
10*N.x
|
| 394 |
+
|
| 395 |
+
"""
|
| 396 |
+
|
| 397 |
+
if value == 0:
|
| 398 |
+
value = Vector(0)
|
| 399 |
+
value = _check_vector(value)
|
| 400 |
+
_check_frame(frame)
|
| 401 |
+
self._vel_dict.update({frame: value})
|
| 402 |
+
|
| 403 |
+
def v1pt_theory(self, otherpoint, outframe, interframe):
|
| 404 |
+
"""Sets the velocity of this point with the 1-point theory.
|
| 405 |
+
|
| 406 |
+
The 1-point theory for point velocity looks like this:
|
| 407 |
+
|
| 408 |
+
^N v^P = ^B v^P + ^N v^O + ^N omega^B x r^OP
|
| 409 |
+
|
| 410 |
+
where O is a point fixed in B, P is a point moving in B, and B is
|
| 411 |
+
rotating in frame N.
|
| 412 |
+
|
| 413 |
+
Parameters
|
| 414 |
+
==========
|
| 415 |
+
|
| 416 |
+
otherpoint : Point
|
| 417 |
+
The first point of the 1-point theory (O)
|
| 418 |
+
outframe : ReferenceFrame
|
| 419 |
+
The frame we want this point's velocity defined in (N)
|
| 420 |
+
interframe : ReferenceFrame
|
| 421 |
+
The intermediate frame in this calculation (B)
|
| 422 |
+
|
| 423 |
+
Examples
|
| 424 |
+
========
|
| 425 |
+
|
| 426 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame
|
| 427 |
+
>>> from sympy.physics.vector import dynamicsymbols
|
| 428 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 429 |
+
>>> init_vprinting(pretty_print=False)
|
| 430 |
+
>>> q = dynamicsymbols('q')
|
| 431 |
+
>>> q2 = dynamicsymbols('q2')
|
| 432 |
+
>>> qd = dynamicsymbols('q', 1)
|
| 433 |
+
>>> q2d = dynamicsymbols('q2', 1)
|
| 434 |
+
>>> N = ReferenceFrame('N')
|
| 435 |
+
>>> B = ReferenceFrame('B')
|
| 436 |
+
>>> B.set_ang_vel(N, 5 * B.y)
|
| 437 |
+
>>> O = Point('O')
|
| 438 |
+
>>> P = O.locatenew('P', q * B.x + q2 * B.y)
|
| 439 |
+
>>> P.set_vel(B, qd * B.x + q2d * B.y)
|
| 440 |
+
>>> O.set_vel(N, 0)
|
| 441 |
+
>>> P.v1pt_theory(O, N, B)
|
| 442 |
+
q'*B.x + q2'*B.y - 5*q*B.z
|
| 443 |
+
|
| 444 |
+
"""
|
| 445 |
+
|
| 446 |
+
_check_frame(outframe)
|
| 447 |
+
_check_frame(interframe)
|
| 448 |
+
self._check_point(otherpoint)
|
| 449 |
+
dist = self.pos_from(otherpoint)
|
| 450 |
+
v1 = self.vel(interframe)
|
| 451 |
+
v2 = otherpoint.vel(outframe)
|
| 452 |
+
omega = interframe.ang_vel_in(outframe)
|
| 453 |
+
self.set_vel(outframe, v1 + v2 + (omega.cross(dist)))
|
| 454 |
+
return self.vel(outframe)
|
| 455 |
+
|
| 456 |
+
def v2pt_theory(self, otherpoint, outframe, fixedframe):
|
| 457 |
+
"""Sets the velocity of this point with the 2-point theory.
|
| 458 |
+
|
| 459 |
+
The 2-point theory for point velocity looks like this:
|
| 460 |
+
|
| 461 |
+
^N v^P = ^N v^O + ^N omega^B x r^OP
|
| 462 |
+
|
| 463 |
+
where O and P are both points fixed in frame B, which is rotating in
|
| 464 |
+
frame N.
|
| 465 |
+
|
| 466 |
+
Parameters
|
| 467 |
+
==========
|
| 468 |
+
|
| 469 |
+
otherpoint : Point
|
| 470 |
+
The first point of the 2-point theory (O)
|
| 471 |
+
outframe : ReferenceFrame
|
| 472 |
+
The frame we want this point's velocity defined in (N)
|
| 473 |
+
fixedframe : ReferenceFrame
|
| 474 |
+
The frame in which both points are fixed (B)
|
| 475 |
+
|
| 476 |
+
Examples
|
| 477 |
+
========
|
| 478 |
+
|
| 479 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
| 480 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 481 |
+
>>> init_vprinting(pretty_print=False)
|
| 482 |
+
>>> q = dynamicsymbols('q')
|
| 483 |
+
>>> qd = dynamicsymbols('q', 1)
|
| 484 |
+
>>> N = ReferenceFrame('N')
|
| 485 |
+
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
| 486 |
+
>>> O = Point('O')
|
| 487 |
+
>>> P = O.locatenew('P', 10 * B.x)
|
| 488 |
+
>>> O.set_vel(N, 5 * N.x)
|
| 489 |
+
>>> P.v2pt_theory(O, N, B)
|
| 490 |
+
5*N.x + 10*q'*B.y
|
| 491 |
+
|
| 492 |
+
"""
|
| 493 |
+
|
| 494 |
+
_check_frame(outframe)
|
| 495 |
+
_check_frame(fixedframe)
|
| 496 |
+
self._check_point(otherpoint)
|
| 497 |
+
dist = self.pos_from(otherpoint)
|
| 498 |
+
v = otherpoint.vel(outframe)
|
| 499 |
+
omega = fixedframe.ang_vel_in(outframe)
|
| 500 |
+
self.set_vel(outframe, v + (omega.cross(dist)))
|
| 501 |
+
return self.vel(outframe)
|
| 502 |
+
|
| 503 |
+
def vel(self, frame):
|
| 504 |
+
"""The velocity Vector of this Point in the ReferenceFrame.
|
| 505 |
+
|
| 506 |
+
Parameters
|
| 507 |
+
==========
|
| 508 |
+
|
| 509 |
+
frame : ReferenceFrame
|
| 510 |
+
The frame in which the returned velocity vector will be defined in
|
| 511 |
+
|
| 512 |
+
Examples
|
| 513 |
+
========
|
| 514 |
+
|
| 515 |
+
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
| 516 |
+
>>> N = ReferenceFrame('N')
|
| 517 |
+
>>> p1 = Point('p1')
|
| 518 |
+
>>> p1.set_vel(N, 10 * N.x)
|
| 519 |
+
>>> p1.vel(N)
|
| 520 |
+
10*N.x
|
| 521 |
+
|
| 522 |
+
Velocities will be automatically calculated if possible, otherwise a
|
| 523 |
+
``ValueError`` will be returned. If it is possible to calculate
|
| 524 |
+
multiple different velocities from the relative points, the points
|
| 525 |
+
defined most directly relative to this point will be used. In the case
|
| 526 |
+
of inconsistent relative positions of points, incorrect velocities may
|
| 527 |
+
be returned. It is up to the user to define prior relative positions
|
| 528 |
+
and velocities of points in a self-consistent way.
|
| 529 |
+
|
| 530 |
+
>>> p = Point('p')
|
| 531 |
+
>>> q = dynamicsymbols('q')
|
| 532 |
+
>>> p.set_vel(N, 10 * N.x)
|
| 533 |
+
>>> p2 = Point('p2')
|
| 534 |
+
>>> p2.set_pos(p, q*N.x)
|
| 535 |
+
>>> p2.vel(N)
|
| 536 |
+
(Derivative(q(t), t) + 10)*N.x
|
| 537 |
+
|
| 538 |
+
"""
|
| 539 |
+
|
| 540 |
+
_check_frame(frame)
|
| 541 |
+
if not (frame in self._vel_dict):
|
| 542 |
+
valid_neighbor_found = False
|
| 543 |
+
is_cyclic = False
|
| 544 |
+
visited = []
|
| 545 |
+
queue = [self]
|
| 546 |
+
candidate_neighbor = []
|
| 547 |
+
while queue: # BFS to find nearest point
|
| 548 |
+
node = queue.pop(0)
|
| 549 |
+
if node not in visited:
|
| 550 |
+
visited.append(node)
|
| 551 |
+
for neighbor, neighbor_pos in node._pos_dict.items():
|
| 552 |
+
if neighbor in visited:
|
| 553 |
+
continue
|
| 554 |
+
try:
|
| 555 |
+
# Checks if pos vector is valid
|
| 556 |
+
neighbor_pos.express(frame)
|
| 557 |
+
except ValueError:
|
| 558 |
+
continue
|
| 559 |
+
if neighbor in queue:
|
| 560 |
+
is_cyclic = True
|
| 561 |
+
try:
|
| 562 |
+
# Checks if point has its vel defined in req frame
|
| 563 |
+
neighbor_velocity = neighbor._vel_dict[frame]
|
| 564 |
+
except KeyError:
|
| 565 |
+
queue.append(neighbor)
|
| 566 |
+
continue
|
| 567 |
+
candidate_neighbor.append(neighbor)
|
| 568 |
+
if not valid_neighbor_found:
|
| 569 |
+
self.set_vel(frame, self.pos_from(neighbor).dt(frame) + neighbor_velocity)
|
| 570 |
+
valid_neighbor_found = True
|
| 571 |
+
if is_cyclic:
|
| 572 |
+
warn(filldedent("""
|
| 573 |
+
Kinematic loops are defined among the positions of points. This
|
| 574 |
+
is likely not desired and may cause errors in your calculations.
|
| 575 |
+
"""))
|
| 576 |
+
if len(candidate_neighbor) > 1:
|
| 577 |
+
warn(filldedent(f"""
|
| 578 |
+
Velocity of {self.name} automatically calculated based on point
|
| 579 |
+
{candidate_neighbor[0].name} but it is also possible from
|
| 580 |
+
points(s): {str(candidate_neighbor[1:])}. Velocities from these
|
| 581 |
+
points are not necessarily the same. This may cause errors in
|
| 582 |
+
your calculations."""))
|
| 583 |
+
if valid_neighbor_found:
|
| 584 |
+
return self._vel_dict[frame]
|
| 585 |
+
else:
|
| 586 |
+
raise ValueError(filldedent(f"""
|
| 587 |
+
Velocity of point {self.name} has not been defined in
|
| 588 |
+
ReferenceFrame {frame.name}."""))
|
| 589 |
+
|
| 590 |
+
return self._vel_dict[frame]
|
| 591 |
+
|
| 592 |
+
def partial_velocity(self, frame, *gen_speeds):
|
| 593 |
+
"""Returns the partial velocities of the linear velocity vector of this
|
| 594 |
+
point in the given frame with respect to one or more provided
|
| 595 |
+
generalized speeds.
|
| 596 |
+
|
| 597 |
+
Parameters
|
| 598 |
+
==========
|
| 599 |
+
frame : ReferenceFrame
|
| 600 |
+
The frame with which the velocity is defined in.
|
| 601 |
+
gen_speeds : functions of time
|
| 602 |
+
The generalized speeds.
|
| 603 |
+
|
| 604 |
+
Returns
|
| 605 |
+
=======
|
| 606 |
+
partial_velocities : tuple of Vector
|
| 607 |
+
The partial velocity vectors corresponding to the provided
|
| 608 |
+
generalized speeds.
|
| 609 |
+
|
| 610 |
+
Examples
|
| 611 |
+
========
|
| 612 |
+
|
| 613 |
+
>>> from sympy.physics.vector import ReferenceFrame, Point
|
| 614 |
+
>>> from sympy.physics.vector import dynamicsymbols
|
| 615 |
+
>>> N = ReferenceFrame('N')
|
| 616 |
+
>>> A = ReferenceFrame('A')
|
| 617 |
+
>>> p = Point('p')
|
| 618 |
+
>>> u1, u2 = dynamicsymbols('u1, u2')
|
| 619 |
+
>>> p.set_vel(N, u1 * N.x + u2 * A.y)
|
| 620 |
+
>>> p.partial_velocity(N, u1)
|
| 621 |
+
N.x
|
| 622 |
+
>>> p.partial_velocity(N, u1, u2)
|
| 623 |
+
(N.x, A.y)
|
| 624 |
+
|
| 625 |
+
"""
|
| 626 |
+
|
| 627 |
+
from sympy.physics.vector.functions import partial_velocity
|
| 628 |
+
|
| 629 |
+
vel = self.vel(frame)
|
| 630 |
+
partials = partial_velocity([vel], gen_speeds, frame)[0]
|
| 631 |
+
|
| 632 |
+
if len(partials) == 1:
|
| 633 |
+
return partials[0]
|
| 634 |
+
else:
|
| 635 |
+
return tuple(partials)
|
venv/lib/python3.11/site-packages/sympy/physics/vector/printing.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.function import Derivative
|
| 2 |
+
from sympy.core.function import UndefinedFunction, AppliedUndef
|
| 3 |
+
from sympy.core.symbol import Symbol
|
| 4 |
+
from sympy.interactive.printing import init_printing
|
| 5 |
+
from sympy.printing.latex import LatexPrinter
|
| 6 |
+
from sympy.printing.pretty.pretty import PrettyPrinter
|
| 7 |
+
from sympy.printing.pretty.pretty_symbology import center_accent
|
| 8 |
+
from sympy.printing.str import StrPrinter
|
| 9 |
+
from sympy.printing.precedence import PRECEDENCE
|
| 10 |
+
|
| 11 |
+
__all__ = ['vprint', 'vsstrrepr', 'vsprint', 'vpprint', 'vlatex',
|
| 12 |
+
'init_vprinting']
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class VectorStrPrinter(StrPrinter):
|
| 16 |
+
"""String Printer for vector expressions. """
|
| 17 |
+
|
| 18 |
+
def _print_Derivative(self, e):
|
| 19 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 20 |
+
t = dynamicsymbols._t
|
| 21 |
+
if (bool(sum(i == t for i in e.variables)) &
|
| 22 |
+
isinstance(type(e.args[0]), UndefinedFunction)):
|
| 23 |
+
ol = str(e.args[0].func)
|
| 24 |
+
for i, v in enumerate(e.variables):
|
| 25 |
+
ol += dynamicsymbols._str
|
| 26 |
+
return ol
|
| 27 |
+
else:
|
| 28 |
+
return StrPrinter().doprint(e)
|
| 29 |
+
|
| 30 |
+
def _print_Function(self, e):
|
| 31 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 32 |
+
t = dynamicsymbols._t
|
| 33 |
+
if isinstance(type(e), UndefinedFunction):
|
| 34 |
+
return StrPrinter().doprint(e).replace("(%s)" % t, '')
|
| 35 |
+
return e.func.__name__ + "(%s)" % self.stringify(e.args, ", ")
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class VectorStrReprPrinter(VectorStrPrinter):
|
| 39 |
+
"""String repr printer for vector expressions."""
|
| 40 |
+
def _print_str(self, s):
|
| 41 |
+
return repr(s)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class VectorLatexPrinter(LatexPrinter):
|
| 45 |
+
"""Latex Printer for vector expressions. """
|
| 46 |
+
|
| 47 |
+
def _print_Function(self, expr, exp=None):
|
| 48 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 49 |
+
func = expr.func.__name__
|
| 50 |
+
t = dynamicsymbols._t
|
| 51 |
+
|
| 52 |
+
if (hasattr(self, '_print_' + func) and not
|
| 53 |
+
isinstance(type(expr), UndefinedFunction)):
|
| 54 |
+
return getattr(self, '_print_' + func)(expr, exp)
|
| 55 |
+
elif isinstance(type(expr), UndefinedFunction) and (expr.args == (t,)):
|
| 56 |
+
# treat this function like a symbol
|
| 57 |
+
expr = Symbol(func)
|
| 58 |
+
if exp is not None:
|
| 59 |
+
# copied from LatexPrinter._helper_print_standard_power, which
|
| 60 |
+
# we can't call because we only have exp as a string.
|
| 61 |
+
base = self.parenthesize(expr, PRECEDENCE['Pow'])
|
| 62 |
+
base = self.parenthesize_super(base)
|
| 63 |
+
return r"%s^{%s}" % (base, exp)
|
| 64 |
+
else:
|
| 65 |
+
return super()._print(expr)
|
| 66 |
+
else:
|
| 67 |
+
return super()._print_Function(expr, exp)
|
| 68 |
+
|
| 69 |
+
def _print_Derivative(self, der_expr):
|
| 70 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 71 |
+
# make sure it is in the right form
|
| 72 |
+
der_expr = der_expr.doit()
|
| 73 |
+
if not isinstance(der_expr, Derivative):
|
| 74 |
+
return r"\left(%s\right)" % self.doprint(der_expr)
|
| 75 |
+
|
| 76 |
+
# check if expr is a dynamicsymbol
|
| 77 |
+
t = dynamicsymbols._t
|
| 78 |
+
expr = der_expr.expr
|
| 79 |
+
red = expr.atoms(AppliedUndef)
|
| 80 |
+
syms = der_expr.variables
|
| 81 |
+
test1 = not all(True for i in red if i.free_symbols == {t})
|
| 82 |
+
test2 = not all(t == i for i in syms)
|
| 83 |
+
if test1 or test2:
|
| 84 |
+
return super()._print_Derivative(der_expr)
|
| 85 |
+
|
| 86 |
+
# done checking
|
| 87 |
+
dots = len(syms)
|
| 88 |
+
base = self._print_Function(expr)
|
| 89 |
+
base_split = base.split('_', 1)
|
| 90 |
+
base = base_split[0]
|
| 91 |
+
if dots == 1:
|
| 92 |
+
base = r"\dot{%s}" % base
|
| 93 |
+
elif dots == 2:
|
| 94 |
+
base = r"\ddot{%s}" % base
|
| 95 |
+
elif dots == 3:
|
| 96 |
+
base = r"\dddot{%s}" % base
|
| 97 |
+
elif dots == 4:
|
| 98 |
+
base = r"\ddddot{%s}" % base
|
| 99 |
+
else: # Fallback to standard printing
|
| 100 |
+
return super()._print_Derivative(der_expr)
|
| 101 |
+
if len(base_split) != 1:
|
| 102 |
+
base += '_' + base_split[1]
|
| 103 |
+
return base
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class VectorPrettyPrinter(PrettyPrinter):
|
| 107 |
+
"""Pretty Printer for vectorialexpressions. """
|
| 108 |
+
|
| 109 |
+
def _print_Derivative(self, deriv):
|
| 110 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 111 |
+
# XXX use U('PARTIAL DIFFERENTIAL') here ?
|
| 112 |
+
t = dynamicsymbols._t
|
| 113 |
+
dot_i = 0
|
| 114 |
+
syms = list(reversed(deriv.variables))
|
| 115 |
+
|
| 116 |
+
while len(syms) > 0:
|
| 117 |
+
if syms[-1] == t:
|
| 118 |
+
syms.pop()
|
| 119 |
+
dot_i += 1
|
| 120 |
+
else:
|
| 121 |
+
return super()._print_Derivative(deriv)
|
| 122 |
+
|
| 123 |
+
if not (isinstance(type(deriv.expr), UndefinedFunction) and
|
| 124 |
+
(deriv.expr.args == (t,))):
|
| 125 |
+
return super()._print_Derivative(deriv)
|
| 126 |
+
else:
|
| 127 |
+
pform = self._print_Function(deriv.expr)
|
| 128 |
+
|
| 129 |
+
# the following condition would happen with some sort of non-standard
|
| 130 |
+
# dynamic symbol I guess, so we'll just print the SymPy way
|
| 131 |
+
if len(pform.picture) > 1:
|
| 132 |
+
return super()._print_Derivative(deriv)
|
| 133 |
+
|
| 134 |
+
# There are only special symbols up to fourth-order derivatives
|
| 135 |
+
if dot_i >= 5:
|
| 136 |
+
return super()._print_Derivative(deriv)
|
| 137 |
+
|
| 138 |
+
# Deal with special symbols
|
| 139 |
+
dots = {0: "",
|
| 140 |
+
1: "\N{COMBINING DOT ABOVE}",
|
| 141 |
+
2: "\N{COMBINING DIAERESIS}",
|
| 142 |
+
3: "\N{COMBINING THREE DOTS ABOVE}",
|
| 143 |
+
4: "\N{COMBINING FOUR DOTS ABOVE}"}
|
| 144 |
+
|
| 145 |
+
d = pform.__dict__
|
| 146 |
+
# if unicode is false then calculate number of apostrophes needed and
|
| 147 |
+
# add to output
|
| 148 |
+
if not self._use_unicode:
|
| 149 |
+
apostrophes = ""
|
| 150 |
+
for i in range(0, dot_i):
|
| 151 |
+
apostrophes += "'"
|
| 152 |
+
d['picture'][0] += apostrophes + "(t)"
|
| 153 |
+
else:
|
| 154 |
+
d['picture'] = [center_accent(d['picture'][0], dots[dot_i])]
|
| 155 |
+
return pform
|
| 156 |
+
|
| 157 |
+
def _print_Function(self, e):
|
| 158 |
+
from sympy.physics.vector.functions import dynamicsymbols
|
| 159 |
+
t = dynamicsymbols._t
|
| 160 |
+
# XXX works only for applied functions
|
| 161 |
+
func = e.func
|
| 162 |
+
args = e.args
|
| 163 |
+
func_name = func.__name__
|
| 164 |
+
pform = self._print_Symbol(Symbol(func_name))
|
| 165 |
+
# If this function is an Undefined function of t, it is probably a
|
| 166 |
+
# dynamic symbol, so we'll skip the (t). The rest of the code is
|
| 167 |
+
# identical to the normal PrettyPrinter code
|
| 168 |
+
if not (isinstance(func, UndefinedFunction) and (args == (t,))):
|
| 169 |
+
return super()._print_Function(e)
|
| 170 |
+
return pform
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def vprint(expr, **settings):
|
| 174 |
+
r"""Function for printing of expressions generated in the
|
| 175 |
+
sympy.physics vector package.
|
| 176 |
+
|
| 177 |
+
Extends SymPy's StrPrinter, takes the same setting accepted by SymPy's
|
| 178 |
+
:func:`~.sstr`, and is equivalent to ``print(sstr(foo))``.
|
| 179 |
+
|
| 180 |
+
Parameters
|
| 181 |
+
==========
|
| 182 |
+
|
| 183 |
+
expr : valid SymPy object
|
| 184 |
+
SymPy expression to print.
|
| 185 |
+
settings : args
|
| 186 |
+
Same as the settings accepted by SymPy's sstr().
|
| 187 |
+
|
| 188 |
+
Examples
|
| 189 |
+
========
|
| 190 |
+
|
| 191 |
+
>>> from sympy.physics.vector import vprint, dynamicsymbols
|
| 192 |
+
>>> u1 = dynamicsymbols('u1')
|
| 193 |
+
>>> print(u1)
|
| 194 |
+
u1(t)
|
| 195 |
+
>>> vprint(u1)
|
| 196 |
+
u1
|
| 197 |
+
|
| 198 |
+
"""
|
| 199 |
+
|
| 200 |
+
outstr = vsprint(expr, **settings)
|
| 201 |
+
|
| 202 |
+
import builtins
|
| 203 |
+
if (outstr != 'None'):
|
| 204 |
+
builtins._ = outstr
|
| 205 |
+
print(outstr)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def vsstrrepr(expr, **settings):
|
| 209 |
+
"""Function for displaying expression representation's with vector
|
| 210 |
+
printing enabled.
|
| 211 |
+
|
| 212 |
+
Parameters
|
| 213 |
+
==========
|
| 214 |
+
|
| 215 |
+
expr : valid SymPy object
|
| 216 |
+
SymPy expression to print.
|
| 217 |
+
settings : args
|
| 218 |
+
Same as the settings accepted by SymPy's sstrrepr().
|
| 219 |
+
|
| 220 |
+
"""
|
| 221 |
+
p = VectorStrReprPrinter(settings)
|
| 222 |
+
return p.doprint(expr)
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def vsprint(expr, **settings):
|
| 226 |
+
r"""Function for displaying expressions generated in the
|
| 227 |
+
sympy.physics vector package.
|
| 228 |
+
|
| 229 |
+
Returns the output of vprint() as a string.
|
| 230 |
+
|
| 231 |
+
Parameters
|
| 232 |
+
==========
|
| 233 |
+
|
| 234 |
+
expr : valid SymPy object
|
| 235 |
+
SymPy expression to print
|
| 236 |
+
settings : args
|
| 237 |
+
Same as the settings accepted by SymPy's sstr().
|
| 238 |
+
|
| 239 |
+
Examples
|
| 240 |
+
========
|
| 241 |
+
|
| 242 |
+
>>> from sympy.physics.vector import vsprint, dynamicsymbols
|
| 243 |
+
>>> u1, u2 = dynamicsymbols('u1 u2')
|
| 244 |
+
>>> u2d = dynamicsymbols('u2', level=1)
|
| 245 |
+
>>> print("%s = %s" % (u1, u2 + u2d))
|
| 246 |
+
u1(t) = u2(t) + Derivative(u2(t), t)
|
| 247 |
+
>>> print("%s = %s" % (vsprint(u1), vsprint(u2 + u2d)))
|
| 248 |
+
u1 = u2 + u2'
|
| 249 |
+
|
| 250 |
+
"""
|
| 251 |
+
|
| 252 |
+
string_printer = VectorStrPrinter(settings)
|
| 253 |
+
return string_printer.doprint(expr)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def vpprint(expr, **settings):
|
| 257 |
+
r"""Function for pretty printing of expressions generated in the
|
| 258 |
+
sympy.physics vector package.
|
| 259 |
+
|
| 260 |
+
Mainly used for expressions not inside a vector; the output of running
|
| 261 |
+
scripts and generating equations of motion. Takes the same options as
|
| 262 |
+
SymPy's :func:`~.pretty_print`; see that function for more information.
|
| 263 |
+
|
| 264 |
+
Parameters
|
| 265 |
+
==========
|
| 266 |
+
|
| 267 |
+
expr : valid SymPy object
|
| 268 |
+
SymPy expression to pretty print
|
| 269 |
+
settings : args
|
| 270 |
+
Same as those accepted by SymPy's pretty_print.
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
"""
|
| 274 |
+
|
| 275 |
+
pp = VectorPrettyPrinter(settings)
|
| 276 |
+
|
| 277 |
+
# Note that this is copied from sympy.printing.pretty.pretty_print:
|
| 278 |
+
|
| 279 |
+
# XXX: this is an ugly hack, but at least it works
|
| 280 |
+
use_unicode = pp._settings['use_unicode']
|
| 281 |
+
from sympy.printing.pretty.pretty_symbology import pretty_use_unicode
|
| 282 |
+
uflag = pretty_use_unicode(use_unicode)
|
| 283 |
+
|
| 284 |
+
try:
|
| 285 |
+
return pp.doprint(expr)
|
| 286 |
+
finally:
|
| 287 |
+
pretty_use_unicode(uflag)
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def vlatex(expr, **settings):
|
| 291 |
+
r"""Function for printing latex representation of sympy.physics.vector
|
| 292 |
+
objects.
|
| 293 |
+
|
| 294 |
+
For latex representation of Vectors, Dyadics, and dynamicsymbols. Takes the
|
| 295 |
+
same options as SymPy's :func:`~.latex`; see that function for more
|
| 296 |
+
information;
|
| 297 |
+
|
| 298 |
+
Parameters
|
| 299 |
+
==========
|
| 300 |
+
|
| 301 |
+
expr : valid SymPy object
|
| 302 |
+
SymPy expression to represent in LaTeX form
|
| 303 |
+
settings : args
|
| 304 |
+
Same as latex()
|
| 305 |
+
|
| 306 |
+
Examples
|
| 307 |
+
========
|
| 308 |
+
|
| 309 |
+
>>> from sympy.physics.vector import vlatex, ReferenceFrame, dynamicsymbols
|
| 310 |
+
>>> N = ReferenceFrame('N')
|
| 311 |
+
>>> q1, q2 = dynamicsymbols('q1 q2')
|
| 312 |
+
>>> q1d, q2d = dynamicsymbols('q1 q2', 1)
|
| 313 |
+
>>> q1dd, q2dd = dynamicsymbols('q1 q2', 2)
|
| 314 |
+
>>> vlatex(N.x + N.y)
|
| 315 |
+
'\\mathbf{\\hat{n}_x} + \\mathbf{\\hat{n}_y}'
|
| 316 |
+
>>> vlatex(q1 + q2)
|
| 317 |
+
'q_{1} + q_{2}'
|
| 318 |
+
>>> vlatex(q1d)
|
| 319 |
+
'\\dot{q}_{1}'
|
| 320 |
+
>>> vlatex(q1 * q2d)
|
| 321 |
+
'q_{1} \\dot{q}_{2}'
|
| 322 |
+
>>> vlatex(q1dd * q1 / q1d)
|
| 323 |
+
'\\frac{q_{1} \\ddot{q}_{1}}{\\dot{q}_{1}}'
|
| 324 |
+
|
| 325 |
+
"""
|
| 326 |
+
latex_printer = VectorLatexPrinter(settings)
|
| 327 |
+
|
| 328 |
+
return latex_printer.doprint(expr)
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def init_vprinting(**kwargs):
|
| 332 |
+
"""Initializes time derivative printing for all SymPy objects, i.e. any
|
| 333 |
+
functions of time will be displayed in a more compact notation. The main
|
| 334 |
+
benefit of this is for printing of time derivatives; instead of
|
| 335 |
+
displaying as ``Derivative(f(t),t)``, it will display ``f'``. This is
|
| 336 |
+
only actually needed for when derivatives are present and are not in a
|
| 337 |
+
physics.vector.Vector or physics.vector.Dyadic object. This function is a
|
| 338 |
+
light wrapper to :func:`~.init_printing`. Any keyword
|
| 339 |
+
arguments for it are valid here.
|
| 340 |
+
|
| 341 |
+
{0}
|
| 342 |
+
|
| 343 |
+
Examples
|
| 344 |
+
========
|
| 345 |
+
|
| 346 |
+
>>> from sympy import Function, symbols
|
| 347 |
+
>>> t, x = symbols('t, x')
|
| 348 |
+
>>> omega = Function('omega')
|
| 349 |
+
>>> omega(x).diff()
|
| 350 |
+
Derivative(omega(x), x)
|
| 351 |
+
>>> omega(t).diff()
|
| 352 |
+
Derivative(omega(t), t)
|
| 353 |
+
|
| 354 |
+
Now use the string printer:
|
| 355 |
+
|
| 356 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 357 |
+
>>> init_vprinting(pretty_print=False)
|
| 358 |
+
>>> omega(x).diff()
|
| 359 |
+
Derivative(omega(x), x)
|
| 360 |
+
>>> omega(t).diff()
|
| 361 |
+
omega'
|
| 362 |
+
|
| 363 |
+
"""
|
| 364 |
+
kwargs['str_printer'] = vsstrrepr
|
| 365 |
+
kwargs['pretty_printer'] = vpprint
|
| 366 |
+
kwargs['latex_printer'] = vlatex
|
| 367 |
+
init_printing(**kwargs)
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
params = init_printing.__doc__.split('Examples\n ========')[0] # type: ignore
|
| 371 |
+
init_vprinting.__doc__ = init_vprinting.__doc__.format(params) # type: ignore
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/__init__.py
ADDED
|
File without changes
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_dyadic.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.numbers import (Float, pi)
|
| 2 |
+
from sympy.core.symbol import symbols
|
| 3 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 4 |
+
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
| 5 |
+
from sympy.physics.vector import ReferenceFrame, dynamicsymbols, outer
|
| 6 |
+
from sympy.physics.vector.dyadic import _check_dyadic
|
| 7 |
+
from sympy.testing.pytest import raises
|
| 8 |
+
|
| 9 |
+
A = ReferenceFrame('A')
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def test_dyadic():
|
| 13 |
+
d1 = A.x | A.x
|
| 14 |
+
d2 = A.y | A.y
|
| 15 |
+
d3 = A.x | A.y
|
| 16 |
+
assert d1 * 0 == 0
|
| 17 |
+
assert d1 != 0
|
| 18 |
+
assert d1 * 2 == 2 * A.x | A.x
|
| 19 |
+
assert d1 / 2. == 0.5 * d1
|
| 20 |
+
assert d1 & (0 * d1) == 0
|
| 21 |
+
assert d1 & d2 == 0
|
| 22 |
+
assert d1 & A.x == A.x
|
| 23 |
+
assert d1 ^ A.x == 0
|
| 24 |
+
assert d1 ^ A.y == A.x | A.z
|
| 25 |
+
assert d1 ^ A.z == - A.x | A.y
|
| 26 |
+
assert d2 ^ A.x == - A.y | A.z
|
| 27 |
+
assert A.x ^ d1 == 0
|
| 28 |
+
assert A.y ^ d1 == - A.z | A.x
|
| 29 |
+
assert A.z ^ d1 == A.y | A.x
|
| 30 |
+
assert A.x & d1 == A.x
|
| 31 |
+
assert A.y & d1 == 0
|
| 32 |
+
assert A.y & d2 == A.y
|
| 33 |
+
assert d1 & d3 == A.x | A.y
|
| 34 |
+
assert d3 & d1 == 0
|
| 35 |
+
assert d1.dt(A) == 0
|
| 36 |
+
q = dynamicsymbols('q')
|
| 37 |
+
qd = dynamicsymbols('q', 1)
|
| 38 |
+
B = A.orientnew('B', 'Axis', [q, A.z])
|
| 39 |
+
assert d1.express(B) == d1.express(B, B)
|
| 40 |
+
assert d1.express(B) == ((cos(q)**2) * (B.x | B.x) + (-sin(q) * cos(q)) *
|
| 41 |
+
(B.x | B.y) + (-sin(q) * cos(q)) * (B.y | B.x) + (sin(q)**2) *
|
| 42 |
+
(B.y | B.y))
|
| 43 |
+
assert d1.express(B, A) == (cos(q)) * (B.x | A.x) + (-sin(q)) * (B.y | A.x)
|
| 44 |
+
assert d1.express(A, B) == (cos(q)) * (A.x | B.x) + (-sin(q)) * (A.x | B.y)
|
| 45 |
+
assert d1.dt(B) == (-qd) * (A.y | A.x) + (-qd) * (A.x | A.y)
|
| 46 |
+
|
| 47 |
+
assert d1.to_matrix(A) == Matrix([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
|
| 48 |
+
assert d1.to_matrix(A, B) == Matrix([[cos(q), -sin(q), 0],
|
| 49 |
+
[0, 0, 0],
|
| 50 |
+
[0, 0, 0]])
|
| 51 |
+
assert d3.to_matrix(A) == Matrix([[0, 1, 0], [0, 0, 0], [0, 0, 0]])
|
| 52 |
+
a, b, c, d, e, f = symbols('a, b, c, d, e, f')
|
| 53 |
+
v1 = a * A.x + b * A.y + c * A.z
|
| 54 |
+
v2 = d * A.x + e * A.y + f * A.z
|
| 55 |
+
d4 = v1.outer(v2)
|
| 56 |
+
assert d4.to_matrix(A) == Matrix([[a * d, a * e, a * f],
|
| 57 |
+
[b * d, b * e, b * f],
|
| 58 |
+
[c * d, c * e, c * f]])
|
| 59 |
+
d5 = v1.outer(v1)
|
| 60 |
+
C = A.orientnew('C', 'Axis', [q, A.x])
|
| 61 |
+
for expected, actual in zip(C.dcm(A) * d5.to_matrix(A) * C.dcm(A).T,
|
| 62 |
+
d5.to_matrix(C)):
|
| 63 |
+
assert (expected - actual).simplify() == 0
|
| 64 |
+
|
| 65 |
+
raises(TypeError, lambda: d1.applyfunc(0))
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def test_dyadic_simplify():
|
| 69 |
+
x, y, z, k, n, m, w, f, s, A = symbols('x, y, z, k, n, m, w, f, s, A')
|
| 70 |
+
N = ReferenceFrame('N')
|
| 71 |
+
|
| 72 |
+
dy = N.x | N.x
|
| 73 |
+
test1 = (1 / x + 1 / y) * dy
|
| 74 |
+
assert (N.x & test1 & N.x) != (x + y) / (x * y)
|
| 75 |
+
test1 = test1.simplify()
|
| 76 |
+
assert (N.x & test1 & N.x) == (x + y) / (x * y)
|
| 77 |
+
|
| 78 |
+
test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * dy
|
| 79 |
+
test2 = test2.simplify()
|
| 80 |
+
assert (N.x & test2 & N.x) == (A**2 * s**4 / (4 * pi * k * m**3))
|
| 81 |
+
|
| 82 |
+
test3 = ((4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x)) * dy
|
| 83 |
+
test3 = test3.simplify()
|
| 84 |
+
assert (N.x & test3 & N.x) == 0
|
| 85 |
+
|
| 86 |
+
test4 = ((-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2) * dy
|
| 87 |
+
test4 = test4.simplify()
|
| 88 |
+
assert (N.x & test4 & N.x) == -2 * y
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def test_dyadic_subs():
|
| 92 |
+
N = ReferenceFrame('N')
|
| 93 |
+
s = symbols('s')
|
| 94 |
+
a = s*(N.x | N.x)
|
| 95 |
+
assert a.subs({s: 2}) == 2*(N.x | N.x)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def test_check_dyadic():
|
| 99 |
+
raises(TypeError, lambda: _check_dyadic(0))
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def test_dyadic_evalf():
|
| 103 |
+
N = ReferenceFrame('N')
|
| 104 |
+
a = pi * (N.x | N.x)
|
| 105 |
+
assert a.evalf(3) == Float('3.1416', 3) * (N.x | N.x)
|
| 106 |
+
s = symbols('s')
|
| 107 |
+
a = 5 * s * pi* (N.x | N.x)
|
| 108 |
+
assert a.evalf(2) == Float('5', 2) * Float('3.1416', 2) * s * (N.x | N.x)
|
| 109 |
+
assert a.evalf(9, subs={s: 5.124}) == Float('80.48760378', 9) * (N.x | N.x)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def test_dyadic_xreplace():
|
| 113 |
+
x, y, z = symbols('x y z')
|
| 114 |
+
N = ReferenceFrame('N')
|
| 115 |
+
D = outer(N.x, N.x)
|
| 116 |
+
v = x*y * D
|
| 117 |
+
assert v.xreplace({x : cos(x)}) == cos(x)*y * D
|
| 118 |
+
assert v.xreplace({x*y : pi}) == pi * D
|
| 119 |
+
v = (x*y)**z * D
|
| 120 |
+
assert v.xreplace({(x*y)**z : 1}) == D
|
| 121 |
+
assert v.xreplace({x:1, z:0}) == D
|
| 122 |
+
raises(TypeError, lambda: v.xreplace())
|
| 123 |
+
raises(TypeError, lambda: v.xreplace([x, y]))
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_fieldfunctions.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.singleton import S
|
| 2 |
+
from sympy.core.symbol import Symbol
|
| 3 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 4 |
+
from sympy.physics.vector import ReferenceFrame, Vector, Point, \
|
| 5 |
+
dynamicsymbols
|
| 6 |
+
from sympy.physics.vector.fieldfunctions import divergence, \
|
| 7 |
+
gradient, curl, is_conservative, is_solenoidal, \
|
| 8 |
+
scalar_potential, scalar_potential_difference
|
| 9 |
+
from sympy.testing.pytest import raises
|
| 10 |
+
|
| 11 |
+
R = ReferenceFrame('R')
|
| 12 |
+
q = dynamicsymbols('q')
|
| 13 |
+
P = R.orientnew('P', 'Axis', [q, R.z])
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def test_curl():
|
| 17 |
+
assert curl(Vector(0), R) == Vector(0)
|
| 18 |
+
assert curl(R.x, R) == Vector(0)
|
| 19 |
+
assert curl(2*R[1]**2*R.y, R) == Vector(0)
|
| 20 |
+
assert curl(R[0]*R[1]*R.z, R) == R[0]*R.x - R[1]*R.y
|
| 21 |
+
assert curl(R[0]*R[1]*R[2] * (R.x+R.y+R.z), R) == \
|
| 22 |
+
(-R[0]*R[1] + R[0]*R[2])*R.x + (R[0]*R[1] - R[1]*R[2])*R.y + \
|
| 23 |
+
(-R[0]*R[2] + R[1]*R[2])*R.z
|
| 24 |
+
assert curl(2*R[0]**2*R.y, R) == 4*R[0]*R.z
|
| 25 |
+
assert curl(P[0]**2*R.x + P.y, R) == \
|
| 26 |
+
- 2*(R[0]*cos(q) + R[1]*sin(q))*sin(q)*R.z
|
| 27 |
+
assert curl(P[0]*R.y, P) == cos(q)*P.z
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def test_divergence():
|
| 31 |
+
assert divergence(Vector(0), R) is S.Zero
|
| 32 |
+
assert divergence(R.x, R) is S.Zero
|
| 33 |
+
assert divergence(R[0]**2*R.x, R) == 2*R[0]
|
| 34 |
+
assert divergence(R[0]*R[1]*R[2] * (R.x+R.y+R.z), R) == \
|
| 35 |
+
R[0]*R[1] + R[0]*R[2] + R[1]*R[2]
|
| 36 |
+
assert divergence((1/(R[0]*R[1]*R[2])) * (R.x+R.y+R.z), R) == \
|
| 37 |
+
-1/(R[0]*R[1]*R[2]**2) - 1/(R[0]*R[1]**2*R[2]) - \
|
| 38 |
+
1/(R[0]**2*R[1]*R[2])
|
| 39 |
+
v = P[0]*P.x + P[1]*P.y + P[2]*P.z
|
| 40 |
+
assert divergence(v, P) == 3
|
| 41 |
+
assert divergence(v, R).simplify() == 3
|
| 42 |
+
assert divergence(P[0]*R.x + R[0]*P.x, R) == 2*cos(q)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_gradient():
|
| 46 |
+
a = Symbol('a')
|
| 47 |
+
assert gradient(0, R) == Vector(0)
|
| 48 |
+
assert gradient(R[0], R) == R.x
|
| 49 |
+
assert gradient(R[0]*R[1]*R[2], R) == \
|
| 50 |
+
R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z
|
| 51 |
+
assert gradient(2*R[0]**2, R) == 4*R[0]*R.x
|
| 52 |
+
assert gradient(a*sin(R[1])/R[0], R) == \
|
| 53 |
+
- a*sin(R[1])/R[0]**2*R.x + a*cos(R[1])/R[0]*R.y
|
| 54 |
+
assert gradient(P[0]*P[1], R) == \
|
| 55 |
+
((-R[0]*sin(q) + R[1]*cos(q))*cos(q) - (R[0]*cos(q) + R[1]*sin(q))*sin(q))*R.x + \
|
| 56 |
+
((-R[0]*sin(q) + R[1]*cos(q))*sin(q) + (R[0]*cos(q) + R[1]*sin(q))*cos(q))*R.y
|
| 57 |
+
assert gradient(P[0]*R[2], P) == P[2]*P.x + P[0]*P.z
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
scalar_field = 2*R[0]**2*R[1]*R[2]
|
| 61 |
+
grad_field = gradient(scalar_field, R)
|
| 62 |
+
vector_field = R[1]**2*R.x + 3*R[0]*R.y + 5*R[1]*R[2]*R.z
|
| 63 |
+
curl_field = curl(vector_field, R)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def test_conservative():
|
| 67 |
+
assert is_conservative(0) is True
|
| 68 |
+
assert is_conservative(R.x) is True
|
| 69 |
+
assert is_conservative(2 * R.x + 3 * R.y + 4 * R.z) is True
|
| 70 |
+
assert is_conservative(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z) is \
|
| 71 |
+
True
|
| 72 |
+
assert is_conservative(R[0] * R.y) is False
|
| 73 |
+
assert is_conservative(grad_field) is True
|
| 74 |
+
assert is_conservative(curl_field) is False
|
| 75 |
+
assert is_conservative(4*R[0]*R[1]*R[2]*R.x + 2*R[0]**2*R[2]*R.y) is \
|
| 76 |
+
False
|
| 77 |
+
assert is_conservative(R[2]*P.x + P[0]*R.z) is True
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def test_solenoidal():
|
| 81 |
+
assert is_solenoidal(0) is True
|
| 82 |
+
assert is_solenoidal(R.x) is True
|
| 83 |
+
assert is_solenoidal(2 * R.x + 3 * R.y + 4 * R.z) is True
|
| 84 |
+
assert is_solenoidal(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z) is \
|
| 85 |
+
True
|
| 86 |
+
assert is_solenoidal(R[1] * R.y) is False
|
| 87 |
+
assert is_solenoidal(grad_field) is False
|
| 88 |
+
assert is_solenoidal(curl_field) is True
|
| 89 |
+
assert is_solenoidal((-2*R[1] + 3)*R.z) is True
|
| 90 |
+
assert is_solenoidal(cos(q)*R.x + sin(q)*R.y + cos(q)*P.z) is True
|
| 91 |
+
assert is_solenoidal(R[2]*P.x + P[0]*R.z) is True
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def test_scalar_potential():
|
| 95 |
+
assert scalar_potential(0, R) == 0
|
| 96 |
+
assert scalar_potential(R.x, R) == R[0]
|
| 97 |
+
assert scalar_potential(R.y, R) == R[1]
|
| 98 |
+
assert scalar_potential(R.z, R) == R[2]
|
| 99 |
+
assert scalar_potential(R[1]*R[2]*R.x + R[0]*R[2]*R.y + \
|
| 100 |
+
R[0]*R[1]*R.z, R) == R[0]*R[1]*R[2]
|
| 101 |
+
assert scalar_potential(grad_field, R) == scalar_field
|
| 102 |
+
assert scalar_potential(R[2]*P.x + P[0]*R.z, R) == \
|
| 103 |
+
R[0]*R[2]*cos(q) + R[1]*R[2]*sin(q)
|
| 104 |
+
assert scalar_potential(R[2]*P.x + P[0]*R.z, P) == P[0]*P[2]
|
| 105 |
+
raises(ValueError, lambda: scalar_potential(R[0] * R.y, R))
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def test_scalar_potential_difference():
|
| 109 |
+
origin = Point('O')
|
| 110 |
+
point1 = origin.locatenew('P1', 1*R.x + 2*R.y + 3*R.z)
|
| 111 |
+
point2 = origin.locatenew('P2', 4*R.x + 5*R.y + 6*R.z)
|
| 112 |
+
genericpointR = origin.locatenew('RP', R[0]*R.x + R[1]*R.y + R[2]*R.z)
|
| 113 |
+
genericpointP = origin.locatenew('PP', P[0]*P.x + P[1]*P.y + P[2]*P.z)
|
| 114 |
+
assert scalar_potential_difference(S.Zero, R, point1, point2, \
|
| 115 |
+
origin) == 0
|
| 116 |
+
assert scalar_potential_difference(scalar_field, R, origin, \
|
| 117 |
+
genericpointR, origin) == \
|
| 118 |
+
scalar_field
|
| 119 |
+
assert scalar_potential_difference(grad_field, R, origin, \
|
| 120 |
+
genericpointR, origin) == \
|
| 121 |
+
scalar_field
|
| 122 |
+
assert scalar_potential_difference(grad_field, R, point1, point2,
|
| 123 |
+
origin) == 948
|
| 124 |
+
assert scalar_potential_difference(R[1]*R[2]*R.x + R[0]*R[2]*R.y + \
|
| 125 |
+
R[0]*R[1]*R.z, R, point1,
|
| 126 |
+
genericpointR, origin) == \
|
| 127 |
+
R[0]*R[1]*R[2] - 6
|
| 128 |
+
potential_diff_P = 2*P[2]*(P[0]*sin(q) + P[1]*cos(q))*\
|
| 129 |
+
(P[0]*cos(q) - P[1]*sin(q))**2
|
| 130 |
+
assert scalar_potential_difference(grad_field, P, origin, \
|
| 131 |
+
genericpointP, \
|
| 132 |
+
origin).simplify() == \
|
| 133 |
+
potential_diff_P
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_frame.py
ADDED
|
@@ -0,0 +1,761 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.numbers import pi
|
| 2 |
+
from sympy.core.symbol import symbols
|
| 3 |
+
from sympy.simplify import trigsimp
|
| 4 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 5 |
+
from sympy.matrices.dense import (eye, zeros)
|
| 6 |
+
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
| 7 |
+
from sympy.simplify.simplify import simplify
|
| 8 |
+
from sympy.physics.vector import (ReferenceFrame, Vector, CoordinateSym,
|
| 9 |
+
dynamicsymbols, time_derivative, express,
|
| 10 |
+
dot)
|
| 11 |
+
from sympy.physics.vector.frame import _check_frame
|
| 12 |
+
from sympy.physics.vector.vector import VectorTypeError
|
| 13 |
+
from sympy.testing.pytest import raises
|
| 14 |
+
import warnings
|
| 15 |
+
import pickle
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def test_dict_list():
|
| 19 |
+
|
| 20 |
+
A = ReferenceFrame('A')
|
| 21 |
+
B = ReferenceFrame('B')
|
| 22 |
+
C = ReferenceFrame('C')
|
| 23 |
+
D = ReferenceFrame('D')
|
| 24 |
+
E = ReferenceFrame('E')
|
| 25 |
+
F = ReferenceFrame('F')
|
| 26 |
+
|
| 27 |
+
B.orient_axis(A, A.x, 1.0)
|
| 28 |
+
C.orient_axis(B, B.x, 1.0)
|
| 29 |
+
D.orient_axis(C, C.x, 1.0)
|
| 30 |
+
|
| 31 |
+
assert D._dict_list(A, 0) == [D, C, B, A]
|
| 32 |
+
|
| 33 |
+
E.orient_axis(D, D.x, 1.0)
|
| 34 |
+
|
| 35 |
+
assert C._dict_list(A, 0) == [C, B, A]
|
| 36 |
+
assert C._dict_list(E, 0) == [C, D, E]
|
| 37 |
+
|
| 38 |
+
# only 0, 1, 2 permitted for second argument
|
| 39 |
+
raises(ValueError, lambda: C._dict_list(E, 5))
|
| 40 |
+
# no connecting path
|
| 41 |
+
raises(ValueError, lambda: F._dict_list(A, 0))
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def test_coordinate_vars():
|
| 45 |
+
"""Tests the coordinate variables functionality"""
|
| 46 |
+
A = ReferenceFrame('A')
|
| 47 |
+
assert CoordinateSym('Ax', A, 0) == A[0]
|
| 48 |
+
assert CoordinateSym('Ax', A, 1) == A[1]
|
| 49 |
+
assert CoordinateSym('Ax', A, 2) == A[2]
|
| 50 |
+
raises(ValueError, lambda: CoordinateSym('Ax', A, 3))
|
| 51 |
+
q = dynamicsymbols('q')
|
| 52 |
+
qd = dynamicsymbols('q', 1)
|
| 53 |
+
assert isinstance(A[0], CoordinateSym) and \
|
| 54 |
+
isinstance(A[0], CoordinateSym) and \
|
| 55 |
+
isinstance(A[0], CoordinateSym)
|
| 56 |
+
assert A.variable_map(A) == {A[0]:A[0], A[1]:A[1], A[2]:A[2]}
|
| 57 |
+
assert A[0].frame == A
|
| 58 |
+
B = A.orientnew('B', 'Axis', [q, A.z])
|
| 59 |
+
assert B.variable_map(A) == {B[2]: A[2], B[1]: -A[0]*sin(q) + A[1]*cos(q),
|
| 60 |
+
B[0]: A[0]*cos(q) + A[1]*sin(q)}
|
| 61 |
+
assert A.variable_map(B) == {A[0]: B[0]*cos(q) - B[1]*sin(q),
|
| 62 |
+
A[1]: B[0]*sin(q) + B[1]*cos(q), A[2]: B[2]}
|
| 63 |
+
assert time_derivative(B[0], A) == -A[0]*sin(q)*qd + A[1]*cos(q)*qd
|
| 64 |
+
assert time_derivative(B[1], A) == -A[0]*cos(q)*qd - A[1]*sin(q)*qd
|
| 65 |
+
assert time_derivative(B[2], A) == 0
|
| 66 |
+
assert express(B[0], A, variables=True) == A[0]*cos(q) + A[1]*sin(q)
|
| 67 |
+
assert express(B[1], A, variables=True) == -A[0]*sin(q) + A[1]*cos(q)
|
| 68 |
+
assert express(B[2], A, variables=True) == A[2]
|
| 69 |
+
assert time_derivative(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == A[1]*qd*A.x - A[0]*qd*A.y
|
| 70 |
+
assert time_derivative(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == - B[1]*qd*B.x + B[0]*qd*B.y
|
| 71 |
+
assert express(B[0]*B[1]*B[2], A, variables=True) == \
|
| 72 |
+
A[2]*(-A[0]*sin(q) + A[1]*cos(q))*(A[0]*cos(q) + A[1]*sin(q))
|
| 73 |
+
assert (time_derivative(B[0]*B[1]*B[2], A) -
|
| 74 |
+
(A[2]*(-A[0]**2*cos(2*q) -
|
| 75 |
+
2*A[0]*A[1]*sin(2*q) +
|
| 76 |
+
A[1]**2*cos(2*q))*qd)).trigsimp() == 0
|
| 77 |
+
assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == \
|
| 78 |
+
(B[0]*cos(q) - B[1]*sin(q))*A.x + (B[0]*sin(q) + \
|
| 79 |
+
B[1]*cos(q))*A.y + B[2]*A.z
|
| 80 |
+
assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A,
|
| 81 |
+
variables=True).simplify() == A[0]*A.x + A[1]*A.y + A[2]*A.z
|
| 82 |
+
assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == \
|
| 83 |
+
(A[0]*cos(q) + A[1]*sin(q))*B.x + \
|
| 84 |
+
(-A[0]*sin(q) + A[1]*cos(q))*B.y + A[2]*B.z
|
| 85 |
+
assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B,
|
| 86 |
+
variables=True).simplify() == B[0]*B.x + B[1]*B.y + B[2]*B.z
|
| 87 |
+
N = B.orientnew('N', 'Axis', [-q, B.z])
|
| 88 |
+
assert ({k: v.simplify() for k, v in N.variable_map(A).items()} ==
|
| 89 |
+
{N[0]: A[0], N[2]: A[2], N[1]: A[1]})
|
| 90 |
+
C = A.orientnew('C', 'Axis', [q, A.x + A.y + A.z])
|
| 91 |
+
mapping = A.variable_map(C)
|
| 92 |
+
assert trigsimp(mapping[A[0]]) == (2*C[0]*cos(q)/3 + C[0]/3 -
|
| 93 |
+
2*C[1]*sin(q + pi/6)/3 +
|
| 94 |
+
C[1]/3 - 2*C[2]*cos(q + pi/3)/3 +
|
| 95 |
+
C[2]/3)
|
| 96 |
+
assert trigsimp(mapping[A[1]]) == -2*C[0]*cos(q + pi/3)/3 + \
|
| 97 |
+
C[0]/3 + 2*C[1]*cos(q)/3 + C[1]/3 - 2*C[2]*sin(q + pi/6)/3 + C[2]/3
|
| 98 |
+
assert trigsimp(mapping[A[2]]) == -2*C[0]*sin(q + pi/6)/3 + C[0]/3 - \
|
| 99 |
+
2*C[1]*cos(q + pi/3)/3 + C[1]/3 + 2*C[2]*cos(q)/3 + C[2]/3
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def test_ang_vel():
|
| 103 |
+
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
| 104 |
+
q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1)
|
| 105 |
+
N = ReferenceFrame('N')
|
| 106 |
+
A = N.orientnew('A', 'Axis', [q1, N.z])
|
| 107 |
+
B = A.orientnew('B', 'Axis', [q2, A.x])
|
| 108 |
+
C = B.orientnew('C', 'Axis', [q3, B.y])
|
| 109 |
+
D = N.orientnew('D', 'Axis', [q4, N.y])
|
| 110 |
+
u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
| 111 |
+
assert A.ang_vel_in(N) == (q1d)*A.z
|
| 112 |
+
assert B.ang_vel_in(N) == (q2d)*B.x + (q1d)*A.z
|
| 113 |
+
assert C.ang_vel_in(N) == (q3d)*C.y + (q2d)*B.x + (q1d)*A.z
|
| 114 |
+
|
| 115 |
+
A2 = N.orientnew('A2', 'Axis', [q4, N.y])
|
| 116 |
+
assert N.ang_vel_in(N) == 0
|
| 117 |
+
assert N.ang_vel_in(A) == -q1d*N.z
|
| 118 |
+
assert N.ang_vel_in(B) == -q1d*A.z - q2d*B.x
|
| 119 |
+
assert N.ang_vel_in(C) == -q1d*A.z - q2d*B.x - q3d*B.y
|
| 120 |
+
assert N.ang_vel_in(A2) == -q4d*N.y
|
| 121 |
+
|
| 122 |
+
assert A.ang_vel_in(N) == q1d*N.z
|
| 123 |
+
assert A.ang_vel_in(A) == 0
|
| 124 |
+
assert A.ang_vel_in(B) == - q2d*B.x
|
| 125 |
+
assert A.ang_vel_in(C) == - q2d*B.x - q3d*B.y
|
| 126 |
+
assert A.ang_vel_in(A2) == q1d*N.z - q4d*N.y
|
| 127 |
+
|
| 128 |
+
assert B.ang_vel_in(N) == q1d*A.z + q2d*A.x
|
| 129 |
+
assert B.ang_vel_in(A) == q2d*A.x
|
| 130 |
+
assert B.ang_vel_in(B) == 0
|
| 131 |
+
assert B.ang_vel_in(C) == -q3d*B.y
|
| 132 |
+
assert B.ang_vel_in(A2) == q1d*A.z + q2d*A.x - q4d*N.y
|
| 133 |
+
|
| 134 |
+
assert C.ang_vel_in(N) == q1d*A.z + q2d*A.x + q3d*B.y
|
| 135 |
+
assert C.ang_vel_in(A) == q2d*A.x + q3d*C.y
|
| 136 |
+
assert C.ang_vel_in(B) == q3d*B.y
|
| 137 |
+
assert C.ang_vel_in(C) == 0
|
| 138 |
+
assert C.ang_vel_in(A2) == q1d*A.z + q2d*A.x + q3d*B.y - q4d*N.y
|
| 139 |
+
|
| 140 |
+
assert A2.ang_vel_in(N) == q4d*A2.y
|
| 141 |
+
assert A2.ang_vel_in(A) == q4d*A2.y - q1d*N.z
|
| 142 |
+
assert A2.ang_vel_in(B) == q4d*N.y - q1d*A.z - q2d*A.x
|
| 143 |
+
assert A2.ang_vel_in(C) == q4d*N.y - q1d*A.z - q2d*A.x - q3d*B.y
|
| 144 |
+
assert A2.ang_vel_in(A2) == 0
|
| 145 |
+
|
| 146 |
+
C.set_ang_vel(N, u1*C.x + u2*C.y + u3*C.z)
|
| 147 |
+
assert C.ang_vel_in(N) == (u1)*C.x + (u2)*C.y + (u3)*C.z
|
| 148 |
+
assert N.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z
|
| 149 |
+
assert C.ang_vel_in(D) == (u1)*C.x + (u2)*C.y + (u3)*C.z + (-q4d)*D.y
|
| 150 |
+
assert D.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z + (q4d)*D.y
|
| 151 |
+
|
| 152 |
+
q0 = dynamicsymbols('q0')
|
| 153 |
+
q0d = dynamicsymbols('q0', 1)
|
| 154 |
+
E = N.orientnew('E', 'Quaternion', (q0, q1, q2, q3))
|
| 155 |
+
assert E.ang_vel_in(N) == (
|
| 156 |
+
2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1) * E.x +
|
| 157 |
+
2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2) * E.y +
|
| 158 |
+
2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3) * E.z)
|
| 159 |
+
|
| 160 |
+
F = N.orientnew('F', 'Body', (q1, q2, q3), 313)
|
| 161 |
+
assert F.ang_vel_in(N) == ((sin(q2)*sin(q3)*q1d + cos(q3)*q2d)*F.x +
|
| 162 |
+
(sin(q2)*cos(q3)*q1d - sin(q3)*q2d)*F.y + (cos(q2)*q1d + q3d)*F.z)
|
| 163 |
+
G = N.orientnew('G', 'Axis', (q1, N.x + N.y))
|
| 164 |
+
assert G.ang_vel_in(N) == q1d * (N.x + N.y).normalize()
|
| 165 |
+
assert N.ang_vel_in(G) == -q1d * (N.x + N.y).normalize()
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def test_dcm():
|
| 169 |
+
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
| 170 |
+
N = ReferenceFrame('N')
|
| 171 |
+
A = N.orientnew('A', 'Axis', [q1, N.z])
|
| 172 |
+
B = A.orientnew('B', 'Axis', [q2, A.x])
|
| 173 |
+
C = B.orientnew('C', 'Axis', [q3, B.y])
|
| 174 |
+
D = N.orientnew('D', 'Axis', [q4, N.y])
|
| 175 |
+
E = N.orientnew('E', 'Space', [q1, q2, q3], '123')
|
| 176 |
+
assert N.dcm(C) == Matrix([
|
| 177 |
+
[- sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), - sin(q1) *
|
| 178 |
+
cos(q2), sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)], [sin(q1) *
|
| 179 |
+
cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2), sin(q1) *
|
| 180 |
+
sin(q3) - sin(q2) * cos(q1) * cos(q3)], [- sin(q3) * cos(q2), sin(q2),
|
| 181 |
+
cos(q2) * cos(q3)]])
|
| 182 |
+
# This is a little touchy. Is it ok to use simplify in assert?
|
| 183 |
+
test_mat = D.dcm(C) - Matrix(
|
| 184 |
+
[[cos(q1) * cos(q3) * cos(q4) - sin(q3) * (- sin(q4) * cos(q2) +
|
| 185 |
+
sin(q1) * sin(q2) * cos(q4)), - sin(q2) * sin(q4) - sin(q1) *
|
| 186 |
+
cos(q2) * cos(q4), sin(q3) * cos(q1) * cos(q4) + cos(q3) * (- sin(q4) *
|
| 187 |
+
cos(q2) + sin(q1) * sin(q2) * cos(q4))], [sin(q1) * cos(q3) +
|
| 188 |
+
sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2), sin(q1) * sin(q3) -
|
| 189 |
+
sin(q2) * cos(q1) * cos(q3)], [sin(q4) * cos(q1) * cos(q3) -
|
| 190 |
+
sin(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4)), sin(q2) *
|
| 191 |
+
cos(q4) - sin(q1) * sin(q4) * cos(q2), sin(q3) * sin(q4) * cos(q1) +
|
| 192 |
+
cos(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4))]])
|
| 193 |
+
assert test_mat.expand() == zeros(3, 3)
|
| 194 |
+
assert E.dcm(N) == Matrix(
|
| 195 |
+
[[cos(q2)*cos(q3), sin(q3)*cos(q2), -sin(q2)],
|
| 196 |
+
[sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) +
|
| 197 |
+
cos(q1)*cos(q3), sin(q1)*cos(q2)], [sin(q1)*sin(q3) +
|
| 198 |
+
sin(q2)*cos(q1)*cos(q3), - sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1),
|
| 199 |
+
cos(q1)*cos(q2)]])
|
| 200 |
+
|
| 201 |
+
def test_w_diff_dcm1():
|
| 202 |
+
# Ref:
|
| 203 |
+
# Dynamics Theory and Applications, Kane 1985
|
| 204 |
+
# Sec. 2.1 ANGULAR VELOCITY
|
| 205 |
+
A = ReferenceFrame('A')
|
| 206 |
+
B = ReferenceFrame('B')
|
| 207 |
+
|
| 208 |
+
c11, c12, c13 = dynamicsymbols('C11 C12 C13')
|
| 209 |
+
c21, c22, c23 = dynamicsymbols('C21 C22 C23')
|
| 210 |
+
c31, c32, c33 = dynamicsymbols('C31 C32 C33')
|
| 211 |
+
|
| 212 |
+
c11d, c12d, c13d = dynamicsymbols('C11 C12 C13', level=1)
|
| 213 |
+
c21d, c22d, c23d = dynamicsymbols('C21 C22 C23', level=1)
|
| 214 |
+
c31d, c32d, c33d = dynamicsymbols('C31 C32 C33', level=1)
|
| 215 |
+
|
| 216 |
+
DCM = Matrix([
|
| 217 |
+
[c11, c12, c13],
|
| 218 |
+
[c21, c22, c23],
|
| 219 |
+
[c31, c32, c33]
|
| 220 |
+
])
|
| 221 |
+
|
| 222 |
+
B.orient(A, 'DCM', DCM)
|
| 223 |
+
b1a = (B.x).express(A)
|
| 224 |
+
b2a = (B.y).express(A)
|
| 225 |
+
b3a = (B.z).express(A)
|
| 226 |
+
|
| 227 |
+
# Equation (2.1.1)
|
| 228 |
+
B.set_ang_vel(A, B.x*(dot((b3a).dt(A), B.y))
|
| 229 |
+
+ B.y*(dot((b1a).dt(A), B.z))
|
| 230 |
+
+ B.z*(dot((b2a).dt(A), B.x)))
|
| 231 |
+
|
| 232 |
+
# Equation (2.1.21)
|
| 233 |
+
expr = ( (c12*c13d + c22*c23d + c32*c33d)*B.x
|
| 234 |
+
+ (c13*c11d + c23*c21d + c33*c31d)*B.y
|
| 235 |
+
+ (c11*c12d + c21*c22d + c31*c32d)*B.z)
|
| 236 |
+
assert B.ang_vel_in(A) - expr == 0
|
| 237 |
+
|
| 238 |
+
def test_w_diff_dcm2():
|
| 239 |
+
q1, q2, q3 = dynamicsymbols('q1:4')
|
| 240 |
+
N = ReferenceFrame('N')
|
| 241 |
+
A = N.orientnew('A', 'axis', [q1, N.x])
|
| 242 |
+
B = A.orientnew('B', 'axis', [q2, A.y])
|
| 243 |
+
C = B.orientnew('C', 'axis', [q3, B.z])
|
| 244 |
+
|
| 245 |
+
DCM = C.dcm(N).T
|
| 246 |
+
D = N.orientnew('D', 'DCM', DCM)
|
| 247 |
+
|
| 248 |
+
# Frames D and C are the same ReferenceFrame,
|
| 249 |
+
# since they have equal DCM respect to frame N.
|
| 250 |
+
# Therefore, D and C should have same angle velocity in N.
|
| 251 |
+
assert D.dcm(N) == C.dcm(N) == Matrix([
|
| 252 |
+
[cos(q2)*cos(q3), sin(q1)*sin(q2)*cos(q3) +
|
| 253 |
+
sin(q3)*cos(q1), sin(q1)*sin(q3) -
|
| 254 |
+
sin(q2)*cos(q1)*cos(q3)], [-sin(q3)*cos(q2),
|
| 255 |
+
-sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3),
|
| 256 |
+
sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
|
| 257 |
+
[sin(q2), -sin(q1)*cos(q2), cos(q1)*cos(q2)]])
|
| 258 |
+
assert (D.ang_vel_in(N) - C.ang_vel_in(N)).express(N).simplify() == 0
|
| 259 |
+
|
| 260 |
+
def test_orientnew_respects_parent_class():
|
| 261 |
+
class MyReferenceFrame(ReferenceFrame):
|
| 262 |
+
pass
|
| 263 |
+
B = MyReferenceFrame('B')
|
| 264 |
+
C = B.orientnew('C', 'Axis', [0, B.x])
|
| 265 |
+
assert isinstance(C, MyReferenceFrame)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def test_orientnew_respects_input_indices():
|
| 269 |
+
N = ReferenceFrame('N')
|
| 270 |
+
q1 = dynamicsymbols('q1')
|
| 271 |
+
A = N.orientnew('a', 'Axis', [q1, N.z])
|
| 272 |
+
#modify default indices:
|
| 273 |
+
minds = [x+'1' for x in N.indices]
|
| 274 |
+
B = N.orientnew('b', 'Axis', [q1, N.z], indices=minds)
|
| 275 |
+
|
| 276 |
+
assert N.indices == A.indices
|
| 277 |
+
assert B.indices == minds
|
| 278 |
+
|
| 279 |
+
def test_orientnew_respects_input_latexs():
|
| 280 |
+
N = ReferenceFrame('N')
|
| 281 |
+
q1 = dynamicsymbols('q1')
|
| 282 |
+
A = N.orientnew('a', 'Axis', [q1, N.z])
|
| 283 |
+
|
| 284 |
+
#build default and alternate latex_vecs:
|
| 285 |
+
def_latex_vecs = [(r"\mathbf{\hat{%s}_%s}" % (A.name.lower(),
|
| 286 |
+
A.indices[0])), (r"\mathbf{\hat{%s}_%s}" %
|
| 287 |
+
(A.name.lower(), A.indices[1])),
|
| 288 |
+
(r"\mathbf{\hat{%s}_%s}" % (A.name.lower(),
|
| 289 |
+
A.indices[2]))]
|
| 290 |
+
|
| 291 |
+
name = 'b'
|
| 292 |
+
indices = [x+'1' for x in N.indices]
|
| 293 |
+
new_latex_vecs = [(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
|
| 294 |
+
indices[0])), (r"\mathbf{\hat{%s}_{%s}}" %
|
| 295 |
+
(name.lower(), indices[1])),
|
| 296 |
+
(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
|
| 297 |
+
indices[2]))]
|
| 298 |
+
|
| 299 |
+
B = N.orientnew(name, 'Axis', [q1, N.z], latexs=new_latex_vecs)
|
| 300 |
+
|
| 301 |
+
assert A.latex_vecs == def_latex_vecs
|
| 302 |
+
assert B.latex_vecs == new_latex_vecs
|
| 303 |
+
assert B.indices != indices
|
| 304 |
+
|
| 305 |
+
def test_orientnew_respects_input_variables():
|
| 306 |
+
N = ReferenceFrame('N')
|
| 307 |
+
q1 = dynamicsymbols('q1')
|
| 308 |
+
A = N.orientnew('a', 'Axis', [q1, N.z])
|
| 309 |
+
|
| 310 |
+
#build non-standard variable names
|
| 311 |
+
name = 'b'
|
| 312 |
+
new_variables = ['notb_'+x+'1' for x in N.indices]
|
| 313 |
+
B = N.orientnew(name, 'Axis', [q1, N.z], variables=new_variables)
|
| 314 |
+
|
| 315 |
+
for j,var in enumerate(A.varlist):
|
| 316 |
+
assert var.name == A.name + '_' + A.indices[j]
|
| 317 |
+
|
| 318 |
+
for j,var in enumerate(B.varlist):
|
| 319 |
+
assert var.name == new_variables[j]
|
| 320 |
+
|
| 321 |
+
def test_issue_10348():
|
| 322 |
+
u = dynamicsymbols('u:3')
|
| 323 |
+
I = ReferenceFrame('I')
|
| 324 |
+
I.orientnew('A', 'space', u, 'XYZ')
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
def test_issue_11503():
|
| 328 |
+
A = ReferenceFrame("A")
|
| 329 |
+
A.orientnew("B", "Axis", [35, A.y])
|
| 330 |
+
C = ReferenceFrame("C")
|
| 331 |
+
A.orient(C, "Axis", [70, C.z])
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def test_partial_velocity():
|
| 335 |
+
|
| 336 |
+
N = ReferenceFrame('N')
|
| 337 |
+
A = ReferenceFrame('A')
|
| 338 |
+
|
| 339 |
+
u1, u2 = dynamicsymbols('u1, u2')
|
| 340 |
+
|
| 341 |
+
A.set_ang_vel(N, u1 * A.x + u2 * N.y)
|
| 342 |
+
|
| 343 |
+
assert N.partial_velocity(A, u1) == -A.x
|
| 344 |
+
assert N.partial_velocity(A, u1, u2) == (-A.x, -N.y)
|
| 345 |
+
|
| 346 |
+
assert A.partial_velocity(N, u1) == A.x
|
| 347 |
+
assert A.partial_velocity(N, u1, u2) == (A.x, N.y)
|
| 348 |
+
|
| 349 |
+
assert N.partial_velocity(N, u1) == 0
|
| 350 |
+
assert A.partial_velocity(A, u1) == 0
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def test_issue_11498():
|
| 354 |
+
A = ReferenceFrame('A')
|
| 355 |
+
B = ReferenceFrame('B')
|
| 356 |
+
|
| 357 |
+
# Identity transformation
|
| 358 |
+
A.orient(B, 'DCM', eye(3))
|
| 359 |
+
assert A.dcm(B) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
| 360 |
+
assert B.dcm(A) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
| 361 |
+
|
| 362 |
+
# x -> y
|
| 363 |
+
# y -> -z
|
| 364 |
+
# z -> -x
|
| 365 |
+
A.orient(B, 'DCM', Matrix([[0, 1, 0], [0, 0, -1], [-1, 0, 0]]))
|
| 366 |
+
assert B.dcm(A) == Matrix([[0, 1, 0], [0, 0, -1], [-1, 0, 0]])
|
| 367 |
+
assert A.dcm(B) == Matrix([[0, 0, -1], [1, 0, 0], [0, -1, 0]])
|
| 368 |
+
assert B.dcm(A).T == A.dcm(B)
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def test_reference_frame():
|
| 372 |
+
raises(TypeError, lambda: ReferenceFrame(0))
|
| 373 |
+
raises(TypeError, lambda: ReferenceFrame('N', 0))
|
| 374 |
+
raises(ValueError, lambda: ReferenceFrame('N', [0, 1]))
|
| 375 |
+
raises(TypeError, lambda: ReferenceFrame('N', [0, 1, 2]))
|
| 376 |
+
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], 0))
|
| 377 |
+
raises(ValueError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], [0, 1]))
|
| 378 |
+
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], [0, 1, 2]))
|
| 379 |
+
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'],
|
| 380 |
+
['a', 'b', 'c'], 0))
|
| 381 |
+
raises(ValueError, lambda: ReferenceFrame('N', ['a', 'b', 'c'],
|
| 382 |
+
['a', 'b', 'c'], [0, 1]))
|
| 383 |
+
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'],
|
| 384 |
+
['a', 'b', 'c'], [0, 1, 2]))
|
| 385 |
+
N = ReferenceFrame('N')
|
| 386 |
+
assert N[0] == CoordinateSym('N_x', N, 0)
|
| 387 |
+
assert N[1] == CoordinateSym('N_y', N, 1)
|
| 388 |
+
assert N[2] == CoordinateSym('N_z', N, 2)
|
| 389 |
+
raises(ValueError, lambda: N[3])
|
| 390 |
+
N = ReferenceFrame('N', ['a', 'b', 'c'])
|
| 391 |
+
assert N['a'] == N.x
|
| 392 |
+
assert N['b'] == N.y
|
| 393 |
+
assert N['c'] == N.z
|
| 394 |
+
raises(ValueError, lambda: N['d'])
|
| 395 |
+
assert str(N) == 'N'
|
| 396 |
+
|
| 397 |
+
A = ReferenceFrame('A')
|
| 398 |
+
B = ReferenceFrame('B')
|
| 399 |
+
q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
|
| 400 |
+
raises(TypeError, lambda: A.orient(B, 'DCM', 0))
|
| 401 |
+
raises(TypeError, lambda: B.orient(N, 'Space', [q1, q2, q3], '222'))
|
| 402 |
+
raises(TypeError, lambda: B.orient(N, 'Axis', [q1, N.x + 2 * N.y], '222'))
|
| 403 |
+
raises(TypeError, lambda: B.orient(N, 'Axis', q1))
|
| 404 |
+
raises(IndexError, lambda: B.orient(N, 'Axis', [q1]))
|
| 405 |
+
raises(TypeError, lambda: B.orient(N, 'Quaternion', [q0, q1, q2, q3], '222'))
|
| 406 |
+
raises(TypeError, lambda: B.orient(N, 'Quaternion', q0))
|
| 407 |
+
raises(TypeError, lambda: B.orient(N, 'Quaternion', [q0, q1, q2]))
|
| 408 |
+
raises(NotImplementedError, lambda: B.orient(N, 'Foo', [q0, q1, q2]))
|
| 409 |
+
raises(TypeError, lambda: B.orient(N, 'Body', [q1, q2], '232'))
|
| 410 |
+
raises(TypeError, lambda: B.orient(N, 'Space', [q1, q2], '232'))
|
| 411 |
+
|
| 412 |
+
N.set_ang_acc(B, 0)
|
| 413 |
+
assert N.ang_acc_in(B) == Vector(0)
|
| 414 |
+
N.set_ang_vel(B, 0)
|
| 415 |
+
assert N.ang_vel_in(B) == Vector(0)
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
def test_check_frame():
|
| 419 |
+
raises(VectorTypeError, lambda: _check_frame(0))
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
def test_dcm_diff_16824():
|
| 423 |
+
# NOTE : This is a regression test for the bug introduced in PR 14758,
|
| 424 |
+
# identified in 16824, and solved by PR 16828.
|
| 425 |
+
|
| 426 |
+
# This is the solution to Problem 2.2 on page 264 in Kane & Lenvinson's
|
| 427 |
+
# 1985 book.
|
| 428 |
+
|
| 429 |
+
q1, q2, q3 = dynamicsymbols('q1:4')
|
| 430 |
+
|
| 431 |
+
s1 = sin(q1)
|
| 432 |
+
c1 = cos(q1)
|
| 433 |
+
s2 = sin(q2)
|
| 434 |
+
c2 = cos(q2)
|
| 435 |
+
s3 = sin(q3)
|
| 436 |
+
c3 = cos(q3)
|
| 437 |
+
|
| 438 |
+
dcm = Matrix([[c2*c3, s1*s2*c3 - s3*c1, c1*s2*c3 + s3*s1],
|
| 439 |
+
[c2*s3, s1*s2*s3 + c3*c1, c1*s2*s3 - c3*s1],
|
| 440 |
+
[-s2, s1*c2, c1*c2]])
|
| 441 |
+
|
| 442 |
+
A = ReferenceFrame('A')
|
| 443 |
+
B = ReferenceFrame('B')
|
| 444 |
+
B.orient(A, 'DCM', dcm)
|
| 445 |
+
|
| 446 |
+
AwB = B.ang_vel_in(A)
|
| 447 |
+
|
| 448 |
+
alpha2 = s3*c2*q1.diff() + c3*q2.diff()
|
| 449 |
+
beta2 = s1*c2*q3.diff() + c1*q2.diff()
|
| 450 |
+
|
| 451 |
+
assert simplify(AwB.dot(A.y) - alpha2) == 0
|
| 452 |
+
assert simplify(AwB.dot(B.y) - beta2) == 0
|
| 453 |
+
|
| 454 |
+
def test_orient_explicit():
|
| 455 |
+
cxx, cyy, czz = dynamicsymbols('c_{xx}, c_{yy}, c_{zz}')
|
| 456 |
+
cxy, cxz, cyx = dynamicsymbols('c_{xy}, c_{xz}, c_{yx}')
|
| 457 |
+
cyz, czx, czy = dynamicsymbols('c_{yz}, c_{zx}, c_{zy}')
|
| 458 |
+
dcxx, dcyy, dczz = dynamicsymbols('c_{xx}, c_{yy}, c_{zz}', 1)
|
| 459 |
+
dcxy, dcxz, dcyx = dynamicsymbols('c_{xy}, c_{xz}, c_{yx}', 1)
|
| 460 |
+
dcyz, dczx, dczy = dynamicsymbols('c_{yz}, c_{zx}, c_{zy}', 1)
|
| 461 |
+
A = ReferenceFrame('A')
|
| 462 |
+
B = ReferenceFrame('B')
|
| 463 |
+
B_C_A = Matrix([[cxx, cxy, cxz],
|
| 464 |
+
[cyx, cyy, cyz],
|
| 465 |
+
[czx, czy, czz]])
|
| 466 |
+
B_w_A = ((cyx*dczx + cyy*dczy + cyz*dczz)*B.x +
|
| 467 |
+
(czx*dcxx + czy*dcxy + czz*dcxz)*B.y +
|
| 468 |
+
(cxx*dcyx + cxy*dcyy + cxz*dcyz)*B.z)
|
| 469 |
+
A.orient_explicit(B, B_C_A)
|
| 470 |
+
assert B.dcm(A) == B_C_A
|
| 471 |
+
assert A.ang_vel_in(B) == B_w_A
|
| 472 |
+
assert B.ang_vel_in(A) == -B_w_A
|
| 473 |
+
|
| 474 |
+
def test_orient_dcm():
|
| 475 |
+
cxx, cyy, czz = dynamicsymbols('c_{xx}, c_{yy}, c_{zz}')
|
| 476 |
+
cxy, cxz, cyx = dynamicsymbols('c_{xy}, c_{xz}, c_{yx}')
|
| 477 |
+
cyz, czx, czy = dynamicsymbols('c_{yz}, c_{zx}, c_{zy}')
|
| 478 |
+
B_C_A = Matrix([[cxx, cxy, cxz],
|
| 479 |
+
[cyx, cyy, cyz],
|
| 480 |
+
[czx, czy, czz]])
|
| 481 |
+
A = ReferenceFrame('A')
|
| 482 |
+
B = ReferenceFrame('B')
|
| 483 |
+
B.orient_dcm(A, B_C_A)
|
| 484 |
+
assert B.dcm(A) == Matrix([[cxx, cxy, cxz],
|
| 485 |
+
[cyx, cyy, cyz],
|
| 486 |
+
[czx, czy, czz]])
|
| 487 |
+
|
| 488 |
+
def test_orient_axis():
|
| 489 |
+
A = ReferenceFrame('A')
|
| 490 |
+
B = ReferenceFrame('B')
|
| 491 |
+
A.orient_axis(B,-B.x, 1)
|
| 492 |
+
A1 = A.dcm(B)
|
| 493 |
+
A.orient_axis(B, B.x, -1)
|
| 494 |
+
A2 = A.dcm(B)
|
| 495 |
+
A.orient_axis(B, 1, -B.x)
|
| 496 |
+
A3 = A.dcm(B)
|
| 497 |
+
assert A1 == A2
|
| 498 |
+
assert A2 == A3
|
| 499 |
+
raises(TypeError, lambda: A.orient_axis(B, 1, 1))
|
| 500 |
+
|
| 501 |
+
def test_orient_body():
|
| 502 |
+
A = ReferenceFrame('A')
|
| 503 |
+
B = ReferenceFrame('B')
|
| 504 |
+
B.orient_body_fixed(A, (1,1,0), 'XYX')
|
| 505 |
+
assert B.dcm(A) == Matrix([[cos(1), sin(1)**2, -sin(1)*cos(1)], [0, cos(1), sin(1)], [sin(1), -sin(1)*cos(1), cos(1)**2]])
|
| 506 |
+
|
| 507 |
+
|
| 508 |
+
def test_orient_body_advanced():
|
| 509 |
+
q1, q2, q3 = dynamicsymbols('q1:4')
|
| 510 |
+
c1, c2, c3 = symbols('c1:4')
|
| 511 |
+
u1, u2, u3 = dynamicsymbols('q1:4', 1)
|
| 512 |
+
|
| 513 |
+
# Test with everything as dynamicsymbols
|
| 514 |
+
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
| 515 |
+
B.orient_body_fixed(A, (q1, q2, q3), 'zxy')
|
| 516 |
+
assert A.dcm(B) == Matrix([
|
| 517 |
+
[-sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), -sin(q1) * cos(q2),
|
| 518 |
+
sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)],
|
| 519 |
+
[sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2),
|
| 520 |
+
sin(q1) * sin(q3) - sin(q2) * cos(q1) * cos(q3)],
|
| 521 |
+
[-sin(q3) * cos(q2), sin(q2), cos(q2) * cos(q3)]])
|
| 522 |
+
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
| 523 |
+
[-sin(q3) * cos(q2) * u1 + cos(q3) * u2],
|
| 524 |
+
[sin(q2) * u1 + u3],
|
| 525 |
+
[sin(q3) * u2 + cos(q2) * cos(q3) * u1]])
|
| 526 |
+
|
| 527 |
+
# Test with constant symbol
|
| 528 |
+
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
| 529 |
+
B.orient_body_fixed(A, (q1, c2, q3), 131)
|
| 530 |
+
assert A.dcm(B) == Matrix([
|
| 531 |
+
[cos(c2), -sin(c2) * cos(q3), sin(c2) * sin(q3)],
|
| 532 |
+
[sin(c2) * cos(q1), -sin(q1) * sin(q3) + cos(c2) * cos(q1) * cos(q3),
|
| 533 |
+
-sin(q1) * cos(q3) - sin(q3) * cos(c2) * cos(q1)],
|
| 534 |
+
[sin(c2) * sin(q1), sin(q1) * cos(c2) * cos(q3) + sin(q3) * cos(q1),
|
| 535 |
+
-sin(q1) * sin(q3) * cos(c2) + cos(q1) * cos(q3)]])
|
| 536 |
+
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
| 537 |
+
[cos(c2) * u1 + u3],
|
| 538 |
+
[-sin(c2) * cos(q3) * u1],
|
| 539 |
+
[sin(c2) * sin(q3) * u1]])
|
| 540 |
+
|
| 541 |
+
# Test all symbols not time dependent
|
| 542 |
+
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
| 543 |
+
B.orient_body_fixed(A, (c1, c2, c3), 123)
|
| 544 |
+
assert B.ang_vel_in(A) == Vector(0)
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
def test_orient_space_advanced():
|
| 548 |
+
# space fixed is in the end like body fixed only in opposite order
|
| 549 |
+
q1, q2, q3 = dynamicsymbols('q1:4')
|
| 550 |
+
c1, c2, c3 = symbols('c1:4')
|
| 551 |
+
u1, u2, u3 = dynamicsymbols('q1:4', 1)
|
| 552 |
+
|
| 553 |
+
# Test with everything as dynamicsymbols
|
| 554 |
+
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
| 555 |
+
B.orient_space_fixed(A, (q3, q2, q1), 'yxz')
|
| 556 |
+
assert A.dcm(B) == Matrix([
|
| 557 |
+
[-sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), -sin(q1) * cos(q2),
|
| 558 |
+
sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)],
|
| 559 |
+
[sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2),
|
| 560 |
+
sin(q1) * sin(q3) - sin(q2) * cos(q1) * cos(q3)],
|
| 561 |
+
[-sin(q3) * cos(q2), sin(q2), cos(q2) * cos(q3)]])
|
| 562 |
+
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
| 563 |
+
[-sin(q3) * cos(q2) * u1 + cos(q3) * u2],
|
| 564 |
+
[sin(q2) * u1 + u3],
|
| 565 |
+
[sin(q3) * u2 + cos(q2) * cos(q3) * u1]])
|
| 566 |
+
|
| 567 |
+
# Test with constant symbol
|
| 568 |
+
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
| 569 |
+
B.orient_space_fixed(A, (q3, c2, q1), 131)
|
| 570 |
+
assert A.dcm(B) == Matrix([
|
| 571 |
+
[cos(c2), -sin(c2) * cos(q3), sin(c2) * sin(q3)],
|
| 572 |
+
[sin(c2) * cos(q1), -sin(q1) * sin(q3) + cos(c2) * cos(q1) * cos(q3),
|
| 573 |
+
-sin(q1) * cos(q3) - sin(q3) * cos(c2) * cos(q1)],
|
| 574 |
+
[sin(c2) * sin(q1), sin(q1) * cos(c2) * cos(q3) + sin(q3) * cos(q1),
|
| 575 |
+
-sin(q1) * sin(q3) * cos(c2) + cos(q1) * cos(q3)]])
|
| 576 |
+
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
| 577 |
+
[cos(c2) * u1 + u3],
|
| 578 |
+
[-sin(c2) * cos(q3) * u1],
|
| 579 |
+
[sin(c2) * sin(q3) * u1]])
|
| 580 |
+
|
| 581 |
+
# Test all symbols not time dependent
|
| 582 |
+
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
| 583 |
+
B.orient_space_fixed(A, (c1, c2, c3), 123)
|
| 584 |
+
assert B.ang_vel_in(A) == Vector(0)
|
| 585 |
+
|
| 586 |
+
|
| 587 |
+
def test_orient_body_simple_ang_vel():
|
| 588 |
+
"""This test ensures that the simplest form of that linear system solution
|
| 589 |
+
is returned, thus the == for the expression comparison."""
|
| 590 |
+
|
| 591 |
+
psi, theta, phi = dynamicsymbols('psi, theta, varphi')
|
| 592 |
+
t = dynamicsymbols._t
|
| 593 |
+
A = ReferenceFrame('A')
|
| 594 |
+
B = ReferenceFrame('B')
|
| 595 |
+
B.orient_body_fixed(A, (psi, theta, phi), 'ZXZ')
|
| 596 |
+
A_w_B = B.ang_vel_in(A)
|
| 597 |
+
assert A_w_B.args[0][1] == B
|
| 598 |
+
assert A_w_B.args[0][0][0] == (sin(theta)*sin(phi)*psi.diff(t) +
|
| 599 |
+
cos(phi)*theta.diff(t))
|
| 600 |
+
assert A_w_B.args[0][0][1] == (sin(theta)*cos(phi)*psi.diff(t) -
|
| 601 |
+
sin(phi)*theta.diff(t))
|
| 602 |
+
assert A_w_B.args[0][0][2] == cos(theta)*psi.diff(t) + phi.diff(t)
|
| 603 |
+
|
| 604 |
+
|
| 605 |
+
def test_orient_space():
|
| 606 |
+
A = ReferenceFrame('A')
|
| 607 |
+
B = ReferenceFrame('B')
|
| 608 |
+
B.orient_space_fixed(A, (0,0,0), '123')
|
| 609 |
+
assert B.dcm(A) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
| 610 |
+
|
| 611 |
+
def test_orient_quaternion():
|
| 612 |
+
A = ReferenceFrame('A')
|
| 613 |
+
B = ReferenceFrame('B')
|
| 614 |
+
B.orient_quaternion(A, (0,0,0,0))
|
| 615 |
+
assert B.dcm(A) == Matrix([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
|
| 616 |
+
|
| 617 |
+
def test_looped_frame_warning():
|
| 618 |
+
A = ReferenceFrame('A')
|
| 619 |
+
B = ReferenceFrame('B')
|
| 620 |
+
C = ReferenceFrame('C')
|
| 621 |
+
|
| 622 |
+
a, b, c = symbols('a b c')
|
| 623 |
+
B.orient_axis(A, A.x, a)
|
| 624 |
+
C.orient_axis(B, B.x, b)
|
| 625 |
+
|
| 626 |
+
with warnings.catch_warnings(record = True) as w:
|
| 627 |
+
warnings.simplefilter("always")
|
| 628 |
+
A.orient_axis(C, C.x, c)
|
| 629 |
+
assert issubclass(w[-1].category, UserWarning)
|
| 630 |
+
assert 'Loops are defined among the orientation of frames. ' + \
|
| 631 |
+
'This is likely not desired and may cause errors in your calculations.' in str(w[-1].message)
|
| 632 |
+
|
| 633 |
+
def test_frame_dict():
|
| 634 |
+
A = ReferenceFrame('A')
|
| 635 |
+
B = ReferenceFrame('B')
|
| 636 |
+
C = ReferenceFrame('C')
|
| 637 |
+
|
| 638 |
+
a, b, c = symbols('a b c')
|
| 639 |
+
|
| 640 |
+
B.orient_axis(A, A.x, a)
|
| 641 |
+
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])}
|
| 642 |
+
assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]])}
|
| 643 |
+
assert C._dcm_dict == {}
|
| 644 |
+
|
| 645 |
+
B.orient_axis(C, C.x, b)
|
| 646 |
+
# Previous relation is not wiped
|
| 647 |
+
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])}
|
| 648 |
+
assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]]), \
|
| 649 |
+
C: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]])}
|
| 650 |
+
assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
| 651 |
+
|
| 652 |
+
A.orient_axis(B, B.x, c)
|
| 653 |
+
# Previous relation is updated
|
| 654 |
+
assert B._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]]),\
|
| 655 |
+
A: Matrix([[1, 0, 0],[0, cos(c), -sin(c)],[0, sin(c), cos(c)]])}
|
| 656 |
+
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(c), sin(c)],[0, -sin(c), cos(c)]])}
|
| 657 |
+
assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
| 658 |
+
|
| 659 |
+
def test_dcm_cache_dict():
|
| 660 |
+
A = ReferenceFrame('A')
|
| 661 |
+
B = ReferenceFrame('B')
|
| 662 |
+
C = ReferenceFrame('C')
|
| 663 |
+
D = ReferenceFrame('D')
|
| 664 |
+
|
| 665 |
+
a, b, c = symbols('a b c')
|
| 666 |
+
|
| 667 |
+
B.orient_axis(A, A.x, a)
|
| 668 |
+
C.orient_axis(B, B.x, b)
|
| 669 |
+
D.orient_axis(C, C.x, c)
|
| 670 |
+
|
| 671 |
+
assert D._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(c), sin(c)],[0, -sin(c), cos(c)]])}
|
| 672 |
+
assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]]), \
|
| 673 |
+
D: Matrix([[1, 0, 0],[0, cos(c), -sin(c)],[0, sin(c), cos(c)]])}
|
| 674 |
+
assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]]), \
|
| 675 |
+
C: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
| 676 |
+
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])}
|
| 677 |
+
|
| 678 |
+
assert D._dcm_dict == D._dcm_cache
|
| 679 |
+
|
| 680 |
+
D.dcm(A) # Check calculated dcm relation is stored in _dcm_cache and not in _dcm_dict
|
| 681 |
+
assert list(A._dcm_cache.keys()) == [A, B, D]
|
| 682 |
+
assert list(D._dcm_cache.keys()) == [C, A]
|
| 683 |
+
assert list(A._dcm_dict.keys()) == [B]
|
| 684 |
+
assert list(D._dcm_dict.keys()) == [C]
|
| 685 |
+
assert A._dcm_dict != A._dcm_cache
|
| 686 |
+
|
| 687 |
+
A.orient_axis(B, B.x, b) # _dcm_cache of A is wiped out and new relation is stored.
|
| 688 |
+
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]])}
|
| 689 |
+
assert A._dcm_dict == A._dcm_cache
|
| 690 |
+
assert B._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]]), \
|
| 691 |
+
A: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
| 692 |
+
|
| 693 |
+
def test_xx_dyad():
|
| 694 |
+
N = ReferenceFrame('N')
|
| 695 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 696 |
+
assert N.xx == Vector.outer(N.x, N.x)
|
| 697 |
+
assert F.xx == Vector.outer(F.x, F.x)
|
| 698 |
+
|
| 699 |
+
def test_xy_dyad():
|
| 700 |
+
N = ReferenceFrame('N')
|
| 701 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 702 |
+
assert N.xy == Vector.outer(N.x, N.y)
|
| 703 |
+
assert F.xy == Vector.outer(F.x, F.y)
|
| 704 |
+
|
| 705 |
+
def test_xz_dyad():
|
| 706 |
+
N = ReferenceFrame('N')
|
| 707 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 708 |
+
assert N.xz == Vector.outer(N.x, N.z)
|
| 709 |
+
assert F.xz == Vector.outer(F.x, F.z)
|
| 710 |
+
|
| 711 |
+
def test_yx_dyad():
|
| 712 |
+
N = ReferenceFrame('N')
|
| 713 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 714 |
+
assert N.yx == Vector.outer(N.y, N.x)
|
| 715 |
+
assert F.yx == Vector.outer(F.y, F.x)
|
| 716 |
+
|
| 717 |
+
def test_yy_dyad():
|
| 718 |
+
N = ReferenceFrame('N')
|
| 719 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 720 |
+
assert N.yy == Vector.outer(N.y, N.y)
|
| 721 |
+
assert F.yy == Vector.outer(F.y, F.y)
|
| 722 |
+
|
| 723 |
+
def test_yz_dyad():
|
| 724 |
+
N = ReferenceFrame('N')
|
| 725 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 726 |
+
assert N.yz == Vector.outer(N.y, N.z)
|
| 727 |
+
assert F.yz == Vector.outer(F.y, F.z)
|
| 728 |
+
|
| 729 |
+
def test_zx_dyad():
|
| 730 |
+
N = ReferenceFrame('N')
|
| 731 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 732 |
+
assert N.zx == Vector.outer(N.z, N.x)
|
| 733 |
+
assert F.zx == Vector.outer(F.z, F.x)
|
| 734 |
+
|
| 735 |
+
def test_zy_dyad():
|
| 736 |
+
N = ReferenceFrame('N')
|
| 737 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 738 |
+
assert N.zy == Vector.outer(N.z, N.y)
|
| 739 |
+
assert F.zy == Vector.outer(F.z, F.y)
|
| 740 |
+
|
| 741 |
+
def test_zz_dyad():
|
| 742 |
+
N = ReferenceFrame('N')
|
| 743 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 744 |
+
assert N.zz == Vector.outer(N.z, N.z)
|
| 745 |
+
assert F.zz == Vector.outer(F.z, F.z)
|
| 746 |
+
|
| 747 |
+
def test_unit_dyadic():
|
| 748 |
+
N = ReferenceFrame('N')
|
| 749 |
+
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
| 750 |
+
assert N.u == N.xx + N.yy + N.zz
|
| 751 |
+
assert F.u == F.xx + F.yy + F.zz
|
| 752 |
+
|
| 753 |
+
|
| 754 |
+
def test_pickle_frame():
|
| 755 |
+
N = ReferenceFrame('N')
|
| 756 |
+
A = ReferenceFrame('A')
|
| 757 |
+
A.orient_axis(N, N.x, 1)
|
| 758 |
+
A_C_N = A.dcm(N)
|
| 759 |
+
N1 = pickle.loads(pickle.dumps(N))
|
| 760 |
+
A1 = tuple(N1._dcm_dict.keys())[0]
|
| 761 |
+
assert A1.dcm(N1) == A_C_N
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_functions.py
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.numbers import pi
|
| 2 |
+
from sympy.core.singleton import S
|
| 3 |
+
from sympy.core.symbol import symbols
|
| 4 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 5 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 6 |
+
from sympy.integrals.integrals import Integral
|
| 7 |
+
from sympy.physics.vector import Dyadic, Point, ReferenceFrame, Vector
|
| 8 |
+
from sympy.physics.vector.functions import (cross, dot, express,
|
| 9 |
+
time_derivative,
|
| 10 |
+
kinematic_equations, outer,
|
| 11 |
+
partial_velocity,
|
| 12 |
+
get_motion_params, dynamicsymbols)
|
| 13 |
+
from sympy.simplify import trigsimp
|
| 14 |
+
from sympy.testing.pytest import raises
|
| 15 |
+
|
| 16 |
+
q1, q2, q3, q4, q5 = symbols('q1 q2 q3 q4 q5')
|
| 17 |
+
N = ReferenceFrame('N')
|
| 18 |
+
A = N.orientnew('A', 'Axis', [q1, N.z])
|
| 19 |
+
B = A.orientnew('B', 'Axis', [q2, A.x])
|
| 20 |
+
C = B.orientnew('C', 'Axis', [q3, B.y])
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def test_dot():
|
| 24 |
+
assert dot(A.x, A.x) == 1
|
| 25 |
+
assert dot(A.x, A.y) == 0
|
| 26 |
+
assert dot(A.x, A.z) == 0
|
| 27 |
+
|
| 28 |
+
assert dot(A.y, A.x) == 0
|
| 29 |
+
assert dot(A.y, A.y) == 1
|
| 30 |
+
assert dot(A.y, A.z) == 0
|
| 31 |
+
|
| 32 |
+
assert dot(A.z, A.x) == 0
|
| 33 |
+
assert dot(A.z, A.y) == 0
|
| 34 |
+
assert dot(A.z, A.z) == 1
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def test_dot_different_frames():
|
| 38 |
+
assert dot(N.x, A.x) == cos(q1)
|
| 39 |
+
assert dot(N.x, A.y) == -sin(q1)
|
| 40 |
+
assert dot(N.x, A.z) == 0
|
| 41 |
+
assert dot(N.y, A.x) == sin(q1)
|
| 42 |
+
assert dot(N.y, A.y) == cos(q1)
|
| 43 |
+
assert dot(N.y, A.z) == 0
|
| 44 |
+
assert dot(N.z, A.x) == 0
|
| 45 |
+
assert dot(N.z, A.y) == 0
|
| 46 |
+
assert dot(N.z, A.z) == 1
|
| 47 |
+
|
| 48 |
+
assert trigsimp(dot(N.x, A.x + A.y)) == sqrt(2)*cos(q1 + pi/4)
|
| 49 |
+
assert trigsimp(dot(N.x, A.x + A.y)) == trigsimp(dot(A.x + A.y, N.x))
|
| 50 |
+
|
| 51 |
+
assert dot(A.x, C.x) == cos(q3)
|
| 52 |
+
assert dot(A.x, C.y) == 0
|
| 53 |
+
assert dot(A.x, C.z) == sin(q3)
|
| 54 |
+
assert dot(A.y, C.x) == sin(q2)*sin(q3)
|
| 55 |
+
assert dot(A.y, C.y) == cos(q2)
|
| 56 |
+
assert dot(A.y, C.z) == -sin(q2)*cos(q3)
|
| 57 |
+
assert dot(A.z, C.x) == -cos(q2)*sin(q3)
|
| 58 |
+
assert dot(A.z, C.y) == sin(q2)
|
| 59 |
+
assert dot(A.z, C.z) == cos(q2)*cos(q3)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def test_cross():
|
| 63 |
+
assert cross(A.x, A.x) == 0
|
| 64 |
+
assert cross(A.x, A.y) == A.z
|
| 65 |
+
assert cross(A.x, A.z) == -A.y
|
| 66 |
+
|
| 67 |
+
assert cross(A.y, A.x) == -A.z
|
| 68 |
+
assert cross(A.y, A.y) == 0
|
| 69 |
+
assert cross(A.y, A.z) == A.x
|
| 70 |
+
|
| 71 |
+
assert cross(A.z, A.x) == A.y
|
| 72 |
+
assert cross(A.z, A.y) == -A.x
|
| 73 |
+
assert cross(A.z, A.z) == 0
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def test_cross_different_frames():
|
| 77 |
+
assert cross(N.x, A.x) == sin(q1)*A.z
|
| 78 |
+
assert cross(N.x, A.y) == cos(q1)*A.z
|
| 79 |
+
assert cross(N.x, A.z) == -sin(q1)*A.x - cos(q1)*A.y
|
| 80 |
+
assert cross(N.y, A.x) == -cos(q1)*A.z
|
| 81 |
+
assert cross(N.y, A.y) == sin(q1)*A.z
|
| 82 |
+
assert cross(N.y, A.z) == cos(q1)*A.x - sin(q1)*A.y
|
| 83 |
+
assert cross(N.z, A.x) == A.y
|
| 84 |
+
assert cross(N.z, A.y) == -A.x
|
| 85 |
+
assert cross(N.z, A.z) == 0
|
| 86 |
+
|
| 87 |
+
assert cross(N.x, A.x) == sin(q1)*A.z
|
| 88 |
+
assert cross(N.x, A.y) == cos(q1)*A.z
|
| 89 |
+
assert cross(N.x, A.x + A.y) == sin(q1)*A.z + cos(q1)*A.z
|
| 90 |
+
assert cross(A.x + A.y, N.x) == -sin(q1)*A.z - cos(q1)*A.z
|
| 91 |
+
|
| 92 |
+
assert cross(A.x, C.x) == sin(q3)*C.y
|
| 93 |
+
assert cross(A.x, C.y) == -sin(q3)*C.x + cos(q3)*C.z
|
| 94 |
+
assert cross(A.x, C.z) == -cos(q3)*C.y
|
| 95 |
+
assert cross(C.x, A.x) == -sin(q3)*C.y
|
| 96 |
+
assert cross(C.y, A.x).express(C).simplify() == sin(q3)*C.x - cos(q3)*C.z
|
| 97 |
+
assert cross(C.z, A.x) == cos(q3)*C.y
|
| 98 |
+
|
| 99 |
+
def test_operator_match():
|
| 100 |
+
"""Test that the output of dot, cross, outer functions match
|
| 101 |
+
operator behavior.
|
| 102 |
+
"""
|
| 103 |
+
A = ReferenceFrame('A')
|
| 104 |
+
v = A.x + A.y
|
| 105 |
+
d = v | v
|
| 106 |
+
zerov = Vector(0)
|
| 107 |
+
zerod = Dyadic(0)
|
| 108 |
+
|
| 109 |
+
# dot products
|
| 110 |
+
assert d & d == dot(d, d)
|
| 111 |
+
assert d & zerod == dot(d, zerod)
|
| 112 |
+
assert zerod & d == dot(zerod, d)
|
| 113 |
+
assert d & v == dot(d, v)
|
| 114 |
+
assert v & d == dot(v, d)
|
| 115 |
+
assert d & zerov == dot(d, zerov)
|
| 116 |
+
assert zerov & d == dot(zerov, d)
|
| 117 |
+
raises(TypeError, lambda: dot(d, S.Zero))
|
| 118 |
+
raises(TypeError, lambda: dot(S.Zero, d))
|
| 119 |
+
raises(TypeError, lambda: dot(d, 0))
|
| 120 |
+
raises(TypeError, lambda: dot(0, d))
|
| 121 |
+
assert v & v == dot(v, v)
|
| 122 |
+
assert v & zerov == dot(v, zerov)
|
| 123 |
+
assert zerov & v == dot(zerov, v)
|
| 124 |
+
raises(TypeError, lambda: dot(v, S.Zero))
|
| 125 |
+
raises(TypeError, lambda: dot(S.Zero, v))
|
| 126 |
+
raises(TypeError, lambda: dot(v, 0))
|
| 127 |
+
raises(TypeError, lambda: dot(0, v))
|
| 128 |
+
|
| 129 |
+
# cross products
|
| 130 |
+
raises(TypeError, lambda: cross(d, d))
|
| 131 |
+
raises(TypeError, lambda: cross(d, zerod))
|
| 132 |
+
raises(TypeError, lambda: cross(zerod, d))
|
| 133 |
+
assert d ^ v == cross(d, v)
|
| 134 |
+
assert v ^ d == cross(v, d)
|
| 135 |
+
assert d ^ zerov == cross(d, zerov)
|
| 136 |
+
assert zerov ^ d == cross(zerov, d)
|
| 137 |
+
assert zerov ^ d == cross(zerov, d)
|
| 138 |
+
raises(TypeError, lambda: cross(d, S.Zero))
|
| 139 |
+
raises(TypeError, lambda: cross(S.Zero, d))
|
| 140 |
+
raises(TypeError, lambda: cross(d, 0))
|
| 141 |
+
raises(TypeError, lambda: cross(0, d))
|
| 142 |
+
assert v ^ v == cross(v, v)
|
| 143 |
+
assert v ^ zerov == cross(v, zerov)
|
| 144 |
+
assert zerov ^ v == cross(zerov, v)
|
| 145 |
+
raises(TypeError, lambda: cross(v, S.Zero))
|
| 146 |
+
raises(TypeError, lambda: cross(S.Zero, v))
|
| 147 |
+
raises(TypeError, lambda: cross(v, 0))
|
| 148 |
+
raises(TypeError, lambda: cross(0, v))
|
| 149 |
+
|
| 150 |
+
# outer products
|
| 151 |
+
raises(TypeError, lambda: outer(d, d))
|
| 152 |
+
raises(TypeError, lambda: outer(d, zerod))
|
| 153 |
+
raises(TypeError, lambda: outer(zerod, d))
|
| 154 |
+
raises(TypeError, lambda: outer(d, v))
|
| 155 |
+
raises(TypeError, lambda: outer(v, d))
|
| 156 |
+
raises(TypeError, lambda: outer(d, zerov))
|
| 157 |
+
raises(TypeError, lambda: outer(zerov, d))
|
| 158 |
+
raises(TypeError, lambda: outer(zerov, d))
|
| 159 |
+
raises(TypeError, lambda: outer(d, S.Zero))
|
| 160 |
+
raises(TypeError, lambda: outer(S.Zero, d))
|
| 161 |
+
raises(TypeError, lambda: outer(d, 0))
|
| 162 |
+
raises(TypeError, lambda: outer(0, d))
|
| 163 |
+
assert v | v == outer(v, v)
|
| 164 |
+
assert v | zerov == outer(v, zerov)
|
| 165 |
+
assert zerov | v == outer(zerov, v)
|
| 166 |
+
raises(TypeError, lambda: outer(v, S.Zero))
|
| 167 |
+
raises(TypeError, lambda: outer(S.Zero, v))
|
| 168 |
+
raises(TypeError, lambda: outer(v, 0))
|
| 169 |
+
raises(TypeError, lambda: outer(0, v))
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def test_express():
|
| 173 |
+
assert express(Vector(0), N) == Vector(0)
|
| 174 |
+
assert express(S.Zero, N) is S.Zero
|
| 175 |
+
assert express(A.x, C) == cos(q3)*C.x + sin(q3)*C.z
|
| 176 |
+
assert express(A.y, C) == sin(q2)*sin(q3)*C.x + cos(q2)*C.y - \
|
| 177 |
+
sin(q2)*cos(q3)*C.z
|
| 178 |
+
assert express(A.z, C) == -sin(q3)*cos(q2)*C.x + sin(q2)*C.y + \
|
| 179 |
+
cos(q2)*cos(q3)*C.z
|
| 180 |
+
assert express(A.x, N) == cos(q1)*N.x + sin(q1)*N.y
|
| 181 |
+
assert express(A.y, N) == -sin(q1)*N.x + cos(q1)*N.y
|
| 182 |
+
assert express(A.z, N) == N.z
|
| 183 |
+
assert express(A.x, A) == A.x
|
| 184 |
+
assert express(A.y, A) == A.y
|
| 185 |
+
assert express(A.z, A) == A.z
|
| 186 |
+
assert express(A.x, B) == B.x
|
| 187 |
+
assert express(A.y, B) == cos(q2)*B.y - sin(q2)*B.z
|
| 188 |
+
assert express(A.z, B) == sin(q2)*B.y + cos(q2)*B.z
|
| 189 |
+
assert express(A.x, C) == cos(q3)*C.x + sin(q3)*C.z
|
| 190 |
+
assert express(A.y, C) == sin(q2)*sin(q3)*C.x + cos(q2)*C.y - \
|
| 191 |
+
sin(q2)*cos(q3)*C.z
|
| 192 |
+
assert express(A.z, C) == -sin(q3)*cos(q2)*C.x + sin(q2)*C.y + \
|
| 193 |
+
cos(q2)*cos(q3)*C.z
|
| 194 |
+
# Check to make sure UnitVectors get converted properly
|
| 195 |
+
assert express(N.x, N) == N.x
|
| 196 |
+
assert express(N.y, N) == N.y
|
| 197 |
+
assert express(N.z, N) == N.z
|
| 198 |
+
assert express(N.x, A) == (cos(q1)*A.x - sin(q1)*A.y)
|
| 199 |
+
assert express(N.y, A) == (sin(q1)*A.x + cos(q1)*A.y)
|
| 200 |
+
assert express(N.z, A) == A.z
|
| 201 |
+
assert express(N.x, B) == (cos(q1)*B.x - sin(q1)*cos(q2)*B.y +
|
| 202 |
+
sin(q1)*sin(q2)*B.z)
|
| 203 |
+
assert express(N.y, B) == (sin(q1)*B.x + cos(q1)*cos(q2)*B.y -
|
| 204 |
+
sin(q2)*cos(q1)*B.z)
|
| 205 |
+
assert express(N.z, B) == (sin(q2)*B.y + cos(q2)*B.z)
|
| 206 |
+
assert express(N.x, C) == (
|
| 207 |
+
(cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*C.x -
|
| 208 |
+
sin(q1)*cos(q2)*C.y +
|
| 209 |
+
(sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*C.z)
|
| 210 |
+
assert express(N.y, C) == (
|
| 211 |
+
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*C.x +
|
| 212 |
+
cos(q1)*cos(q2)*C.y +
|
| 213 |
+
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*C.z)
|
| 214 |
+
assert express(N.z, C) == (-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
| 215 |
+
cos(q2)*cos(q3)*C.z)
|
| 216 |
+
|
| 217 |
+
assert express(A.x, N) == (cos(q1)*N.x + sin(q1)*N.y)
|
| 218 |
+
assert express(A.y, N) == (-sin(q1)*N.x + cos(q1)*N.y)
|
| 219 |
+
assert express(A.z, N) == N.z
|
| 220 |
+
assert express(A.x, A) == A.x
|
| 221 |
+
assert express(A.y, A) == A.y
|
| 222 |
+
assert express(A.z, A) == A.z
|
| 223 |
+
assert express(A.x, B) == B.x
|
| 224 |
+
assert express(A.y, B) == (cos(q2)*B.y - sin(q2)*B.z)
|
| 225 |
+
assert express(A.z, B) == (sin(q2)*B.y + cos(q2)*B.z)
|
| 226 |
+
assert express(A.x, C) == (cos(q3)*C.x + sin(q3)*C.z)
|
| 227 |
+
assert express(A.y, C) == (sin(q2)*sin(q3)*C.x + cos(q2)*C.y -
|
| 228 |
+
sin(q2)*cos(q3)*C.z)
|
| 229 |
+
assert express(A.z, C) == (-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
| 230 |
+
cos(q2)*cos(q3)*C.z)
|
| 231 |
+
|
| 232 |
+
assert express(B.x, N) == (cos(q1)*N.x + sin(q1)*N.y)
|
| 233 |
+
assert express(B.y, N) == (-sin(q1)*cos(q2)*N.x +
|
| 234 |
+
cos(q1)*cos(q2)*N.y + sin(q2)*N.z)
|
| 235 |
+
assert express(B.z, N) == (sin(q1)*sin(q2)*N.x -
|
| 236 |
+
sin(q2)*cos(q1)*N.y + cos(q2)*N.z)
|
| 237 |
+
assert express(B.x, A) == A.x
|
| 238 |
+
assert express(B.y, A) == (cos(q2)*A.y + sin(q2)*A.z)
|
| 239 |
+
assert express(B.z, A) == (-sin(q2)*A.y + cos(q2)*A.z)
|
| 240 |
+
assert express(B.x, B) == B.x
|
| 241 |
+
assert express(B.y, B) == B.y
|
| 242 |
+
assert express(B.z, B) == B.z
|
| 243 |
+
assert express(B.x, C) == (cos(q3)*C.x + sin(q3)*C.z)
|
| 244 |
+
assert express(B.y, C) == C.y
|
| 245 |
+
assert express(B.z, C) == (-sin(q3)*C.x + cos(q3)*C.z)
|
| 246 |
+
|
| 247 |
+
assert express(C.x, N) == (
|
| 248 |
+
(cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*N.x +
|
| 249 |
+
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*N.y -
|
| 250 |
+
sin(q3)*cos(q2)*N.z)
|
| 251 |
+
assert express(C.y, N) == (
|
| 252 |
+
-sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z)
|
| 253 |
+
assert express(C.z, N) == (
|
| 254 |
+
(sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*N.x +
|
| 255 |
+
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*N.y +
|
| 256 |
+
cos(q2)*cos(q3)*N.z)
|
| 257 |
+
assert express(C.x, A) == (cos(q3)*A.x + sin(q2)*sin(q3)*A.y -
|
| 258 |
+
sin(q3)*cos(q2)*A.z)
|
| 259 |
+
assert express(C.y, A) == (cos(q2)*A.y + sin(q2)*A.z)
|
| 260 |
+
assert express(C.z, A) == (sin(q3)*A.x - sin(q2)*cos(q3)*A.y +
|
| 261 |
+
cos(q2)*cos(q3)*A.z)
|
| 262 |
+
assert express(C.x, B) == (cos(q3)*B.x - sin(q3)*B.z)
|
| 263 |
+
assert express(C.y, B) == B.y
|
| 264 |
+
assert express(C.z, B) == (sin(q3)*B.x + cos(q3)*B.z)
|
| 265 |
+
assert express(C.x, C) == C.x
|
| 266 |
+
assert express(C.y, C) == C.y
|
| 267 |
+
assert express(C.z, C) == C.z == (C.z)
|
| 268 |
+
|
| 269 |
+
# Check to make sure Vectors get converted back to UnitVectors
|
| 270 |
+
assert N.x == express((cos(q1)*A.x - sin(q1)*A.y), N).simplify()
|
| 271 |
+
assert N.y == express((sin(q1)*A.x + cos(q1)*A.y), N).simplify()
|
| 272 |
+
assert N.x == express((cos(q1)*B.x - sin(q1)*cos(q2)*B.y +
|
| 273 |
+
sin(q1)*sin(q2)*B.z), N).simplify()
|
| 274 |
+
assert N.y == express((sin(q1)*B.x + cos(q1)*cos(q2)*B.y -
|
| 275 |
+
sin(q2)*cos(q1)*B.z), N).simplify()
|
| 276 |
+
assert N.z == express((sin(q2)*B.y + cos(q2)*B.z), N).simplify()
|
| 277 |
+
|
| 278 |
+
"""
|
| 279 |
+
These don't really test our code, they instead test the auto simplification
|
| 280 |
+
(or lack thereof) of SymPy.
|
| 281 |
+
assert N.x == express((
|
| 282 |
+
(cos(q1)*cos(q3)-sin(q1)*sin(q2)*sin(q3))*C.x -
|
| 283 |
+
sin(q1)*cos(q2)*C.y +
|
| 284 |
+
(sin(q3)*cos(q1)+sin(q1)*sin(q2)*cos(q3))*C.z), N)
|
| 285 |
+
assert N.y == express((
|
| 286 |
+
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*C.x +
|
| 287 |
+
cos(q1)*cos(q2)*C.y +
|
| 288 |
+
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*C.z), N)
|
| 289 |
+
assert N.z == express((-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
| 290 |
+
cos(q2)*cos(q3)*C.z), N)
|
| 291 |
+
"""
|
| 292 |
+
|
| 293 |
+
assert A.x == express((cos(q1)*N.x + sin(q1)*N.y), A).simplify()
|
| 294 |
+
assert A.y == express((-sin(q1)*N.x + cos(q1)*N.y), A).simplify()
|
| 295 |
+
|
| 296 |
+
assert A.y == express((cos(q2)*B.y - sin(q2)*B.z), A).simplify()
|
| 297 |
+
assert A.z == express((sin(q2)*B.y + cos(q2)*B.z), A).simplify()
|
| 298 |
+
|
| 299 |
+
assert A.x == express((cos(q3)*C.x + sin(q3)*C.z), A).simplify()
|
| 300 |
+
|
| 301 |
+
# Tripsimp messes up here too.
|
| 302 |
+
#print express((sin(q2)*sin(q3)*C.x + cos(q2)*C.y -
|
| 303 |
+
# sin(q2)*cos(q3)*C.z), A)
|
| 304 |
+
assert A.y == express((sin(q2)*sin(q3)*C.x + cos(q2)*C.y -
|
| 305 |
+
sin(q2)*cos(q3)*C.z), A).simplify()
|
| 306 |
+
|
| 307 |
+
assert A.z == express((-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
| 308 |
+
cos(q2)*cos(q3)*C.z), A).simplify()
|
| 309 |
+
assert B.x == express((cos(q1)*N.x + sin(q1)*N.y), B).simplify()
|
| 310 |
+
assert B.y == express((-sin(q1)*cos(q2)*N.x +
|
| 311 |
+
cos(q1)*cos(q2)*N.y + sin(q2)*N.z), B).simplify()
|
| 312 |
+
|
| 313 |
+
assert B.z == express((sin(q1)*sin(q2)*N.x -
|
| 314 |
+
sin(q2)*cos(q1)*N.y + cos(q2)*N.z), B).simplify()
|
| 315 |
+
|
| 316 |
+
assert B.y == express((cos(q2)*A.y + sin(q2)*A.z), B).simplify()
|
| 317 |
+
assert B.z == express((-sin(q2)*A.y + cos(q2)*A.z), B).simplify()
|
| 318 |
+
assert B.x == express((cos(q3)*C.x + sin(q3)*C.z), B).simplify()
|
| 319 |
+
assert B.z == express((-sin(q3)*C.x + cos(q3)*C.z), B).simplify()
|
| 320 |
+
|
| 321 |
+
"""
|
| 322 |
+
assert C.x == express((
|
| 323 |
+
(cos(q1)*cos(q3)-sin(q1)*sin(q2)*sin(q3))*N.x +
|
| 324 |
+
(sin(q1)*cos(q3)+sin(q2)*sin(q3)*cos(q1))*N.y -
|
| 325 |
+
sin(q3)*cos(q2)*N.z), C)
|
| 326 |
+
assert C.y == express((
|
| 327 |
+
-sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z), C)
|
| 328 |
+
assert C.z == express((
|
| 329 |
+
(sin(q3)*cos(q1)+sin(q1)*sin(q2)*cos(q3))*N.x +
|
| 330 |
+
(sin(q1)*sin(q3)-sin(q2)*cos(q1)*cos(q3))*N.y +
|
| 331 |
+
cos(q2)*cos(q3)*N.z), C)
|
| 332 |
+
"""
|
| 333 |
+
assert C.x == express((cos(q3)*A.x + sin(q2)*sin(q3)*A.y -
|
| 334 |
+
sin(q3)*cos(q2)*A.z), C).simplify()
|
| 335 |
+
assert C.y == express((cos(q2)*A.y + sin(q2)*A.z), C).simplify()
|
| 336 |
+
assert C.z == express((sin(q3)*A.x - sin(q2)*cos(q3)*A.y +
|
| 337 |
+
cos(q2)*cos(q3)*A.z), C).simplify()
|
| 338 |
+
assert C.x == express((cos(q3)*B.x - sin(q3)*B.z), C).simplify()
|
| 339 |
+
assert C.z == express((sin(q3)*B.x + cos(q3)*B.z), C).simplify()
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
def test_time_derivative():
|
| 343 |
+
#The use of time_derivative for calculations pertaining to scalar
|
| 344 |
+
#fields has been tested in test_coordinate_vars in test_essential.py
|
| 345 |
+
A = ReferenceFrame('A')
|
| 346 |
+
q = dynamicsymbols('q')
|
| 347 |
+
qd = dynamicsymbols('q', 1)
|
| 348 |
+
B = A.orientnew('B', 'Axis', [q, A.z])
|
| 349 |
+
d = A.x | A.x
|
| 350 |
+
assert time_derivative(d, B) == (-qd) * (A.y | A.x) + \
|
| 351 |
+
(-qd) * (A.x | A.y)
|
| 352 |
+
d1 = A.x | B.y
|
| 353 |
+
assert time_derivative(d1, A) == - qd*(A.x|B.x)
|
| 354 |
+
assert time_derivative(d1, B) == - qd*(A.y|B.y)
|
| 355 |
+
d2 = A.x | B.x
|
| 356 |
+
assert time_derivative(d2, A) == qd*(A.x|B.y)
|
| 357 |
+
assert time_derivative(d2, B) == - qd*(A.y|B.x)
|
| 358 |
+
d3 = A.x | B.z
|
| 359 |
+
assert time_derivative(d3, A) == 0
|
| 360 |
+
assert time_derivative(d3, B) == - qd*(A.y|B.z)
|
| 361 |
+
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
| 362 |
+
q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1)
|
| 363 |
+
q1dd, q2dd, q3dd, q4dd = dynamicsymbols('q1 q2 q3 q4', 2)
|
| 364 |
+
C = B.orientnew('C', 'Axis', [q4, B.x])
|
| 365 |
+
v1 = q1 * A.z
|
| 366 |
+
v2 = q2*A.x + q3*B.y
|
| 367 |
+
v3 = q1*A.x + q2*A.y + q3*A.z
|
| 368 |
+
assert time_derivative(B.x, C) == 0
|
| 369 |
+
assert time_derivative(B.y, C) == - q4d*B.z
|
| 370 |
+
assert time_derivative(B.z, C) == q4d*B.y
|
| 371 |
+
assert time_derivative(v1, B) == q1d*A.z
|
| 372 |
+
assert time_derivative(v1, C) == - q1*sin(q)*q4d*A.x + \
|
| 373 |
+
q1*cos(q)*q4d*A.y + q1d*A.z
|
| 374 |
+
assert time_derivative(v2, A) == q2d*A.x - q3*qd*B.x + q3d*B.y
|
| 375 |
+
assert time_derivative(v2, C) == q2d*A.x - q2*qd*A.y + \
|
| 376 |
+
q2*sin(q)*q4d*A.z + q3d*B.y - q3*q4d*B.z
|
| 377 |
+
assert time_derivative(v3, B) == (q2*qd + q1d)*A.x + \
|
| 378 |
+
(-q1*qd + q2d)*A.y + q3d*A.z
|
| 379 |
+
assert time_derivative(d, C) == - qd*(A.y|A.x) + \
|
| 380 |
+
sin(q)*q4d*(A.z|A.x) - qd*(A.x|A.y) + sin(q)*q4d*(A.x|A.z)
|
| 381 |
+
raises(ValueError, lambda: time_derivative(B.x, C, order=0.5))
|
| 382 |
+
raises(ValueError, lambda: time_derivative(B.x, C, order=-1))
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
def test_get_motion_methods():
|
| 386 |
+
#Initialization
|
| 387 |
+
t = dynamicsymbols._t
|
| 388 |
+
s1, s2, s3 = symbols('s1 s2 s3')
|
| 389 |
+
S1, S2, S3 = symbols('S1 S2 S3')
|
| 390 |
+
S4, S5, S6 = symbols('S4 S5 S6')
|
| 391 |
+
t1, t2 = symbols('t1 t2')
|
| 392 |
+
a, b, c = dynamicsymbols('a b c')
|
| 393 |
+
ad, bd, cd = dynamicsymbols('a b c', 1)
|
| 394 |
+
a2d, b2d, c2d = dynamicsymbols('a b c', 2)
|
| 395 |
+
v0 = S1*N.x + S2*N.y + S3*N.z
|
| 396 |
+
v01 = S4*N.x + S5*N.y + S6*N.z
|
| 397 |
+
v1 = s1*N.x + s2*N.y + s3*N.z
|
| 398 |
+
v2 = a*N.x + b*N.y + c*N.z
|
| 399 |
+
v2d = ad*N.x + bd*N.y + cd*N.z
|
| 400 |
+
v2dd = a2d*N.x + b2d*N.y + c2d*N.z
|
| 401 |
+
#Test position parameter
|
| 402 |
+
assert get_motion_params(frame = N) == (0, 0, 0)
|
| 403 |
+
assert get_motion_params(N, position=v1) == (0, 0, v1)
|
| 404 |
+
assert get_motion_params(N, position=v2) == (v2dd, v2d, v2)
|
| 405 |
+
#Test velocity parameter
|
| 406 |
+
assert get_motion_params(N, velocity=v1) == (0, v1, v1 * t)
|
| 407 |
+
assert get_motion_params(N, velocity=v1, position=v0, timevalue1=t1) == \
|
| 408 |
+
(0, v1, v0 + v1*(t - t1))
|
| 409 |
+
answer = get_motion_params(N, velocity=v1, position=v2, timevalue1=t1)
|
| 410 |
+
answer_expected = (0, v1, v1*t - v1*t1 + v2.subs(t, t1))
|
| 411 |
+
assert answer == answer_expected
|
| 412 |
+
|
| 413 |
+
answer = get_motion_params(N, velocity=v2, position=v0, timevalue1=t1)
|
| 414 |
+
integral_vector = Integral(a, (t, t1, t))*N.x + Integral(b, (t, t1, t))*N.y \
|
| 415 |
+
+ Integral(c, (t, t1, t))*N.z
|
| 416 |
+
answer_expected = (v2d, v2, v0 + integral_vector)
|
| 417 |
+
assert answer == answer_expected
|
| 418 |
+
|
| 419 |
+
#Test acceleration parameter
|
| 420 |
+
assert get_motion_params(N, acceleration=v1) == \
|
| 421 |
+
(v1, v1 * t, v1 * t**2/2)
|
| 422 |
+
assert get_motion_params(N, acceleration=v1, velocity=v0,
|
| 423 |
+
position=v2, timevalue1=t1, timevalue2=t2) == \
|
| 424 |
+
(v1, (v0 + v1*t - v1*t2),
|
| 425 |
+
-v0*t1 + v1*t**2/2 + v1*t2*t1 - \
|
| 426 |
+
v1*t1**2/2 + t*(v0 - v1*t2) + \
|
| 427 |
+
v2.subs(t, t1))
|
| 428 |
+
assert get_motion_params(N, acceleration=v1, velocity=v0,
|
| 429 |
+
position=v01, timevalue1=t1, timevalue2=t2) == \
|
| 430 |
+
(v1, v0 + v1*t - v1*t2,
|
| 431 |
+
-v0*t1 + v01 + v1*t**2/2 + \
|
| 432 |
+
v1*t2*t1 - v1*t1**2/2 + \
|
| 433 |
+
t*(v0 - v1*t2))
|
| 434 |
+
answer = get_motion_params(N, acceleration=a*N.x, velocity=S1*N.x,
|
| 435 |
+
position=S2*N.x, timevalue1=t1, timevalue2=t2)
|
| 436 |
+
i1 = Integral(a, (t, t2, t))
|
| 437 |
+
answer_expected = (a*N.x, (S1 + i1)*N.x, \
|
| 438 |
+
(S2 + Integral(S1 + i1, (t, t1, t)))*N.x)
|
| 439 |
+
assert answer == answer_expected
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def test_kin_eqs():
|
| 443 |
+
q0, q1, q2, q3 = dynamicsymbols('q0 q1 q2 q3')
|
| 444 |
+
q0d, q1d, q2d, q3d = dynamicsymbols('q0 q1 q2 q3', 1)
|
| 445 |
+
u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
| 446 |
+
ke = kinematic_equations([u1,u2,u3], [q1,q2,q3], 'body', 313)
|
| 447 |
+
assert ke == kinematic_equations([u1,u2,u3], [q1,q2,q3], 'body', '313')
|
| 448 |
+
kds = kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'quaternion')
|
| 449 |
+
assert kds == [-0.5 * q0 * u1 - 0.5 * q2 * u3 + 0.5 * q3 * u2 + q1d,
|
| 450 |
+
-0.5 * q0 * u2 + 0.5 * q1 * u3 - 0.5 * q3 * u1 + q2d,
|
| 451 |
+
-0.5 * q0 * u3 - 0.5 * q1 * u2 + 0.5 * q2 * u1 + q3d,
|
| 452 |
+
0.5 * q1 * u1 + 0.5 * q2 * u2 + 0.5 * q3 * u3 + q0d]
|
| 453 |
+
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2], 'quaternion'))
|
| 454 |
+
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'quaternion', '123'))
|
| 455 |
+
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'foo'))
|
| 456 |
+
raises(TypeError, lambda: kinematic_equations(u1, [q0, q1, q2, q3], 'quaternion'))
|
| 457 |
+
raises(TypeError, lambda: kinematic_equations([u1], [q0, q1, q2, q3], 'quaternion'))
|
| 458 |
+
raises(TypeError, lambda: kinematic_equations([u1, u2, u3], q0, 'quaternion'))
|
| 459 |
+
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'body'))
|
| 460 |
+
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'space'))
|
| 461 |
+
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2], 'body', '222'))
|
| 462 |
+
assert kinematic_equations([0, 0, 0], [q0, q1, q2], 'space') == [S.Zero, S.Zero, S.Zero]
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
def test_partial_velocity():
|
| 466 |
+
q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3')
|
| 467 |
+
u4, u5 = dynamicsymbols('u4, u5')
|
| 468 |
+
r = symbols('r')
|
| 469 |
+
|
| 470 |
+
N = ReferenceFrame('N')
|
| 471 |
+
Y = N.orientnew('Y', 'Axis', [q1, N.z])
|
| 472 |
+
L = Y.orientnew('L', 'Axis', [q2, Y.x])
|
| 473 |
+
R = L.orientnew('R', 'Axis', [q3, L.y])
|
| 474 |
+
R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z)
|
| 475 |
+
|
| 476 |
+
C = Point('C')
|
| 477 |
+
C.set_vel(N, u4 * L.x + u5 * (Y.z ^ L.x))
|
| 478 |
+
Dmc = C.locatenew('Dmc', r * L.z)
|
| 479 |
+
Dmc.v2pt_theory(C, N, R)
|
| 480 |
+
|
| 481 |
+
vel_list = [Dmc.vel(N), C.vel(N), R.ang_vel_in(N)]
|
| 482 |
+
u_list = [u1, u2, u3, u4, u5]
|
| 483 |
+
assert (partial_velocity(vel_list, u_list, N) ==
|
| 484 |
+
[[- r*L.y, r*L.x, 0, L.x, cos(q2)*L.y - sin(q2)*L.z],
|
| 485 |
+
[0, 0, 0, L.x, cos(q2)*L.y - sin(q2)*L.z],
|
| 486 |
+
[L.x, L.y, L.z, 0, 0]])
|
| 487 |
+
|
| 488 |
+
# Make sure that partial velocities can be computed regardless if the
|
| 489 |
+
# orientation between frames is defined or not.
|
| 490 |
+
A = ReferenceFrame('A')
|
| 491 |
+
B = ReferenceFrame('B')
|
| 492 |
+
v = u4 * A.x + u5 * B.y
|
| 493 |
+
assert partial_velocity((v, ), (u4, u5), A) == [[A.x, B.y]]
|
| 494 |
+
|
| 495 |
+
raises(TypeError, lambda: partial_velocity(Dmc.vel(N), u_list, N))
|
| 496 |
+
raises(TypeError, lambda: partial_velocity(vel_list, u1, N))
|
| 497 |
+
|
| 498 |
+
def test_dynamicsymbols():
|
| 499 |
+
#Tests to check the assumptions applied to dynamicsymbols
|
| 500 |
+
f1 = dynamicsymbols('f1')
|
| 501 |
+
f2 = dynamicsymbols('f2', real=True)
|
| 502 |
+
f3 = dynamicsymbols('f3', positive=True)
|
| 503 |
+
f4, f5 = dynamicsymbols('f4,f5', commutative=False)
|
| 504 |
+
f6 = dynamicsymbols('f6', integer=True)
|
| 505 |
+
assert f1.is_real is None
|
| 506 |
+
assert f2.is_real
|
| 507 |
+
assert f3.is_positive
|
| 508 |
+
assert f4*f5 != f5*f4
|
| 509 |
+
assert f6.is_integer
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_output.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.singleton import S
|
| 2 |
+
from sympy.physics.vector import Vector, ReferenceFrame, Dyadic
|
| 3 |
+
from sympy.testing.pytest import raises
|
| 4 |
+
|
| 5 |
+
A = ReferenceFrame('A')
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_output_type():
|
| 9 |
+
A = ReferenceFrame('A')
|
| 10 |
+
v = A.x + A.y
|
| 11 |
+
d = v | v
|
| 12 |
+
zerov = Vector(0)
|
| 13 |
+
zerod = Dyadic(0)
|
| 14 |
+
|
| 15 |
+
# dot products
|
| 16 |
+
assert isinstance(d & d, Dyadic)
|
| 17 |
+
assert isinstance(d & zerod, Dyadic)
|
| 18 |
+
assert isinstance(zerod & d, Dyadic)
|
| 19 |
+
assert isinstance(d & v, Vector)
|
| 20 |
+
assert isinstance(v & d, Vector)
|
| 21 |
+
assert isinstance(d & zerov, Vector)
|
| 22 |
+
assert isinstance(zerov & d, Vector)
|
| 23 |
+
raises(TypeError, lambda: d & S.Zero)
|
| 24 |
+
raises(TypeError, lambda: S.Zero & d)
|
| 25 |
+
raises(TypeError, lambda: d & 0)
|
| 26 |
+
raises(TypeError, lambda: 0 & d)
|
| 27 |
+
assert not isinstance(v & v, (Vector, Dyadic))
|
| 28 |
+
assert not isinstance(v & zerov, (Vector, Dyadic))
|
| 29 |
+
assert not isinstance(zerov & v, (Vector, Dyadic))
|
| 30 |
+
raises(TypeError, lambda: v & S.Zero)
|
| 31 |
+
raises(TypeError, lambda: S.Zero & v)
|
| 32 |
+
raises(TypeError, lambda: v & 0)
|
| 33 |
+
raises(TypeError, lambda: 0 & v)
|
| 34 |
+
|
| 35 |
+
# cross products
|
| 36 |
+
raises(TypeError, lambda: d ^ d)
|
| 37 |
+
raises(TypeError, lambda: d ^ zerod)
|
| 38 |
+
raises(TypeError, lambda: zerod ^ d)
|
| 39 |
+
assert isinstance(d ^ v, Dyadic)
|
| 40 |
+
assert isinstance(v ^ d, Dyadic)
|
| 41 |
+
assert isinstance(d ^ zerov, Dyadic)
|
| 42 |
+
assert isinstance(zerov ^ d, Dyadic)
|
| 43 |
+
assert isinstance(zerov ^ d, Dyadic)
|
| 44 |
+
raises(TypeError, lambda: d ^ S.Zero)
|
| 45 |
+
raises(TypeError, lambda: S.Zero ^ d)
|
| 46 |
+
raises(TypeError, lambda: d ^ 0)
|
| 47 |
+
raises(TypeError, lambda: 0 ^ d)
|
| 48 |
+
assert isinstance(v ^ v, Vector)
|
| 49 |
+
assert isinstance(v ^ zerov, Vector)
|
| 50 |
+
assert isinstance(zerov ^ v, Vector)
|
| 51 |
+
raises(TypeError, lambda: v ^ S.Zero)
|
| 52 |
+
raises(TypeError, lambda: S.Zero ^ v)
|
| 53 |
+
raises(TypeError, lambda: v ^ 0)
|
| 54 |
+
raises(TypeError, lambda: 0 ^ v)
|
| 55 |
+
|
| 56 |
+
# outer products
|
| 57 |
+
raises(TypeError, lambda: d | d)
|
| 58 |
+
raises(TypeError, lambda: d | zerod)
|
| 59 |
+
raises(TypeError, lambda: zerod | d)
|
| 60 |
+
raises(TypeError, lambda: d | v)
|
| 61 |
+
raises(TypeError, lambda: v | d)
|
| 62 |
+
raises(TypeError, lambda: d | zerov)
|
| 63 |
+
raises(TypeError, lambda: zerov | d)
|
| 64 |
+
raises(TypeError, lambda: zerov | d)
|
| 65 |
+
raises(TypeError, lambda: d | S.Zero)
|
| 66 |
+
raises(TypeError, lambda: S.Zero | d)
|
| 67 |
+
raises(TypeError, lambda: d | 0)
|
| 68 |
+
raises(TypeError, lambda: 0 | d)
|
| 69 |
+
assert isinstance(v | v, Dyadic)
|
| 70 |
+
assert isinstance(v | zerov, Dyadic)
|
| 71 |
+
assert isinstance(zerov | v, Dyadic)
|
| 72 |
+
raises(TypeError, lambda: v | S.Zero)
|
| 73 |
+
raises(TypeError, lambda: S.Zero | v)
|
| 74 |
+
raises(TypeError, lambda: v | 0)
|
| 75 |
+
raises(TypeError, lambda: 0 | v)
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_point.py
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.physics.vector import dynamicsymbols, Point, ReferenceFrame
|
| 2 |
+
from sympy.testing.pytest import raises, ignore_warnings
|
| 3 |
+
import warnings
|
| 4 |
+
|
| 5 |
+
def test_point_v1pt_theorys():
|
| 6 |
+
q, q2 = dynamicsymbols('q q2')
|
| 7 |
+
qd, q2d = dynamicsymbols('q q2', 1)
|
| 8 |
+
qdd, q2dd = dynamicsymbols('q q2', 2)
|
| 9 |
+
N = ReferenceFrame('N')
|
| 10 |
+
B = ReferenceFrame('B')
|
| 11 |
+
B.set_ang_vel(N, qd * B.z)
|
| 12 |
+
O = Point('O')
|
| 13 |
+
P = O.locatenew('P', B.x)
|
| 14 |
+
P.set_vel(B, 0)
|
| 15 |
+
O.set_vel(N, 0)
|
| 16 |
+
assert P.v1pt_theory(O, N, B) == qd * B.y
|
| 17 |
+
O.set_vel(N, N.x)
|
| 18 |
+
assert P.v1pt_theory(O, N, B) == N.x + qd * B.y
|
| 19 |
+
P.set_vel(B, B.z)
|
| 20 |
+
assert P.v1pt_theory(O, N, B) == B.z + N.x + qd * B.y
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def test_point_a1pt_theorys():
|
| 24 |
+
q, q2 = dynamicsymbols('q q2')
|
| 25 |
+
qd, q2d = dynamicsymbols('q q2', 1)
|
| 26 |
+
qdd, q2dd = dynamicsymbols('q q2', 2)
|
| 27 |
+
N = ReferenceFrame('N')
|
| 28 |
+
B = ReferenceFrame('B')
|
| 29 |
+
B.set_ang_vel(N, qd * B.z)
|
| 30 |
+
O = Point('O')
|
| 31 |
+
P = O.locatenew('P', B.x)
|
| 32 |
+
P.set_vel(B, 0)
|
| 33 |
+
O.set_vel(N, 0)
|
| 34 |
+
assert P.a1pt_theory(O, N, B) == -(qd**2) * B.x + qdd * B.y
|
| 35 |
+
P.set_vel(B, q2d * B.z)
|
| 36 |
+
assert P.a1pt_theory(O, N, B) == -(qd**2) * B.x + qdd * B.y + q2dd * B.z
|
| 37 |
+
O.set_vel(N, q2d * B.x)
|
| 38 |
+
assert P.a1pt_theory(O, N, B) == ((q2dd - qd**2) * B.x + (q2d * qd + qdd) * B.y +
|
| 39 |
+
q2dd * B.z)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def test_point_v2pt_theorys():
|
| 43 |
+
q = dynamicsymbols('q')
|
| 44 |
+
qd = dynamicsymbols('q', 1)
|
| 45 |
+
N = ReferenceFrame('N')
|
| 46 |
+
B = N.orientnew('B', 'Axis', [q, N.z])
|
| 47 |
+
O = Point('O')
|
| 48 |
+
P = O.locatenew('P', 0)
|
| 49 |
+
O.set_vel(N, 0)
|
| 50 |
+
assert P.v2pt_theory(O, N, B) == 0
|
| 51 |
+
P = O.locatenew('P', B.x)
|
| 52 |
+
assert P.v2pt_theory(O, N, B) == (qd * B.z ^ B.x)
|
| 53 |
+
O.set_vel(N, N.x)
|
| 54 |
+
assert P.v2pt_theory(O, N, B) == N.x + qd * B.y
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_point_a2pt_theorys():
|
| 58 |
+
q = dynamicsymbols('q')
|
| 59 |
+
qd = dynamicsymbols('q', 1)
|
| 60 |
+
qdd = dynamicsymbols('q', 2)
|
| 61 |
+
N = ReferenceFrame('N')
|
| 62 |
+
B = N.orientnew('B', 'Axis', [q, N.z])
|
| 63 |
+
O = Point('O')
|
| 64 |
+
P = O.locatenew('P', 0)
|
| 65 |
+
O.set_vel(N, 0)
|
| 66 |
+
assert P.a2pt_theory(O, N, B) == 0
|
| 67 |
+
P.set_pos(O, B.x)
|
| 68 |
+
assert P.a2pt_theory(O, N, B) == (-qd**2) * B.x + (qdd) * B.y
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def test_point_funcs():
|
| 72 |
+
q, q2 = dynamicsymbols('q q2')
|
| 73 |
+
qd, q2d = dynamicsymbols('q q2', 1)
|
| 74 |
+
qdd, q2dd = dynamicsymbols('q q2', 2)
|
| 75 |
+
N = ReferenceFrame('N')
|
| 76 |
+
B = ReferenceFrame('B')
|
| 77 |
+
B.set_ang_vel(N, 5 * B.y)
|
| 78 |
+
O = Point('O')
|
| 79 |
+
P = O.locatenew('P', q * B.x + q2 * B.y)
|
| 80 |
+
assert P.pos_from(O) == q * B.x + q2 * B.y
|
| 81 |
+
P.set_vel(B, qd * B.x + q2d * B.y)
|
| 82 |
+
assert P.vel(B) == qd * B.x + q2d * B.y
|
| 83 |
+
O.set_vel(N, 0)
|
| 84 |
+
assert O.vel(N) == 0
|
| 85 |
+
assert P.a1pt_theory(O, N, B) == ((-25 * q + qdd) * B.x + (q2dd) * B.y +
|
| 86 |
+
(-10 * qd) * B.z)
|
| 87 |
+
|
| 88 |
+
B = N.orientnew('B', 'Axis', [q, N.z])
|
| 89 |
+
O = Point('O')
|
| 90 |
+
P = O.locatenew('P', 10 * B.x)
|
| 91 |
+
O.set_vel(N, 5 * N.x)
|
| 92 |
+
assert O.vel(N) == 5 * N.x
|
| 93 |
+
assert P.a2pt_theory(O, N, B) == (-10 * qd**2) * B.x + (10 * qdd) * B.y
|
| 94 |
+
|
| 95 |
+
B.set_ang_vel(N, 5 * B.y)
|
| 96 |
+
O = Point('O')
|
| 97 |
+
P = O.locatenew('P', q * B.x + q2 * B.y)
|
| 98 |
+
P.set_vel(B, qd * B.x + q2d * B.y)
|
| 99 |
+
O.set_vel(N, 0)
|
| 100 |
+
assert P.v1pt_theory(O, N, B) == qd * B.x + q2d * B.y - 5 * q * B.z
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def test_point_pos():
|
| 104 |
+
q = dynamicsymbols('q')
|
| 105 |
+
N = ReferenceFrame('N')
|
| 106 |
+
B = N.orientnew('B', 'Axis', [q, N.z])
|
| 107 |
+
O = Point('O')
|
| 108 |
+
P = O.locatenew('P', 10 * N.x + 5 * B.x)
|
| 109 |
+
assert P.pos_from(O) == 10 * N.x + 5 * B.x
|
| 110 |
+
Q = P.locatenew('Q', 10 * N.y + 5 * B.y)
|
| 111 |
+
assert Q.pos_from(P) == 10 * N.y + 5 * B.y
|
| 112 |
+
assert Q.pos_from(O) == 10 * N.x + 10 * N.y + 5 * B.x + 5 * B.y
|
| 113 |
+
assert O.pos_from(Q) == -10 * N.x - 10 * N.y - 5 * B.x - 5 * B.y
|
| 114 |
+
|
| 115 |
+
def test_point_partial_velocity():
|
| 116 |
+
|
| 117 |
+
N = ReferenceFrame('N')
|
| 118 |
+
A = ReferenceFrame('A')
|
| 119 |
+
|
| 120 |
+
p = Point('p')
|
| 121 |
+
|
| 122 |
+
u1, u2 = dynamicsymbols('u1, u2')
|
| 123 |
+
|
| 124 |
+
p.set_vel(N, u1 * A.x + u2 * N.y)
|
| 125 |
+
|
| 126 |
+
assert p.partial_velocity(N, u1) == A.x
|
| 127 |
+
assert p.partial_velocity(N, u1, u2) == (A.x, N.y)
|
| 128 |
+
raises(ValueError, lambda: p.partial_velocity(A, u1))
|
| 129 |
+
|
| 130 |
+
def test_point_vel(): #Basic functionality
|
| 131 |
+
q1, q2 = dynamicsymbols('q1 q2')
|
| 132 |
+
N = ReferenceFrame('N')
|
| 133 |
+
B = ReferenceFrame('B')
|
| 134 |
+
Q = Point('Q')
|
| 135 |
+
O = Point('O')
|
| 136 |
+
Q.set_pos(O, q1 * N.x)
|
| 137 |
+
raises(ValueError , lambda: Q.vel(N)) # Velocity of O in N is not defined
|
| 138 |
+
O.set_vel(N, q2 * N.y)
|
| 139 |
+
assert O.vel(N) == q2 * N.y
|
| 140 |
+
raises(ValueError , lambda : O.vel(B)) #Velocity of O is not defined in B
|
| 141 |
+
|
| 142 |
+
def test_auto_point_vel():
|
| 143 |
+
t = dynamicsymbols._t
|
| 144 |
+
q1, q2 = dynamicsymbols('q1 q2')
|
| 145 |
+
N = ReferenceFrame('N')
|
| 146 |
+
B = ReferenceFrame('B')
|
| 147 |
+
O = Point('O')
|
| 148 |
+
Q = Point('Q')
|
| 149 |
+
Q.set_pos(O, q1 * N.x)
|
| 150 |
+
O.set_vel(N, q2 * N.y)
|
| 151 |
+
assert Q.vel(N) == q1.diff(t) * N.x + q2 * N.y # Velocity of Q using O
|
| 152 |
+
P1 = Point('P1')
|
| 153 |
+
P1.set_pos(O, q1 * B.x)
|
| 154 |
+
P2 = Point('P2')
|
| 155 |
+
P2.set_pos(P1, q2 * B.z)
|
| 156 |
+
raises(ValueError, lambda : P2.vel(B)) # O's velocity is defined in different frame, and no
|
| 157 |
+
#point in between has its velocity defined
|
| 158 |
+
raises(ValueError, lambda: P2.vel(N)) # Velocity of O not defined in N
|
| 159 |
+
|
| 160 |
+
def test_auto_point_vel_multiple_point_path():
|
| 161 |
+
t = dynamicsymbols._t
|
| 162 |
+
q1, q2 = dynamicsymbols('q1 q2')
|
| 163 |
+
B = ReferenceFrame('B')
|
| 164 |
+
P = Point('P')
|
| 165 |
+
P.set_vel(B, q1 * B.x)
|
| 166 |
+
P1 = Point('P1')
|
| 167 |
+
P1.set_pos(P, q2 * B.y)
|
| 168 |
+
P1.set_vel(B, q1 * B.z)
|
| 169 |
+
P2 = Point('P2')
|
| 170 |
+
P2.set_pos(P1, q1 * B.z)
|
| 171 |
+
P3 = Point('P3')
|
| 172 |
+
P3.set_pos(P2, 10 * q1 * B.y)
|
| 173 |
+
assert P3.vel(B) == 10 * q1.diff(t) * B.y + (q1 + q1.diff(t)) * B.z
|
| 174 |
+
|
| 175 |
+
def test_auto_vel_dont_overwrite():
|
| 176 |
+
t = dynamicsymbols._t
|
| 177 |
+
q1, q2, u1 = dynamicsymbols('q1, q2, u1')
|
| 178 |
+
N = ReferenceFrame('N')
|
| 179 |
+
P = Point('P1')
|
| 180 |
+
P.set_vel(N, u1 * N.x)
|
| 181 |
+
P1 = Point('P1')
|
| 182 |
+
P1.set_pos(P, q2 * N.y)
|
| 183 |
+
assert P1.vel(N) == q2.diff(t) * N.y + u1 * N.x
|
| 184 |
+
assert P.vel(N) == u1 * N.x
|
| 185 |
+
P1.set_vel(N, u1 * N.z)
|
| 186 |
+
assert P1.vel(N) == u1 * N.z
|
| 187 |
+
|
| 188 |
+
def test_auto_point_vel_if_tree_has_vel_but_inappropriate_pos_vector():
|
| 189 |
+
q1, q2 = dynamicsymbols('q1 q2')
|
| 190 |
+
B = ReferenceFrame('B')
|
| 191 |
+
S = ReferenceFrame('S')
|
| 192 |
+
P = Point('P')
|
| 193 |
+
P.set_vel(B, q1 * B.x)
|
| 194 |
+
P1 = Point('P1')
|
| 195 |
+
P1.set_pos(P, S.y)
|
| 196 |
+
raises(ValueError, lambda : P1.vel(B)) # P1.pos_from(P) can't be expressed in B
|
| 197 |
+
raises(ValueError, lambda : P1.vel(S)) # P.vel(S) not defined
|
| 198 |
+
|
| 199 |
+
def test_auto_point_vel_shortest_path():
|
| 200 |
+
t = dynamicsymbols._t
|
| 201 |
+
q1, q2, u1, u2 = dynamicsymbols('q1 q2 u1 u2')
|
| 202 |
+
B = ReferenceFrame('B')
|
| 203 |
+
P = Point('P')
|
| 204 |
+
P.set_vel(B, u1 * B.x)
|
| 205 |
+
P1 = Point('P1')
|
| 206 |
+
P1.set_pos(P, q2 * B.y)
|
| 207 |
+
P1.set_vel(B, q1 * B.z)
|
| 208 |
+
P2 = Point('P2')
|
| 209 |
+
P2.set_pos(P1, q1 * B.z)
|
| 210 |
+
P3 = Point('P3')
|
| 211 |
+
P3.set_pos(P2, 10 * q1 * B.y)
|
| 212 |
+
P4 = Point('P4')
|
| 213 |
+
P4.set_pos(P3, q1 * B.x)
|
| 214 |
+
O = Point('O')
|
| 215 |
+
O.set_vel(B, u2 * B.y)
|
| 216 |
+
O1 = Point('O1')
|
| 217 |
+
O1.set_pos(O, q2 * B.z)
|
| 218 |
+
P4.set_pos(O1, q1 * B.x + q2 * B.z)
|
| 219 |
+
with warnings.catch_warnings(): #There are two possible paths in this point tree, thus a warning is raised
|
| 220 |
+
warnings.simplefilter('error')
|
| 221 |
+
with ignore_warnings(UserWarning):
|
| 222 |
+
assert P4.vel(B) == q1.diff(t) * B.x + u2 * B.y + 2 * q2.diff(t) * B.z
|
| 223 |
+
|
| 224 |
+
def test_auto_point_vel_connected_frames():
|
| 225 |
+
t = dynamicsymbols._t
|
| 226 |
+
q, q1, q2, u = dynamicsymbols('q q1 q2 u')
|
| 227 |
+
N = ReferenceFrame('N')
|
| 228 |
+
B = ReferenceFrame('B')
|
| 229 |
+
O = Point('O')
|
| 230 |
+
O.set_vel(N, u * N.x)
|
| 231 |
+
P = Point('P')
|
| 232 |
+
P.set_pos(O, q1 * N.x + q2 * B.y)
|
| 233 |
+
raises(ValueError, lambda: P.vel(N))
|
| 234 |
+
N.orient(B, 'Axis', (q, B.x))
|
| 235 |
+
assert P.vel(N) == (u + q1.diff(t)) * N.x + q2.diff(t) * B.y - q2 * q.diff(t) * B.z
|
| 236 |
+
|
| 237 |
+
def test_auto_point_vel_multiple_paths_warning_arises():
|
| 238 |
+
q, u = dynamicsymbols('q u')
|
| 239 |
+
N = ReferenceFrame('N')
|
| 240 |
+
O = Point('O')
|
| 241 |
+
P = Point('P')
|
| 242 |
+
Q = Point('Q')
|
| 243 |
+
R = Point('R')
|
| 244 |
+
P.set_vel(N, u * N.x)
|
| 245 |
+
Q.set_vel(N, u *N.y)
|
| 246 |
+
R.set_vel(N, u * N.z)
|
| 247 |
+
O.set_pos(P, q * N.z)
|
| 248 |
+
O.set_pos(Q, q * N.y)
|
| 249 |
+
O.set_pos(R, q * N.x)
|
| 250 |
+
with warnings.catch_warnings(): #There are two possible paths in this point tree, thus a warning is raised
|
| 251 |
+
warnings.simplefilter("error")
|
| 252 |
+
raises(UserWarning ,lambda: O.vel(N))
|
| 253 |
+
|
| 254 |
+
def test_auto_vel_cyclic_warning_arises():
|
| 255 |
+
P = Point('P')
|
| 256 |
+
P1 = Point('P1')
|
| 257 |
+
P2 = Point('P2')
|
| 258 |
+
P3 = Point('P3')
|
| 259 |
+
N = ReferenceFrame('N')
|
| 260 |
+
P.set_vel(N, N.x)
|
| 261 |
+
P1.set_pos(P, N.x)
|
| 262 |
+
P2.set_pos(P1, N.y)
|
| 263 |
+
P3.set_pos(P2, N.z)
|
| 264 |
+
P1.set_pos(P3, N.x + N.y)
|
| 265 |
+
with warnings.catch_warnings(): #The path is cyclic at P1, thus a warning is raised
|
| 266 |
+
warnings.simplefilter("error")
|
| 267 |
+
raises(UserWarning ,lambda: P2.vel(N))
|
| 268 |
+
|
| 269 |
+
def test_auto_vel_cyclic_warning_msg():
|
| 270 |
+
P = Point('P')
|
| 271 |
+
P1 = Point('P1')
|
| 272 |
+
P2 = Point('P2')
|
| 273 |
+
P3 = Point('P3')
|
| 274 |
+
N = ReferenceFrame('N')
|
| 275 |
+
P.set_vel(N, N.x)
|
| 276 |
+
P1.set_pos(P, N.x)
|
| 277 |
+
P2.set_pos(P1, N.y)
|
| 278 |
+
P3.set_pos(P2, N.z)
|
| 279 |
+
P1.set_pos(P3, N.x + N.y)
|
| 280 |
+
with warnings.catch_warnings(record = True) as w: #The path is cyclic at P1, thus a warning is raised
|
| 281 |
+
warnings.simplefilter("always")
|
| 282 |
+
P2.vel(N)
|
| 283 |
+
msg = str(w[-1].message).replace("\n", " ")
|
| 284 |
+
assert issubclass(w[-1].category, UserWarning)
|
| 285 |
+
assert 'Kinematic loops are defined among the positions of points. This is likely not desired and may cause errors in your calculations.' in msg
|
| 286 |
+
|
| 287 |
+
def test_auto_vel_multiple_path_warning_msg():
|
| 288 |
+
N = ReferenceFrame('N')
|
| 289 |
+
O = Point('O')
|
| 290 |
+
P = Point('P')
|
| 291 |
+
Q = Point('Q')
|
| 292 |
+
P.set_vel(N, N.x)
|
| 293 |
+
Q.set_vel(N, N.y)
|
| 294 |
+
O.set_pos(P, N.z)
|
| 295 |
+
O.set_pos(Q, N.y)
|
| 296 |
+
with warnings.catch_warnings(record = True) as w: #There are two possible paths in this point tree, thus a warning is raised
|
| 297 |
+
warnings.simplefilter("always")
|
| 298 |
+
O.vel(N)
|
| 299 |
+
msg = str(w[-1].message).replace("\n", " ")
|
| 300 |
+
assert issubclass(w[-1].category, UserWarning)
|
| 301 |
+
assert 'Velocity' in msg
|
| 302 |
+
assert 'automatically calculated based on point' in msg
|
| 303 |
+
assert 'Velocities from these points are not necessarily the same. This may cause errors in your calculations.' in msg
|
| 304 |
+
|
| 305 |
+
def test_auto_vel_derivative():
|
| 306 |
+
q1, q2 = dynamicsymbols('q1:3')
|
| 307 |
+
u1, u2 = dynamicsymbols('u1:3', 1)
|
| 308 |
+
A = ReferenceFrame('A')
|
| 309 |
+
B = ReferenceFrame('B')
|
| 310 |
+
C = ReferenceFrame('C')
|
| 311 |
+
B.orient_axis(A, A.z, q1)
|
| 312 |
+
B.set_ang_vel(A, u1 * A.z)
|
| 313 |
+
C.orient_axis(B, B.z, q2)
|
| 314 |
+
C.set_ang_vel(B, u2 * B.z)
|
| 315 |
+
|
| 316 |
+
Am = Point('Am')
|
| 317 |
+
Am.set_vel(A, 0)
|
| 318 |
+
Bm = Point('Bm')
|
| 319 |
+
Bm.set_pos(Am, B.x)
|
| 320 |
+
Bm.set_vel(B, 0)
|
| 321 |
+
Bm.set_vel(C, 0)
|
| 322 |
+
Cm = Point('Cm')
|
| 323 |
+
Cm.set_pos(Bm, C.x)
|
| 324 |
+
Cm.set_vel(C, 0)
|
| 325 |
+
temp = Cm._vel_dict.copy()
|
| 326 |
+
assert Cm.vel(A) == (u1 * B.y + (u1 + u2) * C.y)
|
| 327 |
+
Cm._vel_dict = temp
|
| 328 |
+
Cm.v2pt_theory(Bm, B, C)
|
| 329 |
+
assert Cm.vel(A) == (u1 * B.y + (u1 + u2) * C.y)
|
| 330 |
+
|
| 331 |
+
def test_auto_point_acc_zero_vel():
|
| 332 |
+
N = ReferenceFrame('N')
|
| 333 |
+
O = Point('O')
|
| 334 |
+
O.set_vel(N, 0)
|
| 335 |
+
assert O.acc(N) == 0 * N.x
|
| 336 |
+
|
| 337 |
+
def test_auto_point_acc_compute_vel():
|
| 338 |
+
t = dynamicsymbols._t
|
| 339 |
+
q1 = dynamicsymbols('q1')
|
| 340 |
+
N = ReferenceFrame('N')
|
| 341 |
+
A = ReferenceFrame('A')
|
| 342 |
+
A.orient_axis(N, N.z, q1)
|
| 343 |
+
|
| 344 |
+
O = Point('O')
|
| 345 |
+
O.set_vel(N, 0)
|
| 346 |
+
P = Point('P')
|
| 347 |
+
P.set_pos(O, A.x)
|
| 348 |
+
assert P.acc(N) == -q1.diff(t) ** 2 * A.x + q1.diff(t, 2) * A.y
|
| 349 |
+
|
| 350 |
+
def test_auto_acc_derivative():
|
| 351 |
+
# Tests whether the Point.acc method gives the correct acceleration of the
|
| 352 |
+
# end point of two linkages in series, while getting minimal information.
|
| 353 |
+
q1, q2 = dynamicsymbols('q1:3')
|
| 354 |
+
u1, u2 = dynamicsymbols('q1:3', 1)
|
| 355 |
+
v1, v2 = dynamicsymbols('q1:3', 2)
|
| 356 |
+
A = ReferenceFrame('A')
|
| 357 |
+
B = ReferenceFrame('B')
|
| 358 |
+
C = ReferenceFrame('C')
|
| 359 |
+
B.orient_axis(A, A.z, q1)
|
| 360 |
+
C.orient_axis(B, B.z, q2)
|
| 361 |
+
|
| 362 |
+
Am = Point('Am')
|
| 363 |
+
Am.set_vel(A, 0)
|
| 364 |
+
Bm = Point('Bm')
|
| 365 |
+
Bm.set_pos(Am, B.x)
|
| 366 |
+
Bm.set_vel(B, 0)
|
| 367 |
+
Bm.set_vel(C, 0)
|
| 368 |
+
Cm = Point('Cm')
|
| 369 |
+
Cm.set_pos(Bm, C.x)
|
| 370 |
+
Cm.set_vel(C, 0)
|
| 371 |
+
|
| 372 |
+
# Copy dictionaries to later check the calculation using the 2pt_theories
|
| 373 |
+
Bm_vel_dict, Cm_vel_dict = Bm._vel_dict.copy(), Cm._vel_dict.copy()
|
| 374 |
+
Bm_acc_dict, Cm_acc_dict = Bm._acc_dict.copy(), Cm._acc_dict.copy()
|
| 375 |
+
check = -u1 ** 2 * B.x + v1 * B.y - (u1 + u2) ** 2 * C.x + (v1 + v2) * C.y
|
| 376 |
+
assert Cm.acc(A) == check
|
| 377 |
+
Bm._vel_dict, Cm._vel_dict = Bm_vel_dict, Cm_vel_dict
|
| 378 |
+
Bm._acc_dict, Cm._acc_dict = Bm_acc_dict, Cm_acc_dict
|
| 379 |
+
Bm.v2pt_theory(Am, A, B)
|
| 380 |
+
Cm.v2pt_theory(Bm, A, C)
|
| 381 |
+
Bm.a2pt_theory(Am, A, B)
|
| 382 |
+
assert Cm.a2pt_theory(Bm, A, C) == check
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_printing.py
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
from sympy.core.function import Function
|
| 4 |
+
from sympy.core.symbol import symbols
|
| 5 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 6 |
+
from sympy.functions.elementary.trigonometric import (asin, cos, sin)
|
| 7 |
+
from sympy.physics.vector import ReferenceFrame, dynamicsymbols, Dyadic
|
| 8 |
+
from sympy.physics.vector.printing import (VectorLatexPrinter, vpprint,
|
| 9 |
+
vsprint, vsstrrepr, vlatex)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
a, b, c = symbols('a, b, c')
|
| 13 |
+
alpha, omega, beta = dynamicsymbols('alpha, omega, beta')
|
| 14 |
+
|
| 15 |
+
A = ReferenceFrame('A')
|
| 16 |
+
N = ReferenceFrame('N')
|
| 17 |
+
|
| 18 |
+
v = a ** 2 * N.x + b * N.y + c * sin(alpha) * N.z
|
| 19 |
+
w = alpha * N.x + sin(omega) * N.y + alpha * beta * N.z
|
| 20 |
+
ww = alpha * N.x + asin(omega) * N.y - alpha.diff() * beta * N.z
|
| 21 |
+
o = a/b * N.x + (c+b)/a * N.y + c**2/b * N.z
|
| 22 |
+
|
| 23 |
+
y = a ** 2 * (N.x | N.y) + b * (N.y | N.y) + c * sin(alpha) * (N.z | N.y)
|
| 24 |
+
x = alpha * (N.x | N.x) + sin(omega) * (N.y | N.z) + alpha * beta * (N.z | N.x)
|
| 25 |
+
xx = N.x | (-N.y - N.z)
|
| 26 |
+
xx2 = N.x | (N.y + N.z)
|
| 27 |
+
|
| 28 |
+
def ascii_vpretty(expr):
|
| 29 |
+
return vpprint(expr, use_unicode=False, wrap_line=False)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def unicode_vpretty(expr):
|
| 33 |
+
return vpprint(expr, use_unicode=True, wrap_line=False)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def test_latex_printer():
|
| 37 |
+
r = Function('r')('t')
|
| 38 |
+
assert VectorLatexPrinter().doprint(r ** 2) == "r^{2}"
|
| 39 |
+
r2 = Function('r^2')('t')
|
| 40 |
+
assert VectorLatexPrinter().doprint(r2.diff()) == r'\dot{r^{2}}'
|
| 41 |
+
ra = Function('r__a')('t')
|
| 42 |
+
assert VectorLatexPrinter().doprint(ra.diff().diff()) == r'\ddot{r^{a}}'
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_vector_pretty_print():
|
| 46 |
+
|
| 47 |
+
# TODO : The unit vectors should print with subscripts but they just
|
| 48 |
+
# print as `n_x` instead of making `x` a subscript with unicode.
|
| 49 |
+
|
| 50 |
+
# TODO : The pretty print division does not print correctly here:
|
| 51 |
+
# w = alpha * N.x + sin(omega) * N.y + alpha / beta * N.z
|
| 52 |
+
|
| 53 |
+
expected = """\
|
| 54 |
+
2 \n\
|
| 55 |
+
a n_x + b n_y + c*sin(alpha) n_z\
|
| 56 |
+
"""
|
| 57 |
+
uexpected = """\
|
| 58 |
+
2 \n\
|
| 59 |
+
a n_x + b n_y + c⋅sin(α) n_z\
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
assert ascii_vpretty(v) == expected
|
| 63 |
+
assert unicode_vpretty(v) == uexpected
|
| 64 |
+
|
| 65 |
+
expected = 'alpha n_x + sin(omega) n_y + alpha*beta n_z'
|
| 66 |
+
uexpected = 'α n_x + sin(ω) n_y + α⋅β n_z'
|
| 67 |
+
|
| 68 |
+
assert ascii_vpretty(w) == expected
|
| 69 |
+
assert unicode_vpretty(w) == uexpected
|
| 70 |
+
|
| 71 |
+
expected = """\
|
| 72 |
+
2 \n\
|
| 73 |
+
a b + c c \n\
|
| 74 |
+
- n_x + ----- n_y + -- n_z\n\
|
| 75 |
+
b a b \
|
| 76 |
+
"""
|
| 77 |
+
uexpected = """\
|
| 78 |
+
2 \n\
|
| 79 |
+
a b + c c \n\
|
| 80 |
+
─ n_x + ───── n_y + ── n_z\n\
|
| 81 |
+
b a b \
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
assert ascii_vpretty(o) == expected
|
| 85 |
+
assert unicode_vpretty(o) == uexpected
|
| 86 |
+
|
| 87 |
+
# https://github.com/sympy/sympy/issues/26731
|
| 88 |
+
assert ascii_vpretty(-A.x) == '-a_x'
|
| 89 |
+
assert unicode_vpretty(-A.x) == '-a_x'
|
| 90 |
+
|
| 91 |
+
# https://github.com/sympy/sympy/issues/26799
|
| 92 |
+
assert ascii_vpretty(0*A.x) == '0'
|
| 93 |
+
assert unicode_vpretty(0*A.x) == '0'
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def test_vector_latex():
|
| 97 |
+
|
| 98 |
+
a, b, c, d, omega = symbols('a, b, c, d, omega')
|
| 99 |
+
|
| 100 |
+
v = (a ** 2 + b / c) * A.x + sqrt(d) * A.y + cos(omega) * A.z
|
| 101 |
+
|
| 102 |
+
assert vlatex(v) == (r'(a^{2} + \frac{b}{c})\mathbf{\hat{a}_x} + '
|
| 103 |
+
r'\sqrt{d}\mathbf{\hat{a}_y} + '
|
| 104 |
+
r'\cos{\left(\omega \right)}'
|
| 105 |
+
r'\mathbf{\hat{a}_z}')
|
| 106 |
+
|
| 107 |
+
theta, omega, alpha, q = dynamicsymbols('theta, omega, alpha, q')
|
| 108 |
+
|
| 109 |
+
v = theta * A.x + omega * omega * A.y + (q * alpha) * A.z
|
| 110 |
+
|
| 111 |
+
assert vlatex(v) == (r'\theta\mathbf{\hat{a}_x} + '
|
| 112 |
+
r'\omega^{2}\mathbf{\hat{a}_y} + '
|
| 113 |
+
r'\alpha q\mathbf{\hat{a}_z}')
|
| 114 |
+
|
| 115 |
+
phi1, phi2, phi3 = dynamicsymbols('phi1, phi2, phi3')
|
| 116 |
+
theta1, theta2, theta3 = symbols('theta1, theta2, theta3')
|
| 117 |
+
|
| 118 |
+
v = (sin(theta1) * A.x +
|
| 119 |
+
cos(phi1) * cos(phi2) * A.y +
|
| 120 |
+
cos(theta1 + phi3) * A.z)
|
| 121 |
+
|
| 122 |
+
assert vlatex(v) == (r'\sin{\left(\theta_{1} \right)}'
|
| 123 |
+
r'\mathbf{\hat{a}_x} + \cos{'
|
| 124 |
+
r'\left(\phi_{1} \right)} \cos{'
|
| 125 |
+
r'\left(\phi_{2} \right)}\mathbf{\hat{a}_y} + '
|
| 126 |
+
r'\cos{\left(\theta_{1} + '
|
| 127 |
+
r'\phi_{3} \right)}\mathbf{\hat{a}_z}')
|
| 128 |
+
|
| 129 |
+
N = ReferenceFrame('N')
|
| 130 |
+
|
| 131 |
+
a, b, c, d, omega = symbols('a, b, c, d, omega')
|
| 132 |
+
|
| 133 |
+
v = (a ** 2 + b / c) * N.x + sqrt(d) * N.y + cos(omega) * N.z
|
| 134 |
+
|
| 135 |
+
expected = (r'(a^{2} + \frac{b}{c})\mathbf{\hat{n}_x} + '
|
| 136 |
+
r'\sqrt{d}\mathbf{\hat{n}_y} + '
|
| 137 |
+
r'\cos{\left(\omega \right)}'
|
| 138 |
+
r'\mathbf{\hat{n}_z}')
|
| 139 |
+
|
| 140 |
+
assert vlatex(v) == expected
|
| 141 |
+
|
| 142 |
+
# Try custom unit vectors.
|
| 143 |
+
|
| 144 |
+
N = ReferenceFrame('N', latexs=(r'\hat{i}', r'\hat{j}', r'\hat{k}'))
|
| 145 |
+
|
| 146 |
+
v = (a ** 2 + b / c) * N.x + sqrt(d) * N.y + cos(omega) * N.z
|
| 147 |
+
|
| 148 |
+
expected = (r'(a^{2} + \frac{b}{c})\hat{i} + '
|
| 149 |
+
r'\sqrt{d}\hat{j} + '
|
| 150 |
+
r'\cos{\left(\omega \right)}\hat{k}')
|
| 151 |
+
assert vlatex(v) == expected
|
| 152 |
+
|
| 153 |
+
expected = r'\alpha\mathbf{\hat{n}_x} + \operatorname{asin}{\left(\omega ' \
|
| 154 |
+
r'\right)}\mathbf{\hat{n}_y} - \beta \dot{\alpha}\mathbf{\hat{n}_z}'
|
| 155 |
+
assert vlatex(ww) == expected
|
| 156 |
+
|
| 157 |
+
expected = r'- \mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_y} - ' \
|
| 158 |
+
r'\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_z}'
|
| 159 |
+
assert vlatex(xx) == expected
|
| 160 |
+
|
| 161 |
+
expected = r'\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_y} + ' \
|
| 162 |
+
r'\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_z}'
|
| 163 |
+
assert vlatex(xx2) == expected
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def test_vector_latex_arguments():
|
| 167 |
+
assert vlatex(N.x * 3.0, full_prec=False) == r'3.0\mathbf{\hat{n}_x}'
|
| 168 |
+
assert vlatex(N.x * 3.0, full_prec=True) == r'3.00000000000000\mathbf{\hat{n}_x}'
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def test_vector_latex_with_functions():
|
| 172 |
+
|
| 173 |
+
N = ReferenceFrame('N')
|
| 174 |
+
|
| 175 |
+
omega, alpha = dynamicsymbols('omega, alpha')
|
| 176 |
+
|
| 177 |
+
v = omega.diff() * N.x
|
| 178 |
+
|
| 179 |
+
assert vlatex(v) == r'\dot{\omega}\mathbf{\hat{n}_x}'
|
| 180 |
+
|
| 181 |
+
v = omega.diff() ** alpha * N.x
|
| 182 |
+
|
| 183 |
+
assert vlatex(v) == (r'\dot{\omega}^{\alpha}'
|
| 184 |
+
r'\mathbf{\hat{n}_x}')
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def test_dyadic_pretty_print():
|
| 188 |
+
|
| 189 |
+
expected = """\
|
| 190 |
+
2
|
| 191 |
+
a n_x|n_y + b n_y|n_y + c*sin(alpha) n_z|n_y\
|
| 192 |
+
"""
|
| 193 |
+
|
| 194 |
+
uexpected = """\
|
| 195 |
+
2
|
| 196 |
+
a n_x⊗n_y + b n_y⊗n_y + c⋅sin(α) n_z⊗n_y\
|
| 197 |
+
"""
|
| 198 |
+
assert ascii_vpretty(y) == expected
|
| 199 |
+
assert unicode_vpretty(y) == uexpected
|
| 200 |
+
|
| 201 |
+
expected = 'alpha n_x|n_x + sin(omega) n_y|n_z + alpha*beta n_z|n_x'
|
| 202 |
+
uexpected = 'α n_x⊗n_x + sin(ω) n_y⊗n_z + α⋅β n_z⊗n_x'
|
| 203 |
+
assert ascii_vpretty(x) == expected
|
| 204 |
+
assert unicode_vpretty(x) == uexpected
|
| 205 |
+
|
| 206 |
+
assert ascii_vpretty(Dyadic([])) == '0'
|
| 207 |
+
assert unicode_vpretty(Dyadic([])) == '0'
|
| 208 |
+
|
| 209 |
+
assert ascii_vpretty(xx) == '- n_x|n_y - n_x|n_z'
|
| 210 |
+
assert unicode_vpretty(xx) == '- n_x⊗n_y - n_x⊗n_z'
|
| 211 |
+
|
| 212 |
+
assert ascii_vpretty(xx2) == 'n_x|n_y + n_x|n_z'
|
| 213 |
+
assert unicode_vpretty(xx2) == 'n_x⊗n_y + n_x⊗n_z'
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def test_dyadic_latex():
|
| 217 |
+
|
| 218 |
+
expected = (r'a^{2}\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_y} + '
|
| 219 |
+
r'b\mathbf{\hat{n}_y}\otimes \mathbf{\hat{n}_y} + '
|
| 220 |
+
r'c \sin{\left(\alpha \right)}'
|
| 221 |
+
r'\mathbf{\hat{n}_z}\otimes \mathbf{\hat{n}_y}')
|
| 222 |
+
|
| 223 |
+
assert vlatex(y) == expected
|
| 224 |
+
|
| 225 |
+
expected = (r'\alpha\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_x} + '
|
| 226 |
+
r'\sin{\left(\omega \right)}\mathbf{\hat{n}_y}'
|
| 227 |
+
r'\otimes \mathbf{\hat{n}_z} + '
|
| 228 |
+
r'\alpha \beta\mathbf{\hat{n}_z}\otimes \mathbf{\hat{n}_x}')
|
| 229 |
+
|
| 230 |
+
assert vlatex(x) == expected
|
| 231 |
+
|
| 232 |
+
assert vlatex(Dyadic([])) == '0'
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def test_dyadic_str():
|
| 236 |
+
assert vsprint(Dyadic([])) == '0'
|
| 237 |
+
assert vsprint(y) == 'a**2*(N.x|N.y) + b*(N.y|N.y) + c*sin(alpha)*(N.z|N.y)'
|
| 238 |
+
assert vsprint(x) == 'alpha*(N.x|N.x) + sin(omega)*(N.y|N.z) + alpha*beta*(N.z|N.x)'
|
| 239 |
+
assert vsprint(ww) == "alpha*N.x + asin(omega)*N.y - beta*alpha'*N.z"
|
| 240 |
+
assert vsprint(xx) == '- (N.x|N.y) - (N.x|N.z)'
|
| 241 |
+
assert vsprint(xx2) == '(N.x|N.y) + (N.x|N.z)'
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
def test_vlatex(): # vlatex is broken #12078
|
| 245 |
+
from sympy.physics.vector import vlatex
|
| 246 |
+
|
| 247 |
+
x = symbols('x')
|
| 248 |
+
J = symbols('J')
|
| 249 |
+
|
| 250 |
+
f = Function('f')
|
| 251 |
+
g = Function('g')
|
| 252 |
+
h = Function('h')
|
| 253 |
+
|
| 254 |
+
expected = r'J \left(\frac{d}{d x} g{\left(x \right)} - \frac{d}{d x} h{\left(x \right)}\right)'
|
| 255 |
+
|
| 256 |
+
expr = J*f(x).diff(x).subs(f(x), g(x)-h(x))
|
| 257 |
+
|
| 258 |
+
assert vlatex(expr) == expected
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def test_issue_13354():
|
| 262 |
+
"""
|
| 263 |
+
Test for proper pretty printing of physics vectors with ADD
|
| 264 |
+
instances in arguments.
|
| 265 |
+
|
| 266 |
+
Test is exactly the one suggested in the original bug report by
|
| 267 |
+
@moorepants.
|
| 268 |
+
"""
|
| 269 |
+
|
| 270 |
+
a, b, c = symbols('a, b, c')
|
| 271 |
+
A = ReferenceFrame('A')
|
| 272 |
+
v = a * A.x + b * A.y + c * A.z
|
| 273 |
+
w = b * A.x + c * A.y + a * A.z
|
| 274 |
+
z = w + v
|
| 275 |
+
|
| 276 |
+
expected = """(a + b) a_x + (b + c) a_y + (a + c) a_z"""
|
| 277 |
+
|
| 278 |
+
assert ascii_vpretty(z) == expected
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def test_vector_derivative_printing():
|
| 282 |
+
# First order
|
| 283 |
+
v = omega.diff() * N.x
|
| 284 |
+
assert unicode_vpretty(v) == 'ω̇ n_x'
|
| 285 |
+
assert ascii_vpretty(v) == "omega'(t) n_x"
|
| 286 |
+
|
| 287 |
+
# Second order
|
| 288 |
+
v = omega.diff().diff() * N.x
|
| 289 |
+
|
| 290 |
+
assert vlatex(v) == r'\ddot{\omega}\mathbf{\hat{n}_x}'
|
| 291 |
+
assert unicode_vpretty(v) == 'ω̈ n_x'
|
| 292 |
+
assert ascii_vpretty(v) == "omega''(t) n_x"
|
| 293 |
+
|
| 294 |
+
# Third order
|
| 295 |
+
v = omega.diff().diff().diff() * N.x
|
| 296 |
+
|
| 297 |
+
assert vlatex(v) == r'\dddot{\omega}\mathbf{\hat{n}_x}'
|
| 298 |
+
assert unicode_vpretty(v) == 'ω⃛ n_x'
|
| 299 |
+
assert ascii_vpretty(v) == "omega'''(t) n_x"
|
| 300 |
+
|
| 301 |
+
# Fourth order
|
| 302 |
+
v = omega.diff().diff().diff().diff() * N.x
|
| 303 |
+
|
| 304 |
+
assert vlatex(v) == r'\ddddot{\omega}\mathbf{\hat{n}_x}'
|
| 305 |
+
assert unicode_vpretty(v) == 'ω⃜ n_x'
|
| 306 |
+
assert ascii_vpretty(v) == "omega''''(t) n_x"
|
| 307 |
+
|
| 308 |
+
# Fifth order
|
| 309 |
+
v = omega.diff().diff().diff().diff().diff() * N.x
|
| 310 |
+
|
| 311 |
+
assert vlatex(v) == r'\frac{d^{5}}{d t^{5}} \omega\mathbf{\hat{n}_x}'
|
| 312 |
+
expected = '''\
|
| 313 |
+
5 \n\
|
| 314 |
+
d \n\
|
| 315 |
+
---(omega) n_x\n\
|
| 316 |
+
5 \n\
|
| 317 |
+
dt \
|
| 318 |
+
'''
|
| 319 |
+
uexpected = '''\
|
| 320 |
+
5 \n\
|
| 321 |
+
d \n\
|
| 322 |
+
───(ω) n_x\n\
|
| 323 |
+
5 \n\
|
| 324 |
+
dt \
|
| 325 |
+
'''
|
| 326 |
+
assert unicode_vpretty(v) == uexpected
|
| 327 |
+
assert ascii_vpretty(v) == expected
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def test_vector_str_printing():
|
| 331 |
+
assert vsprint(w) == 'alpha*N.x + sin(omega)*N.y + alpha*beta*N.z'
|
| 332 |
+
assert vsprint(omega.diff() * N.x) == "omega'*N.x"
|
| 333 |
+
assert vsstrrepr(w) == 'alpha*N.x + sin(omega)*N.y + alpha*beta*N.z'
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
def test_vector_str_arguments():
|
| 337 |
+
assert vsprint(N.x * 3.0, full_prec=False) == '3.0*N.x'
|
| 338 |
+
assert vsprint(N.x * 3.0, full_prec=True) == '3.00000000000000*N.x'
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
def test_issue_14041():
|
| 342 |
+
import sympy.physics.mechanics as me
|
| 343 |
+
|
| 344 |
+
A_frame = me.ReferenceFrame('A')
|
| 345 |
+
thetad, phid = me.dynamicsymbols('theta, phi', 1)
|
| 346 |
+
L = symbols('L')
|
| 347 |
+
|
| 348 |
+
assert vlatex(L*(phid + thetad)**2*A_frame.x) == \
|
| 349 |
+
r"L \left(\dot{\phi} + \dot{\theta}\right)^{2}\mathbf{\hat{a}_x}"
|
| 350 |
+
assert vlatex((phid + thetad)**2*A_frame.x) == \
|
| 351 |
+
r"\left(\dot{\phi} + \dot{\theta}\right)^{2}\mathbf{\hat{a}_x}"
|
| 352 |
+
assert vlatex((phid*thetad)**a*A_frame.x) == \
|
| 353 |
+
r"\left(\dot{\phi} \dot{\theta}\right)^{a}\mathbf{\hat{a}_x}"
|
venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_vector.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.numbers import (Float, pi)
|
| 2 |
+
from sympy.core.symbol import symbols
|
| 3 |
+
from sympy.core.sorting import ordered
|
| 4 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 5 |
+
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
| 6 |
+
from sympy.physics.vector import ReferenceFrame, Vector, dynamicsymbols, dot
|
| 7 |
+
from sympy.physics.vector.vector import VectorTypeError
|
| 8 |
+
from sympy.abc import x, y, z
|
| 9 |
+
from sympy.testing.pytest import raises
|
| 10 |
+
|
| 11 |
+
A = ReferenceFrame('A')
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_free_dynamicsymbols():
|
| 15 |
+
A, B, C, D = symbols('A, B, C, D', cls=ReferenceFrame)
|
| 16 |
+
a, b, c, d, e, f = dynamicsymbols('a, b, c, d, e, f')
|
| 17 |
+
B.orient_axis(A, a, A.x)
|
| 18 |
+
C.orient_axis(B, b, B.y)
|
| 19 |
+
D.orient_axis(C, c, C.x)
|
| 20 |
+
|
| 21 |
+
v = d*D.x + e*D.y + f*D.z
|
| 22 |
+
|
| 23 |
+
assert set(ordered(v.free_dynamicsymbols(A))) == {a, b, c, d, e, f}
|
| 24 |
+
assert set(ordered(v.free_dynamicsymbols(B))) == {b, c, d, e, f}
|
| 25 |
+
assert set(ordered(v.free_dynamicsymbols(C))) == {c, d, e, f}
|
| 26 |
+
assert set(ordered(v.free_dynamicsymbols(D))) == {d, e, f}
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def test_Vector():
|
| 30 |
+
assert A.x != A.y
|
| 31 |
+
assert A.y != A.z
|
| 32 |
+
assert A.z != A.x
|
| 33 |
+
|
| 34 |
+
assert A.x + 0 == A.x
|
| 35 |
+
|
| 36 |
+
v1 = x*A.x + y*A.y + z*A.z
|
| 37 |
+
v2 = x**2*A.x + y**2*A.y + z**2*A.z
|
| 38 |
+
v3 = v1 + v2
|
| 39 |
+
v4 = v1 - v2
|
| 40 |
+
|
| 41 |
+
assert isinstance(v1, Vector)
|
| 42 |
+
assert dot(v1, A.x) == x
|
| 43 |
+
assert dot(v1, A.y) == y
|
| 44 |
+
assert dot(v1, A.z) == z
|
| 45 |
+
|
| 46 |
+
assert isinstance(v2, Vector)
|
| 47 |
+
assert dot(v2, A.x) == x**2
|
| 48 |
+
assert dot(v2, A.y) == y**2
|
| 49 |
+
assert dot(v2, A.z) == z**2
|
| 50 |
+
|
| 51 |
+
assert isinstance(v3, Vector)
|
| 52 |
+
# We probably shouldn't be using simplify in dot...
|
| 53 |
+
assert dot(v3, A.x) == x**2 + x
|
| 54 |
+
assert dot(v3, A.y) == y**2 + y
|
| 55 |
+
assert dot(v3, A.z) == z**2 + z
|
| 56 |
+
|
| 57 |
+
assert isinstance(v4, Vector)
|
| 58 |
+
# We probably shouldn't be using simplify in dot...
|
| 59 |
+
assert dot(v4, A.x) == x - x**2
|
| 60 |
+
assert dot(v4, A.y) == y - y**2
|
| 61 |
+
assert dot(v4, A.z) == z - z**2
|
| 62 |
+
|
| 63 |
+
assert v1.to_matrix(A) == Matrix([[x], [y], [z]])
|
| 64 |
+
q = symbols('q')
|
| 65 |
+
B = A.orientnew('B', 'Axis', (q, A.x))
|
| 66 |
+
assert v1.to_matrix(B) == Matrix([[x],
|
| 67 |
+
[ y * cos(q) + z * sin(q)],
|
| 68 |
+
[-y * sin(q) + z * cos(q)]])
|
| 69 |
+
|
| 70 |
+
#Test the separate method
|
| 71 |
+
B = ReferenceFrame('B')
|
| 72 |
+
v5 = x*A.x + y*A.y + z*B.z
|
| 73 |
+
assert Vector(0).separate() == {}
|
| 74 |
+
assert v1.separate() == {A: v1}
|
| 75 |
+
assert v5.separate() == {A: x*A.x + y*A.y, B: z*B.z}
|
| 76 |
+
|
| 77 |
+
#Test the free_symbols property
|
| 78 |
+
v6 = x*A.x + y*A.y + z*A.z
|
| 79 |
+
assert v6.free_symbols(A) == {x,y,z}
|
| 80 |
+
|
| 81 |
+
raises(TypeError, lambda: v3.applyfunc(v1))
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def test_Vector_diffs():
|
| 85 |
+
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
| 86 |
+
q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1)
|
| 87 |
+
q1dd, q2dd, q3dd, q4dd = dynamicsymbols('q1 q2 q3 q4', 2)
|
| 88 |
+
N = ReferenceFrame('N')
|
| 89 |
+
A = N.orientnew('A', 'Axis', [q3, N.z])
|
| 90 |
+
B = A.orientnew('B', 'Axis', [q2, A.x])
|
| 91 |
+
v1 = q2 * A.x + q3 * N.y
|
| 92 |
+
v2 = q3 * B.x + v1
|
| 93 |
+
v3 = v1.dt(B)
|
| 94 |
+
v4 = v2.dt(B)
|
| 95 |
+
v5 = q1*A.x + q2*A.y + q3*A.z
|
| 96 |
+
|
| 97 |
+
assert v1.dt(N) == q2d * A.x + q2 * q3d * A.y + q3d * N.y
|
| 98 |
+
assert v1.dt(A) == q2d * A.x + q3 * q3d * N.x + q3d * N.y
|
| 99 |
+
assert v1.dt(B) == (q2d * A.x + q3 * q3d * N.x + q3d *
|
| 100 |
+
N.y - q3 * cos(q3) * q2d * N.z)
|
| 101 |
+
assert v2.dt(N) == (q2d * A.x + (q2 + q3) * q3d * A.y + q3d * B.x + q3d *
|
| 102 |
+
N.y)
|
| 103 |
+
assert v2.dt(A) == q2d * A.x + q3d * B.x + q3 * q3d * N.x + q3d * N.y
|
| 104 |
+
assert v2.dt(B) == (q2d * A.x + q3d * B.x + q3 * q3d * N.x + q3d * N.y -
|
| 105 |
+
q3 * cos(q3) * q2d * N.z)
|
| 106 |
+
assert v3.dt(N) == (q2dd * A.x + q2d * q3d * A.y + (q3d**2 + q3 * q3dd) *
|
| 107 |
+
N.x + q3dd * N.y + (q3 * sin(q3) * q2d * q3d -
|
| 108 |
+
cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z)
|
| 109 |
+
assert v3.dt(A) == (q2dd * A.x + (2 * q3d**2 + q3 * q3dd) * N.x + (q3dd -
|
| 110 |
+
q3 * q3d**2) * N.y + (q3 * sin(q3) * q2d * q3d -
|
| 111 |
+
cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z)
|
| 112 |
+
assert (v3.dt(B) - (q2dd*A.x - q3*cos(q3)*q2d**2*A.y + (2*q3d**2 +
|
| 113 |
+
q3*q3dd)*N.x + (q3dd - q3*q3d**2)*N.y + (2*q3*sin(q3)*q2d*q3d -
|
| 114 |
+
2*cos(q3)*q2d*q3d - q3*cos(q3)*q2dd)*N.z)).express(B).simplify() == 0
|
| 115 |
+
assert v4.dt(N) == (q2dd * A.x + q3d * (q2d + q3d) * A.y + q3dd * B.x +
|
| 116 |
+
(q3d**2 + q3 * q3dd) * N.x + q3dd * N.y + (q3 *
|
| 117 |
+
sin(q3) * q2d * q3d - cos(q3) * q2d * q3d - q3 *
|
| 118 |
+
cos(q3) * q2dd) * N.z)
|
| 119 |
+
assert v4.dt(A) == (q2dd * A.x + q3dd * B.x + (2 * q3d**2 + q3 * q3dd) *
|
| 120 |
+
N.x + (q3dd - q3 * q3d**2) * N.y + (q3 * sin(q3) *
|
| 121 |
+
q2d * q3d - cos(q3) * q2d * q3d - q3 * cos(q3) *
|
| 122 |
+
q2dd) * N.z)
|
| 123 |
+
assert (v4.dt(B) - (q2dd*A.x - q3*cos(q3)*q2d**2*A.y + q3dd*B.x +
|
| 124 |
+
(2*q3d**2 + q3*q3dd)*N.x + (q3dd - q3*q3d**2)*N.y +
|
| 125 |
+
(2*q3*sin(q3)*q2d*q3d - 2*cos(q3)*q2d*q3d -
|
| 126 |
+
q3*cos(q3)*q2dd)*N.z)).express(B).simplify() == 0
|
| 127 |
+
assert v5.dt(B) == q1d*A.x + (q3*q2d + q2d)*A.y + (-q2*q2d + q3d)*A.z
|
| 128 |
+
assert v5.dt(A) == q1d*A.x + q2d*A.y + q3d*A.z
|
| 129 |
+
assert v5.dt(N) == (-q2*q3d + q1d)*A.x + (q1*q3d + q2d)*A.y + q3d*A.z
|
| 130 |
+
assert v3.diff(q1d, N) == 0
|
| 131 |
+
assert v3.diff(q2d, N) == A.x - q3 * cos(q3) * N.z
|
| 132 |
+
assert v3.diff(q3d, N) == q3 * N.x + N.y
|
| 133 |
+
assert v3.diff(q1d, A) == 0
|
| 134 |
+
assert v3.diff(q2d, A) == A.x - q3 * cos(q3) * N.z
|
| 135 |
+
assert v3.diff(q3d, A) == q3 * N.x + N.y
|
| 136 |
+
assert v3.diff(q1d, B) == 0
|
| 137 |
+
assert v3.diff(q2d, B) == A.x - q3 * cos(q3) * N.z
|
| 138 |
+
assert v3.diff(q3d, B) == q3 * N.x + N.y
|
| 139 |
+
assert v4.diff(q1d, N) == 0
|
| 140 |
+
assert v4.diff(q2d, N) == A.x - q3 * cos(q3) * N.z
|
| 141 |
+
assert v4.diff(q3d, N) == B.x + q3 * N.x + N.y
|
| 142 |
+
assert v4.diff(q1d, A) == 0
|
| 143 |
+
assert v4.diff(q2d, A) == A.x - q3 * cos(q3) * N.z
|
| 144 |
+
assert v4.diff(q3d, A) == B.x + q3 * N.x + N.y
|
| 145 |
+
assert v4.diff(q1d, B) == 0
|
| 146 |
+
assert v4.diff(q2d, B) == A.x - q3 * cos(q3) * N.z
|
| 147 |
+
assert v4.diff(q3d, B) == B.x + q3 * N.x + N.y
|
| 148 |
+
|
| 149 |
+
# diff() should only express vector components in the derivative frame if
|
| 150 |
+
# the orientation of the component's frame depends on the variable
|
| 151 |
+
v6 = q2**2*N.y + q2**2*A.y + q2**2*B.y
|
| 152 |
+
# already expressed in N
|
| 153 |
+
n_measy = 2*q2
|
| 154 |
+
# A_C_N does not depend on q2, so don't express in N
|
| 155 |
+
a_measy = 2*q2
|
| 156 |
+
# B_C_N depends on q2, so express in N
|
| 157 |
+
b_measx = (q2**2*B.y).dot(N.x).diff(q2)
|
| 158 |
+
b_measy = (q2**2*B.y).dot(N.y).diff(q2)
|
| 159 |
+
b_measz = (q2**2*B.y).dot(N.z).diff(q2)
|
| 160 |
+
n_comp, a_comp = v6.diff(q2, N).args
|
| 161 |
+
assert len(v6.diff(q2, N).args) == 2 # only N and A parts
|
| 162 |
+
assert n_comp[1] == N
|
| 163 |
+
assert a_comp[1] == A
|
| 164 |
+
assert n_comp[0] == Matrix([b_measx, b_measy + n_measy, b_measz])
|
| 165 |
+
assert a_comp[0] == Matrix([0, a_measy, 0])
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def test_vector_var_in_dcm():
|
| 169 |
+
|
| 170 |
+
N = ReferenceFrame('N')
|
| 171 |
+
A = ReferenceFrame('A')
|
| 172 |
+
B = ReferenceFrame('B')
|
| 173 |
+
u1, u2, u3, u4 = dynamicsymbols('u1 u2 u3 u4')
|
| 174 |
+
|
| 175 |
+
v = u1 * u2 * A.x + u3 * N.y + u4**2 * N.z
|
| 176 |
+
|
| 177 |
+
assert v.diff(u1, N, var_in_dcm=False) == u2 * A.x
|
| 178 |
+
assert v.diff(u1, A, var_in_dcm=False) == u2 * A.x
|
| 179 |
+
assert v.diff(u3, N, var_in_dcm=False) == N.y
|
| 180 |
+
assert v.diff(u3, A, var_in_dcm=False) == N.y
|
| 181 |
+
assert v.diff(u3, B, var_in_dcm=False) == N.y
|
| 182 |
+
assert v.diff(u4, N, var_in_dcm=False) == 2 * u4 * N.z
|
| 183 |
+
|
| 184 |
+
raises(ValueError, lambda: v.diff(u1, N))
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def test_vector_simplify():
|
| 188 |
+
x, y, z, k, n, m, w, f, s, A = symbols('x, y, z, k, n, m, w, f, s, A')
|
| 189 |
+
N = ReferenceFrame('N')
|
| 190 |
+
|
| 191 |
+
test1 = (1 / x + 1 / y) * N.x
|
| 192 |
+
assert (test1 & N.x) != (x + y) / (x * y)
|
| 193 |
+
test1 = test1.simplify()
|
| 194 |
+
assert (test1 & N.x) == (x + y) / (x * y)
|
| 195 |
+
|
| 196 |
+
test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * N.x
|
| 197 |
+
test2 = test2.simplify()
|
| 198 |
+
assert (test2 & N.x) == (A**2 * s**4 / (4 * pi * k * m**3))
|
| 199 |
+
|
| 200 |
+
test3 = ((4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x)) * N.x
|
| 201 |
+
test3 = test3.simplify()
|
| 202 |
+
assert (test3 & N.x) == 0
|
| 203 |
+
|
| 204 |
+
test4 = ((-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2) * N.x
|
| 205 |
+
test4 = test4.simplify()
|
| 206 |
+
assert (test4 & N.x) == -2 * y
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def test_vector_evalf():
|
| 210 |
+
a, b = symbols('a b')
|
| 211 |
+
v = pi * A.x
|
| 212 |
+
assert v.evalf(2) == Float('3.1416', 2) * A.x
|
| 213 |
+
v = pi * A.x + 5 * a * A.y - b * A.z
|
| 214 |
+
assert v.evalf(3) == Float('3.1416', 3) * A.x + Float('5', 3) * a * A.y - b * A.z
|
| 215 |
+
assert v.evalf(5, subs={a: 1.234, b:5.8973}) == Float('3.1415926536', 5) * A.x + Float('6.17', 5) * A.y - Float('5.8973', 5) * A.z
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def test_vector_angle():
|
| 219 |
+
A = ReferenceFrame('A')
|
| 220 |
+
v1 = A.x + A.y
|
| 221 |
+
v2 = A.z
|
| 222 |
+
assert v1.angle_between(v2) == pi/2
|
| 223 |
+
B = ReferenceFrame('B')
|
| 224 |
+
B.orient_axis(A, A.x, pi)
|
| 225 |
+
v3 = A.x
|
| 226 |
+
v4 = B.x
|
| 227 |
+
assert v3.angle_between(v4) == 0
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def test_vector_xreplace():
|
| 231 |
+
x, y, z = symbols('x y z')
|
| 232 |
+
v = x**2 * A.x + x*y * A.y + x*y*z * A.z
|
| 233 |
+
assert v.xreplace({x : cos(x)}) == cos(x)**2 * A.x + y*cos(x) * A.y + y*z*cos(x) * A.z
|
| 234 |
+
assert v.xreplace({x*y : pi}) == x**2 * A.x + pi * A.y + x*y*z * A.z
|
| 235 |
+
assert v.xreplace({x*y*z : 1}) == x**2*A.x + x*y*A.y + A.z
|
| 236 |
+
assert v.xreplace({x:1, z:0}) == A.x + y * A.y
|
| 237 |
+
raises(TypeError, lambda: v.xreplace())
|
| 238 |
+
raises(TypeError, lambda: v.xreplace([x, y]))
|
| 239 |
+
|
| 240 |
+
def test_issue_23366():
|
| 241 |
+
u1 = dynamicsymbols('u1')
|
| 242 |
+
N = ReferenceFrame('N')
|
| 243 |
+
N_v_A = u1*N.x
|
| 244 |
+
raises(VectorTypeError, lambda: N_v_A.diff(N, u1))
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def test_vector_outer():
|
| 248 |
+
a, b, c, d, e, f = symbols('a, b, c, d, e, f')
|
| 249 |
+
N = ReferenceFrame('N')
|
| 250 |
+
v1 = a*N.x + b*N.y + c*N.z
|
| 251 |
+
v2 = d*N.x + e*N.y + f*N.z
|
| 252 |
+
v1v2 = Matrix([[a*d, a*e, a*f],
|
| 253 |
+
[b*d, b*e, b*f],
|
| 254 |
+
[c*d, c*e, c*f]])
|
| 255 |
+
assert v1.outer(v2).to_matrix(N) == v1v2
|
| 256 |
+
assert (v1 | v2).to_matrix(N) == v1v2
|
| 257 |
+
v2v1 = Matrix([[d*a, d*b, d*c],
|
| 258 |
+
[e*a, e*b, e*c],
|
| 259 |
+
[f*a, f*b, f*c]])
|
| 260 |
+
assert v2.outer(v1).to_matrix(N) == v2v1
|
| 261 |
+
assert (v2 | v1).to_matrix(N) == v2v1
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def test_overloaded_operators():
|
| 265 |
+
a, b, c, d, e, f = symbols('a, b, c, d, e, f')
|
| 266 |
+
N = ReferenceFrame('N')
|
| 267 |
+
v1 = a*N.x + b*N.y + c*N.z
|
| 268 |
+
v2 = d*N.x + e*N.y + f*N.z
|
| 269 |
+
|
| 270 |
+
assert v1 + v2 == v2 + v1
|
| 271 |
+
assert v1 - v2 == -v2 + v1
|
| 272 |
+
assert v1 & v2 == v2 & v1
|
| 273 |
+
assert v1 ^ v2 == v1.cross(v2)
|
| 274 |
+
assert v2 ^ v1 == v2.cross(v1)
|
venv/lib/python3.11/site-packages/sympy/physics/vector/vector.py
ADDED
|
@@ -0,0 +1,806 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy import (S, sympify, expand, sqrt, Add, zeros, acos,
|
| 2 |
+
ImmutableMatrix as Matrix, simplify)
|
| 3 |
+
from sympy.simplify.trigsimp import trigsimp
|
| 4 |
+
from sympy.printing.defaults import Printable
|
| 5 |
+
from sympy.utilities.misc import filldedent
|
| 6 |
+
from sympy.core.evalf import EvalfMixin
|
| 7 |
+
|
| 8 |
+
from mpmath.libmp.libmpf import prec_to_dps
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
__all__ = ['Vector']
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class Vector(Printable, EvalfMixin):
|
| 15 |
+
"""The class used to define vectors.
|
| 16 |
+
|
| 17 |
+
It along with ReferenceFrame are the building blocks of describing a
|
| 18 |
+
classical mechanics system in PyDy and sympy.physics.vector.
|
| 19 |
+
|
| 20 |
+
Attributes
|
| 21 |
+
==========
|
| 22 |
+
|
| 23 |
+
simp : Boolean
|
| 24 |
+
Let certain methods use trigsimp on their outputs
|
| 25 |
+
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
simp = False
|
| 29 |
+
is_number = False
|
| 30 |
+
|
| 31 |
+
def __init__(self, inlist):
|
| 32 |
+
"""This is the constructor for the Vector class. You should not be
|
| 33 |
+
calling this, it should only be used by other functions. You should be
|
| 34 |
+
treating Vectors like you would with if you were doing the math by
|
| 35 |
+
hand, and getting the first 3 from the standard basis vectors from a
|
| 36 |
+
ReferenceFrame.
|
| 37 |
+
|
| 38 |
+
The only exception is to create a zero vector:
|
| 39 |
+
zv = Vector(0)
|
| 40 |
+
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
self.args = []
|
| 44 |
+
if inlist == 0:
|
| 45 |
+
inlist = []
|
| 46 |
+
if isinstance(inlist, dict):
|
| 47 |
+
d = inlist
|
| 48 |
+
else:
|
| 49 |
+
d = {}
|
| 50 |
+
for inp in inlist:
|
| 51 |
+
if inp[1] in d:
|
| 52 |
+
d[inp[1]] += inp[0]
|
| 53 |
+
else:
|
| 54 |
+
d[inp[1]] = inp[0]
|
| 55 |
+
|
| 56 |
+
for k, v in d.items():
|
| 57 |
+
if v != Matrix([0, 0, 0]):
|
| 58 |
+
self.args.append((v, k))
|
| 59 |
+
|
| 60 |
+
@property
|
| 61 |
+
def func(self):
|
| 62 |
+
"""Returns the class Vector. """
|
| 63 |
+
return Vector
|
| 64 |
+
|
| 65 |
+
def __hash__(self):
|
| 66 |
+
return hash(tuple(self.args))
|
| 67 |
+
|
| 68 |
+
def __add__(self, other):
|
| 69 |
+
"""The add operator for Vector. """
|
| 70 |
+
if other == 0:
|
| 71 |
+
return self
|
| 72 |
+
other = _check_vector(other)
|
| 73 |
+
return Vector(self.args + other.args)
|
| 74 |
+
|
| 75 |
+
def dot(self, other):
|
| 76 |
+
"""Dot product of two vectors.
|
| 77 |
+
|
| 78 |
+
Returns a scalar, the dot product of the two Vectors
|
| 79 |
+
|
| 80 |
+
Parameters
|
| 81 |
+
==========
|
| 82 |
+
|
| 83 |
+
other : Vector
|
| 84 |
+
The Vector which we are dotting with
|
| 85 |
+
|
| 86 |
+
Examples
|
| 87 |
+
========
|
| 88 |
+
|
| 89 |
+
>>> from sympy.physics.vector import ReferenceFrame, dot
|
| 90 |
+
>>> from sympy import symbols
|
| 91 |
+
>>> q1 = symbols('q1')
|
| 92 |
+
>>> N = ReferenceFrame('N')
|
| 93 |
+
>>> dot(N.x, N.x)
|
| 94 |
+
1
|
| 95 |
+
>>> dot(N.x, N.y)
|
| 96 |
+
0
|
| 97 |
+
>>> A = N.orientnew('A', 'Axis', [q1, N.x])
|
| 98 |
+
>>> dot(N.y, A.y)
|
| 99 |
+
cos(q1)
|
| 100 |
+
|
| 101 |
+
"""
|
| 102 |
+
|
| 103 |
+
from sympy.physics.vector.dyadic import Dyadic, _check_dyadic
|
| 104 |
+
if isinstance(other, Dyadic):
|
| 105 |
+
other = _check_dyadic(other)
|
| 106 |
+
ol = Vector(0)
|
| 107 |
+
for v in other.args:
|
| 108 |
+
ol += v[0] * v[2] * (v[1].dot(self))
|
| 109 |
+
return ol
|
| 110 |
+
other = _check_vector(other)
|
| 111 |
+
out = S.Zero
|
| 112 |
+
for v1 in self.args:
|
| 113 |
+
for v2 in other.args:
|
| 114 |
+
out += ((v2[0].T) * (v2[1].dcm(v1[1])) * (v1[0]))[0]
|
| 115 |
+
if Vector.simp:
|
| 116 |
+
return trigsimp(out, recursive=True)
|
| 117 |
+
else:
|
| 118 |
+
return out
|
| 119 |
+
|
| 120 |
+
def __truediv__(self, other):
|
| 121 |
+
"""This uses mul and inputs self and 1 divided by other. """
|
| 122 |
+
return self.__mul__(S.One / other)
|
| 123 |
+
|
| 124 |
+
def __eq__(self, other):
|
| 125 |
+
"""Tests for equality.
|
| 126 |
+
|
| 127 |
+
It is very import to note that this is only as good as the SymPy
|
| 128 |
+
equality test; False does not always mean they are not equivalent
|
| 129 |
+
Vectors.
|
| 130 |
+
If other is 0, and self is empty, returns True.
|
| 131 |
+
If other is 0 and self is not empty, returns False.
|
| 132 |
+
If none of the above, only accepts other as a Vector.
|
| 133 |
+
|
| 134 |
+
"""
|
| 135 |
+
|
| 136 |
+
if other == 0:
|
| 137 |
+
other = Vector(0)
|
| 138 |
+
try:
|
| 139 |
+
other = _check_vector(other)
|
| 140 |
+
except TypeError:
|
| 141 |
+
return False
|
| 142 |
+
if (self.args == []) and (other.args == []):
|
| 143 |
+
return True
|
| 144 |
+
elif (self.args == []) or (other.args == []):
|
| 145 |
+
return False
|
| 146 |
+
|
| 147 |
+
frame = self.args[0][1]
|
| 148 |
+
for v in frame:
|
| 149 |
+
if expand((self - other).dot(v)) != 0:
|
| 150 |
+
return False
|
| 151 |
+
return True
|
| 152 |
+
|
| 153 |
+
def __mul__(self, other):
|
| 154 |
+
"""Multiplies the Vector by a sympifyable expression.
|
| 155 |
+
|
| 156 |
+
Parameters
|
| 157 |
+
==========
|
| 158 |
+
|
| 159 |
+
other : Sympifyable
|
| 160 |
+
The scalar to multiply this Vector with
|
| 161 |
+
|
| 162 |
+
Examples
|
| 163 |
+
========
|
| 164 |
+
|
| 165 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 166 |
+
>>> from sympy import Symbol
|
| 167 |
+
>>> N = ReferenceFrame('N')
|
| 168 |
+
>>> b = Symbol('b')
|
| 169 |
+
>>> V = 10 * b * N.x
|
| 170 |
+
>>> print(V)
|
| 171 |
+
10*b*N.x
|
| 172 |
+
|
| 173 |
+
"""
|
| 174 |
+
|
| 175 |
+
newlist = list(self.args)
|
| 176 |
+
other = sympify(other)
|
| 177 |
+
for i in range(len(newlist)):
|
| 178 |
+
newlist[i] = (other * newlist[i][0], newlist[i][1])
|
| 179 |
+
return Vector(newlist)
|
| 180 |
+
|
| 181 |
+
def __neg__(self):
|
| 182 |
+
return self * -1
|
| 183 |
+
|
| 184 |
+
def outer(self, other):
|
| 185 |
+
"""Outer product between two Vectors.
|
| 186 |
+
|
| 187 |
+
A rank increasing operation, which returns a Dyadic from two Vectors
|
| 188 |
+
|
| 189 |
+
Parameters
|
| 190 |
+
==========
|
| 191 |
+
|
| 192 |
+
other : Vector
|
| 193 |
+
The Vector to take the outer product with
|
| 194 |
+
|
| 195 |
+
Examples
|
| 196 |
+
========
|
| 197 |
+
|
| 198 |
+
>>> from sympy.physics.vector import ReferenceFrame, outer
|
| 199 |
+
>>> N = ReferenceFrame('N')
|
| 200 |
+
>>> outer(N.x, N.x)
|
| 201 |
+
(N.x|N.x)
|
| 202 |
+
|
| 203 |
+
"""
|
| 204 |
+
|
| 205 |
+
from sympy.physics.vector.dyadic import Dyadic
|
| 206 |
+
other = _check_vector(other)
|
| 207 |
+
ol = Dyadic(0)
|
| 208 |
+
for v in self.args:
|
| 209 |
+
for v2 in other.args:
|
| 210 |
+
# it looks this way because if we are in the same frame and
|
| 211 |
+
# use the enumerate function on the same frame in a nested
|
| 212 |
+
# fashion, then bad things happen
|
| 213 |
+
ol += Dyadic([(v[0][0] * v2[0][0], v[1].x, v2[1].x)])
|
| 214 |
+
ol += Dyadic([(v[0][0] * v2[0][1], v[1].x, v2[1].y)])
|
| 215 |
+
ol += Dyadic([(v[0][0] * v2[0][2], v[1].x, v2[1].z)])
|
| 216 |
+
ol += Dyadic([(v[0][1] * v2[0][0], v[1].y, v2[1].x)])
|
| 217 |
+
ol += Dyadic([(v[0][1] * v2[0][1], v[1].y, v2[1].y)])
|
| 218 |
+
ol += Dyadic([(v[0][1] * v2[0][2], v[1].y, v2[1].z)])
|
| 219 |
+
ol += Dyadic([(v[0][2] * v2[0][0], v[1].z, v2[1].x)])
|
| 220 |
+
ol += Dyadic([(v[0][2] * v2[0][1], v[1].z, v2[1].y)])
|
| 221 |
+
ol += Dyadic([(v[0][2] * v2[0][2], v[1].z, v2[1].z)])
|
| 222 |
+
return ol
|
| 223 |
+
|
| 224 |
+
def _latex(self, printer):
|
| 225 |
+
"""Latex Printing method. """
|
| 226 |
+
|
| 227 |
+
ar = self.args # just to shorten things
|
| 228 |
+
if len(ar) == 0:
|
| 229 |
+
return str(0)
|
| 230 |
+
ol = [] # output list, to be concatenated to a string
|
| 231 |
+
for v in ar:
|
| 232 |
+
for j in 0, 1, 2:
|
| 233 |
+
# if the coef of the basis vector is 1, we skip the 1
|
| 234 |
+
if v[0][j] == 1:
|
| 235 |
+
ol.append(' + ' + v[1].latex_vecs[j])
|
| 236 |
+
# if the coef of the basis vector is -1, we skip the 1
|
| 237 |
+
elif v[0][j] == -1:
|
| 238 |
+
ol.append(' - ' + v[1].latex_vecs[j])
|
| 239 |
+
elif v[0][j] != 0:
|
| 240 |
+
# If the coefficient of the basis vector is not 1 or -1;
|
| 241 |
+
# also, we might wrap it in parentheses, for readability.
|
| 242 |
+
arg_str = printer._print(v[0][j])
|
| 243 |
+
if isinstance(v[0][j], Add):
|
| 244 |
+
arg_str = "(%s)" % arg_str
|
| 245 |
+
if arg_str[0] == '-':
|
| 246 |
+
arg_str = arg_str[1:]
|
| 247 |
+
str_start = ' - '
|
| 248 |
+
else:
|
| 249 |
+
str_start = ' + '
|
| 250 |
+
ol.append(str_start + arg_str + v[1].latex_vecs[j])
|
| 251 |
+
outstr = ''.join(ol)
|
| 252 |
+
if outstr.startswith(' + '):
|
| 253 |
+
outstr = outstr[3:]
|
| 254 |
+
elif outstr.startswith(' '):
|
| 255 |
+
outstr = outstr[1:]
|
| 256 |
+
return outstr
|
| 257 |
+
|
| 258 |
+
def _pretty(self, printer):
|
| 259 |
+
"""Pretty Printing method. """
|
| 260 |
+
from sympy.printing.pretty.stringpict import prettyForm
|
| 261 |
+
|
| 262 |
+
terms = []
|
| 263 |
+
|
| 264 |
+
def juxtapose(a, b):
|
| 265 |
+
pa = printer._print(a)
|
| 266 |
+
pb = printer._print(b)
|
| 267 |
+
if a.is_Add:
|
| 268 |
+
pa = prettyForm(*pa.parens())
|
| 269 |
+
return printer._print_seq([pa, pb], delimiter=' ')
|
| 270 |
+
|
| 271 |
+
for M, N in self.args:
|
| 272 |
+
for i in range(3):
|
| 273 |
+
if M[i] == 0:
|
| 274 |
+
continue
|
| 275 |
+
elif M[i] == 1:
|
| 276 |
+
terms.append(prettyForm(N.pretty_vecs[i]))
|
| 277 |
+
elif M[i] == -1:
|
| 278 |
+
terms.append(prettyForm("-1") * prettyForm(N.pretty_vecs[i]))
|
| 279 |
+
else:
|
| 280 |
+
terms.append(juxtapose(M[i], N.pretty_vecs[i]))
|
| 281 |
+
|
| 282 |
+
if terms:
|
| 283 |
+
pretty_result = prettyForm.__add__(*terms)
|
| 284 |
+
else:
|
| 285 |
+
pretty_result = prettyForm("0")
|
| 286 |
+
|
| 287 |
+
return pretty_result
|
| 288 |
+
|
| 289 |
+
def __rsub__(self, other):
|
| 290 |
+
return (-1 * self) + other
|
| 291 |
+
|
| 292 |
+
def _sympystr(self, printer, order=True):
|
| 293 |
+
"""Printing method. """
|
| 294 |
+
if not order or len(self.args) == 1:
|
| 295 |
+
ar = list(self.args)
|
| 296 |
+
elif len(self.args) == 0:
|
| 297 |
+
return printer._print(0)
|
| 298 |
+
else:
|
| 299 |
+
d = {v[1]: v[0] for v in self.args}
|
| 300 |
+
keys = sorted(d.keys(), key=lambda x: x.index)
|
| 301 |
+
ar = []
|
| 302 |
+
for key in keys:
|
| 303 |
+
ar.append((d[key], key))
|
| 304 |
+
ol = [] # output list, to be concatenated to a string
|
| 305 |
+
for v in ar:
|
| 306 |
+
for j in 0, 1, 2:
|
| 307 |
+
# if the coef of the basis vector is 1, we skip the 1
|
| 308 |
+
if v[0][j] == 1:
|
| 309 |
+
ol.append(' + ' + v[1].str_vecs[j])
|
| 310 |
+
# if the coef of the basis vector is -1, we skip the 1
|
| 311 |
+
elif v[0][j] == -1:
|
| 312 |
+
ol.append(' - ' + v[1].str_vecs[j])
|
| 313 |
+
elif v[0][j] != 0:
|
| 314 |
+
# If the coefficient of the basis vector is not 1 or -1;
|
| 315 |
+
# also, we might wrap it in parentheses, for readability.
|
| 316 |
+
arg_str = printer._print(v[0][j])
|
| 317 |
+
if isinstance(v[0][j], Add):
|
| 318 |
+
arg_str = "(%s)" % arg_str
|
| 319 |
+
if arg_str[0] == '-':
|
| 320 |
+
arg_str = arg_str[1:]
|
| 321 |
+
str_start = ' - '
|
| 322 |
+
else:
|
| 323 |
+
str_start = ' + '
|
| 324 |
+
ol.append(str_start + arg_str + '*' + v[1].str_vecs[j])
|
| 325 |
+
outstr = ''.join(ol)
|
| 326 |
+
if outstr.startswith(' + '):
|
| 327 |
+
outstr = outstr[3:]
|
| 328 |
+
elif outstr.startswith(' '):
|
| 329 |
+
outstr = outstr[1:]
|
| 330 |
+
return outstr
|
| 331 |
+
|
| 332 |
+
def __sub__(self, other):
|
| 333 |
+
"""The subtraction operator. """
|
| 334 |
+
return self.__add__(other * -1)
|
| 335 |
+
|
| 336 |
+
def cross(self, other):
|
| 337 |
+
"""The cross product operator for two Vectors.
|
| 338 |
+
|
| 339 |
+
Returns a Vector, expressed in the same ReferenceFrames as self.
|
| 340 |
+
|
| 341 |
+
Parameters
|
| 342 |
+
==========
|
| 343 |
+
|
| 344 |
+
other : Vector
|
| 345 |
+
The Vector which we are crossing with
|
| 346 |
+
|
| 347 |
+
Examples
|
| 348 |
+
========
|
| 349 |
+
|
| 350 |
+
>>> from sympy import symbols
|
| 351 |
+
>>> from sympy.physics.vector import ReferenceFrame, cross
|
| 352 |
+
>>> q1 = symbols('q1')
|
| 353 |
+
>>> N = ReferenceFrame('N')
|
| 354 |
+
>>> cross(N.x, N.y)
|
| 355 |
+
N.z
|
| 356 |
+
>>> A = ReferenceFrame('A')
|
| 357 |
+
>>> A.orient_axis(N, q1, N.x)
|
| 358 |
+
>>> cross(A.x, N.y)
|
| 359 |
+
N.z
|
| 360 |
+
>>> cross(N.y, A.x)
|
| 361 |
+
- sin(q1)*A.y - cos(q1)*A.z
|
| 362 |
+
|
| 363 |
+
"""
|
| 364 |
+
|
| 365 |
+
from sympy.physics.vector.dyadic import Dyadic, _check_dyadic
|
| 366 |
+
if isinstance(other, Dyadic):
|
| 367 |
+
other = _check_dyadic(other)
|
| 368 |
+
ol = Dyadic(0)
|
| 369 |
+
for i, v in enumerate(other.args):
|
| 370 |
+
ol += v[0] * ((self.cross(v[1])).outer(v[2]))
|
| 371 |
+
return ol
|
| 372 |
+
other = _check_vector(other)
|
| 373 |
+
if other.args == []:
|
| 374 |
+
return Vector(0)
|
| 375 |
+
|
| 376 |
+
def _det(mat):
|
| 377 |
+
"""This is needed as a little method for to find the determinant
|
| 378 |
+
of a list in python; needs to work for a 3x3 list.
|
| 379 |
+
SymPy's Matrix will not take in Vector, so need a custom function.
|
| 380 |
+
You should not be calling this.
|
| 381 |
+
|
| 382 |
+
"""
|
| 383 |
+
|
| 384 |
+
return (mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1])
|
| 385 |
+
+ mat[0][1] * (mat[1][2] * mat[2][0] - mat[1][0] *
|
| 386 |
+
mat[2][2]) + mat[0][2] * (mat[1][0] * mat[2][1] -
|
| 387 |
+
mat[1][1] * mat[2][0]))
|
| 388 |
+
|
| 389 |
+
outlist = []
|
| 390 |
+
ar = other.args # For brevity
|
| 391 |
+
for v in ar:
|
| 392 |
+
tempx = v[1].x
|
| 393 |
+
tempy = v[1].y
|
| 394 |
+
tempz = v[1].z
|
| 395 |
+
tempm = ([[tempx, tempy, tempz],
|
| 396 |
+
[self.dot(tempx), self.dot(tempy), self.dot(tempz)],
|
| 397 |
+
[Vector([v]).dot(tempx), Vector([v]).dot(tempy),
|
| 398 |
+
Vector([v]).dot(tempz)]])
|
| 399 |
+
outlist += _det(tempm).args
|
| 400 |
+
return Vector(outlist)
|
| 401 |
+
|
| 402 |
+
__radd__ = __add__
|
| 403 |
+
__rmul__ = __mul__
|
| 404 |
+
|
| 405 |
+
def separate(self):
|
| 406 |
+
"""
|
| 407 |
+
The constituents of this vector in different reference frames,
|
| 408 |
+
as per its definition.
|
| 409 |
+
|
| 410 |
+
Returns a dict mapping each ReferenceFrame to the corresponding
|
| 411 |
+
constituent Vector.
|
| 412 |
+
|
| 413 |
+
Examples
|
| 414 |
+
========
|
| 415 |
+
|
| 416 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 417 |
+
>>> R1 = ReferenceFrame('R1')
|
| 418 |
+
>>> R2 = ReferenceFrame('R2')
|
| 419 |
+
>>> v = R1.x + R2.x
|
| 420 |
+
>>> v.separate() == {R1: R1.x, R2: R2.x}
|
| 421 |
+
True
|
| 422 |
+
|
| 423 |
+
"""
|
| 424 |
+
|
| 425 |
+
components = {}
|
| 426 |
+
for x in self.args:
|
| 427 |
+
components[x[1]] = Vector([x])
|
| 428 |
+
return components
|
| 429 |
+
|
| 430 |
+
def __and__(self, other):
|
| 431 |
+
return self.dot(other)
|
| 432 |
+
__and__.__doc__ = dot.__doc__
|
| 433 |
+
__rand__ = __and__
|
| 434 |
+
|
| 435 |
+
def __xor__(self, other):
|
| 436 |
+
return self.cross(other)
|
| 437 |
+
__xor__.__doc__ = cross.__doc__
|
| 438 |
+
|
| 439 |
+
def __or__(self, other):
|
| 440 |
+
return self.outer(other)
|
| 441 |
+
__or__.__doc__ = outer.__doc__
|
| 442 |
+
|
| 443 |
+
def diff(self, var, frame, var_in_dcm=True):
|
| 444 |
+
"""Returns the partial derivative of the vector with respect to a
|
| 445 |
+
variable in the provided reference frame.
|
| 446 |
+
|
| 447 |
+
Parameters
|
| 448 |
+
==========
|
| 449 |
+
var : Symbol
|
| 450 |
+
What the partial derivative is taken with respect to.
|
| 451 |
+
frame : ReferenceFrame
|
| 452 |
+
The reference frame that the partial derivative is taken in.
|
| 453 |
+
var_in_dcm : boolean
|
| 454 |
+
If true, the differentiation algorithm assumes that the variable
|
| 455 |
+
may be present in any of the direction cosine matrices that relate
|
| 456 |
+
the frame to the frames of any component of the vector. But if it
|
| 457 |
+
is known that the variable is not present in the direction cosine
|
| 458 |
+
matrices, false can be set to skip full reexpression in the desired
|
| 459 |
+
frame.
|
| 460 |
+
|
| 461 |
+
Examples
|
| 462 |
+
========
|
| 463 |
+
|
| 464 |
+
>>> from sympy import Symbol
|
| 465 |
+
>>> from sympy.physics.vector import dynamicsymbols, ReferenceFrame
|
| 466 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 467 |
+
>>> init_vprinting(pretty_print=False)
|
| 468 |
+
>>> t = Symbol('t')
|
| 469 |
+
>>> q1 = dynamicsymbols('q1')
|
| 470 |
+
>>> N = ReferenceFrame('N')
|
| 471 |
+
>>> A = N.orientnew('A', 'Axis', [q1, N.y])
|
| 472 |
+
>>> A.x.diff(t, N)
|
| 473 |
+
- sin(q1)*q1'*N.x - cos(q1)*q1'*N.z
|
| 474 |
+
>>> A.x.diff(t, N).express(A).simplify()
|
| 475 |
+
- q1'*A.z
|
| 476 |
+
>>> B = ReferenceFrame('B')
|
| 477 |
+
>>> u1, u2 = dynamicsymbols('u1, u2')
|
| 478 |
+
>>> v = u1 * A.x + u2 * B.y
|
| 479 |
+
>>> v.diff(u2, N, var_in_dcm=False)
|
| 480 |
+
B.y
|
| 481 |
+
|
| 482 |
+
"""
|
| 483 |
+
|
| 484 |
+
from sympy.physics.vector.frame import _check_frame
|
| 485 |
+
|
| 486 |
+
_check_frame(frame)
|
| 487 |
+
var = sympify(var)
|
| 488 |
+
|
| 489 |
+
inlist = []
|
| 490 |
+
|
| 491 |
+
for vector_component in self.args:
|
| 492 |
+
measure_number = vector_component[0]
|
| 493 |
+
component_frame = vector_component[1]
|
| 494 |
+
if component_frame == frame:
|
| 495 |
+
inlist += [(measure_number.diff(var), frame)]
|
| 496 |
+
else:
|
| 497 |
+
# If the direction cosine matrix relating the component frame
|
| 498 |
+
# with the derivative frame does not contain the variable.
|
| 499 |
+
if not var_in_dcm or (frame.dcm(component_frame).diff(var) ==
|
| 500 |
+
zeros(3, 3)):
|
| 501 |
+
inlist += [(measure_number.diff(var), component_frame)]
|
| 502 |
+
else: # else express in the frame
|
| 503 |
+
reexp_vec_comp = Vector([vector_component]).express(frame)
|
| 504 |
+
deriv = reexp_vec_comp.args[0][0].diff(var)
|
| 505 |
+
inlist += Vector([(deriv, frame)]).args
|
| 506 |
+
|
| 507 |
+
return Vector(inlist)
|
| 508 |
+
|
| 509 |
+
def express(self, otherframe, variables=False):
|
| 510 |
+
"""
|
| 511 |
+
Returns a Vector equivalent to this one, expressed in otherframe.
|
| 512 |
+
Uses the global express method.
|
| 513 |
+
|
| 514 |
+
Parameters
|
| 515 |
+
==========
|
| 516 |
+
|
| 517 |
+
otherframe : ReferenceFrame
|
| 518 |
+
The frame for this Vector to be described in
|
| 519 |
+
|
| 520 |
+
variables : boolean
|
| 521 |
+
If True, the coordinate symbols(if present) in this Vector
|
| 522 |
+
are re-expressed in terms otherframe
|
| 523 |
+
|
| 524 |
+
Examples
|
| 525 |
+
========
|
| 526 |
+
|
| 527 |
+
>>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
|
| 528 |
+
>>> from sympy.physics.vector import init_vprinting
|
| 529 |
+
>>> init_vprinting(pretty_print=False)
|
| 530 |
+
>>> q1 = dynamicsymbols('q1')
|
| 531 |
+
>>> N = ReferenceFrame('N')
|
| 532 |
+
>>> A = N.orientnew('A', 'Axis', [q1, N.y])
|
| 533 |
+
>>> A.x.express(N)
|
| 534 |
+
cos(q1)*N.x - sin(q1)*N.z
|
| 535 |
+
|
| 536 |
+
"""
|
| 537 |
+
from sympy.physics.vector import express
|
| 538 |
+
return express(self, otherframe, variables=variables)
|
| 539 |
+
|
| 540 |
+
def to_matrix(self, reference_frame):
|
| 541 |
+
"""Returns the matrix form of the vector with respect to the given
|
| 542 |
+
frame.
|
| 543 |
+
|
| 544 |
+
Parameters
|
| 545 |
+
----------
|
| 546 |
+
reference_frame : ReferenceFrame
|
| 547 |
+
The reference frame that the rows of the matrix correspond to.
|
| 548 |
+
|
| 549 |
+
Returns
|
| 550 |
+
-------
|
| 551 |
+
matrix : ImmutableMatrix, shape(3,1)
|
| 552 |
+
The matrix that gives the 1D vector.
|
| 553 |
+
|
| 554 |
+
Examples
|
| 555 |
+
========
|
| 556 |
+
|
| 557 |
+
>>> from sympy import symbols
|
| 558 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 559 |
+
>>> a, b, c = symbols('a, b, c')
|
| 560 |
+
>>> N = ReferenceFrame('N')
|
| 561 |
+
>>> vector = a * N.x + b * N.y + c * N.z
|
| 562 |
+
>>> vector.to_matrix(N)
|
| 563 |
+
Matrix([
|
| 564 |
+
[a],
|
| 565 |
+
[b],
|
| 566 |
+
[c]])
|
| 567 |
+
>>> beta = symbols('beta')
|
| 568 |
+
>>> A = N.orientnew('A', 'Axis', (beta, N.x))
|
| 569 |
+
>>> vector.to_matrix(A)
|
| 570 |
+
Matrix([
|
| 571 |
+
[ a],
|
| 572 |
+
[ b*cos(beta) + c*sin(beta)],
|
| 573 |
+
[-b*sin(beta) + c*cos(beta)]])
|
| 574 |
+
|
| 575 |
+
"""
|
| 576 |
+
|
| 577 |
+
return Matrix([self.dot(unit_vec) for unit_vec in
|
| 578 |
+
reference_frame]).reshape(3, 1)
|
| 579 |
+
|
| 580 |
+
def doit(self, **hints):
|
| 581 |
+
"""Calls .doit() on each term in the Vector"""
|
| 582 |
+
d = {}
|
| 583 |
+
for v in self.args:
|
| 584 |
+
d[v[1]] = v[0].applyfunc(lambda x: x.doit(**hints))
|
| 585 |
+
return Vector(d)
|
| 586 |
+
|
| 587 |
+
def dt(self, otherframe):
|
| 588 |
+
"""
|
| 589 |
+
Returns a Vector which is the time derivative of
|
| 590 |
+
the self Vector, taken in frame otherframe.
|
| 591 |
+
|
| 592 |
+
Calls the global time_derivative method
|
| 593 |
+
|
| 594 |
+
Parameters
|
| 595 |
+
==========
|
| 596 |
+
|
| 597 |
+
otherframe : ReferenceFrame
|
| 598 |
+
The frame to calculate the time derivative in
|
| 599 |
+
|
| 600 |
+
"""
|
| 601 |
+
from sympy.physics.vector import time_derivative
|
| 602 |
+
return time_derivative(self, otherframe)
|
| 603 |
+
|
| 604 |
+
def simplify(self):
|
| 605 |
+
"""Returns a simplified Vector."""
|
| 606 |
+
d = {}
|
| 607 |
+
for v in self.args:
|
| 608 |
+
d[v[1]] = simplify(v[0])
|
| 609 |
+
return Vector(d)
|
| 610 |
+
|
| 611 |
+
def subs(self, *args, **kwargs):
|
| 612 |
+
"""Substitution on the Vector.
|
| 613 |
+
|
| 614 |
+
Examples
|
| 615 |
+
========
|
| 616 |
+
|
| 617 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 618 |
+
>>> from sympy import Symbol
|
| 619 |
+
>>> N = ReferenceFrame('N')
|
| 620 |
+
>>> s = Symbol('s')
|
| 621 |
+
>>> a = N.x * s
|
| 622 |
+
>>> a.subs({s: 2})
|
| 623 |
+
2*N.x
|
| 624 |
+
|
| 625 |
+
"""
|
| 626 |
+
|
| 627 |
+
d = {}
|
| 628 |
+
for v in self.args:
|
| 629 |
+
d[v[1]] = v[0].subs(*args, **kwargs)
|
| 630 |
+
return Vector(d)
|
| 631 |
+
|
| 632 |
+
def magnitude(self):
|
| 633 |
+
"""Returns the magnitude (Euclidean norm) of self.
|
| 634 |
+
|
| 635 |
+
Warnings
|
| 636 |
+
========
|
| 637 |
+
|
| 638 |
+
Python ignores the leading negative sign so that might
|
| 639 |
+
give wrong results.
|
| 640 |
+
``-A.x.magnitude()`` would be treated as ``-(A.x.magnitude())``,
|
| 641 |
+
instead of ``(-A.x).magnitude()``.
|
| 642 |
+
|
| 643 |
+
"""
|
| 644 |
+
return sqrt(self.dot(self))
|
| 645 |
+
|
| 646 |
+
def normalize(self):
|
| 647 |
+
"""Returns a Vector of magnitude 1, codirectional with self."""
|
| 648 |
+
return Vector(self.args + []) / self.magnitude()
|
| 649 |
+
|
| 650 |
+
def applyfunc(self, f):
|
| 651 |
+
"""Apply a function to each component of a vector."""
|
| 652 |
+
if not callable(f):
|
| 653 |
+
raise TypeError("`f` must be callable.")
|
| 654 |
+
|
| 655 |
+
d = {}
|
| 656 |
+
for v in self.args:
|
| 657 |
+
d[v[1]] = v[0].applyfunc(f)
|
| 658 |
+
return Vector(d)
|
| 659 |
+
|
| 660 |
+
def angle_between(self, vec):
|
| 661 |
+
"""
|
| 662 |
+
Returns the smallest angle between Vector 'vec' and self.
|
| 663 |
+
|
| 664 |
+
Parameter
|
| 665 |
+
=========
|
| 666 |
+
|
| 667 |
+
vec : Vector
|
| 668 |
+
The Vector between which angle is needed.
|
| 669 |
+
|
| 670 |
+
Examples
|
| 671 |
+
========
|
| 672 |
+
|
| 673 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 674 |
+
>>> A = ReferenceFrame("A")
|
| 675 |
+
>>> v1 = A.x
|
| 676 |
+
>>> v2 = A.y
|
| 677 |
+
>>> v1.angle_between(v2)
|
| 678 |
+
pi/2
|
| 679 |
+
|
| 680 |
+
>>> v3 = A.x + A.y + A.z
|
| 681 |
+
>>> v1.angle_between(v3)
|
| 682 |
+
acos(sqrt(3)/3)
|
| 683 |
+
|
| 684 |
+
Warnings
|
| 685 |
+
========
|
| 686 |
+
|
| 687 |
+
Python ignores the leading negative sign so that might give wrong
|
| 688 |
+
results. ``-A.x.angle_between()`` would be treated as
|
| 689 |
+
``-(A.x.angle_between())``, instead of ``(-A.x).angle_between()``.
|
| 690 |
+
|
| 691 |
+
"""
|
| 692 |
+
|
| 693 |
+
vec1 = self.normalize()
|
| 694 |
+
vec2 = vec.normalize()
|
| 695 |
+
angle = acos(vec1.dot(vec2))
|
| 696 |
+
return angle
|
| 697 |
+
|
| 698 |
+
def free_symbols(self, reference_frame):
|
| 699 |
+
"""Returns the free symbols in the measure numbers of the vector
|
| 700 |
+
expressed in the given reference frame.
|
| 701 |
+
|
| 702 |
+
Parameters
|
| 703 |
+
==========
|
| 704 |
+
reference_frame : ReferenceFrame
|
| 705 |
+
The frame with respect to which the free symbols of the given
|
| 706 |
+
vector is to be determined.
|
| 707 |
+
|
| 708 |
+
Returns
|
| 709 |
+
=======
|
| 710 |
+
set of Symbol
|
| 711 |
+
set of symbols present in the measure numbers of
|
| 712 |
+
``reference_frame``.
|
| 713 |
+
|
| 714 |
+
"""
|
| 715 |
+
|
| 716 |
+
return self.to_matrix(reference_frame).free_symbols
|
| 717 |
+
|
| 718 |
+
def free_dynamicsymbols(self, reference_frame):
|
| 719 |
+
"""Returns the free dynamic symbols (functions of time ``t``) in the
|
| 720 |
+
measure numbers of the vector expressed in the given reference frame.
|
| 721 |
+
|
| 722 |
+
Parameters
|
| 723 |
+
==========
|
| 724 |
+
reference_frame : ReferenceFrame
|
| 725 |
+
The frame with respect to which the free dynamic symbols of the
|
| 726 |
+
given vector is to be determined.
|
| 727 |
+
|
| 728 |
+
Returns
|
| 729 |
+
=======
|
| 730 |
+
set
|
| 731 |
+
Set of functions of time ``t``, e.g.
|
| 732 |
+
``Function('f')(me.dynamicsymbols._t)``.
|
| 733 |
+
|
| 734 |
+
"""
|
| 735 |
+
# TODO : Circular dependency if imported at top. Should move
|
| 736 |
+
# find_dynamicsymbols into physics.vector.functions.
|
| 737 |
+
from sympy.physics.mechanics.functions import find_dynamicsymbols
|
| 738 |
+
|
| 739 |
+
return find_dynamicsymbols(self, reference_frame=reference_frame)
|
| 740 |
+
|
| 741 |
+
def _eval_evalf(self, prec):
|
| 742 |
+
if not self.args:
|
| 743 |
+
return self
|
| 744 |
+
new_args = []
|
| 745 |
+
dps = prec_to_dps(prec)
|
| 746 |
+
for mat, frame in self.args:
|
| 747 |
+
new_args.append([mat.evalf(n=dps), frame])
|
| 748 |
+
return Vector(new_args)
|
| 749 |
+
|
| 750 |
+
def xreplace(self, rule):
|
| 751 |
+
"""Replace occurrences of objects within the measure numbers of the
|
| 752 |
+
vector.
|
| 753 |
+
|
| 754 |
+
Parameters
|
| 755 |
+
==========
|
| 756 |
+
|
| 757 |
+
rule : dict-like
|
| 758 |
+
Expresses a replacement rule.
|
| 759 |
+
|
| 760 |
+
Returns
|
| 761 |
+
=======
|
| 762 |
+
|
| 763 |
+
Vector
|
| 764 |
+
Result of the replacement.
|
| 765 |
+
|
| 766 |
+
Examples
|
| 767 |
+
========
|
| 768 |
+
|
| 769 |
+
>>> from sympy import symbols, pi
|
| 770 |
+
>>> from sympy.physics.vector import ReferenceFrame
|
| 771 |
+
>>> A = ReferenceFrame('A')
|
| 772 |
+
>>> x, y, z = symbols('x y z')
|
| 773 |
+
>>> ((1 + x*y) * A.x).xreplace({x: pi})
|
| 774 |
+
(pi*y + 1)*A.x
|
| 775 |
+
>>> ((1 + x*y) * A.x).xreplace({x: pi, y: 2})
|
| 776 |
+
(1 + 2*pi)*A.x
|
| 777 |
+
|
| 778 |
+
Replacements occur only if an entire node in the expression tree is
|
| 779 |
+
matched:
|
| 780 |
+
|
| 781 |
+
>>> ((x*y + z) * A.x).xreplace({x*y: pi})
|
| 782 |
+
(z + pi)*A.x
|
| 783 |
+
>>> ((x*y*z) * A.x).xreplace({x*y: pi})
|
| 784 |
+
x*y*z*A.x
|
| 785 |
+
|
| 786 |
+
"""
|
| 787 |
+
|
| 788 |
+
new_args = []
|
| 789 |
+
for mat, frame in self.args:
|
| 790 |
+
mat = mat.xreplace(rule)
|
| 791 |
+
new_args.append([mat, frame])
|
| 792 |
+
return Vector(new_args)
|
| 793 |
+
|
| 794 |
+
|
| 795 |
+
class VectorTypeError(TypeError):
|
| 796 |
+
|
| 797 |
+
def __init__(self, other, want):
|
| 798 |
+
msg = filldedent("Expected an instance of %s, but received object "
|
| 799 |
+
"'%s' of %s." % (type(want), other, type(other)))
|
| 800 |
+
super().__init__(msg)
|
| 801 |
+
|
| 802 |
+
|
| 803 |
+
def _check_vector(other):
|
| 804 |
+
if not isinstance(other, Vector):
|
| 805 |
+
raise TypeError('A Vector must be supplied')
|
| 806 |
+
return other
|
venv/lib/python3.11/site-packages/sympy/physics/wigner.py
ADDED
|
@@ -0,0 +1,1213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
r"""
|
| 3 |
+
Wigner, Clebsch-Gordan, Racah, and Gaunt coefficients
|
| 4 |
+
|
| 5 |
+
Collection of functions for calculating Wigner 3j, 6j, 9j,
|
| 6 |
+
Clebsch-Gordan, Racah as well as Gaunt coefficients exactly, all
|
| 7 |
+
evaluating to a rational number times the square root of a rational
|
| 8 |
+
number [Rasch03]_.
|
| 9 |
+
|
| 10 |
+
Please see the description of the individual functions for further
|
| 11 |
+
details and examples.
|
| 12 |
+
|
| 13 |
+
References
|
| 14 |
+
==========
|
| 15 |
+
|
| 16 |
+
.. [Regge58] 'Symmetry Properties of Clebsch-Gordan Coefficients',
|
| 17 |
+
T. Regge, Nuovo Cimento, Volume 10, pp. 544 (1958)
|
| 18 |
+
.. [Regge59] 'Symmetry Properties of Racah Coefficients',
|
| 19 |
+
T. Regge, Nuovo Cimento, Volume 11, pp. 116 (1959)
|
| 20 |
+
.. [Edmonds74] A. R. Edmonds. Angular momentum in quantum mechanics.
|
| 21 |
+
Investigations in physics, 4.; Investigations in physics, no. 4.
|
| 22 |
+
Princeton, N.J., Princeton University Press, 1957.
|
| 23 |
+
.. [Rasch03] J. Rasch and A. C. H. Yu, 'Efficient Storage Scheme for
|
| 24 |
+
Pre-calculated Wigner 3j, 6j and Gaunt Coefficients', SIAM
|
| 25 |
+
J. Sci. Comput. Volume 25, Issue 4, pp. 1416-1428 (2003)
|
| 26 |
+
.. [Liberatodebrito82] 'FORTRAN program for the integral of three
|
| 27 |
+
spherical harmonics', A. Liberato de Brito,
|
| 28 |
+
Comput. Phys. Commun., Volume 25, pp. 81-85 (1982)
|
| 29 |
+
.. [Homeier96] 'Some Properties of the Coupling Coefficients of Real
|
| 30 |
+
Spherical Harmonics and Their Relation to Gaunt Coefficients',
|
| 31 |
+
H. H. H. Homeier and E. O. Steinborn J. Mol. Struct., Volume 368,
|
| 32 |
+
pp. 31-37 (1996)
|
| 33 |
+
|
| 34 |
+
Credits and Copyright
|
| 35 |
+
=====================
|
| 36 |
+
|
| 37 |
+
This code was taken from Sage with the permission of all authors:
|
| 38 |
+
|
| 39 |
+
https://groups.google.com/forum/#!topic/sage-devel/M4NZdu-7O38
|
| 40 |
+
|
| 41 |
+
Authors
|
| 42 |
+
=======
|
| 43 |
+
|
| 44 |
+
- Jens Rasch (2009-03-24): initial version for Sage
|
| 45 |
+
|
| 46 |
+
- Jens Rasch (2009-05-31): updated to sage-4.0
|
| 47 |
+
|
| 48 |
+
- Oscar Gerardo Lazo Arjona (2017-06-18): added Wigner D matrices
|
| 49 |
+
|
| 50 |
+
- Phil Adam LeMaitre (2022-09-19): added real Gaunt coefficient
|
| 51 |
+
|
| 52 |
+
Copyright (C) 2008 Jens Rasch <jyr2000@gmail.com>
|
| 53 |
+
|
| 54 |
+
"""
|
| 55 |
+
from sympy.concrete.summations import Sum
|
| 56 |
+
from sympy.core.add import Add
|
| 57 |
+
from sympy.core.numbers import int_valued
|
| 58 |
+
from sympy.core.function import Function
|
| 59 |
+
from sympy.core.numbers import (Float, I, Integer, pi, Rational)
|
| 60 |
+
from sympy.core.singleton import S
|
| 61 |
+
from sympy.core.symbol import Dummy
|
| 62 |
+
from sympy.core.sympify import sympify
|
| 63 |
+
from sympy.functions.combinatorial.factorials import (binomial, factorial)
|
| 64 |
+
from sympy.functions.elementary.complexes import re
|
| 65 |
+
from sympy.functions.elementary.exponential import exp
|
| 66 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 67 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 68 |
+
from sympy.functions.special.spherical_harmonics import Ynm
|
| 69 |
+
from sympy.matrices.dense import zeros
|
| 70 |
+
from sympy.matrices.immutable import ImmutableMatrix
|
| 71 |
+
from sympy.utilities.misc import as_int
|
| 72 |
+
|
| 73 |
+
# This list of precomputed factorials is needed to massively
|
| 74 |
+
# accelerate future calculations of the various coefficients
|
| 75 |
+
_Factlist = [1]
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def _calc_factlist(nn):
|
| 79 |
+
r"""
|
| 80 |
+
Function calculates a list of precomputed factorials in order to
|
| 81 |
+
massively accelerate future calculations of the various
|
| 82 |
+
coefficients.
|
| 83 |
+
|
| 84 |
+
Parameters
|
| 85 |
+
==========
|
| 86 |
+
|
| 87 |
+
nn : integer
|
| 88 |
+
Highest factorial to be computed.
|
| 89 |
+
|
| 90 |
+
Returns
|
| 91 |
+
=======
|
| 92 |
+
|
| 93 |
+
list of integers :
|
| 94 |
+
The list of precomputed factorials.
|
| 95 |
+
|
| 96 |
+
Examples
|
| 97 |
+
========
|
| 98 |
+
|
| 99 |
+
Calculate list of factorials::
|
| 100 |
+
|
| 101 |
+
sage: from sage.functions.wigner import _calc_factlist
|
| 102 |
+
sage: _calc_factlist(10)
|
| 103 |
+
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
|
| 104 |
+
"""
|
| 105 |
+
if nn >= len(_Factlist):
|
| 106 |
+
for ii in range(len(_Factlist), int(nn + 1)):
|
| 107 |
+
_Factlist.append(_Factlist[ii - 1] * ii)
|
| 108 |
+
return _Factlist[:int(nn) + 1]
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _int_or_halfint(value):
|
| 112 |
+
"""return Python int unless value is half-int (then return float)"""
|
| 113 |
+
if isinstance(value, int):
|
| 114 |
+
return value
|
| 115 |
+
elif type(value) is float:
|
| 116 |
+
if value.is_integer():
|
| 117 |
+
return int(value) # an int
|
| 118 |
+
if (2*value).is_integer():
|
| 119 |
+
return value # a float
|
| 120 |
+
elif isinstance(value, Rational):
|
| 121 |
+
if value.q == 2:
|
| 122 |
+
return value.p/value.q # a float
|
| 123 |
+
elif value.q == 1:
|
| 124 |
+
return value.p # an int
|
| 125 |
+
elif isinstance(value, Float):
|
| 126 |
+
return _int_or_halfint(float(value))
|
| 127 |
+
raise ValueError("expecting integer or half-integer, got %s" % value)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def wigner_3j(j_1, j_2, j_3, m_1, m_2, m_3):
|
| 131 |
+
r"""
|
| 132 |
+
Calculate the Wigner 3j symbol `\operatorname{Wigner3j}(j_1,j_2,j_3,m_1,m_2,m_3)`.
|
| 133 |
+
|
| 134 |
+
Parameters
|
| 135 |
+
==========
|
| 136 |
+
|
| 137 |
+
j_1, j_2, j_3, m_1, m_2, m_3 :
|
| 138 |
+
Integer or half integer.
|
| 139 |
+
|
| 140 |
+
Returns
|
| 141 |
+
=======
|
| 142 |
+
|
| 143 |
+
Rational number times the square root of a rational number.
|
| 144 |
+
|
| 145 |
+
Examples
|
| 146 |
+
========
|
| 147 |
+
|
| 148 |
+
>>> from sympy.physics.wigner import wigner_3j
|
| 149 |
+
>>> wigner_3j(2, 6, 4, 0, 0, 0)
|
| 150 |
+
sqrt(715)/143
|
| 151 |
+
>>> wigner_3j(2, 6, 4, 0, 0, 1)
|
| 152 |
+
0
|
| 153 |
+
|
| 154 |
+
It is an error to have arguments that are not integer or half
|
| 155 |
+
integer values::
|
| 156 |
+
|
| 157 |
+
sage: wigner_3j(2.1, 6, 4, 0, 0, 0)
|
| 158 |
+
Traceback (most recent call last):
|
| 159 |
+
...
|
| 160 |
+
ValueError: j values must be integer or half integer
|
| 161 |
+
sage: wigner_3j(2, 6, 4, 1, 0, -1.1)
|
| 162 |
+
Traceback (most recent call last):
|
| 163 |
+
...
|
| 164 |
+
ValueError: m values must be integer or half integer
|
| 165 |
+
|
| 166 |
+
Notes
|
| 167 |
+
=====
|
| 168 |
+
|
| 169 |
+
The Wigner 3j symbol obeys the following symmetry rules:
|
| 170 |
+
|
| 171 |
+
- invariant under any permutation of the columns (with the
|
| 172 |
+
exception of a sign change where `J:=j_1+j_2+j_3`):
|
| 173 |
+
|
| 174 |
+
.. math::
|
| 175 |
+
|
| 176 |
+
\begin{aligned}
|
| 177 |
+
\operatorname{Wigner3j}(j_1,j_2,j_3,m_1,m_2,m_3)
|
| 178 |
+
&=\operatorname{Wigner3j}(j_3,j_1,j_2,m_3,m_1,m_2) \\
|
| 179 |
+
&=\operatorname{Wigner3j}(j_2,j_3,j_1,m_2,m_3,m_1) \\
|
| 180 |
+
&=(-1)^J \operatorname{Wigner3j}(j_3,j_2,j_1,m_3,m_2,m_1) \\
|
| 181 |
+
&=(-1)^J \operatorname{Wigner3j}(j_1,j_3,j_2,m_1,m_3,m_2) \\
|
| 182 |
+
&=(-1)^J \operatorname{Wigner3j}(j_2,j_1,j_3,m_2,m_1,m_3)
|
| 183 |
+
\end{aligned}
|
| 184 |
+
|
| 185 |
+
- invariant under space inflection, i.e.
|
| 186 |
+
|
| 187 |
+
.. math::
|
| 188 |
+
|
| 189 |
+
\operatorname{Wigner3j}(j_1,j_2,j_3,m_1,m_2,m_3)
|
| 190 |
+
=(-1)^J \operatorname{Wigner3j}(j_1,j_2,j_3,-m_1,-m_2,-m_3)
|
| 191 |
+
|
| 192 |
+
- symmetric with respect to the 72 additional symmetries based on
|
| 193 |
+
the work by [Regge58]_
|
| 194 |
+
|
| 195 |
+
- zero for `j_1`, `j_2`, `j_3` not fulfilling triangle relation
|
| 196 |
+
|
| 197 |
+
- zero for `m_1 + m_2 + m_3 \neq 0`
|
| 198 |
+
|
| 199 |
+
- zero for violating any one of the conditions
|
| 200 |
+
`m_1 \in \{-|j_1|, \ldots, |j_1|\}`,
|
| 201 |
+
`m_2 \in \{-|j_2|, \ldots, |j_2|\}`,
|
| 202 |
+
`m_3 \in \{-|j_3|, \ldots, |j_3|\}`
|
| 203 |
+
|
| 204 |
+
Algorithm
|
| 205 |
+
=========
|
| 206 |
+
|
| 207 |
+
This function uses the algorithm of [Edmonds74]_ to calculate the
|
| 208 |
+
value of the 3j symbol exactly. Note that the formula contains
|
| 209 |
+
alternating sums over large factorials and is therefore unsuitable
|
| 210 |
+
for finite precision arithmetic and only useful for a computer
|
| 211 |
+
algebra system [Rasch03]_.
|
| 212 |
+
|
| 213 |
+
Authors
|
| 214 |
+
=======
|
| 215 |
+
|
| 216 |
+
- Jens Rasch (2009-03-24): initial version
|
| 217 |
+
"""
|
| 218 |
+
|
| 219 |
+
j_1, j_2, j_3, m_1, m_2, m_3 = \
|
| 220 |
+
map(_int_or_halfint, map(sympify,
|
| 221 |
+
[j_1, j_2, j_3, m_1, m_2, m_3]))
|
| 222 |
+
|
| 223 |
+
if m_1 + m_2 + m_3 != 0:
|
| 224 |
+
return S.Zero
|
| 225 |
+
a1 = j_1 + j_2 - j_3
|
| 226 |
+
if a1 < 0:
|
| 227 |
+
return S.Zero
|
| 228 |
+
a2 = j_1 - j_2 + j_3
|
| 229 |
+
if a2 < 0:
|
| 230 |
+
return S.Zero
|
| 231 |
+
a3 = -j_1 + j_2 + j_3
|
| 232 |
+
if a3 < 0:
|
| 233 |
+
return S.Zero
|
| 234 |
+
if (abs(m_1) > j_1) or (abs(m_2) > j_2) or (abs(m_3) > j_3):
|
| 235 |
+
return S.Zero
|
| 236 |
+
if not (int_valued(j_1 - m_1) and \
|
| 237 |
+
int_valued(j_2 - m_2) and \
|
| 238 |
+
int_valued(j_3 - m_3)):
|
| 239 |
+
return S.Zero
|
| 240 |
+
|
| 241 |
+
maxfact = max(j_1 + j_2 + j_3 + 1, j_1 + abs(m_1), j_2 + abs(m_2),
|
| 242 |
+
j_3 + abs(m_3))
|
| 243 |
+
_calc_factlist(int(maxfact))
|
| 244 |
+
|
| 245 |
+
argsqrt = Integer(_Factlist[int(j_1 + j_2 - j_3)] *
|
| 246 |
+
_Factlist[int(j_1 - j_2 + j_3)] *
|
| 247 |
+
_Factlist[int(-j_1 + j_2 + j_3)] *
|
| 248 |
+
_Factlist[int(j_1 - m_1)] *
|
| 249 |
+
_Factlist[int(j_1 + m_1)] *
|
| 250 |
+
_Factlist[int(j_2 - m_2)] *
|
| 251 |
+
_Factlist[int(j_2 + m_2)] *
|
| 252 |
+
_Factlist[int(j_3 - m_3)] *
|
| 253 |
+
_Factlist[int(j_3 + m_3)]) / \
|
| 254 |
+
_Factlist[int(j_1 + j_2 + j_3 + 1)]
|
| 255 |
+
|
| 256 |
+
ressqrt = sqrt(argsqrt)
|
| 257 |
+
if ressqrt.is_complex or ressqrt.is_infinite:
|
| 258 |
+
ressqrt = ressqrt.as_real_imag()[0]
|
| 259 |
+
|
| 260 |
+
imin = max(-j_3 + j_1 + m_2, -j_3 + j_2 - m_1, 0)
|
| 261 |
+
imax = min(j_2 + m_2, j_1 - m_1, j_1 + j_2 - j_3)
|
| 262 |
+
sumres = 0
|
| 263 |
+
for ii in range(int(imin), int(imax) + 1):
|
| 264 |
+
den = _Factlist[ii] * \
|
| 265 |
+
_Factlist[int(ii + j_3 - j_1 - m_2)] * \
|
| 266 |
+
_Factlist[int(j_2 + m_2 - ii)] * \
|
| 267 |
+
_Factlist[int(j_1 - ii - m_1)] * \
|
| 268 |
+
_Factlist[int(ii + j_3 - j_2 + m_1)] * \
|
| 269 |
+
_Factlist[int(j_1 + j_2 - j_3 - ii)]
|
| 270 |
+
sumres = sumres + Integer((-1) ** ii) / den
|
| 271 |
+
|
| 272 |
+
prefid = Integer((-1) ** int(j_1 - j_2 - m_3))
|
| 273 |
+
res = ressqrt * sumres * prefid
|
| 274 |
+
return res
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def clebsch_gordan(j_1, j_2, j_3, m_1, m_2, m_3):
|
| 278 |
+
r"""
|
| 279 |
+
Calculates the Clebsch-Gordan coefficient.
|
| 280 |
+
`\left\langle j_1 m_1 \; j_2 m_2 | j_3 m_3 \right\rangle`.
|
| 281 |
+
|
| 282 |
+
The reference for this function is [Edmonds74]_.
|
| 283 |
+
|
| 284 |
+
Parameters
|
| 285 |
+
==========
|
| 286 |
+
|
| 287 |
+
j_1, j_2, j_3, m_1, m_2, m_3 :
|
| 288 |
+
Integer or half integer.
|
| 289 |
+
|
| 290 |
+
Returns
|
| 291 |
+
=======
|
| 292 |
+
|
| 293 |
+
Rational number times the square root of a rational number.
|
| 294 |
+
|
| 295 |
+
Examples
|
| 296 |
+
========
|
| 297 |
+
|
| 298 |
+
>>> from sympy import S
|
| 299 |
+
>>> from sympy.physics.wigner import clebsch_gordan
|
| 300 |
+
>>> clebsch_gordan(S(3)/2, S(1)/2, 2, S(3)/2, S(1)/2, 2)
|
| 301 |
+
1
|
| 302 |
+
>>> clebsch_gordan(S(3)/2, S(1)/2, 1, S(3)/2, -S(1)/2, 1)
|
| 303 |
+
sqrt(3)/2
|
| 304 |
+
>>> clebsch_gordan(S(3)/2, S(1)/2, 1, -S(1)/2, S(1)/2, 0)
|
| 305 |
+
-sqrt(2)/2
|
| 306 |
+
|
| 307 |
+
Notes
|
| 308 |
+
=====
|
| 309 |
+
|
| 310 |
+
The Clebsch-Gordan coefficient will be evaluated via its relation
|
| 311 |
+
to Wigner 3j symbols:
|
| 312 |
+
|
| 313 |
+
.. math::
|
| 314 |
+
|
| 315 |
+
\left\langle j_1 m_1 \; j_2 m_2 | j_3 m_3 \right\rangle
|
| 316 |
+
=(-1)^{j_1-j_2+m_3} \sqrt{2j_3+1}
|
| 317 |
+
\operatorname{Wigner3j}(j_1,j_2,j_3,m_1,m_2,-m_3)
|
| 318 |
+
|
| 319 |
+
See also the documentation on Wigner 3j symbols which exhibit much
|
| 320 |
+
higher symmetry relations than the Clebsch-Gordan coefficient.
|
| 321 |
+
|
| 322 |
+
Authors
|
| 323 |
+
=======
|
| 324 |
+
|
| 325 |
+
- Jens Rasch (2009-03-24): initial version
|
| 326 |
+
"""
|
| 327 |
+
j_1 = sympify(j_1)
|
| 328 |
+
j_2 = sympify(j_2)
|
| 329 |
+
j_3 = sympify(j_3)
|
| 330 |
+
m_1 = sympify(m_1)
|
| 331 |
+
m_2 = sympify(m_2)
|
| 332 |
+
m_3 = sympify(m_3)
|
| 333 |
+
|
| 334 |
+
w = wigner_3j(j_1, j_2, j_3, m_1, m_2, -m_3)
|
| 335 |
+
|
| 336 |
+
return (-1) ** (j_1 - j_2 + m_3) * sqrt(2 * j_3 + 1) * w
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
def _big_delta_coeff(aa, bb, cc, prec=None):
|
| 340 |
+
r"""
|
| 341 |
+
Calculates the Delta coefficient of the 3 angular momenta for
|
| 342 |
+
Racah symbols. Also checks that the differences are of integer
|
| 343 |
+
value.
|
| 344 |
+
|
| 345 |
+
Parameters
|
| 346 |
+
==========
|
| 347 |
+
|
| 348 |
+
aa :
|
| 349 |
+
First angular momentum, integer or half integer.
|
| 350 |
+
bb :
|
| 351 |
+
Second angular momentum, integer or half integer.
|
| 352 |
+
cc :
|
| 353 |
+
Third angular momentum, integer or half integer.
|
| 354 |
+
prec :
|
| 355 |
+
Precision of the ``sqrt()`` calculation.
|
| 356 |
+
|
| 357 |
+
Returns
|
| 358 |
+
=======
|
| 359 |
+
|
| 360 |
+
double : Value of the Delta coefficient.
|
| 361 |
+
|
| 362 |
+
Examples
|
| 363 |
+
========
|
| 364 |
+
|
| 365 |
+
sage: from sage.functions.wigner import _big_delta_coeff
|
| 366 |
+
sage: _big_delta_coeff(1,1,1)
|
| 367 |
+
1/2*sqrt(1/6)
|
| 368 |
+
"""
|
| 369 |
+
|
| 370 |
+
# the triangle test will only pass if a) all 3 values are ints or
|
| 371 |
+
# b) 1 is an int and the other two are half-ints
|
| 372 |
+
if not int_valued(aa + bb - cc):
|
| 373 |
+
raise ValueError("j values must be integer or half integer and fulfill the triangle relation")
|
| 374 |
+
if not int_valued(aa + cc - bb):
|
| 375 |
+
raise ValueError("j values must be integer or half integer and fulfill the triangle relation")
|
| 376 |
+
if not int_valued(bb + cc - aa):
|
| 377 |
+
raise ValueError("j values must be integer or half integer and fulfill the triangle relation")
|
| 378 |
+
if (aa + bb - cc) < 0:
|
| 379 |
+
return S.Zero
|
| 380 |
+
if (aa + cc - bb) < 0:
|
| 381 |
+
return S.Zero
|
| 382 |
+
if (bb + cc - aa) < 0:
|
| 383 |
+
return S.Zero
|
| 384 |
+
|
| 385 |
+
maxfact = max(aa + bb - cc, aa + cc - bb, bb + cc - aa, aa + bb + cc + 1)
|
| 386 |
+
_calc_factlist(maxfact)
|
| 387 |
+
|
| 388 |
+
argsqrt = Integer(_Factlist[int(aa + bb - cc)] *
|
| 389 |
+
_Factlist[int(aa + cc - bb)] *
|
| 390 |
+
_Factlist[int(bb + cc - aa)]) / \
|
| 391 |
+
Integer(_Factlist[int(aa + bb + cc + 1)])
|
| 392 |
+
|
| 393 |
+
ressqrt = sqrt(argsqrt)
|
| 394 |
+
if prec:
|
| 395 |
+
ressqrt = ressqrt.evalf(prec).as_real_imag()[0]
|
| 396 |
+
return ressqrt
|
| 397 |
+
|
| 398 |
+
|
| 399 |
+
def racah(aa, bb, cc, dd, ee, ff, prec=None):
|
| 400 |
+
r"""
|
| 401 |
+
Calculate the Racah symbol `W(a,b,c,d;e,f)`.
|
| 402 |
+
|
| 403 |
+
Parameters
|
| 404 |
+
==========
|
| 405 |
+
|
| 406 |
+
a, ..., f :
|
| 407 |
+
Integer or half integer.
|
| 408 |
+
prec :
|
| 409 |
+
Precision, default: ``None``. Providing a precision can
|
| 410 |
+
drastically speed up the calculation.
|
| 411 |
+
|
| 412 |
+
Returns
|
| 413 |
+
=======
|
| 414 |
+
|
| 415 |
+
Rational number times the square root of a rational number
|
| 416 |
+
(if ``prec=None``), or real number if a precision is given.
|
| 417 |
+
|
| 418 |
+
Examples
|
| 419 |
+
========
|
| 420 |
+
|
| 421 |
+
>>> from sympy.physics.wigner import racah
|
| 422 |
+
>>> racah(3,3,3,3,3,3)
|
| 423 |
+
-1/14
|
| 424 |
+
|
| 425 |
+
Notes
|
| 426 |
+
=====
|
| 427 |
+
|
| 428 |
+
The Racah symbol is related to the Wigner 6j symbol:
|
| 429 |
+
|
| 430 |
+
.. math::
|
| 431 |
+
|
| 432 |
+
\operatorname{Wigner6j}(j_1,j_2,j_3,j_4,j_5,j_6)
|
| 433 |
+
=(-1)^{j_1+j_2+j_4+j_5} W(j_1,j_2,j_5,j_4,j_3,j_6)
|
| 434 |
+
|
| 435 |
+
Please see the 6j symbol for its much richer symmetries and for
|
| 436 |
+
additional properties.
|
| 437 |
+
|
| 438 |
+
Algorithm
|
| 439 |
+
=========
|
| 440 |
+
|
| 441 |
+
This function uses the algorithm of [Edmonds74]_ to calculate the
|
| 442 |
+
value of the 6j symbol exactly. Note that the formula contains
|
| 443 |
+
alternating sums over large factorials and is therefore unsuitable
|
| 444 |
+
for finite precision arithmetic and only useful for a computer
|
| 445 |
+
algebra system [Rasch03]_.
|
| 446 |
+
|
| 447 |
+
Authors
|
| 448 |
+
=======
|
| 449 |
+
|
| 450 |
+
- Jens Rasch (2009-03-24): initial version
|
| 451 |
+
"""
|
| 452 |
+
prefac = _big_delta_coeff(aa, bb, ee, prec) * \
|
| 453 |
+
_big_delta_coeff(cc, dd, ee, prec) * \
|
| 454 |
+
_big_delta_coeff(aa, cc, ff, prec) * \
|
| 455 |
+
_big_delta_coeff(bb, dd, ff, prec)
|
| 456 |
+
if prefac == 0:
|
| 457 |
+
return S.Zero
|
| 458 |
+
imin = max(aa + bb + ee, cc + dd + ee, aa + cc + ff, bb + dd + ff)
|
| 459 |
+
imax = min(aa + bb + cc + dd, aa + dd + ee + ff, bb + cc + ee + ff)
|
| 460 |
+
|
| 461 |
+
maxfact = max(imax + 1, aa + bb + cc + dd, aa + dd + ee + ff,
|
| 462 |
+
bb + cc + ee + ff)
|
| 463 |
+
_calc_factlist(maxfact)
|
| 464 |
+
|
| 465 |
+
sumres = 0
|
| 466 |
+
for kk in range(int(imin), int(imax) + 1):
|
| 467 |
+
den = _Factlist[int(kk - aa - bb - ee)] * \
|
| 468 |
+
_Factlist[int(kk - cc - dd - ee)] * \
|
| 469 |
+
_Factlist[int(kk - aa - cc - ff)] * \
|
| 470 |
+
_Factlist[int(kk - bb - dd - ff)] * \
|
| 471 |
+
_Factlist[int(aa + bb + cc + dd - kk)] * \
|
| 472 |
+
_Factlist[int(aa + dd + ee + ff - kk)] * \
|
| 473 |
+
_Factlist[int(bb + cc + ee + ff - kk)]
|
| 474 |
+
sumres = sumres + Integer((-1) ** kk * _Factlist[kk + 1]) / den
|
| 475 |
+
|
| 476 |
+
res = prefac * sumres * (-1) ** int(aa + bb + cc + dd)
|
| 477 |
+
return res
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
def wigner_6j(j_1, j_2, j_3, j_4, j_5, j_6, prec=None):
|
| 481 |
+
r"""
|
| 482 |
+
Calculate the Wigner 6j symbol `\operatorname{Wigner6j}(j_1,j_2,j_3,j_4,j_5,j_6)`.
|
| 483 |
+
|
| 484 |
+
Parameters
|
| 485 |
+
==========
|
| 486 |
+
|
| 487 |
+
j_1, ..., j_6 :
|
| 488 |
+
Integer or half integer.
|
| 489 |
+
prec :
|
| 490 |
+
Precision, default: ``None``. Providing a precision can
|
| 491 |
+
drastically speed up the calculation.
|
| 492 |
+
|
| 493 |
+
Returns
|
| 494 |
+
=======
|
| 495 |
+
|
| 496 |
+
Rational number times the square root of a rational number
|
| 497 |
+
(if ``prec=None``), or real number if a precision is given.
|
| 498 |
+
|
| 499 |
+
Examples
|
| 500 |
+
========
|
| 501 |
+
|
| 502 |
+
>>> from sympy.physics.wigner import wigner_6j
|
| 503 |
+
>>> wigner_6j(3,3,3,3,3,3)
|
| 504 |
+
-1/14
|
| 505 |
+
>>> wigner_6j(5,5,5,5,5,5)
|
| 506 |
+
1/52
|
| 507 |
+
|
| 508 |
+
It is an error to have arguments that are not integer or half
|
| 509 |
+
integer values or do not fulfill the triangle relation::
|
| 510 |
+
|
| 511 |
+
sage: wigner_6j(2.5,2.5,2.5,2.5,2.5,2.5)
|
| 512 |
+
Traceback (most recent call last):
|
| 513 |
+
...
|
| 514 |
+
ValueError: j values must be integer or half integer and fulfill the triangle relation
|
| 515 |
+
sage: wigner_6j(0.5,0.5,1.1,0.5,0.5,1.1)
|
| 516 |
+
Traceback (most recent call last):
|
| 517 |
+
...
|
| 518 |
+
ValueError: j values must be integer or half integer and fulfill the triangle relation
|
| 519 |
+
|
| 520 |
+
Notes
|
| 521 |
+
=====
|
| 522 |
+
|
| 523 |
+
The Wigner 6j symbol is related to the Racah symbol but exhibits
|
| 524 |
+
more symmetries as detailed below.
|
| 525 |
+
|
| 526 |
+
.. math::
|
| 527 |
+
|
| 528 |
+
\operatorname{Wigner6j}(j_1,j_2,j_3,j_4,j_5,j_6)
|
| 529 |
+
=(-1)^{j_1+j_2+j_4+j_5} W(j_1,j_2,j_5,j_4,j_3,j_6)
|
| 530 |
+
|
| 531 |
+
The Wigner 6j symbol obeys the following symmetry rules:
|
| 532 |
+
|
| 533 |
+
- Wigner 6j symbols are left invariant under any permutation of
|
| 534 |
+
the columns:
|
| 535 |
+
|
| 536 |
+
.. math::
|
| 537 |
+
|
| 538 |
+
\begin{aligned}
|
| 539 |
+
\operatorname{Wigner6j}(j_1,j_2,j_3,j_4,j_5,j_6)
|
| 540 |
+
&=\operatorname{Wigner6j}(j_3,j_1,j_2,j_6,j_4,j_5) \\
|
| 541 |
+
&=\operatorname{Wigner6j}(j_2,j_3,j_1,j_5,j_6,j_4) \\
|
| 542 |
+
&=\operatorname{Wigner6j}(j_3,j_2,j_1,j_6,j_5,j_4) \\
|
| 543 |
+
&=\operatorname{Wigner6j}(j_1,j_3,j_2,j_4,j_6,j_5) \\
|
| 544 |
+
&=\operatorname{Wigner6j}(j_2,j_1,j_3,j_5,j_4,j_6)
|
| 545 |
+
\end{aligned}
|
| 546 |
+
|
| 547 |
+
- They are invariant under the exchange of the upper and lower
|
| 548 |
+
arguments in each of any two columns, i.e.
|
| 549 |
+
|
| 550 |
+
.. math::
|
| 551 |
+
|
| 552 |
+
\begin{aligned}
|
| 553 |
+
\operatorname{Wigner6j}(j_1,j_2,j_3,j_4,j_5,j_6)
|
| 554 |
+
&=\operatorname{Wigner6j}(j_1,j_5,j_6,j_4,j_2,j_3)\\
|
| 555 |
+
&=\operatorname{Wigner6j}(j_4,j_2,j_6,j_1,j_5,j_3)\\
|
| 556 |
+
&=\operatorname{Wigner6j}(j_4,j_5,j_3,j_1,j_2,j_6)
|
| 557 |
+
\end{aligned}
|
| 558 |
+
|
| 559 |
+
- additional 6 symmetries [Regge59]_ giving rise to 144 symmetries
|
| 560 |
+
in total
|
| 561 |
+
|
| 562 |
+
- only non-zero if any triple of `j`'s fulfill a triangle relation
|
| 563 |
+
|
| 564 |
+
Algorithm
|
| 565 |
+
=========
|
| 566 |
+
|
| 567 |
+
This function uses the algorithm of [Edmonds74]_ to calculate the
|
| 568 |
+
value of the 6j symbol exactly. Note that the formula contains
|
| 569 |
+
alternating sums over large factorials and is therefore unsuitable
|
| 570 |
+
for finite precision arithmetic and only useful for a computer
|
| 571 |
+
algebra system [Rasch03]_.
|
| 572 |
+
|
| 573 |
+
"""
|
| 574 |
+
j_1, j_2, j_3, j_4, j_5, j_6 = map(sympify, \
|
| 575 |
+
[j_1, j_2, j_3, j_4, j_5, j_6])
|
| 576 |
+
res = (-1) ** int(j_1 + j_2 + j_4 + j_5) * \
|
| 577 |
+
racah(j_1, j_2, j_5, j_4, j_3, j_6, prec)
|
| 578 |
+
return res
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
def wigner_9j(j_1, j_2, j_3, j_4, j_5, j_6, j_7, j_8, j_9, prec=None):
|
| 582 |
+
r"""
|
| 583 |
+
Calculate the Wigner 9j symbol
|
| 584 |
+
`\operatorname{Wigner9j}(j_1,j_2,j_3,j_4,j_5,j_6,j_7,j_8,j_9)`.
|
| 585 |
+
|
| 586 |
+
Parameters
|
| 587 |
+
==========
|
| 588 |
+
|
| 589 |
+
j_1, ..., j_9 :
|
| 590 |
+
Integer or half integer.
|
| 591 |
+
prec : precision, default
|
| 592 |
+
``None``. Providing a precision can
|
| 593 |
+
drastically speed up the calculation.
|
| 594 |
+
|
| 595 |
+
Returns
|
| 596 |
+
=======
|
| 597 |
+
|
| 598 |
+
Rational number times the square root of a rational number
|
| 599 |
+
(if ``prec=None``), or real number if a precision is given.
|
| 600 |
+
|
| 601 |
+
Examples
|
| 602 |
+
========
|
| 603 |
+
|
| 604 |
+
>>> from sympy.physics.wigner import wigner_9j
|
| 605 |
+
>>> wigner_9j(1,1,1, 1,1,1, 1,1,0, prec=64)
|
| 606 |
+
0.05555555555555555555555555555555555555555555555555555555555555555
|
| 607 |
+
|
| 608 |
+
>>> wigner_9j(1/2,1/2,0, 1/2,3/2,1, 0,1,1, prec=64)
|
| 609 |
+
0.1666666666666666666666666666666666666666666666666666666666666667
|
| 610 |
+
|
| 611 |
+
It is an error to have arguments that are not integer or half
|
| 612 |
+
integer values or do not fulfill the triangle relation::
|
| 613 |
+
|
| 614 |
+
sage: wigner_9j(0.5,0.5,0.5, 0.5,0.5,0.5, 0.5,0.5,0.5,prec=64)
|
| 615 |
+
Traceback (most recent call last):
|
| 616 |
+
...
|
| 617 |
+
ValueError: j values must be integer or half integer and fulfill the triangle relation
|
| 618 |
+
sage: wigner_9j(1,1,1, 0.5,1,1.5, 0.5,1,2.5,prec=64)
|
| 619 |
+
Traceback (most recent call last):
|
| 620 |
+
...
|
| 621 |
+
ValueError: j values must be integer or half integer and fulfill the triangle relation
|
| 622 |
+
|
| 623 |
+
Algorithm
|
| 624 |
+
=========
|
| 625 |
+
|
| 626 |
+
This function uses the algorithm of [Edmonds74]_ to calculate the
|
| 627 |
+
value of the 3j symbol exactly. Note that the formula contains
|
| 628 |
+
alternating sums over large factorials and is therefore unsuitable
|
| 629 |
+
for finite precision arithmetic and only useful for a computer
|
| 630 |
+
algebra system [Rasch03]_.
|
| 631 |
+
"""
|
| 632 |
+
j_1, j_2, j_3, j_4, j_5, j_6, j_7, j_8, j_9 = map(sympify, \
|
| 633 |
+
[j_1, j_2, j_3, j_4, j_5, j_6, j_7, j_8, j_9])
|
| 634 |
+
imax = int(min(j_1 + j_9, j_2 + j_6, j_4 + j_8) * 2)
|
| 635 |
+
imin = imax % 2
|
| 636 |
+
sumres = 0
|
| 637 |
+
for kk in range(imin, int(imax) + 1, 2):
|
| 638 |
+
sumres = sumres + (kk + 1) * \
|
| 639 |
+
racah(j_1, j_2, j_9, j_6, j_3, kk / 2, prec) * \
|
| 640 |
+
racah(j_4, j_6, j_8, j_2, j_5, kk / 2, prec) * \
|
| 641 |
+
racah(j_1, j_4, j_9, j_8, j_7, kk / 2, prec)
|
| 642 |
+
return sumres
|
| 643 |
+
|
| 644 |
+
|
| 645 |
+
def gaunt(l_1, l_2, l_3, m_1, m_2, m_3, prec=None):
|
| 646 |
+
r"""
|
| 647 |
+
Calculate the Gaunt coefficient.
|
| 648 |
+
|
| 649 |
+
Explanation
|
| 650 |
+
===========
|
| 651 |
+
|
| 652 |
+
The Gaunt coefficient is defined as the integral over three
|
| 653 |
+
spherical harmonics:
|
| 654 |
+
|
| 655 |
+
.. math::
|
| 656 |
+
|
| 657 |
+
\begin{aligned}
|
| 658 |
+
\operatorname{Gaunt}(l_1,l_2,l_3,m_1,m_2,m_3)
|
| 659 |
+
&=\int Y_{l_1,m_1}(\Omega)
|
| 660 |
+
Y_{l_2,m_2}(\Omega) Y_{l_3,m_3}(\Omega) \,d\Omega \\
|
| 661 |
+
&=\sqrt{\frac{(2l_1+1)(2l_2+1)(2l_3+1)}{4\pi}}
|
| 662 |
+
\operatorname{Wigner3j}(l_1,l_2,l_3,0,0,0)
|
| 663 |
+
\operatorname{Wigner3j}(l_1,l_2,l_3,m_1,m_2,m_3)
|
| 664 |
+
\end{aligned}
|
| 665 |
+
|
| 666 |
+
Parameters
|
| 667 |
+
==========
|
| 668 |
+
|
| 669 |
+
l_1, l_2, l_3, m_1, m_2, m_3 :
|
| 670 |
+
Integer.
|
| 671 |
+
prec - precision, default: ``None``.
|
| 672 |
+
Providing a precision can
|
| 673 |
+
drastically speed up the calculation.
|
| 674 |
+
|
| 675 |
+
Returns
|
| 676 |
+
=======
|
| 677 |
+
|
| 678 |
+
Rational number times the square root of a rational number
|
| 679 |
+
(if ``prec=None``), or real number if a precision is given.
|
| 680 |
+
|
| 681 |
+
Examples
|
| 682 |
+
========
|
| 683 |
+
|
| 684 |
+
>>> from sympy.physics.wigner import gaunt
|
| 685 |
+
>>> gaunt(1,0,1,1,0,-1)
|
| 686 |
+
-1/(2*sqrt(pi))
|
| 687 |
+
>>> gaunt(1000,1000,1200,9,3,-12).n(64)
|
| 688 |
+
0.006895004219221134484332976156744208248842039317638217822322799675
|
| 689 |
+
|
| 690 |
+
It is an error to use non-integer values for `l` and `m`::
|
| 691 |
+
|
| 692 |
+
sage: gaunt(1.2,0,1.2,0,0,0)
|
| 693 |
+
Traceback (most recent call last):
|
| 694 |
+
...
|
| 695 |
+
ValueError: l values must be integer
|
| 696 |
+
sage: gaunt(1,0,1,1.1,0,-1.1)
|
| 697 |
+
Traceback (most recent call last):
|
| 698 |
+
...
|
| 699 |
+
ValueError: m values must be integer
|
| 700 |
+
|
| 701 |
+
Notes
|
| 702 |
+
=====
|
| 703 |
+
|
| 704 |
+
The Gaunt coefficient obeys the following symmetry rules:
|
| 705 |
+
|
| 706 |
+
- invariant under any permutation of the columns
|
| 707 |
+
|
| 708 |
+
.. math::
|
| 709 |
+
\begin{aligned}
|
| 710 |
+
Y(l_1,l_2,l_3,m_1,m_2,m_3)
|
| 711 |
+
&=Y(l_3,l_1,l_2,m_3,m_1,m_2) \\
|
| 712 |
+
&=Y(l_2,l_3,l_1,m_2,m_3,m_1) \\
|
| 713 |
+
&=Y(l_3,l_2,l_1,m_3,m_2,m_1) \\
|
| 714 |
+
&=Y(l_1,l_3,l_2,m_1,m_3,m_2) \\
|
| 715 |
+
&=Y(l_2,l_1,l_3,m_2,m_1,m_3)
|
| 716 |
+
\end{aligned}
|
| 717 |
+
|
| 718 |
+
- invariant under space inflection, i.e.
|
| 719 |
+
|
| 720 |
+
.. math::
|
| 721 |
+
Y(l_1,l_2,l_3,m_1,m_2,m_3)
|
| 722 |
+
=Y(l_1,l_2,l_3,-m_1,-m_2,-m_3)
|
| 723 |
+
|
| 724 |
+
- symmetric with respect to the 72 Regge symmetries as inherited
|
| 725 |
+
for the `3j` symbols [Regge58]_
|
| 726 |
+
|
| 727 |
+
- zero for `l_1`, `l_2`, `l_3` not fulfilling triangle relation
|
| 728 |
+
|
| 729 |
+
- zero for violating any one of the conditions: `l_1 \ge |m_1|`,
|
| 730 |
+
`l_2 \ge |m_2|`, `l_3 \ge |m_3|`
|
| 731 |
+
|
| 732 |
+
- non-zero only for an even sum of the `l_i`, i.e.
|
| 733 |
+
`L = l_1 + l_2 + l_3 = 2n` for `n` in `\mathbb{N}`
|
| 734 |
+
|
| 735 |
+
Algorithms
|
| 736 |
+
==========
|
| 737 |
+
|
| 738 |
+
This function uses the algorithm of [Liberatodebrito82]_ to
|
| 739 |
+
calculate the value of the Gaunt coefficient exactly. Note that
|
| 740 |
+
the formula contains alternating sums over large factorials and is
|
| 741 |
+
therefore unsuitable for finite precision arithmetic and only
|
| 742 |
+
useful for a computer algebra system [Rasch03]_.
|
| 743 |
+
|
| 744 |
+
Authors
|
| 745 |
+
=======
|
| 746 |
+
|
| 747 |
+
Jens Rasch (2009-03-24): initial version for Sage.
|
| 748 |
+
"""
|
| 749 |
+
l_1, l_2, l_3, m_1, m_2, m_3 = [
|
| 750 |
+
as_int(i) for i in (l_1, l_2, l_3, m_1, m_2, m_3)]
|
| 751 |
+
|
| 752 |
+
if l_1 + l_2 - l_3 < 0:
|
| 753 |
+
return S.Zero
|
| 754 |
+
if l_1 - l_2 + l_3 < 0:
|
| 755 |
+
return S.Zero
|
| 756 |
+
if -l_1 + l_2 + l_3 < 0:
|
| 757 |
+
return S.Zero
|
| 758 |
+
if (m_1 + m_2 + m_3) != 0:
|
| 759 |
+
return S.Zero
|
| 760 |
+
if (abs(m_1) > l_1) or (abs(m_2) > l_2) or (abs(m_3) > l_3):
|
| 761 |
+
return S.Zero
|
| 762 |
+
bigL, remL = divmod(l_1 + l_2 + l_3, 2)
|
| 763 |
+
if remL % 2:
|
| 764 |
+
return S.Zero
|
| 765 |
+
|
| 766 |
+
imin = max(-l_3 + l_1 + m_2, -l_3 + l_2 - m_1, 0)
|
| 767 |
+
imax = min(l_2 + m_2, l_1 - m_1, l_1 + l_2 - l_3)
|
| 768 |
+
|
| 769 |
+
_calc_factlist(max(l_1 + l_2 + l_3 + 1, imax + 1))
|
| 770 |
+
|
| 771 |
+
ressqrt = sqrt((2 * l_1 + 1) * (2 * l_2 + 1) * (2 * l_3 + 1) * \
|
| 772 |
+
_Factlist[l_1 - m_1] * _Factlist[l_1 + m_1] * _Factlist[l_2 - m_2] * \
|
| 773 |
+
_Factlist[l_2 + m_2] * _Factlist[l_3 - m_3] * _Factlist[l_3 + m_3] / \
|
| 774 |
+
(4*pi))
|
| 775 |
+
|
| 776 |
+
prefac = Integer(_Factlist[bigL] * _Factlist[l_2 - l_1 + l_3] *
|
| 777 |
+
_Factlist[l_1 - l_2 + l_3] * _Factlist[l_1 + l_2 - l_3])/ \
|
| 778 |
+
_Factlist[2 * bigL + 1]/ \
|
| 779 |
+
(_Factlist[bigL - l_1] *
|
| 780 |
+
_Factlist[bigL - l_2] * _Factlist[bigL - l_3])
|
| 781 |
+
|
| 782 |
+
sumres = 0
|
| 783 |
+
for ii in range(int(imin), int(imax) + 1):
|
| 784 |
+
den = _Factlist[ii] * _Factlist[ii + l_3 - l_1 - m_2] * \
|
| 785 |
+
_Factlist[l_2 + m_2 - ii] * _Factlist[l_1 - ii - m_1] * \
|
| 786 |
+
_Factlist[ii + l_3 - l_2 + m_1] * _Factlist[l_1 + l_2 - l_3 - ii]
|
| 787 |
+
sumres = sumres + Integer((-1) ** ii) / den
|
| 788 |
+
|
| 789 |
+
res = ressqrt * prefac * sumres * Integer((-1) ** (bigL + l_3 + m_1 - m_2))
|
| 790 |
+
if prec is not None:
|
| 791 |
+
res = res.n(prec)
|
| 792 |
+
return res
|
| 793 |
+
|
| 794 |
+
|
| 795 |
+
def real_gaunt(l_1, l_2, l_3, mu_1, mu_2, mu_3, prec=None):
|
| 796 |
+
r"""
|
| 797 |
+
Calculate the real Gaunt coefficient.
|
| 798 |
+
|
| 799 |
+
Explanation
|
| 800 |
+
===========
|
| 801 |
+
|
| 802 |
+
The real Gaunt coefficient is defined as the integral over three
|
| 803 |
+
real spherical harmonics:
|
| 804 |
+
|
| 805 |
+
.. math::
|
| 806 |
+
\begin{aligned}
|
| 807 |
+
\operatorname{RealGaunt}(l_1,l_2,l_3,\mu_1,\mu_2,\mu_3)
|
| 808 |
+
&=\int Z^{\mu_1}_{l_1}(\Omega)
|
| 809 |
+
Z^{\mu_2}_{l_2}(\Omega) Z^{\mu_3}_{l_3}(\Omega) \,d\Omega \\
|
| 810 |
+
\end{aligned}
|
| 811 |
+
|
| 812 |
+
Alternatively, it can be defined in terms of the standard Gaunt
|
| 813 |
+
coefficient by relating the real spherical harmonics to the standard
|
| 814 |
+
spherical harmonics via a unitary transformation `U`, i.e.
|
| 815 |
+
`Z^{\mu}_{l}(\Omega)=\sum_{m'}U^{\mu}_{m'}Y^{m'}_{l}(\Omega)` [Homeier96]_.
|
| 816 |
+
The real Gaunt coefficient is then defined as
|
| 817 |
+
|
| 818 |
+
.. math::
|
| 819 |
+
\begin{aligned}
|
| 820 |
+
\operatorname{RealGaunt}(l_1,l_2,l_3,\mu_1,\mu_2,\mu_3)
|
| 821 |
+
&=\int Z^{\mu_1}_{l_1}(\Omega)
|
| 822 |
+
Z^{\mu_2}_{l_2}(\Omega) Z^{\mu_3}_{l_3}(\Omega) \,d\Omega \\
|
| 823 |
+
&=\sum_{m'_1 m'_2 m'_3} U^{\mu_1}_{m'_1}U^{\mu_2}_{m'_2}U^{\mu_3}_{m'_3}
|
| 824 |
+
\operatorname{Gaunt}(l_1,l_2,l_3,m'_1,m'_2,m'_3)
|
| 825 |
+
\end{aligned}
|
| 826 |
+
|
| 827 |
+
The unitary matrix `U` has components
|
| 828 |
+
|
| 829 |
+
.. math::
|
| 830 |
+
\begin{aligned}
|
| 831 |
+
U^\mu_{m} = \delta_{|\mu||m|}*(\delta_{m0}\delta_{\mu 0} + \frac{1}{\sqrt{2}}\big[\Theta(\mu)\big(\delta_{m\mu}+(-1)^{m}\delta_{m-\mu}\big)
|
| 832 |
+
+i \Theta(-\mu)\big((-1)^{m}\delta_{m\mu}-\delta_{m-\mu}\big)\big])
|
| 833 |
+
\end{aligned}
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
where `\delta_{ij}` is the Kronecker delta symbol and `\Theta` is a step
|
| 837 |
+
function defined as
|
| 838 |
+
|
| 839 |
+
.. math::
|
| 840 |
+
\begin{aligned}
|
| 841 |
+
\Theta(x) = \begin{cases} 1 \,\text{for}\, x > 0 \\ 0 \,\text{for}\, x \leq 0 \end{cases}
|
| 842 |
+
\end{aligned}
|
| 843 |
+
|
| 844 |
+
Parameters
|
| 845 |
+
==========
|
| 846 |
+
|
| 847 |
+
l_1, l_2, l_3, mu_1, mu_2, mu_3 :
|
| 848 |
+
Integer degree and order
|
| 849 |
+
|
| 850 |
+
prec - precision, default: ``None``.
|
| 851 |
+
Providing a precision can
|
| 852 |
+
drastically speed up the calculation.
|
| 853 |
+
|
| 854 |
+
Returns
|
| 855 |
+
=======
|
| 856 |
+
|
| 857 |
+
Rational number times the square root of a rational number.
|
| 858 |
+
|
| 859 |
+
Examples
|
| 860 |
+
========
|
| 861 |
+
>>> from sympy.physics.wigner import real_gaunt
|
| 862 |
+
>>> real_gaunt(1,1,2,-1,1,-2)
|
| 863 |
+
sqrt(15)/(10*sqrt(pi))
|
| 864 |
+
>>> real_gaunt(10,10,20,-9,-9,0,prec=64)
|
| 865 |
+
-0.00002480019791932209313156167176797577821140084216297395518482071448
|
| 866 |
+
|
| 867 |
+
It is an error to use non-integer values for `l` and `\mu`::
|
| 868 |
+
real_gaunt(2.8,0.5,1.3,0,0,0)
|
| 869 |
+
Traceback (most recent call last):
|
| 870 |
+
...
|
| 871 |
+
ValueError: l values must be integer
|
| 872 |
+
|
| 873 |
+
real_gaunt(2,2,4,0.7,1,-3.4)
|
| 874 |
+
Traceback (most recent call last):
|
| 875 |
+
...
|
| 876 |
+
ValueError: mu values must be integer
|
| 877 |
+
|
| 878 |
+
Notes
|
| 879 |
+
=====
|
| 880 |
+
|
| 881 |
+
The real Gaunt coefficient inherits from the standard Gaunt coefficient,
|
| 882 |
+
the invariance under any permutation of the pairs `(l_i, \mu_i)` and the
|
| 883 |
+
requirement that the sum of the `l_i` be even to yield a non-zero value.
|
| 884 |
+
It also obeys the following symmetry rules:
|
| 885 |
+
|
| 886 |
+
- zero for `l_1`, `l_2`, `l_3` not fulfilling the condition
|
| 887 |
+
`l_1 \in \{l_{\text{max}}, l_{\text{max}}-2, \ldots, l_{\text{min}}\}`,
|
| 888 |
+
where `l_{\text{max}} = l_2+l_3`,
|
| 889 |
+
|
| 890 |
+
.. math::
|
| 891 |
+
\begin{aligned}
|
| 892 |
+
l_{\text{min}} = \begin{cases} \kappa(l_2, l_3, \mu_2, \mu_3) & \text{if}\,
|
| 893 |
+
\kappa(l_2, l_3, \mu_2, \mu_3) + l_{\text{max}}\, \text{is even} \\
|
| 894 |
+
\kappa(l_2, l_3, \mu_2, \mu_3)+1 & \text{if}\, \kappa(l_2, l_3, \mu_2, \mu_3) +
|
| 895 |
+
l_{\text{max}}\, \text{is odd}\end{cases}
|
| 896 |
+
\end{aligned}
|
| 897 |
+
|
| 898 |
+
and `\kappa(l_2, l_3, \mu_2, \mu_3) = \max{\big(|l_2-l_3|, \min{\big(|\mu_2+\mu_3|,
|
| 899 |
+
|\mu_2-\mu_3|\big)}\big)}`
|
| 900 |
+
|
| 901 |
+
- zero for an odd number of negative `\mu_i`
|
| 902 |
+
|
| 903 |
+
Algorithms
|
| 904 |
+
==========
|
| 905 |
+
|
| 906 |
+
This function uses the algorithms of [Homeier96]_ and [Rasch03]_ to
|
| 907 |
+
calculate the value of the real Gaunt coefficient exactly. Note that
|
| 908 |
+
the formula used in [Rasch03]_ contains alternating sums over large
|
| 909 |
+
factorials and is therefore unsuitable for finite precision arithmetic
|
| 910 |
+
and only useful for a computer algebra system [Rasch03]_. However, this
|
| 911 |
+
function can in principle use any algorithm that computes the Gaunt
|
| 912 |
+
coefficient, so it is suitable for finite precision arithmetic in so far
|
| 913 |
+
as the algorithm which computes the Gaunt coefficient is.
|
| 914 |
+
"""
|
| 915 |
+
l_1, l_2, l_3, mu_1, mu_2, mu_3 = [
|
| 916 |
+
as_int(i) for i in (l_1, l_2, l_3, mu_1, mu_2, mu_3)]
|
| 917 |
+
|
| 918 |
+
# check for quick exits
|
| 919 |
+
if sum(1 for i in (mu_1, mu_2, mu_3) if i < 0) % 2:
|
| 920 |
+
return S.Zero # odd number of negative m
|
| 921 |
+
if (l_1 + l_2 + l_3) % 2:
|
| 922 |
+
return S.Zero # sum of l is odd
|
| 923 |
+
lmax = l_2 + l_3
|
| 924 |
+
lmin = max(abs(l_2 - l_3), min(abs(mu_2 + mu_3), abs(mu_2 - mu_3)))
|
| 925 |
+
if (lmin + lmax) % 2:
|
| 926 |
+
lmin += 1
|
| 927 |
+
if lmin not in range(lmax, lmin - 2, -2):
|
| 928 |
+
return S.Zero
|
| 929 |
+
|
| 930 |
+
kron_del = lambda i, j: 1 if i == j else 0
|
| 931 |
+
s = lambda e: -1 if e % 2 else 1 # (-1)**e to give +/-1, avoiding float when e<0
|
| 932 |
+
|
| 933 |
+
t = lambda x: 1 if x > 0 else 0
|
| 934 |
+
A = lambda mu, m: t(-mu) * (s(m) * kron_del(m, mu) - kron_del(m, -mu))
|
| 935 |
+
B = lambda mu, m: t(mu) * (kron_del(m, mu) + s(m) * kron_del(m, -mu))
|
| 936 |
+
U = lambda mu, m: kron_del(abs(mu), abs(m)) * (kron_del(mu, 0) * kron_del(m, 0) + (B(mu, m) + I * A(mu, m))/sqrt(2))
|
| 937 |
+
|
| 938 |
+
ugnt = 0
|
| 939 |
+
for m1 in range(-l_1, l_1+1):
|
| 940 |
+
U1 = U(mu_1, m1)
|
| 941 |
+
for m2 in range(-l_2, l_2+1):
|
| 942 |
+
U2 = U(mu_2, m2)
|
| 943 |
+
U3 = U(mu_3,-m1-m2)
|
| 944 |
+
ugnt = ugnt + re(U1*U2*U3)*gaunt(l_1, l_2, l_3, m1, m2, -m1 - m2, prec=prec)
|
| 945 |
+
|
| 946 |
+
return ugnt
|
| 947 |
+
|
| 948 |
+
|
| 949 |
+
class Wigner3j(Function):
|
| 950 |
+
|
| 951 |
+
def doit(self, **hints):
|
| 952 |
+
if all(obj.is_number for obj in self.args):
|
| 953 |
+
return wigner_3j(*self.args)
|
| 954 |
+
else:
|
| 955 |
+
return self
|
| 956 |
+
|
| 957 |
+
def dot_rot_grad_Ynm(j, p, l, m, theta, phi):
|
| 958 |
+
r"""
|
| 959 |
+
Returns dot product of rotational gradients of spherical harmonics.
|
| 960 |
+
|
| 961 |
+
Explanation
|
| 962 |
+
===========
|
| 963 |
+
|
| 964 |
+
This function returns the right hand side of the following expression:
|
| 965 |
+
|
| 966 |
+
.. math ::
|
| 967 |
+
\vec{R}Y{_j^{p}} \cdot \vec{R}Y{_l^{m}} = (-1)^{m+p}
|
| 968 |
+
\sum\limits_{k=|l-j|}^{l+j}Y{_k^{m+p}} * \alpha_{l,m,j,p,k} *
|
| 969 |
+
\frac{1}{2} (k^2-j^2-l^2+k-j-l)
|
| 970 |
+
|
| 971 |
+
|
| 972 |
+
Arguments
|
| 973 |
+
=========
|
| 974 |
+
|
| 975 |
+
j, p, l, m .... indices in spherical harmonics (expressions or integers)
|
| 976 |
+
theta, phi .... angle arguments in spherical harmonics
|
| 977 |
+
|
| 978 |
+
Example
|
| 979 |
+
=======
|
| 980 |
+
|
| 981 |
+
>>> from sympy import symbols
|
| 982 |
+
>>> from sympy.physics.wigner import dot_rot_grad_Ynm
|
| 983 |
+
>>> theta, phi = symbols("theta phi")
|
| 984 |
+
>>> dot_rot_grad_Ynm(3, 2, 2, 0, theta, phi).doit()
|
| 985 |
+
3*sqrt(55)*Ynm(5, 2, theta, phi)/(11*sqrt(pi))
|
| 986 |
+
|
| 987 |
+
"""
|
| 988 |
+
j = sympify(j)
|
| 989 |
+
p = sympify(p)
|
| 990 |
+
l = sympify(l)
|
| 991 |
+
m = sympify(m)
|
| 992 |
+
theta = sympify(theta)
|
| 993 |
+
phi = sympify(phi)
|
| 994 |
+
k = Dummy("k")
|
| 995 |
+
|
| 996 |
+
def alpha(l,m,j,p,k):
|
| 997 |
+
return sqrt((2*l+1)*(2*j+1)*(2*k+1)/(4*pi)) * \
|
| 998 |
+
Wigner3j(j, l, k, S.Zero, S.Zero, S.Zero) * \
|
| 999 |
+
Wigner3j(j, l, k, p, m, -m-p)
|
| 1000 |
+
|
| 1001 |
+
return (S.NegativeOne)**(m+p) * Sum(Ynm(k, m+p, theta, phi) * alpha(l,m,j,p,k) / 2 \
|
| 1002 |
+
*(k**2-j**2-l**2+k-j-l), (k, abs(l-j), l+j))
|
| 1003 |
+
|
| 1004 |
+
|
| 1005 |
+
def wigner_d_small(J, beta):
|
| 1006 |
+
"""Return the small Wigner d matrix for angular momentum J.
|
| 1007 |
+
|
| 1008 |
+
Explanation
|
| 1009 |
+
===========
|
| 1010 |
+
|
| 1011 |
+
J : An integer, half-integer, or SymPy symbol for the total angular
|
| 1012 |
+
momentum of the angular momentum space being rotated.
|
| 1013 |
+
beta : A real number representing the Euler angle of rotation about
|
| 1014 |
+
the so-called line of nodes. See [Edmonds74]_.
|
| 1015 |
+
|
| 1016 |
+
Returns
|
| 1017 |
+
=======
|
| 1018 |
+
|
| 1019 |
+
A matrix representing the corresponding Euler angle rotation( in the basis
|
| 1020 |
+
of eigenvectors of `J_z`).
|
| 1021 |
+
|
| 1022 |
+
.. math ::
|
| 1023 |
+
\\mathcal{d}_{\\beta} = \\exp\\big( \\frac{i\\beta}{\\hbar} J_y\\big)
|
| 1024 |
+
|
| 1025 |
+
such that
|
| 1026 |
+
|
| 1027 |
+
.. math ::
|
| 1028 |
+
d^{(J)}_{m',m}(\\beta) = \\mathtt{wigner\\_d\\_small(J,beta)[J-mprime,J-m]}
|
| 1029 |
+
|
| 1030 |
+
The components are calculated using the general form [Edmonds74]_,
|
| 1031 |
+
equation 4.1.15.
|
| 1032 |
+
|
| 1033 |
+
Examples
|
| 1034 |
+
========
|
| 1035 |
+
|
| 1036 |
+
>>> from sympy import Integer, symbols, pi, pprint
|
| 1037 |
+
>>> from sympy.physics.wigner import wigner_d_small
|
| 1038 |
+
>>> half = 1/Integer(2)
|
| 1039 |
+
>>> beta = symbols("beta", real=True)
|
| 1040 |
+
>>> pprint(wigner_d_small(half, beta), use_unicode=True)
|
| 1041 |
+
⎡ ⎛β⎞ ⎛β⎞⎤
|
| 1042 |
+
⎢cos⎜─⎟ sin⎜─⎟⎥
|
| 1043 |
+
⎢ ⎝2⎠ ⎝2⎠⎥
|
| 1044 |
+
⎢ ⎥
|
| 1045 |
+
⎢ ⎛β⎞ ⎛β⎞⎥
|
| 1046 |
+
⎢-sin⎜─⎟ cos⎜─⎟⎥
|
| 1047 |
+
⎣ ⎝2⎠ ⎝2⎠⎦
|
| 1048 |
+
|
| 1049 |
+
>>> pprint(wigner_d_small(2*half, beta), use_unicode=True)
|
| 1050 |
+
⎡ 2⎛β⎞ ⎛β⎞ ⎛β⎞ 2⎛β⎞ ⎤
|
| 1051 |
+
⎢ cos ⎜─⎟ √2⋅sin⎜─⎟⋅cos⎜─⎟ sin ⎜─⎟ ⎥
|
| 1052 |
+
⎢ ⎝2⎠ ⎝2⎠ ⎝2⎠ ⎝2⎠ ⎥
|
| 1053 |
+
⎢ ⎥
|
| 1054 |
+
⎢ ⎛β⎞ ⎛β⎞ 2⎛β⎞ 2⎛β⎞ ⎛β⎞ ⎛β⎞⎥
|
| 1055 |
+
⎢-√2⋅sin⎜─⎟⋅cos⎜─⎟ - sin ⎜─⎟ + cos ⎜─⎟ √2⋅sin⎜─⎟⋅cos⎜─⎟⎥
|
| 1056 |
+
⎢ ⎝2⎠ ⎝2⎠ ⎝2⎠ ⎝2⎠ ⎝2⎠ ⎝2⎠⎥
|
| 1057 |
+
⎢ ⎥
|
| 1058 |
+
⎢ 2⎛β⎞ ⎛β⎞ ⎛β⎞ 2⎛β⎞ ⎥
|
| 1059 |
+
⎢ sin ⎜─⎟ -√2⋅sin⎜─⎟⋅cos⎜─⎟ cos ⎜─⎟ ⎥
|
| 1060 |
+
⎣ ⎝2⎠ ⎝2⎠ ⎝2⎠ ⎝2⎠ ⎦
|
| 1061 |
+
|
| 1062 |
+
From table 4 in [Edmonds74]_
|
| 1063 |
+
|
| 1064 |
+
>>> pprint(wigner_d_small(half, beta).subs({beta:pi/2}), use_unicode=True)
|
| 1065 |
+
⎡ √2 √2⎤
|
| 1066 |
+
⎢ ── ──⎥
|
| 1067 |
+
⎢ 2 2 ⎥
|
| 1068 |
+
⎢ ⎥
|
| 1069 |
+
⎢-√2 √2⎥
|
| 1070 |
+
⎢──── ──⎥
|
| 1071 |
+
⎣ 2 2 ⎦
|
| 1072 |
+
|
| 1073 |
+
>>> pprint(wigner_d_small(2*half, beta).subs({beta:pi/2}),
|
| 1074 |
+
... use_unicode=True)
|
| 1075 |
+
⎡ √2 ⎤
|
| 1076 |
+
⎢1/2 ── 1/2⎥
|
| 1077 |
+
⎢ 2 ⎥
|
| 1078 |
+
⎢ ⎥
|
| 1079 |
+
⎢-√2 √2 ⎥
|
| 1080 |
+
⎢──── 0 ── ⎥
|
| 1081 |
+
⎢ 2 2 ⎥
|
| 1082 |
+
⎢ ⎥
|
| 1083 |
+
⎢ -√2 ⎥
|
| 1084 |
+
⎢1/2 ──── 1/2⎥
|
| 1085 |
+
⎣ 2 ⎦
|
| 1086 |
+
|
| 1087 |
+
>>> pprint(wigner_d_small(3*half, beta).subs({beta:pi/2}),
|
| 1088 |
+
... use_unicode=True)
|
| 1089 |
+
⎡ √2 √6 √6 √2⎤
|
| 1090 |
+
⎢ ── ── ── ──⎥
|
| 1091 |
+
⎢ 4 4 4 4 ⎥
|
| 1092 |
+
⎢ ⎥
|
| 1093 |
+
⎢-√6 -√2 √2 √6⎥
|
| 1094 |
+
⎢──── ──── ── ──⎥
|
| 1095 |
+
⎢ 4 4 4 4 ⎥
|
| 1096 |
+
⎢ ⎥
|
| 1097 |
+
⎢ √6 -√2 -√2 √6⎥
|
| 1098 |
+
⎢ ── ──── ──── ──⎥
|
| 1099 |
+
⎢ 4 4 4 4 ⎥
|
| 1100 |
+
⎢ ⎥
|
| 1101 |
+
⎢-√2 √6 -√6 √2⎥
|
| 1102 |
+
⎢──── ── ──── ──⎥
|
| 1103 |
+
⎣ 4 4 4 4 ⎦
|
| 1104 |
+
|
| 1105 |
+
>>> pprint(wigner_d_small(4*half, beta).subs({beta:pi/2}),
|
| 1106 |
+
... use_unicode=True)
|
| 1107 |
+
⎡ √6 ⎤
|
| 1108 |
+
⎢1/4 1/2 ── 1/2 1/4⎥
|
| 1109 |
+
⎢ 4 ⎥
|
| 1110 |
+
⎢ ⎥
|
| 1111 |
+
⎢-1/2 -1/2 0 1/2 1/2⎥
|
| 1112 |
+
⎢ ⎥
|
| 1113 |
+
⎢ √6 √6 ⎥
|
| 1114 |
+
⎢ ── 0 -1/2 0 ── ⎥
|
| 1115 |
+
⎢ 4 4 ⎥
|
| 1116 |
+
⎢ ⎥
|
| 1117 |
+
⎢-1/2 1/2 0 -1/2 1/2⎥
|
| 1118 |
+
⎢ ⎥
|
| 1119 |
+
⎢ √6 ⎥
|
| 1120 |
+
⎢1/4 -1/2 ── -1/2 1/4⎥
|
| 1121 |
+
⎣ 4 ⎦
|
| 1122 |
+
|
| 1123 |
+
"""
|
| 1124 |
+
M = [J-i for i in range(2*J+1)]
|
| 1125 |
+
d = zeros(2*J+1)
|
| 1126 |
+
|
| 1127 |
+
# Mi corresponds to Edmonds' $m'$, and Mj to $m$.
|
| 1128 |
+
for i, Mi in enumerate(M):
|
| 1129 |
+
for j, Mj in enumerate(M):
|
| 1130 |
+
|
| 1131 |
+
# We get the maximum and minimum value of sigma.
|
| 1132 |
+
sigmamax = min([J-Mi, J-Mj])
|
| 1133 |
+
sigmamin = max([0, -Mi-Mj])
|
| 1134 |
+
|
| 1135 |
+
dij = sqrt(factorial(J+Mi)*factorial(J-Mi) /
|
| 1136 |
+
factorial(J+Mj)/factorial(J-Mj))
|
| 1137 |
+
terms = [(-1)**(J-Mi-s) *
|
| 1138 |
+
binomial(J+Mj, J-Mi-s) *
|
| 1139 |
+
binomial(J-Mj, s) *
|
| 1140 |
+
cos(beta/2)**(2*s+Mi+Mj) *
|
| 1141 |
+
sin(beta/2)**(2*J-2*s-Mj-Mi)
|
| 1142 |
+
for s in range(sigmamin, sigmamax+1)]
|
| 1143 |
+
|
| 1144 |
+
d[i, j] = dij*Add(*terms)
|
| 1145 |
+
|
| 1146 |
+
return ImmutableMatrix(d)
|
| 1147 |
+
|
| 1148 |
+
|
| 1149 |
+
def wigner_d(J, alpha, beta, gamma):
|
| 1150 |
+
"""Return the Wigner D matrix for angular momentum J.
|
| 1151 |
+
|
| 1152 |
+
Explanation
|
| 1153 |
+
===========
|
| 1154 |
+
|
| 1155 |
+
J :
|
| 1156 |
+
An integer, half-integer, or SymPy symbol for the total angular
|
| 1157 |
+
momentum of the angular momentum space being rotated.
|
| 1158 |
+
alpha, beta, gamma - Real numbers representing the Euler.
|
| 1159 |
+
Angles of rotation about the so-called figure axis, line of nodes,
|
| 1160 |
+
and vertical. See [Edmonds74]_, however note that the symbols alpha
|
| 1161 |
+
and gamma are swapped in this implementation.
|
| 1162 |
+
|
| 1163 |
+
Returns
|
| 1164 |
+
=======
|
| 1165 |
+
|
| 1166 |
+
A matrix representing the corresponding Euler angle rotation (in the basis
|
| 1167 |
+
of eigenvectors of `J_z`).
|
| 1168 |
+
|
| 1169 |
+
.. math ::
|
| 1170 |
+
\\mathcal{D}_{\\alpha \\beta \\gamma} =
|
| 1171 |
+
\\exp\\big( \\frac{i\\alpha}{\\hbar} J_z\\big)
|
| 1172 |
+
\\exp\\big( \\frac{i\\beta}{\\hbar} J_y\\big)
|
| 1173 |
+
\\exp\\big( \\frac{i\\gamma}{\\hbar} J_z\\big)
|
| 1174 |
+
|
| 1175 |
+
such that
|
| 1176 |
+
|
| 1177 |
+
.. math ::
|
| 1178 |
+
\\mathcal{D}^{(J)}_{m',m}(\\alpha, \\beta, \\gamma) =
|
| 1179 |
+
\\mathtt{wigner_d(J, alpha, beta, gamma)[J-mprime,J-m]}
|
| 1180 |
+
|
| 1181 |
+
The components are calculated using the general form [Edmonds74]_,
|
| 1182 |
+
equation 4.1.12, however note that the angles alpha and gamma are swapped
|
| 1183 |
+
in this implementation.
|
| 1184 |
+
|
| 1185 |
+
Examples
|
| 1186 |
+
========
|
| 1187 |
+
|
| 1188 |
+
The simplest possible example:
|
| 1189 |
+
|
| 1190 |
+
>>> from sympy.physics.wigner import wigner_d
|
| 1191 |
+
>>> from sympy import Integer, symbols, pprint
|
| 1192 |
+
>>> half = 1/Integer(2)
|
| 1193 |
+
>>> alpha, beta, gamma = symbols("alpha, beta, gamma", real=True)
|
| 1194 |
+
>>> pprint(wigner_d(half, alpha, beta, gamma), use_unicode=True)
|
| 1195 |
+
⎡ ⅈ⋅α ⅈ⋅γ ⅈ⋅α -ⅈ⋅γ ⎤
|
| 1196 |
+
⎢ ─── ─── ─── ───── ⎥
|
| 1197 |
+
⎢ 2 2 ⎛β⎞ 2 2 ⎛β⎞ ⎥
|
| 1198 |
+
⎢ ℯ ⋅ℯ ⋅cos⎜─⎟ ℯ ⋅ℯ ⋅sin⎜─⎟ ⎥
|
| 1199 |
+
⎢ ⎝2⎠ ⎝2⎠ ⎥
|
| 1200 |
+
⎢ ⎥
|
| 1201 |
+
⎢ -ⅈ⋅α ⅈ⋅γ -ⅈ⋅α -ⅈ⋅γ ⎥
|
| 1202 |
+
⎢ ───── ─── ───── ───── ⎥
|
| 1203 |
+
⎢ 2 2 ⎛β⎞ 2 2 ⎛β⎞⎥
|
| 1204 |
+
⎢-ℯ ⋅ℯ ⋅sin⎜─⎟ ℯ ⋅ℯ ⋅cos⎜─⎟⎥
|
| 1205 |
+
⎣ ⎝2⎠ ⎝2⎠⎦
|
| 1206 |
+
|
| 1207 |
+
"""
|
| 1208 |
+
d = wigner_d_small(J, beta)
|
| 1209 |
+
M = [J-i for i in range(2*J+1)]
|
| 1210 |
+
# Mi corresponds to Edmonds' $m'$, and Mj to $m$.
|
| 1211 |
+
D = [[exp(I*Mi*alpha)*d[i, j]*exp(I*Mj*gamma)
|
| 1212 |
+
for j, Mj in enumerate(M)] for i, Mi in enumerate(M)]
|
| 1213 |
+
return ImmutableMatrix(D)
|
venv/lib/python3.11/site-packages/sympy/plotting/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .plot import plot_backends
|
| 2 |
+
from .plot_implicit import plot_implicit
|
| 3 |
+
from .textplot import textplot
|
| 4 |
+
from .pygletplot import PygletPlot
|
| 5 |
+
from .plot import PlotGrid
|
| 6 |
+
from .plot import (plot, plot_parametric, plot3d, plot3d_parametric_surface,
|
| 7 |
+
plot3d_parametric_line, plot_contour)
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
'plot_backends',
|
| 11 |
+
|
| 12 |
+
'plot_implicit',
|
| 13 |
+
|
| 14 |
+
'textplot',
|
| 15 |
+
|
| 16 |
+
'PygletPlot',
|
| 17 |
+
|
| 18 |
+
'PlotGrid',
|
| 19 |
+
|
| 20 |
+
'plot', 'plot_parametric', 'plot3d', 'plot3d_parametric_surface',
|
| 21 |
+
'plot3d_parametric_line', 'plot_contour'
|
| 22 |
+
]
|
venv/lib/python3.11/site-packages/sympy/plotting/backends/__init__.py
ADDED
|
File without changes
|
venv/lib/python3.11/site-packages/sympy/plotting/backends/base_backend.py
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.series import BaseSeries, GenericDataSeries
|
| 2 |
+
from sympy.utilities.exceptions import sympy_deprecation_warning
|
| 3 |
+
from sympy.utilities.iterables import is_sequence
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__doctest_requires__ = {
|
| 7 |
+
('Plot.append', 'Plot.extend'): ['matplotlib'],
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# Global variable
|
| 12 |
+
# Set to False when running tests / doctests so that the plots don't show.
|
| 13 |
+
_show = True
|
| 14 |
+
|
| 15 |
+
def unset_show():
|
| 16 |
+
"""
|
| 17 |
+
Disable show(). For use in the tests.
|
| 18 |
+
"""
|
| 19 |
+
global _show
|
| 20 |
+
_show = False
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _deprecation_msg_m_a_r_f(attr):
|
| 24 |
+
sympy_deprecation_warning(
|
| 25 |
+
f"The `{attr}` property is deprecated. The `{attr}` keyword "
|
| 26 |
+
"argument should be passed to a plotting function, which generates "
|
| 27 |
+
"the appropriate data series. If needed, index the plot object to "
|
| 28 |
+
"retrieve a specific data series.",
|
| 29 |
+
deprecated_since_version="1.13",
|
| 30 |
+
active_deprecations_target="deprecated-markers-annotations-fill-rectangles",
|
| 31 |
+
stacklevel=4)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _create_generic_data_series(**kwargs):
|
| 35 |
+
keywords = ["annotations", "markers", "fill", "rectangles"]
|
| 36 |
+
series = []
|
| 37 |
+
for kw in keywords:
|
| 38 |
+
dictionaries = kwargs.pop(kw, [])
|
| 39 |
+
if dictionaries is None:
|
| 40 |
+
dictionaries = []
|
| 41 |
+
if isinstance(dictionaries, dict):
|
| 42 |
+
dictionaries = [dictionaries]
|
| 43 |
+
for d in dictionaries:
|
| 44 |
+
args = d.pop("args", [])
|
| 45 |
+
series.append(GenericDataSeries(kw, *args, **d))
|
| 46 |
+
return series
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class Plot:
|
| 50 |
+
"""Base class for all backends. A backend represents the plotting library,
|
| 51 |
+
which implements the necessary functionalities in order to use SymPy
|
| 52 |
+
plotting functions.
|
| 53 |
+
|
| 54 |
+
For interactive work the function :func:`plot` is better suited.
|
| 55 |
+
|
| 56 |
+
This class permits the plotting of SymPy expressions using numerous
|
| 57 |
+
backends (:external:mod:`matplotlib`, textplot, the old pyglet module for SymPy, Google
|
| 58 |
+
charts api, etc).
|
| 59 |
+
|
| 60 |
+
The figure can contain an arbitrary number of plots of SymPy expressions,
|
| 61 |
+
lists of coordinates of points, etc. Plot has a private attribute _series that
|
| 62 |
+
contains all data series to be plotted (expressions for lines or surfaces,
|
| 63 |
+
lists of points, etc (all subclasses of BaseSeries)). Those data series are
|
| 64 |
+
instances of classes not imported by ``from sympy import *``.
|
| 65 |
+
|
| 66 |
+
The customization of the figure is on two levels. Global options that
|
| 67 |
+
concern the figure as a whole (e.g. title, xlabel, scale, etc) and
|
| 68 |
+
per-data series options (e.g. name) and aesthetics (e.g. color, point shape,
|
| 69 |
+
line type, etc.).
|
| 70 |
+
|
| 71 |
+
The difference between options and aesthetics is that an aesthetic can be
|
| 72 |
+
a function of the coordinates (or parameters in a parametric plot). The
|
| 73 |
+
supported values for an aesthetic are:
|
| 74 |
+
|
| 75 |
+
- None (the backend uses default values)
|
| 76 |
+
- a constant
|
| 77 |
+
- a function of one variable (the first coordinate or parameter)
|
| 78 |
+
- a function of two variables (the first and second coordinate or parameters)
|
| 79 |
+
- a function of three variables (only in nonparametric 3D plots)
|
| 80 |
+
|
| 81 |
+
Their implementation depends on the backend so they may not work in some
|
| 82 |
+
backends.
|
| 83 |
+
|
| 84 |
+
If the plot is parametric and the arity of the aesthetic function permits
|
| 85 |
+
it the aesthetic is calculated over parameters and not over coordinates.
|
| 86 |
+
If the arity does not permit calculation over parameters the calculation is
|
| 87 |
+
done over coordinates.
|
| 88 |
+
|
| 89 |
+
Only cartesian coordinates are supported for the moment, but you can use
|
| 90 |
+
the parametric plots to plot in polar, spherical and cylindrical
|
| 91 |
+
coordinates.
|
| 92 |
+
|
| 93 |
+
The arguments for the constructor Plot must be subclasses of BaseSeries.
|
| 94 |
+
|
| 95 |
+
Any global option can be specified as a keyword argument.
|
| 96 |
+
|
| 97 |
+
The global options for a figure are:
|
| 98 |
+
|
| 99 |
+
- title : str
|
| 100 |
+
- xlabel : str or Symbol
|
| 101 |
+
- ylabel : str or Symbol
|
| 102 |
+
- zlabel : str or Symbol
|
| 103 |
+
- legend : bool
|
| 104 |
+
- xscale : {'linear', 'log'}
|
| 105 |
+
- yscale : {'linear', 'log'}
|
| 106 |
+
- axis : bool
|
| 107 |
+
- axis_center : tuple of two floats or {'center', 'auto'}
|
| 108 |
+
- xlim : tuple of two floats
|
| 109 |
+
- ylim : tuple of two floats
|
| 110 |
+
- aspect_ratio : tuple of two floats or {'auto'}
|
| 111 |
+
- autoscale : bool
|
| 112 |
+
- margin : float in [0, 1]
|
| 113 |
+
- backend : {'default', 'matplotlib', 'text'} or a subclass of BaseBackend
|
| 114 |
+
- size : optional tuple of two floats, (width, height); default: None
|
| 115 |
+
|
| 116 |
+
The per data series options and aesthetics are:
|
| 117 |
+
There are none in the base series. See below for options for subclasses.
|
| 118 |
+
|
| 119 |
+
Some data series support additional aesthetics or options:
|
| 120 |
+
|
| 121 |
+
:class:`~.LineOver1DRangeSeries`, :class:`~.Parametric2DLineSeries`, and
|
| 122 |
+
:class:`~.Parametric3DLineSeries` support the following:
|
| 123 |
+
|
| 124 |
+
Aesthetics:
|
| 125 |
+
|
| 126 |
+
- line_color : string, or float, or function, optional
|
| 127 |
+
Specifies the color for the plot, which depends on the backend being
|
| 128 |
+
used.
|
| 129 |
+
|
| 130 |
+
For example, if ``MatplotlibBackend`` is being used, then
|
| 131 |
+
Matplotlib string colors are acceptable (``"red"``, ``"r"``,
|
| 132 |
+
``"cyan"``, ``"c"``, ...).
|
| 133 |
+
Alternatively, we can use a float number, 0 < color < 1, wrapped in a
|
| 134 |
+
string (for example, ``line_color="0.5"``) to specify grayscale colors.
|
| 135 |
+
Alternatively, We can specify a function returning a single
|
| 136 |
+
float value: this will be used to apply a color-loop (for example,
|
| 137 |
+
``line_color=lambda x: math.cos(x)``).
|
| 138 |
+
|
| 139 |
+
Note that by setting line_color, it would be applied simultaneously
|
| 140 |
+
to all the series.
|
| 141 |
+
|
| 142 |
+
Options:
|
| 143 |
+
|
| 144 |
+
- label : str
|
| 145 |
+
- steps : bool
|
| 146 |
+
- integers_only : bool
|
| 147 |
+
|
| 148 |
+
:class:`~.SurfaceOver2DRangeSeries` and :class:`~.ParametricSurfaceSeries`
|
| 149 |
+
support the following:
|
| 150 |
+
|
| 151 |
+
Aesthetics:
|
| 152 |
+
|
| 153 |
+
- surface_color : function which returns a float.
|
| 154 |
+
|
| 155 |
+
Notes
|
| 156 |
+
=====
|
| 157 |
+
|
| 158 |
+
How the plotting module works:
|
| 159 |
+
|
| 160 |
+
1. Whenever a plotting function is called, the provided expressions are
|
| 161 |
+
processed and a list of instances of the
|
| 162 |
+
:class:`~sympy.plotting.series.BaseSeries` class is created, containing
|
| 163 |
+
the necessary information to plot the expressions
|
| 164 |
+
(e.g. the expression, ranges, series name, ...). Eventually, these
|
| 165 |
+
objects will generate the numerical data to be plotted.
|
| 166 |
+
2. A subclass of :class:`~.Plot` class is instantiaed (referred to as
|
| 167 |
+
backend, from now on), which stores the list of series and the main
|
| 168 |
+
attributes of the plot (e.g. axis labels, title, ...).
|
| 169 |
+
The backend implements the logic to generate the actual figure with
|
| 170 |
+
some plotting library.
|
| 171 |
+
3. When the ``show`` command is executed, series are processed one by one
|
| 172 |
+
to generate numerical data and add it to the figure. The backend is also
|
| 173 |
+
going to set the axis labels, title, ..., according to the values stored
|
| 174 |
+
in the Plot instance.
|
| 175 |
+
|
| 176 |
+
The backend should check if it supports the data series that it is given
|
| 177 |
+
(e.g. :class:`TextBackend` supports only
|
| 178 |
+
:class:`~sympy.plotting.series.LineOver1DRangeSeries`).
|
| 179 |
+
|
| 180 |
+
It is the backend responsibility to know how to use the class of data series
|
| 181 |
+
that it's given. Note that the current implementation of the ``*Series``
|
| 182 |
+
classes is "matplotlib-centric": the numerical data returned by the
|
| 183 |
+
``get_points`` and ``get_meshes`` methods is meant to be used directly by
|
| 184 |
+
Matplotlib. Therefore, the new backend will have to pre-process the
|
| 185 |
+
numerical data to make it compatible with the chosen plotting library.
|
| 186 |
+
Keep in mind that future SymPy versions may improve the ``*Series`` classes
|
| 187 |
+
in order to return numerical data "non-matplotlib-centric", hence if you code
|
| 188 |
+
a new backend you have the responsibility to check if its working on each
|
| 189 |
+
SymPy release.
|
| 190 |
+
|
| 191 |
+
Please explore the :class:`MatplotlibBackend` source code to understand
|
| 192 |
+
how a backend should be coded.
|
| 193 |
+
|
| 194 |
+
In order to be used by SymPy plotting functions, a backend must implement
|
| 195 |
+
the following methods:
|
| 196 |
+
|
| 197 |
+
* show(self): used to loop over the data series, generate the numerical
|
| 198 |
+
data, plot it and set the axis labels, title, ...
|
| 199 |
+
* save(self, path): used to save the current plot to the specified file
|
| 200 |
+
path.
|
| 201 |
+
* close(self): used to close the current plot backend (note: some plotting
|
| 202 |
+
library does not support this functionality. In that case, just raise a
|
| 203 |
+
warning).
|
| 204 |
+
"""
|
| 205 |
+
|
| 206 |
+
def __init__(self, *args,
|
| 207 |
+
title=None, xlabel=None, ylabel=None, zlabel=None, aspect_ratio='auto',
|
| 208 |
+
xlim=None, ylim=None, axis_center='auto', axis=True,
|
| 209 |
+
xscale='linear', yscale='linear', legend=False, autoscale=True,
|
| 210 |
+
margin=0, annotations=None, markers=None, rectangles=None,
|
| 211 |
+
fill=None, backend='default', size=None, **kwargs):
|
| 212 |
+
|
| 213 |
+
# Options for the graph as a whole.
|
| 214 |
+
# The possible values for each option are described in the docstring of
|
| 215 |
+
# Plot. They are based purely on convention, no checking is done.
|
| 216 |
+
self.title = title
|
| 217 |
+
self.xlabel = xlabel
|
| 218 |
+
self.ylabel = ylabel
|
| 219 |
+
self.zlabel = zlabel
|
| 220 |
+
self.aspect_ratio = aspect_ratio
|
| 221 |
+
self.axis_center = axis_center
|
| 222 |
+
self.axis = axis
|
| 223 |
+
self.xscale = xscale
|
| 224 |
+
self.yscale = yscale
|
| 225 |
+
self.legend = legend
|
| 226 |
+
self.autoscale = autoscale
|
| 227 |
+
self.margin = margin
|
| 228 |
+
self._annotations = annotations
|
| 229 |
+
self._markers = markers
|
| 230 |
+
self._rectangles = rectangles
|
| 231 |
+
self._fill = fill
|
| 232 |
+
|
| 233 |
+
# Contains the data objects to be plotted. The backend should be smart
|
| 234 |
+
# enough to iterate over this list.
|
| 235 |
+
self._series = []
|
| 236 |
+
self._series.extend(args)
|
| 237 |
+
self._series.extend(_create_generic_data_series(
|
| 238 |
+
annotations=annotations, markers=markers, rectangles=rectangles,
|
| 239 |
+
fill=fill))
|
| 240 |
+
|
| 241 |
+
is_real = \
|
| 242 |
+
lambda lim: all(getattr(i, 'is_real', True) for i in lim)
|
| 243 |
+
is_finite = \
|
| 244 |
+
lambda lim: all(getattr(i, 'is_finite', True) for i in lim)
|
| 245 |
+
|
| 246 |
+
# reduce code repetition
|
| 247 |
+
def check_and_set(t_name, t):
|
| 248 |
+
if t:
|
| 249 |
+
if not is_real(t):
|
| 250 |
+
raise ValueError(
|
| 251 |
+
"All numbers from {}={} must be real".format(t_name, t))
|
| 252 |
+
if not is_finite(t):
|
| 253 |
+
raise ValueError(
|
| 254 |
+
"All numbers from {}={} must be finite".format(t_name, t))
|
| 255 |
+
setattr(self, t_name, (float(t[0]), float(t[1])))
|
| 256 |
+
|
| 257 |
+
self.xlim = None
|
| 258 |
+
check_and_set("xlim", xlim)
|
| 259 |
+
self.ylim = None
|
| 260 |
+
check_and_set("ylim", ylim)
|
| 261 |
+
self.size = None
|
| 262 |
+
check_and_set("size", size)
|
| 263 |
+
|
| 264 |
+
@property
|
| 265 |
+
def _backend(self):
|
| 266 |
+
return self
|
| 267 |
+
|
| 268 |
+
@property
|
| 269 |
+
def backend(self):
|
| 270 |
+
return type(self)
|
| 271 |
+
|
| 272 |
+
def __str__(self):
|
| 273 |
+
series_strs = [('[%d]: ' % i) + str(s)
|
| 274 |
+
for i, s in enumerate(self._series)]
|
| 275 |
+
return 'Plot object containing:\n' + '\n'.join(series_strs)
|
| 276 |
+
|
| 277 |
+
def __getitem__(self, index):
|
| 278 |
+
return self._series[index]
|
| 279 |
+
|
| 280 |
+
def __setitem__(self, index, *args):
|
| 281 |
+
if len(args) == 1 and isinstance(args[0], BaseSeries):
|
| 282 |
+
self._series[index] = args
|
| 283 |
+
|
| 284 |
+
def __delitem__(self, index):
|
| 285 |
+
del self._series[index]
|
| 286 |
+
|
| 287 |
+
def append(self, arg):
|
| 288 |
+
"""Adds an element from a plot's series to an existing plot.
|
| 289 |
+
|
| 290 |
+
Examples
|
| 291 |
+
========
|
| 292 |
+
|
| 293 |
+
Consider two ``Plot`` objects, ``p1`` and ``p2``. To add the
|
| 294 |
+
second plot's first series object to the first, use the
|
| 295 |
+
``append`` method, like so:
|
| 296 |
+
|
| 297 |
+
.. plot::
|
| 298 |
+
:format: doctest
|
| 299 |
+
:include-source: True
|
| 300 |
+
|
| 301 |
+
>>> from sympy import symbols
|
| 302 |
+
>>> from sympy.plotting import plot
|
| 303 |
+
>>> x = symbols('x')
|
| 304 |
+
>>> p1 = plot(x*x, show=False)
|
| 305 |
+
>>> p2 = plot(x, show=False)
|
| 306 |
+
>>> p1.append(p2[0])
|
| 307 |
+
>>> p1
|
| 308 |
+
Plot object containing:
|
| 309 |
+
[0]: cartesian line: x**2 for x over (-10.0, 10.0)
|
| 310 |
+
[1]: cartesian line: x for x over (-10.0, 10.0)
|
| 311 |
+
>>> p1.show()
|
| 312 |
+
|
| 313 |
+
See Also
|
| 314 |
+
========
|
| 315 |
+
|
| 316 |
+
extend
|
| 317 |
+
|
| 318 |
+
"""
|
| 319 |
+
if isinstance(arg, BaseSeries):
|
| 320 |
+
self._series.append(arg)
|
| 321 |
+
else:
|
| 322 |
+
raise TypeError('Must specify element of plot to append.')
|
| 323 |
+
|
| 324 |
+
def extend(self, arg):
|
| 325 |
+
"""Adds all series from another plot.
|
| 326 |
+
|
| 327 |
+
Examples
|
| 328 |
+
========
|
| 329 |
+
|
| 330 |
+
Consider two ``Plot`` objects, ``p1`` and ``p2``. To add the
|
| 331 |
+
second plot to the first, use the ``extend`` method, like so:
|
| 332 |
+
|
| 333 |
+
.. plot::
|
| 334 |
+
:format: doctest
|
| 335 |
+
:include-source: True
|
| 336 |
+
|
| 337 |
+
>>> from sympy import symbols
|
| 338 |
+
>>> from sympy.plotting import plot
|
| 339 |
+
>>> x = symbols('x')
|
| 340 |
+
>>> p1 = plot(x**2, show=False)
|
| 341 |
+
>>> p2 = plot(x, -x, show=False)
|
| 342 |
+
>>> p1.extend(p2)
|
| 343 |
+
>>> p1
|
| 344 |
+
Plot object containing:
|
| 345 |
+
[0]: cartesian line: x**2 for x over (-10.0, 10.0)
|
| 346 |
+
[1]: cartesian line: x for x over (-10.0, 10.0)
|
| 347 |
+
[2]: cartesian line: -x for x over (-10.0, 10.0)
|
| 348 |
+
>>> p1.show()
|
| 349 |
+
|
| 350 |
+
"""
|
| 351 |
+
if isinstance(arg, Plot):
|
| 352 |
+
self._series.extend(arg._series)
|
| 353 |
+
elif is_sequence(arg):
|
| 354 |
+
self._series.extend(arg)
|
| 355 |
+
else:
|
| 356 |
+
raise TypeError('Expecting Plot or sequence of BaseSeries')
|
| 357 |
+
|
| 358 |
+
def show(self):
|
| 359 |
+
raise NotImplementedError
|
| 360 |
+
|
| 361 |
+
def save(self, path):
|
| 362 |
+
raise NotImplementedError
|
| 363 |
+
|
| 364 |
+
def close(self):
|
| 365 |
+
raise NotImplementedError
|
| 366 |
+
|
| 367 |
+
# deprecations
|
| 368 |
+
|
| 369 |
+
@property
|
| 370 |
+
def markers(self):
|
| 371 |
+
""".. deprecated:: 1.13"""
|
| 372 |
+
_deprecation_msg_m_a_r_f("markers")
|
| 373 |
+
return self._markers
|
| 374 |
+
|
| 375 |
+
@markers.setter
|
| 376 |
+
def markers(self, v):
|
| 377 |
+
""".. deprecated:: 1.13"""
|
| 378 |
+
_deprecation_msg_m_a_r_f("markers")
|
| 379 |
+
self._series.extend(_create_generic_data_series(markers=v))
|
| 380 |
+
self._markers = v
|
| 381 |
+
|
| 382 |
+
@property
|
| 383 |
+
def annotations(self):
|
| 384 |
+
""".. deprecated:: 1.13"""
|
| 385 |
+
_deprecation_msg_m_a_r_f("annotations")
|
| 386 |
+
return self._annotations
|
| 387 |
+
|
| 388 |
+
@annotations.setter
|
| 389 |
+
def annotations(self, v):
|
| 390 |
+
""".. deprecated:: 1.13"""
|
| 391 |
+
_deprecation_msg_m_a_r_f("annotations")
|
| 392 |
+
self._series.extend(_create_generic_data_series(annotations=v))
|
| 393 |
+
self._annotations = v
|
| 394 |
+
|
| 395 |
+
@property
|
| 396 |
+
def rectangles(self):
|
| 397 |
+
""".. deprecated:: 1.13"""
|
| 398 |
+
_deprecation_msg_m_a_r_f("rectangles")
|
| 399 |
+
return self._rectangles
|
| 400 |
+
|
| 401 |
+
@rectangles.setter
|
| 402 |
+
def rectangles(self, v):
|
| 403 |
+
""".. deprecated:: 1.13"""
|
| 404 |
+
_deprecation_msg_m_a_r_f("rectangles")
|
| 405 |
+
self._series.extend(_create_generic_data_series(rectangles=v))
|
| 406 |
+
self._rectangles = v
|
| 407 |
+
|
| 408 |
+
@property
|
| 409 |
+
def fill(self):
|
| 410 |
+
""".. deprecated:: 1.13"""
|
| 411 |
+
_deprecation_msg_m_a_r_f("fill")
|
| 412 |
+
return self._fill
|
| 413 |
+
|
| 414 |
+
@fill.setter
|
| 415 |
+
def fill(self, v):
|
| 416 |
+
""".. deprecated:: 1.13"""
|
| 417 |
+
_deprecation_msg_m_a_r_f("fill")
|
| 418 |
+
self._series.extend(_create_generic_data_series(fill=v))
|
| 419 |
+
self._fill = v
|
venv/lib/python3.11/site-packages/sympy/plotting/backends/matplotlibbackend/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.backends.matplotlibbackend.matplotlib import (
|
| 2 |
+
MatplotlibBackend, _matplotlib_list
|
| 3 |
+
)
|
| 4 |
+
|
| 5 |
+
__all__ = ["MatplotlibBackend", "_matplotlib_list"]
|
venv/lib/python3.11/site-packages/sympy/plotting/backends/matplotlibbackend/matplotlib.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections.abc import Callable
|
| 2 |
+
from sympy.core.basic import Basic
|
| 3 |
+
from sympy.external import import_module
|
| 4 |
+
import sympy.plotting.backends.base_backend as base_backend
|
| 5 |
+
from sympy.printing.latex import latex
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
# N.B.
|
| 9 |
+
# When changing the minimum module version for matplotlib, please change
|
| 10 |
+
# the same in the `SymPyDocTestFinder`` in `sympy/testing/runtests.py`
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def _str_or_latex(label):
|
| 14 |
+
if isinstance(label, Basic):
|
| 15 |
+
return latex(label, mode='inline')
|
| 16 |
+
return str(label)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _matplotlib_list(interval_list):
|
| 20 |
+
"""
|
| 21 |
+
Returns lists for matplotlib ``fill`` command from a list of bounding
|
| 22 |
+
rectangular intervals
|
| 23 |
+
"""
|
| 24 |
+
xlist = []
|
| 25 |
+
ylist = []
|
| 26 |
+
if len(interval_list):
|
| 27 |
+
for intervals in interval_list:
|
| 28 |
+
intervalx = intervals[0]
|
| 29 |
+
intervaly = intervals[1]
|
| 30 |
+
xlist.extend([intervalx.start, intervalx.start,
|
| 31 |
+
intervalx.end, intervalx.end, None])
|
| 32 |
+
ylist.extend([intervaly.start, intervaly.end,
|
| 33 |
+
intervaly.end, intervaly.start, None])
|
| 34 |
+
else:
|
| 35 |
+
#XXX Ugly hack. Matplotlib does not accept empty lists for ``fill``
|
| 36 |
+
xlist.extend((None, None, None, None))
|
| 37 |
+
ylist.extend((None, None, None, None))
|
| 38 |
+
return xlist, ylist
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
# Don't have to check for the success of importing matplotlib in each case;
|
| 42 |
+
# we will only be using this backend if we can successfully import matploblib
|
| 43 |
+
class MatplotlibBackend(base_backend.Plot):
|
| 44 |
+
""" This class implements the functionalities to use Matplotlib with SymPy
|
| 45 |
+
plotting functions.
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
def __init__(self, *series, **kwargs):
|
| 49 |
+
super().__init__(*series, **kwargs)
|
| 50 |
+
self.matplotlib = import_module('matplotlib',
|
| 51 |
+
import_kwargs={'fromlist': ['pyplot', 'cm', 'collections']},
|
| 52 |
+
min_module_version='1.1.0', catch=(RuntimeError,))
|
| 53 |
+
self.plt = self.matplotlib.pyplot
|
| 54 |
+
self.cm = self.matplotlib.cm
|
| 55 |
+
self.LineCollection = self.matplotlib.collections.LineCollection
|
| 56 |
+
self.aspect = kwargs.get('aspect_ratio', 'auto')
|
| 57 |
+
if self.aspect != 'auto':
|
| 58 |
+
self.aspect = float(self.aspect[1]) / self.aspect[0]
|
| 59 |
+
# PlotGrid can provide its figure and axes to be populated with
|
| 60 |
+
# the data from the series.
|
| 61 |
+
self._plotgrid_fig = kwargs.pop("fig", None)
|
| 62 |
+
self._plotgrid_ax = kwargs.pop("ax", None)
|
| 63 |
+
|
| 64 |
+
def _create_figure(self):
|
| 65 |
+
def set_spines(ax):
|
| 66 |
+
ax.spines['left'].set_position('zero')
|
| 67 |
+
ax.spines['right'].set_color('none')
|
| 68 |
+
ax.spines['bottom'].set_position('zero')
|
| 69 |
+
ax.spines['top'].set_color('none')
|
| 70 |
+
ax.xaxis.set_ticks_position('bottom')
|
| 71 |
+
ax.yaxis.set_ticks_position('left')
|
| 72 |
+
|
| 73 |
+
if self._plotgrid_fig is not None:
|
| 74 |
+
self.fig = self._plotgrid_fig
|
| 75 |
+
self.ax = self._plotgrid_ax
|
| 76 |
+
if not any(s.is_3D for s in self._series):
|
| 77 |
+
set_spines(self.ax)
|
| 78 |
+
else:
|
| 79 |
+
self.fig = self.plt.figure(figsize=self.size)
|
| 80 |
+
if any(s.is_3D for s in self._series):
|
| 81 |
+
self.ax = self.fig.add_subplot(1, 1, 1, projection="3d")
|
| 82 |
+
else:
|
| 83 |
+
self.ax = self.fig.add_subplot(1, 1, 1)
|
| 84 |
+
set_spines(self.ax)
|
| 85 |
+
|
| 86 |
+
@staticmethod
|
| 87 |
+
def get_segments(x, y, z=None):
|
| 88 |
+
""" Convert two list of coordinates to a list of segments to be used
|
| 89 |
+
with Matplotlib's :external:class:`~matplotlib.collections.LineCollection`.
|
| 90 |
+
|
| 91 |
+
Parameters
|
| 92 |
+
==========
|
| 93 |
+
x : list
|
| 94 |
+
List of x-coordinates
|
| 95 |
+
|
| 96 |
+
y : list
|
| 97 |
+
List of y-coordinates
|
| 98 |
+
|
| 99 |
+
z : list
|
| 100 |
+
List of z-coordinates for a 3D line.
|
| 101 |
+
"""
|
| 102 |
+
np = import_module('numpy')
|
| 103 |
+
if z is not None:
|
| 104 |
+
dim = 3
|
| 105 |
+
points = (x, y, z)
|
| 106 |
+
else:
|
| 107 |
+
dim = 2
|
| 108 |
+
points = (x, y)
|
| 109 |
+
points = np.ma.array(points).T.reshape(-1, 1, dim)
|
| 110 |
+
return np.ma.concatenate([points[:-1], points[1:]], axis=1)
|
| 111 |
+
|
| 112 |
+
def _process_series(self, series, ax):
|
| 113 |
+
np = import_module('numpy')
|
| 114 |
+
mpl_toolkits = import_module(
|
| 115 |
+
'mpl_toolkits', import_kwargs={'fromlist': ['mplot3d']})
|
| 116 |
+
|
| 117 |
+
# XXX Workaround for matplotlib issue
|
| 118 |
+
# https://github.com/matplotlib/matplotlib/issues/17130
|
| 119 |
+
xlims, ylims, zlims = [], [], []
|
| 120 |
+
|
| 121 |
+
for s in series:
|
| 122 |
+
# Create the collections
|
| 123 |
+
if s.is_2Dline:
|
| 124 |
+
if s.is_parametric:
|
| 125 |
+
x, y, param = s.get_data()
|
| 126 |
+
else:
|
| 127 |
+
x, y = s.get_data()
|
| 128 |
+
if (isinstance(s.line_color, (int, float)) or
|
| 129 |
+
callable(s.line_color)):
|
| 130 |
+
segments = self.get_segments(x, y)
|
| 131 |
+
collection = self.LineCollection(segments)
|
| 132 |
+
collection.set_array(s.get_color_array())
|
| 133 |
+
ax.add_collection(collection)
|
| 134 |
+
else:
|
| 135 |
+
lbl = _str_or_latex(s.label)
|
| 136 |
+
line, = ax.plot(x, y, label=lbl, color=s.line_color)
|
| 137 |
+
elif s.is_contour:
|
| 138 |
+
ax.contour(*s.get_data())
|
| 139 |
+
elif s.is_3Dline:
|
| 140 |
+
x, y, z, param = s.get_data()
|
| 141 |
+
if (isinstance(s.line_color, (int, float)) or
|
| 142 |
+
callable(s.line_color)):
|
| 143 |
+
art3d = mpl_toolkits.mplot3d.art3d
|
| 144 |
+
segments = self.get_segments(x, y, z)
|
| 145 |
+
collection = art3d.Line3DCollection(segments)
|
| 146 |
+
collection.set_array(s.get_color_array())
|
| 147 |
+
ax.add_collection(collection)
|
| 148 |
+
else:
|
| 149 |
+
lbl = _str_or_latex(s.label)
|
| 150 |
+
ax.plot(x, y, z, label=lbl, color=s.line_color)
|
| 151 |
+
|
| 152 |
+
xlims.append(s._xlim)
|
| 153 |
+
ylims.append(s._ylim)
|
| 154 |
+
zlims.append(s._zlim)
|
| 155 |
+
elif s.is_3Dsurface:
|
| 156 |
+
if s.is_parametric:
|
| 157 |
+
x, y, z, u, v = s.get_data()
|
| 158 |
+
else:
|
| 159 |
+
x, y, z = s.get_data()
|
| 160 |
+
collection = ax.plot_surface(x, y, z,
|
| 161 |
+
cmap=getattr(self.cm, 'viridis', self.cm.jet),
|
| 162 |
+
rstride=1, cstride=1, linewidth=0.1)
|
| 163 |
+
if isinstance(s.surface_color, (float, int, Callable)):
|
| 164 |
+
color_array = s.get_color_array()
|
| 165 |
+
color_array = color_array.reshape(color_array.size)
|
| 166 |
+
collection.set_array(color_array)
|
| 167 |
+
else:
|
| 168 |
+
collection.set_color(s.surface_color)
|
| 169 |
+
|
| 170 |
+
xlims.append(s._xlim)
|
| 171 |
+
ylims.append(s._ylim)
|
| 172 |
+
zlims.append(s._zlim)
|
| 173 |
+
elif s.is_implicit:
|
| 174 |
+
points = s.get_data()
|
| 175 |
+
if len(points) == 2:
|
| 176 |
+
# interval math plotting
|
| 177 |
+
x, y = _matplotlib_list(points[0])
|
| 178 |
+
ax.fill(x, y, facecolor=s.line_color, edgecolor='None')
|
| 179 |
+
else:
|
| 180 |
+
# use contourf or contour depending on whether it is
|
| 181 |
+
# an inequality or equality.
|
| 182 |
+
# XXX: ``contour`` plots multiple lines. Should be fixed.
|
| 183 |
+
ListedColormap = self.matplotlib.colors.ListedColormap
|
| 184 |
+
colormap = ListedColormap(["white", s.line_color])
|
| 185 |
+
xarray, yarray, zarray, plot_type = points
|
| 186 |
+
if plot_type == 'contour':
|
| 187 |
+
ax.contour(xarray, yarray, zarray, cmap=colormap)
|
| 188 |
+
else:
|
| 189 |
+
ax.contourf(xarray, yarray, zarray, cmap=colormap)
|
| 190 |
+
elif s.is_generic:
|
| 191 |
+
if s.type == "markers":
|
| 192 |
+
# s.rendering_kw["color"] = s.line_color
|
| 193 |
+
ax.plot(*s.args, **s.rendering_kw)
|
| 194 |
+
elif s.type == "annotations":
|
| 195 |
+
ax.annotate(*s.args, **s.rendering_kw)
|
| 196 |
+
elif s.type == "fill":
|
| 197 |
+
# s.rendering_kw["color"] = s.line_color
|
| 198 |
+
ax.fill_between(*s.args, **s.rendering_kw)
|
| 199 |
+
elif s.type == "rectangles":
|
| 200 |
+
# s.rendering_kw["color"] = s.line_color
|
| 201 |
+
ax.add_patch(
|
| 202 |
+
self.matplotlib.patches.Rectangle(
|
| 203 |
+
*s.args, **s.rendering_kw))
|
| 204 |
+
else:
|
| 205 |
+
raise NotImplementedError(
|
| 206 |
+
'{} is not supported in the SymPy plotting module '
|
| 207 |
+
'with matplotlib backend. Please report this issue.'
|
| 208 |
+
.format(ax))
|
| 209 |
+
|
| 210 |
+
Axes3D = mpl_toolkits.mplot3d.Axes3D
|
| 211 |
+
if not isinstance(ax, Axes3D):
|
| 212 |
+
ax.autoscale_view(
|
| 213 |
+
scalex=ax.get_autoscalex_on(),
|
| 214 |
+
scaley=ax.get_autoscaley_on())
|
| 215 |
+
else:
|
| 216 |
+
# XXX Workaround for matplotlib issue
|
| 217 |
+
# https://github.com/matplotlib/matplotlib/issues/17130
|
| 218 |
+
if xlims:
|
| 219 |
+
xlims = np.array(xlims)
|
| 220 |
+
xlim = (np.amin(xlims[:, 0]), np.amax(xlims[:, 1]))
|
| 221 |
+
ax.set_xlim(xlim)
|
| 222 |
+
else:
|
| 223 |
+
ax.set_xlim([0, 1])
|
| 224 |
+
|
| 225 |
+
if ylims:
|
| 226 |
+
ylims = np.array(ylims)
|
| 227 |
+
ylim = (np.amin(ylims[:, 0]), np.amax(ylims[:, 1]))
|
| 228 |
+
ax.set_ylim(ylim)
|
| 229 |
+
else:
|
| 230 |
+
ax.set_ylim([0, 1])
|
| 231 |
+
|
| 232 |
+
if zlims:
|
| 233 |
+
zlims = np.array(zlims)
|
| 234 |
+
zlim = (np.amin(zlims[:, 0]), np.amax(zlims[:, 1]))
|
| 235 |
+
ax.set_zlim(zlim)
|
| 236 |
+
else:
|
| 237 |
+
ax.set_zlim([0, 1])
|
| 238 |
+
|
| 239 |
+
# Set global options.
|
| 240 |
+
# TODO The 3D stuff
|
| 241 |
+
# XXX The order of those is important.
|
| 242 |
+
if self.xscale and not isinstance(ax, Axes3D):
|
| 243 |
+
ax.set_xscale(self.xscale)
|
| 244 |
+
if self.yscale and not isinstance(ax, Axes3D):
|
| 245 |
+
ax.set_yscale(self.yscale)
|
| 246 |
+
if not isinstance(ax, Axes3D) or self.matplotlib.__version__ >= '1.2.0': # XXX in the distant future remove this check
|
| 247 |
+
ax.set_autoscale_on(self.autoscale)
|
| 248 |
+
if self.axis_center:
|
| 249 |
+
val = self.axis_center
|
| 250 |
+
if isinstance(ax, Axes3D):
|
| 251 |
+
pass
|
| 252 |
+
elif val == 'center':
|
| 253 |
+
ax.spines['left'].set_position('center')
|
| 254 |
+
ax.spines['bottom'].set_position('center')
|
| 255 |
+
elif val == 'auto':
|
| 256 |
+
xl, xh = ax.get_xlim()
|
| 257 |
+
yl, yh = ax.get_ylim()
|
| 258 |
+
pos_left = ('data', 0) if xl*xh <= 0 else 'center'
|
| 259 |
+
pos_bottom = ('data', 0) if yl*yh <= 0 else 'center'
|
| 260 |
+
ax.spines['left'].set_position(pos_left)
|
| 261 |
+
ax.spines['bottom'].set_position(pos_bottom)
|
| 262 |
+
else:
|
| 263 |
+
ax.spines['left'].set_position(('data', val[0]))
|
| 264 |
+
ax.spines['bottom'].set_position(('data', val[1]))
|
| 265 |
+
if not self.axis:
|
| 266 |
+
ax.set_axis_off()
|
| 267 |
+
if self.legend:
|
| 268 |
+
if ax.legend():
|
| 269 |
+
ax.legend_.set_visible(self.legend)
|
| 270 |
+
if self.margin:
|
| 271 |
+
ax.set_xmargin(self.margin)
|
| 272 |
+
ax.set_ymargin(self.margin)
|
| 273 |
+
if self.title:
|
| 274 |
+
ax.set_title(self.title)
|
| 275 |
+
if self.xlabel:
|
| 276 |
+
xlbl = _str_or_latex(self.xlabel)
|
| 277 |
+
ax.set_xlabel(xlbl, position=(1, 0))
|
| 278 |
+
if self.ylabel:
|
| 279 |
+
ylbl = _str_or_latex(self.ylabel)
|
| 280 |
+
ax.set_ylabel(ylbl, position=(0, 1))
|
| 281 |
+
if isinstance(ax, Axes3D) and self.zlabel:
|
| 282 |
+
zlbl = _str_or_latex(self.zlabel)
|
| 283 |
+
ax.set_zlabel(zlbl, position=(0, 1))
|
| 284 |
+
|
| 285 |
+
# xlim and ylim should always be set at last so that plot limits
|
| 286 |
+
# doesn't get altered during the process.
|
| 287 |
+
if self.xlim:
|
| 288 |
+
ax.set_xlim(self.xlim)
|
| 289 |
+
if self.ylim:
|
| 290 |
+
ax.set_ylim(self.ylim)
|
| 291 |
+
self.ax.set_aspect(self.aspect)
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
def process_series(self):
|
| 295 |
+
"""
|
| 296 |
+
Iterates over every ``Plot`` object and further calls
|
| 297 |
+
_process_series()
|
| 298 |
+
"""
|
| 299 |
+
self._create_figure()
|
| 300 |
+
self._process_series(self._series, self.ax)
|
| 301 |
+
|
| 302 |
+
def show(self):
|
| 303 |
+
self.process_series()
|
| 304 |
+
#TODO after fixing https://github.com/ipython/ipython/issues/1255
|
| 305 |
+
# you can uncomment the next line and remove the pyplot.show() call
|
| 306 |
+
#self.fig.show()
|
| 307 |
+
if base_backend._show:
|
| 308 |
+
self.fig.tight_layout()
|
| 309 |
+
self.plt.show()
|
| 310 |
+
else:
|
| 311 |
+
self.close()
|
| 312 |
+
|
| 313 |
+
def save(self, path):
|
| 314 |
+
self.process_series()
|
| 315 |
+
self.fig.savefig(path)
|
| 316 |
+
|
| 317 |
+
def close(self):
|
| 318 |
+
self.plt.close(self.fig)
|
venv/lib/python3.11/site-packages/sympy/plotting/backends/textbackend/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.backends.textbackend.text import TextBackend
|
| 2 |
+
|
| 3 |
+
__all__ = ["TextBackend"]
|
venv/lib/python3.11/site-packages/sympy/plotting/backends/textbackend/text.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sympy.plotting.backends.base_backend as base_backend
|
| 2 |
+
from sympy.plotting.series import LineOver1DRangeSeries
|
| 3 |
+
from sympy.plotting.textplot import textplot
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class TextBackend(base_backend.Plot):
|
| 7 |
+
def __init__(self, *args, **kwargs):
|
| 8 |
+
super().__init__(*args, **kwargs)
|
| 9 |
+
|
| 10 |
+
def show(self):
|
| 11 |
+
if not base_backend._show:
|
| 12 |
+
return
|
| 13 |
+
if len(self._series) != 1:
|
| 14 |
+
raise ValueError(
|
| 15 |
+
'The TextBackend supports only one graph per Plot.')
|
| 16 |
+
elif not isinstance(self._series[0], LineOver1DRangeSeries):
|
| 17 |
+
raise ValueError(
|
| 18 |
+
'The TextBackend supports only expressions over a 1D range')
|
| 19 |
+
else:
|
| 20 |
+
ser = self._series[0]
|
| 21 |
+
textplot(ser.expr, ser.start, ser.end)
|
| 22 |
+
|
| 23 |
+
def close(self):
|
| 24 |
+
pass
|
venv/lib/python3.11/site-packages/sympy/plotting/experimental_lambdify.py
ADDED
|
@@ -0,0 +1,641 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" rewrite of lambdify - This stuff is not stable at all.
|
| 2 |
+
|
| 3 |
+
It is for internal use in the new plotting module.
|
| 4 |
+
It may (will! see the Q'n'A in the source) be rewritten.
|
| 5 |
+
|
| 6 |
+
It's completely self contained. Especially it does not use lambdarepr.
|
| 7 |
+
|
| 8 |
+
It does not aim to replace the current lambdify. Most importantly it will never
|
| 9 |
+
ever support anything else than SymPy expressions (no Matrices, dictionaries
|
| 10 |
+
and so on).
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
import re
|
| 15 |
+
from sympy.core.numbers import (I, NumberSymbol, oo, zoo)
|
| 16 |
+
from sympy.core.symbol import Symbol
|
| 17 |
+
from sympy.utilities.iterables import numbered_symbols
|
| 18 |
+
|
| 19 |
+
# We parse the expression string into a tree that identifies functions. Then
|
| 20 |
+
# we translate the names of the functions and we translate also some strings
|
| 21 |
+
# that are not names of functions (all this according to translation
|
| 22 |
+
# dictionaries).
|
| 23 |
+
# If the translation goes to another module (like numpy) the
|
| 24 |
+
# module is imported and 'func' is translated to 'module.func'.
|
| 25 |
+
# If a function can not be translated, the inner nodes of that part of the
|
| 26 |
+
# tree are not translated. So if we have Integral(sqrt(x)), sqrt is not
|
| 27 |
+
# translated to np.sqrt and the Integral does not crash.
|
| 28 |
+
# A namespace for all this is generated by crawling the (func, args) tree of
|
| 29 |
+
# the expression. The creation of this namespace involves many ugly
|
| 30 |
+
# workarounds.
|
| 31 |
+
# The namespace consists of all the names needed for the SymPy expression and
|
| 32 |
+
# all the name of modules used for translation. Those modules are imported only
|
| 33 |
+
# as a name (import numpy as np) in order to keep the namespace small and
|
| 34 |
+
# manageable.
|
| 35 |
+
|
| 36 |
+
# Please, if there is a bug, do not try to fix it here! Rewrite this by using
|
| 37 |
+
# the method proposed in the last Q'n'A below. That way the new function will
|
| 38 |
+
# work just as well, be just as simple, but it wont need any new workarounds.
|
| 39 |
+
# If you insist on fixing it here, look at the workarounds in the function
|
| 40 |
+
# sympy_expression_namespace and in lambdify.
|
| 41 |
+
|
| 42 |
+
# Q: Why are you not using Python abstract syntax tree?
|
| 43 |
+
# A: Because it is more complicated and not much more powerful in this case.
|
| 44 |
+
|
| 45 |
+
# Q: What if I have Symbol('sin') or g=Function('f')?
|
| 46 |
+
# A: You will break the algorithm. We should use srepr to defend against this?
|
| 47 |
+
# The problem with Symbol('sin') is that it will be printed as 'sin'. The
|
| 48 |
+
# parser will distinguish it from the function 'sin' because functions are
|
| 49 |
+
# detected thanks to the opening parenthesis, but the lambda expression won't
|
| 50 |
+
# understand the difference if we have also the sin function.
|
| 51 |
+
# The solution (complicated) is to use srepr and maybe ast.
|
| 52 |
+
# The problem with the g=Function('f') is that it will be printed as 'f' but in
|
| 53 |
+
# the global namespace we have only 'g'. But as the same printer is used in the
|
| 54 |
+
# constructor of the namespace there will be no problem.
|
| 55 |
+
|
| 56 |
+
# Q: What if some of the printers are not printing as expected?
|
| 57 |
+
# A: The algorithm wont work. You must use srepr for those cases. But even
|
| 58 |
+
# srepr may not print well. All problems with printers should be considered
|
| 59 |
+
# bugs.
|
| 60 |
+
|
| 61 |
+
# Q: What about _imp_ functions?
|
| 62 |
+
# A: Those are taken care for by evalf. A special case treatment will work
|
| 63 |
+
# faster but it's not worth the code complexity.
|
| 64 |
+
|
| 65 |
+
# Q: Will ast fix all possible problems?
|
| 66 |
+
# A: No. You will always have to use some printer. Even srepr may not work in
|
| 67 |
+
# some cases. But if the printer does not work, that should be considered a
|
| 68 |
+
# bug.
|
| 69 |
+
|
| 70 |
+
# Q: Is there same way to fix all possible problems?
|
| 71 |
+
# A: Probably by constructing our strings ourself by traversing the (func,
|
| 72 |
+
# args) tree and creating the namespace at the same time. That actually sounds
|
| 73 |
+
# good.
|
| 74 |
+
|
| 75 |
+
from sympy.external import import_module
|
| 76 |
+
import warnings
|
| 77 |
+
|
| 78 |
+
#TODO debugging output
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class vectorized_lambdify:
|
| 82 |
+
""" Return a sufficiently smart, vectorized and lambdified function.
|
| 83 |
+
|
| 84 |
+
Returns only reals.
|
| 85 |
+
|
| 86 |
+
Explanation
|
| 87 |
+
===========
|
| 88 |
+
|
| 89 |
+
This function uses experimental_lambdify to created a lambdified
|
| 90 |
+
expression ready to be used with numpy. Many of the functions in SymPy
|
| 91 |
+
are not implemented in numpy so in some cases we resort to Python cmath or
|
| 92 |
+
even to evalf.
|
| 93 |
+
|
| 94 |
+
The following translations are tried:
|
| 95 |
+
only numpy complex
|
| 96 |
+
- on errors raised by SymPy trying to work with ndarray:
|
| 97 |
+
only Python cmath and then vectorize complex128
|
| 98 |
+
|
| 99 |
+
When using Python cmath there is no need for evalf or float/complex
|
| 100 |
+
because Python cmath calls those.
|
| 101 |
+
|
| 102 |
+
This function never tries to mix numpy directly with evalf because numpy
|
| 103 |
+
does not understand SymPy Float. If this is needed one can use the
|
| 104 |
+
float_wrap_evalf/complex_wrap_evalf options of experimental_lambdify or
|
| 105 |
+
better one can be explicit about the dtypes that numpy works with.
|
| 106 |
+
Check numpy bug http://projects.scipy.org/numpy/ticket/1013 to know what
|
| 107 |
+
types of errors to expect.
|
| 108 |
+
"""
|
| 109 |
+
def __init__(self, args, expr):
|
| 110 |
+
self.args = args
|
| 111 |
+
self.expr = expr
|
| 112 |
+
self.np = import_module('numpy')
|
| 113 |
+
|
| 114 |
+
self.lambda_func_1 = experimental_lambdify(
|
| 115 |
+
args, expr, use_np=True)
|
| 116 |
+
self.vector_func_1 = self.lambda_func_1
|
| 117 |
+
|
| 118 |
+
self.lambda_func_2 = experimental_lambdify(
|
| 119 |
+
args, expr, use_python_cmath=True)
|
| 120 |
+
self.vector_func_2 = self.np.vectorize(
|
| 121 |
+
self.lambda_func_2, otypes=[complex])
|
| 122 |
+
|
| 123 |
+
self.vector_func = self.vector_func_1
|
| 124 |
+
self.failure = False
|
| 125 |
+
|
| 126 |
+
def __call__(self, *args):
|
| 127 |
+
np = self.np
|
| 128 |
+
|
| 129 |
+
try:
|
| 130 |
+
temp_args = (np.array(a, dtype=complex) for a in args)
|
| 131 |
+
results = self.vector_func(*temp_args)
|
| 132 |
+
results = np.ma.masked_where(
|
| 133 |
+
np.abs(results.imag) > 1e-7 * np.abs(results),
|
| 134 |
+
results.real, copy=False)
|
| 135 |
+
return results
|
| 136 |
+
except ValueError:
|
| 137 |
+
if self.failure:
|
| 138 |
+
raise
|
| 139 |
+
|
| 140 |
+
self.failure = True
|
| 141 |
+
self.vector_func = self.vector_func_2
|
| 142 |
+
warnings.warn(
|
| 143 |
+
'The evaluation of the expression is problematic. '
|
| 144 |
+
'We are trying a failback method that may still work. '
|
| 145 |
+
'Please report this as a bug.')
|
| 146 |
+
return self.__call__(*args)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
class lambdify:
|
| 150 |
+
"""Returns the lambdified function.
|
| 151 |
+
|
| 152 |
+
Explanation
|
| 153 |
+
===========
|
| 154 |
+
|
| 155 |
+
This function uses experimental_lambdify to create a lambdified
|
| 156 |
+
expression. It uses cmath to lambdify the expression. If the function
|
| 157 |
+
is not implemented in Python cmath, Python cmath calls evalf on those
|
| 158 |
+
functions.
|
| 159 |
+
"""
|
| 160 |
+
|
| 161 |
+
def __init__(self, args, expr):
|
| 162 |
+
self.args = args
|
| 163 |
+
self.expr = expr
|
| 164 |
+
self.lambda_func_1 = experimental_lambdify(
|
| 165 |
+
args, expr, use_python_cmath=True, use_evalf=True)
|
| 166 |
+
self.lambda_func_2 = experimental_lambdify(
|
| 167 |
+
args, expr, use_python_math=True, use_evalf=True)
|
| 168 |
+
self.lambda_func_3 = experimental_lambdify(
|
| 169 |
+
args, expr, use_evalf=True, complex_wrap_evalf=True)
|
| 170 |
+
self.lambda_func = self.lambda_func_1
|
| 171 |
+
self.failure = False
|
| 172 |
+
|
| 173 |
+
def __call__(self, args):
|
| 174 |
+
try:
|
| 175 |
+
#The result can be sympy.Float. Hence wrap it with complex type.
|
| 176 |
+
result = complex(self.lambda_func(args))
|
| 177 |
+
if abs(result.imag) > 1e-7 * abs(result):
|
| 178 |
+
return None
|
| 179 |
+
return result.real
|
| 180 |
+
except (ZeroDivisionError, OverflowError):
|
| 181 |
+
return None
|
| 182 |
+
except TypeError as e:
|
| 183 |
+
if self.failure:
|
| 184 |
+
raise e
|
| 185 |
+
|
| 186 |
+
if self.lambda_func == self.lambda_func_1:
|
| 187 |
+
self.lambda_func = self.lambda_func_2
|
| 188 |
+
return self.__call__(args)
|
| 189 |
+
|
| 190 |
+
self.failure = True
|
| 191 |
+
self.lambda_func = self.lambda_func_3
|
| 192 |
+
warnings.warn(
|
| 193 |
+
'The evaluation of the expression is problematic. '
|
| 194 |
+
'We are trying a failback method that may still work. '
|
| 195 |
+
'Please report this as a bug.', stacklevel=2)
|
| 196 |
+
return self.__call__(args)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def experimental_lambdify(*args, **kwargs):
|
| 200 |
+
l = Lambdifier(*args, **kwargs)
|
| 201 |
+
return l
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
class Lambdifier:
|
| 205 |
+
def __init__(self, args, expr, print_lambda=False, use_evalf=False,
|
| 206 |
+
float_wrap_evalf=False, complex_wrap_evalf=False,
|
| 207 |
+
use_np=False, use_python_math=False, use_python_cmath=False,
|
| 208 |
+
use_interval=False):
|
| 209 |
+
|
| 210 |
+
self.print_lambda = print_lambda
|
| 211 |
+
self.use_evalf = use_evalf
|
| 212 |
+
self.float_wrap_evalf = float_wrap_evalf
|
| 213 |
+
self.complex_wrap_evalf = complex_wrap_evalf
|
| 214 |
+
self.use_np = use_np
|
| 215 |
+
self.use_python_math = use_python_math
|
| 216 |
+
self.use_python_cmath = use_python_cmath
|
| 217 |
+
self.use_interval = use_interval
|
| 218 |
+
|
| 219 |
+
# Constructing the argument string
|
| 220 |
+
# - check
|
| 221 |
+
if not all(isinstance(a, Symbol) for a in args):
|
| 222 |
+
raise ValueError('The arguments must be Symbols.')
|
| 223 |
+
# - use numbered symbols
|
| 224 |
+
syms = numbered_symbols(exclude=expr.free_symbols)
|
| 225 |
+
newargs = [next(syms) for _ in args]
|
| 226 |
+
expr = expr.xreplace(dict(zip(args, newargs)))
|
| 227 |
+
argstr = ', '.join([str(a) for a in newargs])
|
| 228 |
+
del syms, newargs, args
|
| 229 |
+
|
| 230 |
+
# Constructing the translation dictionaries and making the translation
|
| 231 |
+
self.dict_str = self.get_dict_str()
|
| 232 |
+
self.dict_fun = self.get_dict_fun()
|
| 233 |
+
exprstr = str(expr)
|
| 234 |
+
newexpr = self.tree2str_translate(self.str2tree(exprstr))
|
| 235 |
+
|
| 236 |
+
# Constructing the namespaces
|
| 237 |
+
namespace = {}
|
| 238 |
+
namespace.update(self.sympy_atoms_namespace(expr))
|
| 239 |
+
namespace.update(self.sympy_expression_namespace(expr))
|
| 240 |
+
# XXX Workaround
|
| 241 |
+
# Ugly workaround because Pow(a,Half) prints as sqrt(a)
|
| 242 |
+
# and sympy_expression_namespace can not catch it.
|
| 243 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 244 |
+
namespace.update({'sqrt': sqrt})
|
| 245 |
+
namespace.update({'Eq': lambda x, y: x == y})
|
| 246 |
+
namespace.update({'Ne': lambda x, y: x != y})
|
| 247 |
+
# End workaround.
|
| 248 |
+
if use_python_math:
|
| 249 |
+
namespace.update({'math': __import__('math')})
|
| 250 |
+
if use_python_cmath:
|
| 251 |
+
namespace.update({'cmath': __import__('cmath')})
|
| 252 |
+
if use_np:
|
| 253 |
+
try:
|
| 254 |
+
namespace.update({'np': __import__('numpy')})
|
| 255 |
+
except ImportError:
|
| 256 |
+
raise ImportError(
|
| 257 |
+
'experimental_lambdify failed to import numpy.')
|
| 258 |
+
if use_interval:
|
| 259 |
+
namespace.update({'imath': __import__(
|
| 260 |
+
'sympy.plotting.intervalmath', fromlist=['intervalmath'])})
|
| 261 |
+
namespace.update({'math': __import__('math')})
|
| 262 |
+
|
| 263 |
+
# Construct the lambda
|
| 264 |
+
if self.print_lambda:
|
| 265 |
+
print(newexpr)
|
| 266 |
+
eval_str = 'lambda %s : ( %s )' % (argstr, newexpr)
|
| 267 |
+
self.eval_str = eval_str
|
| 268 |
+
exec("MYNEWLAMBDA = %s" % eval_str, namespace)
|
| 269 |
+
self.lambda_func = namespace['MYNEWLAMBDA']
|
| 270 |
+
|
| 271 |
+
def __call__(self, *args, **kwargs):
|
| 272 |
+
return self.lambda_func(*args, **kwargs)
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
##############################################################################
|
| 276 |
+
# Dicts for translating from SymPy to other modules
|
| 277 |
+
##############################################################################
|
| 278 |
+
###
|
| 279 |
+
# builtins
|
| 280 |
+
###
|
| 281 |
+
# Functions with different names in builtins
|
| 282 |
+
builtin_functions_different = {
|
| 283 |
+
'Min': 'min',
|
| 284 |
+
'Max': 'max',
|
| 285 |
+
'Abs': 'abs',
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
# Strings that should be translated
|
| 289 |
+
builtin_not_functions = {
|
| 290 |
+
'I': '1j',
|
| 291 |
+
# 'oo': '1e400',
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
###
|
| 295 |
+
# numpy
|
| 296 |
+
###
|
| 297 |
+
|
| 298 |
+
# Functions that are the same in numpy
|
| 299 |
+
numpy_functions_same = [
|
| 300 |
+
'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'exp', 'log',
|
| 301 |
+
'sqrt', 'floor', 'conjugate', 'sign',
|
| 302 |
+
]
|
| 303 |
+
|
| 304 |
+
# Functions with different names in numpy
|
| 305 |
+
numpy_functions_different = {
|
| 306 |
+
"acos": "arccos",
|
| 307 |
+
"acosh": "arccosh",
|
| 308 |
+
"arg": "angle",
|
| 309 |
+
"asin": "arcsin",
|
| 310 |
+
"asinh": "arcsinh",
|
| 311 |
+
"atan": "arctan",
|
| 312 |
+
"atan2": "arctan2",
|
| 313 |
+
"atanh": "arctanh",
|
| 314 |
+
"ceiling": "ceil",
|
| 315 |
+
"im": "imag",
|
| 316 |
+
"ln": "log",
|
| 317 |
+
"Max": "amax",
|
| 318 |
+
"Min": "amin",
|
| 319 |
+
"re": "real",
|
| 320 |
+
"Abs": "abs",
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
# Strings that should be translated
|
| 324 |
+
numpy_not_functions = {
|
| 325 |
+
'pi': 'np.pi',
|
| 326 |
+
'oo': 'np.inf',
|
| 327 |
+
'E': 'np.e',
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
###
|
| 331 |
+
# Python math
|
| 332 |
+
###
|
| 333 |
+
|
| 334 |
+
# Functions that are the same in math
|
| 335 |
+
math_functions_same = [
|
| 336 |
+
'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'atan2',
|
| 337 |
+
'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh',
|
| 338 |
+
'exp', 'log', 'erf', 'sqrt', 'floor', 'factorial', 'gamma',
|
| 339 |
+
]
|
| 340 |
+
|
| 341 |
+
# Functions with different names in math
|
| 342 |
+
math_functions_different = {
|
| 343 |
+
'ceiling': 'ceil',
|
| 344 |
+
'ln': 'log',
|
| 345 |
+
'loggamma': 'lgamma'
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
# Strings that should be translated
|
| 349 |
+
math_not_functions = {
|
| 350 |
+
'pi': 'math.pi',
|
| 351 |
+
'E': 'math.e',
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
###
|
| 355 |
+
# Python cmath
|
| 356 |
+
###
|
| 357 |
+
|
| 358 |
+
# Functions that are the same in cmath
|
| 359 |
+
cmath_functions_same = [
|
| 360 |
+
'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
|
| 361 |
+
'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh',
|
| 362 |
+
'exp', 'log', 'sqrt',
|
| 363 |
+
]
|
| 364 |
+
|
| 365 |
+
# Functions with different names in cmath
|
| 366 |
+
cmath_functions_different = {
|
| 367 |
+
'ln': 'log',
|
| 368 |
+
'arg': 'phase',
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
# Strings that should be translated
|
| 372 |
+
cmath_not_functions = {
|
| 373 |
+
'pi': 'cmath.pi',
|
| 374 |
+
'E': 'cmath.e',
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
###
|
| 378 |
+
# intervalmath
|
| 379 |
+
###
|
| 380 |
+
|
| 381 |
+
interval_not_functions = {
|
| 382 |
+
'pi': 'math.pi',
|
| 383 |
+
'E': 'math.e'
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
interval_functions_same = [
|
| 387 |
+
'sin', 'cos', 'exp', 'tan', 'atan', 'log',
|
| 388 |
+
'sqrt', 'cosh', 'sinh', 'tanh', 'floor',
|
| 389 |
+
'acos', 'asin', 'acosh', 'asinh', 'atanh',
|
| 390 |
+
'Abs', 'And', 'Or'
|
| 391 |
+
]
|
| 392 |
+
|
| 393 |
+
interval_functions_different = {
|
| 394 |
+
'Min': 'imin',
|
| 395 |
+
'Max': 'imax',
|
| 396 |
+
'ceiling': 'ceil',
|
| 397 |
+
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
###
|
| 401 |
+
# mpmath, etc
|
| 402 |
+
###
|
| 403 |
+
#TODO
|
| 404 |
+
|
| 405 |
+
###
|
| 406 |
+
# Create the final ordered tuples of dictionaries
|
| 407 |
+
###
|
| 408 |
+
|
| 409 |
+
# For strings
|
| 410 |
+
def get_dict_str(self):
|
| 411 |
+
dict_str = dict(self.builtin_not_functions)
|
| 412 |
+
if self.use_np:
|
| 413 |
+
dict_str.update(self.numpy_not_functions)
|
| 414 |
+
if self.use_python_math:
|
| 415 |
+
dict_str.update(self.math_not_functions)
|
| 416 |
+
if self.use_python_cmath:
|
| 417 |
+
dict_str.update(self.cmath_not_functions)
|
| 418 |
+
if self.use_interval:
|
| 419 |
+
dict_str.update(self.interval_not_functions)
|
| 420 |
+
return dict_str
|
| 421 |
+
|
| 422 |
+
# For functions
|
| 423 |
+
def get_dict_fun(self):
|
| 424 |
+
dict_fun = dict(self.builtin_functions_different)
|
| 425 |
+
if self.use_np:
|
| 426 |
+
for s in self.numpy_functions_same:
|
| 427 |
+
dict_fun[s] = 'np.' + s
|
| 428 |
+
for k, v in self.numpy_functions_different.items():
|
| 429 |
+
dict_fun[k] = 'np.' + v
|
| 430 |
+
if self.use_python_math:
|
| 431 |
+
for s in self.math_functions_same:
|
| 432 |
+
dict_fun[s] = 'math.' + s
|
| 433 |
+
for k, v in self.math_functions_different.items():
|
| 434 |
+
dict_fun[k] = 'math.' + v
|
| 435 |
+
if self.use_python_cmath:
|
| 436 |
+
for s in self.cmath_functions_same:
|
| 437 |
+
dict_fun[s] = 'cmath.' + s
|
| 438 |
+
for k, v in self.cmath_functions_different.items():
|
| 439 |
+
dict_fun[k] = 'cmath.' + v
|
| 440 |
+
if self.use_interval:
|
| 441 |
+
for s in self.interval_functions_same:
|
| 442 |
+
dict_fun[s] = 'imath.' + s
|
| 443 |
+
for k, v in self.interval_functions_different.items():
|
| 444 |
+
dict_fun[k] = 'imath.' + v
|
| 445 |
+
return dict_fun
|
| 446 |
+
|
| 447 |
+
##############################################################################
|
| 448 |
+
# The translator functions, tree parsers, etc.
|
| 449 |
+
##############################################################################
|
| 450 |
+
|
| 451 |
+
def str2tree(self, exprstr):
|
| 452 |
+
"""Converts an expression string to a tree.
|
| 453 |
+
|
| 454 |
+
Explanation
|
| 455 |
+
===========
|
| 456 |
+
|
| 457 |
+
Functions are represented by ('func_name(', tree_of_arguments).
|
| 458 |
+
Other expressions are (head_string, mid_tree, tail_str).
|
| 459 |
+
Expressions that do not contain functions are directly returned.
|
| 460 |
+
|
| 461 |
+
Examples
|
| 462 |
+
========
|
| 463 |
+
|
| 464 |
+
>>> from sympy.abc import x, y, z
|
| 465 |
+
>>> from sympy import Integral, sin
|
| 466 |
+
>>> from sympy.plotting.experimental_lambdify import Lambdifier
|
| 467 |
+
>>> str2tree = Lambdifier([x], x).str2tree
|
| 468 |
+
|
| 469 |
+
>>> str2tree(str(Integral(x, (x, 1, y))))
|
| 470 |
+
('', ('Integral(', 'x, (x, 1, y)'), ')')
|
| 471 |
+
>>> str2tree(str(x+y))
|
| 472 |
+
'x + y'
|
| 473 |
+
>>> str2tree(str(x+y*sin(z)+1))
|
| 474 |
+
('x + y*', ('sin(', 'z'), ') + 1')
|
| 475 |
+
>>> str2tree('sin(y*(y + 1.1) + (sin(y)))')
|
| 476 |
+
('', ('sin(', ('y*(y + 1.1) + (', ('sin(', 'y'), '))')), ')')
|
| 477 |
+
"""
|
| 478 |
+
#matches the first 'function_name('
|
| 479 |
+
first_par = re.search(r'(\w+\()', exprstr)
|
| 480 |
+
if first_par is None:
|
| 481 |
+
return exprstr
|
| 482 |
+
else:
|
| 483 |
+
start = first_par.start()
|
| 484 |
+
end = first_par.end()
|
| 485 |
+
head = exprstr[:start]
|
| 486 |
+
func = exprstr[start:end]
|
| 487 |
+
tail = exprstr[end:]
|
| 488 |
+
count = 0
|
| 489 |
+
for i, c in enumerate(tail):
|
| 490 |
+
if c == '(':
|
| 491 |
+
count += 1
|
| 492 |
+
elif c == ')':
|
| 493 |
+
count -= 1
|
| 494 |
+
if count == -1:
|
| 495 |
+
break
|
| 496 |
+
func_tail = self.str2tree(tail[:i])
|
| 497 |
+
tail = self.str2tree(tail[i:])
|
| 498 |
+
return (head, (func, func_tail), tail)
|
| 499 |
+
|
| 500 |
+
@classmethod
|
| 501 |
+
def tree2str(cls, tree):
|
| 502 |
+
"""Converts a tree to string without translations.
|
| 503 |
+
|
| 504 |
+
Examples
|
| 505 |
+
========
|
| 506 |
+
|
| 507 |
+
>>> from sympy.abc import x, y, z
|
| 508 |
+
>>> from sympy import sin
|
| 509 |
+
>>> from sympy.plotting.experimental_lambdify import Lambdifier
|
| 510 |
+
>>> str2tree = Lambdifier([x], x).str2tree
|
| 511 |
+
>>> tree2str = Lambdifier([x], x).tree2str
|
| 512 |
+
|
| 513 |
+
>>> tree2str(str2tree(str(x+y*sin(z)+1)))
|
| 514 |
+
'x + y*sin(z) + 1'
|
| 515 |
+
"""
|
| 516 |
+
if isinstance(tree, str):
|
| 517 |
+
return tree
|
| 518 |
+
else:
|
| 519 |
+
return ''.join(map(cls.tree2str, tree))
|
| 520 |
+
|
| 521 |
+
def tree2str_translate(self, tree):
|
| 522 |
+
"""Converts a tree to string with translations.
|
| 523 |
+
|
| 524 |
+
Explanation
|
| 525 |
+
===========
|
| 526 |
+
|
| 527 |
+
Function names are translated by translate_func.
|
| 528 |
+
Other strings are translated by translate_str.
|
| 529 |
+
"""
|
| 530 |
+
if isinstance(tree, str):
|
| 531 |
+
return self.translate_str(tree)
|
| 532 |
+
elif isinstance(tree, tuple) and len(tree) == 2:
|
| 533 |
+
return self.translate_func(tree[0][:-1], tree[1])
|
| 534 |
+
else:
|
| 535 |
+
return ''.join([self.tree2str_translate(t) for t in tree])
|
| 536 |
+
|
| 537 |
+
def translate_str(self, estr):
|
| 538 |
+
"""Translate substrings of estr using in order the dictionaries in
|
| 539 |
+
dict_tuple_str."""
|
| 540 |
+
for pattern, repl in self.dict_str.items():
|
| 541 |
+
estr = re.sub(pattern, repl, estr)
|
| 542 |
+
return estr
|
| 543 |
+
|
| 544 |
+
def translate_func(self, func_name, argtree):
|
| 545 |
+
"""Translate function names and the tree of arguments.
|
| 546 |
+
|
| 547 |
+
Explanation
|
| 548 |
+
===========
|
| 549 |
+
|
| 550 |
+
If the function name is not in the dictionaries of dict_tuple_fun then the
|
| 551 |
+
function is surrounded by a float((...).evalf()).
|
| 552 |
+
|
| 553 |
+
The use of float is necessary as np.<function>(sympy.Float(..)) raises an
|
| 554 |
+
error."""
|
| 555 |
+
if func_name in self.dict_fun:
|
| 556 |
+
new_name = self.dict_fun[func_name]
|
| 557 |
+
argstr = self.tree2str_translate(argtree)
|
| 558 |
+
return new_name + '(' + argstr
|
| 559 |
+
elif func_name in ['Eq', 'Ne']:
|
| 560 |
+
op = {'Eq': '==', 'Ne': '!='}
|
| 561 |
+
return "(lambda x, y: x {} y)({}".format(op[func_name], self.tree2str_translate(argtree))
|
| 562 |
+
else:
|
| 563 |
+
template = '(%s(%s)).evalf(' if self.use_evalf else '%s(%s'
|
| 564 |
+
if self.float_wrap_evalf:
|
| 565 |
+
template = 'float(%s)' % template
|
| 566 |
+
elif self.complex_wrap_evalf:
|
| 567 |
+
template = 'complex(%s)' % template
|
| 568 |
+
|
| 569 |
+
# Wrapping should only happen on the outermost expression, which
|
| 570 |
+
# is the only thing we know will be a number.
|
| 571 |
+
float_wrap_evalf = self.float_wrap_evalf
|
| 572 |
+
complex_wrap_evalf = self.complex_wrap_evalf
|
| 573 |
+
self.float_wrap_evalf = False
|
| 574 |
+
self.complex_wrap_evalf = False
|
| 575 |
+
ret = template % (func_name, self.tree2str_translate(argtree))
|
| 576 |
+
self.float_wrap_evalf = float_wrap_evalf
|
| 577 |
+
self.complex_wrap_evalf = complex_wrap_evalf
|
| 578 |
+
return ret
|
| 579 |
+
|
| 580 |
+
##############################################################################
|
| 581 |
+
# The namespace constructors
|
| 582 |
+
##############################################################################
|
| 583 |
+
|
| 584 |
+
@classmethod
|
| 585 |
+
def sympy_expression_namespace(cls, expr):
|
| 586 |
+
"""Traverses the (func, args) tree of an expression and creates a SymPy
|
| 587 |
+
namespace. All other modules are imported only as a module name. That way
|
| 588 |
+
the namespace is not polluted and rests quite small. It probably causes much
|
| 589 |
+
more variable lookups and so it takes more time, but there are no tests on
|
| 590 |
+
that for the moment."""
|
| 591 |
+
if expr is None:
|
| 592 |
+
return {}
|
| 593 |
+
else:
|
| 594 |
+
funcname = str(expr.func)
|
| 595 |
+
# XXX Workaround
|
| 596 |
+
# Here we add an ugly workaround because str(func(x))
|
| 597 |
+
# is not always the same as str(func). Eg
|
| 598 |
+
# >>> str(Integral(x))
|
| 599 |
+
# "Integral(x)"
|
| 600 |
+
# >>> str(Integral)
|
| 601 |
+
# "<class 'sympy.integrals.integrals.Integral'>"
|
| 602 |
+
# >>> str(sqrt(x))
|
| 603 |
+
# "sqrt(x)"
|
| 604 |
+
# >>> str(sqrt)
|
| 605 |
+
# "<function sqrt at 0x3d92de8>"
|
| 606 |
+
# >>> str(sin(x))
|
| 607 |
+
# "sin(x)"
|
| 608 |
+
# >>> str(sin)
|
| 609 |
+
# "sin"
|
| 610 |
+
# Either one of those can be used but not all at the same time.
|
| 611 |
+
# The code considers the sin example as the right one.
|
| 612 |
+
regexlist = [
|
| 613 |
+
r'<class \'sympy[\w.]*?.([\w]*)\'>$',
|
| 614 |
+
# the example Integral
|
| 615 |
+
r'<function ([\w]*) at 0x[\w]*>$', # the example sqrt
|
| 616 |
+
]
|
| 617 |
+
for r in regexlist:
|
| 618 |
+
m = re.match(r, funcname)
|
| 619 |
+
if m is not None:
|
| 620 |
+
funcname = m.groups()[0]
|
| 621 |
+
# End of the workaround
|
| 622 |
+
# XXX debug: print funcname
|
| 623 |
+
args_dict = {}
|
| 624 |
+
for a in expr.args:
|
| 625 |
+
if (isinstance(a, (Symbol, NumberSymbol)) or a in [I, zoo, oo]):
|
| 626 |
+
continue
|
| 627 |
+
else:
|
| 628 |
+
args_dict.update(cls.sympy_expression_namespace(a))
|
| 629 |
+
args_dict.update({funcname: expr.func})
|
| 630 |
+
return args_dict
|
| 631 |
+
|
| 632 |
+
@staticmethod
|
| 633 |
+
def sympy_atoms_namespace(expr):
|
| 634 |
+
"""For no real reason this function is separated from
|
| 635 |
+
sympy_expression_namespace. It can be moved to it."""
|
| 636 |
+
atoms = expr.atoms(Symbol, NumberSymbol, I, zoo, oo)
|
| 637 |
+
d = {}
|
| 638 |
+
for a in atoms:
|
| 639 |
+
# XXX debug: print 'atom:' + str(a)
|
| 640 |
+
d[str(a)] = a
|
| 641 |
+
return d
|
venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .interval_arithmetic import interval
|
| 2 |
+
from .lib_interval import (Abs, exp, log, log10, sin, cos, tan, sqrt,
|
| 3 |
+
imin, imax, sinh, cosh, tanh, acosh, asinh, atanh,
|
| 4 |
+
asin, acos, atan, ceil, floor, And, Or)
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
'interval',
|
| 8 |
+
|
| 9 |
+
'Abs', 'exp', 'log', 'log10', 'sin', 'cos', 'tan', 'sqrt', 'imin', 'imax',
|
| 10 |
+
'sinh', 'cosh', 'tanh', 'acosh', 'asinh', 'atanh', 'asin', 'acos', 'atan',
|
| 11 |
+
'ceil', 'floor', 'And', 'Or',
|
| 12 |
+
]
|
venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Interval Arithmetic for plotting.
|
| 3 |
+
This module does not implement interval arithmetic accurately and
|
| 4 |
+
hence cannot be used for purposes other than plotting. If you want
|
| 5 |
+
to use interval arithmetic, use mpmath's interval arithmetic.
|
| 6 |
+
|
| 7 |
+
The module implements interval arithmetic using numpy and
|
| 8 |
+
python floating points. The rounding up and down is not handled
|
| 9 |
+
and hence this is not an accurate implementation of interval
|
| 10 |
+
arithmetic.
|
| 11 |
+
|
| 12 |
+
The module uses numpy for speed which cannot be achieved with mpmath.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
# Q: Why use numpy? Why not simply use mpmath's interval arithmetic?
|
| 16 |
+
# A: mpmath's interval arithmetic simulates a floating point unit
|
| 17 |
+
# and hence is slow, while numpy evaluations are orders of magnitude
|
| 18 |
+
# faster.
|
| 19 |
+
|
| 20 |
+
# Q: Why create a separate class for intervals? Why not use SymPy's
|
| 21 |
+
# Interval Sets?
|
| 22 |
+
# A: The functionalities that will be required for plotting is quite
|
| 23 |
+
# different from what Interval Sets implement.
|
| 24 |
+
|
| 25 |
+
# Q: Why is rounding up and down according to IEEE754 not handled?
|
| 26 |
+
# A: It is not possible to do it in both numpy and python. An external
|
| 27 |
+
# library has to used, which defeats the whole purpose i.e., speed. Also
|
| 28 |
+
# rounding is handled for very few functions in those libraries.
|
| 29 |
+
|
| 30 |
+
# Q Will my plots be affected?
|
| 31 |
+
# A It will not affect most of the plots. The interval arithmetic
|
| 32 |
+
# module based suffers the same problems as that of floating point
|
| 33 |
+
# arithmetic.
|
| 34 |
+
|
| 35 |
+
from sympy.core.numbers import int_valued
|
| 36 |
+
from sympy.core.logic import fuzzy_and
|
| 37 |
+
from sympy.simplify.simplify import nsimplify
|
| 38 |
+
|
| 39 |
+
from .interval_membership import intervalMembership
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class interval:
|
| 43 |
+
""" Represents an interval containing floating points as start and
|
| 44 |
+
end of the interval
|
| 45 |
+
The is_valid variable tracks whether the interval obtained as the
|
| 46 |
+
result of the function is in the domain and is continuous.
|
| 47 |
+
- True: Represents the interval result of a function is continuous and
|
| 48 |
+
in the domain of the function.
|
| 49 |
+
- False: The interval argument of the function was not in the domain of
|
| 50 |
+
the function, hence the is_valid of the result interval is False
|
| 51 |
+
- None: The function was not continuous over the interval or
|
| 52 |
+
the function's argument interval is partly in the domain of the
|
| 53 |
+
function
|
| 54 |
+
|
| 55 |
+
A comparison between an interval and a real number, or a
|
| 56 |
+
comparison between two intervals may return ``intervalMembership``
|
| 57 |
+
of two 3-valued logic values.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
def __init__(self, *args, is_valid=True, **kwargs):
|
| 61 |
+
self.is_valid = is_valid
|
| 62 |
+
if len(args) == 1:
|
| 63 |
+
if isinstance(args[0], interval):
|
| 64 |
+
self.start, self.end = args[0].start, args[0].end
|
| 65 |
+
else:
|
| 66 |
+
self.start = float(args[0])
|
| 67 |
+
self.end = float(args[0])
|
| 68 |
+
elif len(args) == 2:
|
| 69 |
+
if args[0] < args[1]:
|
| 70 |
+
self.start = float(args[0])
|
| 71 |
+
self.end = float(args[1])
|
| 72 |
+
else:
|
| 73 |
+
self.start = float(args[1])
|
| 74 |
+
self.end = float(args[0])
|
| 75 |
+
|
| 76 |
+
else:
|
| 77 |
+
raise ValueError("interval takes a maximum of two float values "
|
| 78 |
+
"as arguments")
|
| 79 |
+
|
| 80 |
+
@property
|
| 81 |
+
def mid(self):
|
| 82 |
+
return (self.start + self.end) / 2.0
|
| 83 |
+
|
| 84 |
+
@property
|
| 85 |
+
def width(self):
|
| 86 |
+
return self.end - self.start
|
| 87 |
+
|
| 88 |
+
def __repr__(self):
|
| 89 |
+
return "interval(%f, %f)" % (self.start, self.end)
|
| 90 |
+
|
| 91 |
+
def __str__(self):
|
| 92 |
+
return "[%f, %f]" % (self.start, self.end)
|
| 93 |
+
|
| 94 |
+
def __lt__(self, other):
|
| 95 |
+
if isinstance(other, (int, float)):
|
| 96 |
+
if self.end < other:
|
| 97 |
+
return intervalMembership(True, self.is_valid)
|
| 98 |
+
elif self.start > other:
|
| 99 |
+
return intervalMembership(False, self.is_valid)
|
| 100 |
+
else:
|
| 101 |
+
return intervalMembership(None, self.is_valid)
|
| 102 |
+
|
| 103 |
+
elif isinstance(other, interval):
|
| 104 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 105 |
+
if self.end < other. start:
|
| 106 |
+
return intervalMembership(True, valid)
|
| 107 |
+
if self.start > other.end:
|
| 108 |
+
return intervalMembership(False, valid)
|
| 109 |
+
return intervalMembership(None, valid)
|
| 110 |
+
else:
|
| 111 |
+
return NotImplemented
|
| 112 |
+
|
| 113 |
+
def __gt__(self, other):
|
| 114 |
+
if isinstance(other, (int, float)):
|
| 115 |
+
if self.start > other:
|
| 116 |
+
return intervalMembership(True, self.is_valid)
|
| 117 |
+
elif self.end < other:
|
| 118 |
+
return intervalMembership(False, self.is_valid)
|
| 119 |
+
else:
|
| 120 |
+
return intervalMembership(None, self.is_valid)
|
| 121 |
+
elif isinstance(other, interval):
|
| 122 |
+
return other.__lt__(self)
|
| 123 |
+
else:
|
| 124 |
+
return NotImplemented
|
| 125 |
+
|
| 126 |
+
def __eq__(self, other):
|
| 127 |
+
if isinstance(other, (int, float)):
|
| 128 |
+
if self.start == other and self.end == other:
|
| 129 |
+
return intervalMembership(True, self.is_valid)
|
| 130 |
+
if other in self:
|
| 131 |
+
return intervalMembership(None, self.is_valid)
|
| 132 |
+
else:
|
| 133 |
+
return intervalMembership(False, self.is_valid)
|
| 134 |
+
|
| 135 |
+
if isinstance(other, interval):
|
| 136 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 137 |
+
if self.start == other.start and self.end == other.end:
|
| 138 |
+
return intervalMembership(True, valid)
|
| 139 |
+
elif self.__lt__(other)[0] is not None:
|
| 140 |
+
return intervalMembership(False, valid)
|
| 141 |
+
else:
|
| 142 |
+
return intervalMembership(None, valid)
|
| 143 |
+
else:
|
| 144 |
+
return NotImplemented
|
| 145 |
+
|
| 146 |
+
def __ne__(self, other):
|
| 147 |
+
if isinstance(other, (int, float)):
|
| 148 |
+
if self.start == other and self.end == other:
|
| 149 |
+
return intervalMembership(False, self.is_valid)
|
| 150 |
+
if other in self:
|
| 151 |
+
return intervalMembership(None, self.is_valid)
|
| 152 |
+
else:
|
| 153 |
+
return intervalMembership(True, self.is_valid)
|
| 154 |
+
|
| 155 |
+
if isinstance(other, interval):
|
| 156 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 157 |
+
if self.start == other.start and self.end == other.end:
|
| 158 |
+
return intervalMembership(False, valid)
|
| 159 |
+
if not self.__lt__(other)[0] is None:
|
| 160 |
+
return intervalMembership(True, valid)
|
| 161 |
+
return intervalMembership(None, valid)
|
| 162 |
+
else:
|
| 163 |
+
return NotImplemented
|
| 164 |
+
|
| 165 |
+
def __le__(self, other):
|
| 166 |
+
if isinstance(other, (int, float)):
|
| 167 |
+
if self.end <= other:
|
| 168 |
+
return intervalMembership(True, self.is_valid)
|
| 169 |
+
if self.start > other:
|
| 170 |
+
return intervalMembership(False, self.is_valid)
|
| 171 |
+
else:
|
| 172 |
+
return intervalMembership(None, self.is_valid)
|
| 173 |
+
|
| 174 |
+
if isinstance(other, interval):
|
| 175 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 176 |
+
if self.end <= other.start:
|
| 177 |
+
return intervalMembership(True, valid)
|
| 178 |
+
if self.start > other.end:
|
| 179 |
+
return intervalMembership(False, valid)
|
| 180 |
+
return intervalMembership(None, valid)
|
| 181 |
+
else:
|
| 182 |
+
return NotImplemented
|
| 183 |
+
|
| 184 |
+
def __ge__(self, other):
|
| 185 |
+
if isinstance(other, (int, float)):
|
| 186 |
+
if self.start >= other:
|
| 187 |
+
return intervalMembership(True, self.is_valid)
|
| 188 |
+
elif self.end < other:
|
| 189 |
+
return intervalMembership(False, self.is_valid)
|
| 190 |
+
else:
|
| 191 |
+
return intervalMembership(None, self.is_valid)
|
| 192 |
+
elif isinstance(other, interval):
|
| 193 |
+
return other.__le__(self)
|
| 194 |
+
|
| 195 |
+
def __add__(self, other):
|
| 196 |
+
if isinstance(other, (int, float)):
|
| 197 |
+
if self.is_valid:
|
| 198 |
+
return interval(self.start + other, self.end + other)
|
| 199 |
+
else:
|
| 200 |
+
start = self.start + other
|
| 201 |
+
end = self.end + other
|
| 202 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 203 |
+
|
| 204 |
+
elif isinstance(other, interval):
|
| 205 |
+
start = self.start + other.start
|
| 206 |
+
end = self.end + other.end
|
| 207 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 208 |
+
return interval(start, end, is_valid=valid)
|
| 209 |
+
else:
|
| 210 |
+
return NotImplemented
|
| 211 |
+
|
| 212 |
+
__radd__ = __add__
|
| 213 |
+
|
| 214 |
+
def __sub__(self, other):
|
| 215 |
+
if isinstance(other, (int, float)):
|
| 216 |
+
start = self.start - other
|
| 217 |
+
end = self.end - other
|
| 218 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 219 |
+
|
| 220 |
+
elif isinstance(other, interval):
|
| 221 |
+
start = self.start - other.end
|
| 222 |
+
end = self.end - other.start
|
| 223 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 224 |
+
return interval(start, end, is_valid=valid)
|
| 225 |
+
else:
|
| 226 |
+
return NotImplemented
|
| 227 |
+
|
| 228 |
+
def __rsub__(self, other):
|
| 229 |
+
if isinstance(other, (int, float)):
|
| 230 |
+
start = other - self.end
|
| 231 |
+
end = other - self.start
|
| 232 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 233 |
+
elif isinstance(other, interval):
|
| 234 |
+
return other.__sub__(self)
|
| 235 |
+
else:
|
| 236 |
+
return NotImplemented
|
| 237 |
+
|
| 238 |
+
def __neg__(self):
|
| 239 |
+
if self.is_valid:
|
| 240 |
+
return interval(-self.end, -self.start)
|
| 241 |
+
else:
|
| 242 |
+
return interval(-self.end, -self.start, is_valid=self.is_valid)
|
| 243 |
+
|
| 244 |
+
def __mul__(self, other):
|
| 245 |
+
if isinstance(other, interval):
|
| 246 |
+
if self.is_valid is False or other.is_valid is False:
|
| 247 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 248 |
+
elif self.is_valid is None or other.is_valid is None:
|
| 249 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 250 |
+
else:
|
| 251 |
+
inters = []
|
| 252 |
+
inters.append(self.start * other.start)
|
| 253 |
+
inters.append(self.end * other.start)
|
| 254 |
+
inters.append(self.start * other.end)
|
| 255 |
+
inters.append(self.end * other.end)
|
| 256 |
+
start = min(inters)
|
| 257 |
+
end = max(inters)
|
| 258 |
+
return interval(start, end)
|
| 259 |
+
elif isinstance(other, (int, float)):
|
| 260 |
+
return interval(self.start*other, self.end*other, is_valid=self.is_valid)
|
| 261 |
+
else:
|
| 262 |
+
return NotImplemented
|
| 263 |
+
|
| 264 |
+
__rmul__ = __mul__
|
| 265 |
+
|
| 266 |
+
def __contains__(self, other):
|
| 267 |
+
if isinstance(other, (int, float)):
|
| 268 |
+
return self.start <= other and self.end >= other
|
| 269 |
+
else:
|
| 270 |
+
return self.start <= other.start and other.end <= self.end
|
| 271 |
+
|
| 272 |
+
def __rtruediv__(self, other):
|
| 273 |
+
if isinstance(other, (int, float)):
|
| 274 |
+
other = interval(other)
|
| 275 |
+
return other.__truediv__(self)
|
| 276 |
+
elif isinstance(other, interval):
|
| 277 |
+
return other.__truediv__(self)
|
| 278 |
+
else:
|
| 279 |
+
return NotImplemented
|
| 280 |
+
|
| 281 |
+
def __truediv__(self, other):
|
| 282 |
+
# Both None and False are handled
|
| 283 |
+
if not self.is_valid:
|
| 284 |
+
# Don't divide as the value is not valid
|
| 285 |
+
return interval(-float('inf'), float('inf'), is_valid=self.is_valid)
|
| 286 |
+
if isinstance(other, (int, float)):
|
| 287 |
+
if other == 0:
|
| 288 |
+
# Divide by zero encountered. valid nowhere
|
| 289 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 290 |
+
else:
|
| 291 |
+
return interval(self.start / other, self.end / other)
|
| 292 |
+
|
| 293 |
+
elif isinstance(other, interval):
|
| 294 |
+
if other.is_valid is False or self.is_valid is False:
|
| 295 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 296 |
+
elif other.is_valid is None or self.is_valid is None:
|
| 297 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 298 |
+
else:
|
| 299 |
+
# denominator contains both signs, i.e. being divided by zero
|
| 300 |
+
# return the whole real line with is_valid = None
|
| 301 |
+
if 0 in other:
|
| 302 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 303 |
+
|
| 304 |
+
# denominator negative
|
| 305 |
+
this = self
|
| 306 |
+
if other.end < 0:
|
| 307 |
+
this = -this
|
| 308 |
+
other = -other
|
| 309 |
+
|
| 310 |
+
# denominator positive
|
| 311 |
+
inters = []
|
| 312 |
+
inters.append(this.start / other.start)
|
| 313 |
+
inters.append(this.end / other.start)
|
| 314 |
+
inters.append(this.start / other.end)
|
| 315 |
+
inters.append(this.end / other.end)
|
| 316 |
+
start = max(inters)
|
| 317 |
+
end = min(inters)
|
| 318 |
+
return interval(start, end)
|
| 319 |
+
else:
|
| 320 |
+
return NotImplemented
|
| 321 |
+
|
| 322 |
+
def __pow__(self, other):
|
| 323 |
+
# Implements only power to an integer.
|
| 324 |
+
from .lib_interval import exp, log
|
| 325 |
+
if not self.is_valid:
|
| 326 |
+
return self
|
| 327 |
+
if isinstance(other, interval):
|
| 328 |
+
return exp(other * log(self))
|
| 329 |
+
elif isinstance(other, (float, int)):
|
| 330 |
+
if other < 0:
|
| 331 |
+
return 1 / self.__pow__(abs(other))
|
| 332 |
+
else:
|
| 333 |
+
if int_valued(other):
|
| 334 |
+
return _pow_int(self, other)
|
| 335 |
+
else:
|
| 336 |
+
return _pow_float(self, other)
|
| 337 |
+
else:
|
| 338 |
+
return NotImplemented
|
| 339 |
+
|
| 340 |
+
def __rpow__(self, other):
|
| 341 |
+
if isinstance(other, (float, int)):
|
| 342 |
+
if not self.is_valid:
|
| 343 |
+
#Don't do anything
|
| 344 |
+
return self
|
| 345 |
+
elif other < 0:
|
| 346 |
+
if self.width > 0:
|
| 347 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 348 |
+
else:
|
| 349 |
+
power_rational = nsimplify(self.start)
|
| 350 |
+
num, denom = power_rational.as_numer_denom()
|
| 351 |
+
if denom % 2 == 0:
|
| 352 |
+
return interval(-float('inf'), float('inf'),
|
| 353 |
+
is_valid=False)
|
| 354 |
+
else:
|
| 355 |
+
start = -abs(other)**self.start
|
| 356 |
+
end = start
|
| 357 |
+
return interval(start, end)
|
| 358 |
+
else:
|
| 359 |
+
return interval(other**self.start, other**self.end)
|
| 360 |
+
elif isinstance(other, interval):
|
| 361 |
+
return other.__pow__(self)
|
| 362 |
+
else:
|
| 363 |
+
return NotImplemented
|
| 364 |
+
|
| 365 |
+
def __hash__(self):
|
| 366 |
+
return hash((self.is_valid, self.start, self.end))
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
def _pow_float(inter, power):
|
| 370 |
+
"""Evaluates an interval raised to a floating point."""
|
| 371 |
+
power_rational = nsimplify(power)
|
| 372 |
+
num, denom = power_rational.as_numer_denom()
|
| 373 |
+
if num % 2 == 0:
|
| 374 |
+
start = abs(inter.start)**power
|
| 375 |
+
end = abs(inter.end)**power
|
| 376 |
+
if start < 0:
|
| 377 |
+
ret = interval(0, max(start, end))
|
| 378 |
+
else:
|
| 379 |
+
ret = interval(start, end)
|
| 380 |
+
return ret
|
| 381 |
+
elif denom % 2 == 0:
|
| 382 |
+
if inter.end < 0:
|
| 383 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 384 |
+
elif inter.start < 0:
|
| 385 |
+
return interval(0, inter.end**power, is_valid=None)
|
| 386 |
+
else:
|
| 387 |
+
return interval(inter.start**power, inter.end**power)
|
| 388 |
+
else:
|
| 389 |
+
if inter.start < 0:
|
| 390 |
+
start = -abs(inter.start)**power
|
| 391 |
+
else:
|
| 392 |
+
start = inter.start**power
|
| 393 |
+
|
| 394 |
+
if inter.end < 0:
|
| 395 |
+
end = -abs(inter.end)**power
|
| 396 |
+
else:
|
| 397 |
+
end = inter.end**power
|
| 398 |
+
|
| 399 |
+
return interval(start, end, is_valid=inter.is_valid)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
def _pow_int(inter, power):
|
| 403 |
+
"""Evaluates an interval raised to an integer power"""
|
| 404 |
+
power = int(power)
|
| 405 |
+
if power & 1:
|
| 406 |
+
return interval(inter.start**power, inter.end**power)
|
| 407 |
+
else:
|
| 408 |
+
if inter.start < 0 and inter.end > 0:
|
| 409 |
+
start = 0
|
| 410 |
+
end = max(inter.start**power, inter.end**power)
|
| 411 |
+
return interval(start, end)
|
| 412 |
+
else:
|
| 413 |
+
return interval(inter.start**power, inter.end**power)
|
venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/interval_membership.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.logic import fuzzy_and, fuzzy_or, fuzzy_not, fuzzy_xor
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class intervalMembership:
|
| 5 |
+
"""Represents a boolean expression returned by the comparison of
|
| 6 |
+
the interval object.
|
| 7 |
+
|
| 8 |
+
Parameters
|
| 9 |
+
==========
|
| 10 |
+
|
| 11 |
+
(a, b) : (bool, bool)
|
| 12 |
+
The first value determines the comparison as follows:
|
| 13 |
+
- True: If the comparison is True throughout the intervals.
|
| 14 |
+
- False: If the comparison is False throughout the intervals.
|
| 15 |
+
- None: If the comparison is True for some part of the intervals.
|
| 16 |
+
|
| 17 |
+
The second value is determined as follows:
|
| 18 |
+
- True: If both the intervals in comparison are valid.
|
| 19 |
+
- False: If at least one of the intervals is False, else
|
| 20 |
+
- None
|
| 21 |
+
"""
|
| 22 |
+
def __init__(self, a, b):
|
| 23 |
+
self._wrapped = (a, b)
|
| 24 |
+
|
| 25 |
+
def __getitem__(self, i):
|
| 26 |
+
try:
|
| 27 |
+
return self._wrapped[i]
|
| 28 |
+
except IndexError:
|
| 29 |
+
raise IndexError(
|
| 30 |
+
"{} must be a valid indexing for the 2-tuple."
|
| 31 |
+
.format(i))
|
| 32 |
+
|
| 33 |
+
def __len__(self):
|
| 34 |
+
return 2
|
| 35 |
+
|
| 36 |
+
def __iter__(self):
|
| 37 |
+
return iter(self._wrapped)
|
| 38 |
+
|
| 39 |
+
def __str__(self):
|
| 40 |
+
return "intervalMembership({}, {})".format(*self)
|
| 41 |
+
__repr__ = __str__
|
| 42 |
+
|
| 43 |
+
def __and__(self, other):
|
| 44 |
+
if not isinstance(other, intervalMembership):
|
| 45 |
+
raise ValueError(
|
| 46 |
+
"The comparison is not supported for {}.".format(other))
|
| 47 |
+
|
| 48 |
+
a1, b1 = self
|
| 49 |
+
a2, b2 = other
|
| 50 |
+
return intervalMembership(fuzzy_and([a1, a2]), fuzzy_and([b1, b2]))
|
| 51 |
+
|
| 52 |
+
def __or__(self, other):
|
| 53 |
+
if not isinstance(other, intervalMembership):
|
| 54 |
+
raise ValueError(
|
| 55 |
+
"The comparison is not supported for {}.".format(other))
|
| 56 |
+
|
| 57 |
+
a1, b1 = self
|
| 58 |
+
a2, b2 = other
|
| 59 |
+
return intervalMembership(fuzzy_or([a1, a2]), fuzzy_and([b1, b2]))
|
| 60 |
+
|
| 61 |
+
def __invert__(self):
|
| 62 |
+
a, b = self
|
| 63 |
+
return intervalMembership(fuzzy_not(a), b)
|
| 64 |
+
|
| 65 |
+
def __xor__(self, other):
|
| 66 |
+
if not isinstance(other, intervalMembership):
|
| 67 |
+
raise ValueError(
|
| 68 |
+
"The comparison is not supported for {}.".format(other))
|
| 69 |
+
|
| 70 |
+
a1, b1 = self
|
| 71 |
+
a2, b2 = other
|
| 72 |
+
return intervalMembership(fuzzy_xor([a1, a2]), fuzzy_and([b1, b2]))
|
| 73 |
+
|
| 74 |
+
def __eq__(self, other):
|
| 75 |
+
return self._wrapped == other
|
| 76 |
+
|
| 77 |
+
def __ne__(self, other):
|
| 78 |
+
return self._wrapped != other
|
venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/lib_interval.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" The module contains implemented functions for interval arithmetic."""
|
| 2 |
+
from functools import reduce
|
| 3 |
+
|
| 4 |
+
from sympy.plotting.intervalmath import interval
|
| 5 |
+
from sympy.external import import_module
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def Abs(x):
|
| 9 |
+
if isinstance(x, (int, float)):
|
| 10 |
+
return interval(abs(x))
|
| 11 |
+
elif isinstance(x, interval):
|
| 12 |
+
if x.start < 0 and x.end > 0:
|
| 13 |
+
return interval(0, max(abs(x.start), abs(x.end)), is_valid=x.is_valid)
|
| 14 |
+
else:
|
| 15 |
+
return interval(abs(x.start), abs(x.end))
|
| 16 |
+
else:
|
| 17 |
+
raise NotImplementedError
|
| 18 |
+
|
| 19 |
+
#Monotonic
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def exp(x):
|
| 23 |
+
"""evaluates the exponential of an interval"""
|
| 24 |
+
np = import_module('numpy')
|
| 25 |
+
if isinstance(x, (int, float)):
|
| 26 |
+
return interval(np.exp(x), np.exp(x))
|
| 27 |
+
elif isinstance(x, interval):
|
| 28 |
+
return interval(np.exp(x.start), np.exp(x.end), is_valid=x.is_valid)
|
| 29 |
+
else:
|
| 30 |
+
raise NotImplementedError
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
#Monotonic
|
| 34 |
+
def log(x):
|
| 35 |
+
"""evaluates the natural logarithm of an interval"""
|
| 36 |
+
np = import_module('numpy')
|
| 37 |
+
if isinstance(x, (int, float)):
|
| 38 |
+
if x <= 0:
|
| 39 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 40 |
+
else:
|
| 41 |
+
return interval(np.log(x))
|
| 42 |
+
elif isinstance(x, interval):
|
| 43 |
+
if not x.is_valid:
|
| 44 |
+
return interval(-np.inf, np.inf, is_valid=x.is_valid)
|
| 45 |
+
elif x.end <= 0:
|
| 46 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 47 |
+
elif x.start <= 0:
|
| 48 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 49 |
+
|
| 50 |
+
return interval(np.log(x.start), np.log(x.end))
|
| 51 |
+
else:
|
| 52 |
+
raise NotImplementedError
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
#Monotonic
|
| 56 |
+
def log10(x):
|
| 57 |
+
"""evaluates the logarithm to the base 10 of an interval"""
|
| 58 |
+
np = import_module('numpy')
|
| 59 |
+
if isinstance(x, (int, float)):
|
| 60 |
+
if x <= 0:
|
| 61 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 62 |
+
else:
|
| 63 |
+
return interval(np.log10(x))
|
| 64 |
+
elif isinstance(x, interval):
|
| 65 |
+
if not x.is_valid:
|
| 66 |
+
return interval(-np.inf, np.inf, is_valid=x.is_valid)
|
| 67 |
+
elif x.end <= 0:
|
| 68 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 69 |
+
elif x.start <= 0:
|
| 70 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 71 |
+
return interval(np.log10(x.start), np.log10(x.end))
|
| 72 |
+
else:
|
| 73 |
+
raise NotImplementedError
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
#Monotonic
|
| 77 |
+
def atan(x):
|
| 78 |
+
"""evaluates the tan inverse of an interval"""
|
| 79 |
+
np = import_module('numpy')
|
| 80 |
+
if isinstance(x, (int, float)):
|
| 81 |
+
return interval(np.arctan(x))
|
| 82 |
+
elif isinstance(x, interval):
|
| 83 |
+
start = np.arctan(x.start)
|
| 84 |
+
end = np.arctan(x.end)
|
| 85 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 86 |
+
else:
|
| 87 |
+
raise NotImplementedError
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
#periodic
|
| 91 |
+
def sin(x):
|
| 92 |
+
"""evaluates the sine of an interval"""
|
| 93 |
+
np = import_module('numpy')
|
| 94 |
+
if isinstance(x, (int, float)):
|
| 95 |
+
return interval(np.sin(x))
|
| 96 |
+
elif isinstance(x, interval):
|
| 97 |
+
if not x.is_valid:
|
| 98 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 99 |
+
na, __ = divmod(x.start, np.pi / 2.0)
|
| 100 |
+
nb, __ = divmod(x.end, np.pi / 2.0)
|
| 101 |
+
start = min(np.sin(x.start), np.sin(x.end))
|
| 102 |
+
end = max(np.sin(x.start), np.sin(x.end))
|
| 103 |
+
if nb - na > 4:
|
| 104 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 105 |
+
elif na == nb:
|
| 106 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 107 |
+
else:
|
| 108 |
+
if (na - 1) // 4 != (nb - 1) // 4:
|
| 109 |
+
#sin has max
|
| 110 |
+
end = 1
|
| 111 |
+
if (na - 3) // 4 != (nb - 3) // 4:
|
| 112 |
+
#sin has min
|
| 113 |
+
start = -1
|
| 114 |
+
return interval(start, end)
|
| 115 |
+
else:
|
| 116 |
+
raise NotImplementedError
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
#periodic
|
| 120 |
+
def cos(x):
|
| 121 |
+
"""Evaluates the cos of an interval"""
|
| 122 |
+
np = import_module('numpy')
|
| 123 |
+
if isinstance(x, (int, float)):
|
| 124 |
+
return interval(np.sin(x))
|
| 125 |
+
elif isinstance(x, interval):
|
| 126 |
+
if not (np.isfinite(x.start) and np.isfinite(x.end)):
|
| 127 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 128 |
+
na, __ = divmod(x.start, np.pi / 2.0)
|
| 129 |
+
nb, __ = divmod(x.end, np.pi / 2.0)
|
| 130 |
+
start = min(np.cos(x.start), np.cos(x.end))
|
| 131 |
+
end = max(np.cos(x.start), np.cos(x.end))
|
| 132 |
+
if nb - na > 4:
|
| 133 |
+
#differ more than 2*pi
|
| 134 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 135 |
+
elif na == nb:
|
| 136 |
+
#in the same quadarant
|
| 137 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 138 |
+
else:
|
| 139 |
+
if (na) // 4 != (nb) // 4:
|
| 140 |
+
#cos has max
|
| 141 |
+
end = 1
|
| 142 |
+
if (na - 2) // 4 != (nb - 2) // 4:
|
| 143 |
+
#cos has min
|
| 144 |
+
start = -1
|
| 145 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 146 |
+
else:
|
| 147 |
+
raise NotImplementedError
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def tan(x):
|
| 151 |
+
"""Evaluates the tan of an interval"""
|
| 152 |
+
return sin(x) / cos(x)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
#Monotonic
|
| 156 |
+
def sqrt(x):
|
| 157 |
+
"""Evaluates the square root of an interval"""
|
| 158 |
+
np = import_module('numpy')
|
| 159 |
+
if isinstance(x, (int, float)):
|
| 160 |
+
if x > 0:
|
| 161 |
+
return interval(np.sqrt(x))
|
| 162 |
+
else:
|
| 163 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 164 |
+
elif isinstance(x, interval):
|
| 165 |
+
#Outside the domain
|
| 166 |
+
if x.end < 0:
|
| 167 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 168 |
+
#Partially outside the domain
|
| 169 |
+
elif x.start < 0:
|
| 170 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 171 |
+
else:
|
| 172 |
+
return interval(np.sqrt(x.start), np.sqrt(x.end),
|
| 173 |
+
is_valid=x.is_valid)
|
| 174 |
+
else:
|
| 175 |
+
raise NotImplementedError
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def imin(*args):
|
| 179 |
+
"""Evaluates the minimum of a list of intervals"""
|
| 180 |
+
np = import_module('numpy')
|
| 181 |
+
if not all(isinstance(arg, (int, float, interval)) for arg in args):
|
| 182 |
+
return NotImplementedError
|
| 183 |
+
else:
|
| 184 |
+
new_args = [a for a in args if isinstance(a, (int, float))
|
| 185 |
+
or a.is_valid]
|
| 186 |
+
if len(new_args) == 0:
|
| 187 |
+
if all(a.is_valid is False for a in args):
|
| 188 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 189 |
+
else:
|
| 190 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 191 |
+
start_array = [a if isinstance(a, (int, float)) else a.start
|
| 192 |
+
for a in new_args]
|
| 193 |
+
|
| 194 |
+
end_array = [a if isinstance(a, (int, float)) else a.end
|
| 195 |
+
for a in new_args]
|
| 196 |
+
return interval(min(start_array), min(end_array))
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def imax(*args):
|
| 200 |
+
"""Evaluates the maximum of a list of intervals"""
|
| 201 |
+
np = import_module('numpy')
|
| 202 |
+
if not all(isinstance(arg, (int, float, interval)) for arg in args):
|
| 203 |
+
return NotImplementedError
|
| 204 |
+
else:
|
| 205 |
+
new_args = [a for a in args if isinstance(a, (int, float))
|
| 206 |
+
or a.is_valid]
|
| 207 |
+
if len(new_args) == 0:
|
| 208 |
+
if all(a.is_valid is False for a in args):
|
| 209 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 210 |
+
else:
|
| 211 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 212 |
+
start_array = [a if isinstance(a, (int, float)) else a.start
|
| 213 |
+
for a in new_args]
|
| 214 |
+
|
| 215 |
+
end_array = [a if isinstance(a, (int, float)) else a.end
|
| 216 |
+
for a in new_args]
|
| 217 |
+
|
| 218 |
+
return interval(max(start_array), max(end_array))
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
#Monotonic
|
| 222 |
+
def sinh(x):
|
| 223 |
+
"""Evaluates the hyperbolic sine of an interval"""
|
| 224 |
+
np = import_module('numpy')
|
| 225 |
+
if isinstance(x, (int, float)):
|
| 226 |
+
return interval(np.sinh(x), np.sinh(x))
|
| 227 |
+
elif isinstance(x, interval):
|
| 228 |
+
return interval(np.sinh(x.start), np.sinh(x.end), is_valid=x.is_valid)
|
| 229 |
+
else:
|
| 230 |
+
raise NotImplementedError
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def cosh(x):
|
| 234 |
+
"""Evaluates the hyperbolic cos of an interval"""
|
| 235 |
+
np = import_module('numpy')
|
| 236 |
+
if isinstance(x, (int, float)):
|
| 237 |
+
return interval(np.cosh(x), np.cosh(x))
|
| 238 |
+
elif isinstance(x, interval):
|
| 239 |
+
#both signs
|
| 240 |
+
if x.start < 0 and x.end > 0:
|
| 241 |
+
end = max(np.cosh(x.start), np.cosh(x.end))
|
| 242 |
+
return interval(1, end, is_valid=x.is_valid)
|
| 243 |
+
else:
|
| 244 |
+
#Monotonic
|
| 245 |
+
start = np.cosh(x.start)
|
| 246 |
+
end = np.cosh(x.end)
|
| 247 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 248 |
+
else:
|
| 249 |
+
raise NotImplementedError
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
#Monotonic
|
| 253 |
+
def tanh(x):
|
| 254 |
+
"""Evaluates the hyperbolic tan of an interval"""
|
| 255 |
+
np = import_module('numpy')
|
| 256 |
+
if isinstance(x, (int, float)):
|
| 257 |
+
return interval(np.tanh(x), np.tanh(x))
|
| 258 |
+
elif isinstance(x, interval):
|
| 259 |
+
return interval(np.tanh(x.start), np.tanh(x.end), is_valid=x.is_valid)
|
| 260 |
+
else:
|
| 261 |
+
raise NotImplementedError
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def asin(x):
|
| 265 |
+
"""Evaluates the inverse sine of an interval"""
|
| 266 |
+
np = import_module('numpy')
|
| 267 |
+
if isinstance(x, (int, float)):
|
| 268 |
+
#Outside the domain
|
| 269 |
+
if abs(x) > 1:
|
| 270 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 271 |
+
else:
|
| 272 |
+
return interval(np.arcsin(x), np.arcsin(x))
|
| 273 |
+
elif isinstance(x, interval):
|
| 274 |
+
#Outside the domain
|
| 275 |
+
if x.is_valid is False or x.start > 1 or x.end < -1:
|
| 276 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 277 |
+
#Partially outside the domain
|
| 278 |
+
elif x.start < -1 or x.end > 1:
|
| 279 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 280 |
+
else:
|
| 281 |
+
start = np.arcsin(x.start)
|
| 282 |
+
end = np.arcsin(x.end)
|
| 283 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def acos(x):
|
| 287 |
+
"""Evaluates the inverse cos of an interval"""
|
| 288 |
+
np = import_module('numpy')
|
| 289 |
+
if isinstance(x, (int, float)):
|
| 290 |
+
if abs(x) > 1:
|
| 291 |
+
#Outside the domain
|
| 292 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 293 |
+
else:
|
| 294 |
+
return interval(np.arccos(x), np.arccos(x))
|
| 295 |
+
elif isinstance(x, interval):
|
| 296 |
+
#Outside the domain
|
| 297 |
+
if x.is_valid is False or x.start > 1 or x.end < -1:
|
| 298 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 299 |
+
#Partially outside the domain
|
| 300 |
+
elif x.start < -1 or x.end > 1:
|
| 301 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 302 |
+
else:
|
| 303 |
+
start = np.arccos(x.start)
|
| 304 |
+
end = np.arccos(x.end)
|
| 305 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def ceil(x):
|
| 309 |
+
"""Evaluates the ceiling of an interval"""
|
| 310 |
+
np = import_module('numpy')
|
| 311 |
+
if isinstance(x, (int, float)):
|
| 312 |
+
return interval(np.ceil(x))
|
| 313 |
+
elif isinstance(x, interval):
|
| 314 |
+
if x.is_valid is False:
|
| 315 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 316 |
+
else:
|
| 317 |
+
start = np.ceil(x.start)
|
| 318 |
+
end = np.ceil(x.end)
|
| 319 |
+
#Continuous over the interval
|
| 320 |
+
if start == end:
|
| 321 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 322 |
+
else:
|
| 323 |
+
#Not continuous over the interval
|
| 324 |
+
return interval(start, end, is_valid=None)
|
| 325 |
+
else:
|
| 326 |
+
return NotImplementedError
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def floor(x):
|
| 330 |
+
"""Evaluates the floor of an interval"""
|
| 331 |
+
np = import_module('numpy')
|
| 332 |
+
if isinstance(x, (int, float)):
|
| 333 |
+
return interval(np.floor(x))
|
| 334 |
+
elif isinstance(x, interval):
|
| 335 |
+
if x.is_valid is False:
|
| 336 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 337 |
+
else:
|
| 338 |
+
start = np.floor(x.start)
|
| 339 |
+
end = np.floor(x.end)
|
| 340 |
+
#continuous over the argument
|
| 341 |
+
if start == end:
|
| 342 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 343 |
+
else:
|
| 344 |
+
#not continuous over the interval
|
| 345 |
+
return interval(start, end, is_valid=None)
|
| 346 |
+
else:
|
| 347 |
+
return NotImplementedError
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def acosh(x):
|
| 351 |
+
"""Evaluates the inverse hyperbolic cosine of an interval"""
|
| 352 |
+
np = import_module('numpy')
|
| 353 |
+
if isinstance(x, (int, float)):
|
| 354 |
+
#Outside the domain
|
| 355 |
+
if x < 1:
|
| 356 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 357 |
+
else:
|
| 358 |
+
return interval(np.arccosh(x))
|
| 359 |
+
elif isinstance(x, interval):
|
| 360 |
+
#Outside the domain
|
| 361 |
+
if x.end < 1:
|
| 362 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 363 |
+
#Partly outside the domain
|
| 364 |
+
elif x.start < 1:
|
| 365 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 366 |
+
else:
|
| 367 |
+
start = np.arccosh(x.start)
|
| 368 |
+
end = np.arccosh(x.end)
|
| 369 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 370 |
+
else:
|
| 371 |
+
return NotImplementedError
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
#Monotonic
|
| 375 |
+
def asinh(x):
|
| 376 |
+
"""Evaluates the inverse hyperbolic sine of an interval"""
|
| 377 |
+
np = import_module('numpy')
|
| 378 |
+
if isinstance(x, (int, float)):
|
| 379 |
+
return interval(np.arcsinh(x))
|
| 380 |
+
elif isinstance(x, interval):
|
| 381 |
+
start = np.arcsinh(x.start)
|
| 382 |
+
end = np.arcsinh(x.end)
|
| 383 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 384 |
+
else:
|
| 385 |
+
return NotImplementedError
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def atanh(x):
|
| 389 |
+
"""Evaluates the inverse hyperbolic tangent of an interval"""
|
| 390 |
+
np = import_module('numpy')
|
| 391 |
+
if isinstance(x, (int, float)):
|
| 392 |
+
#Outside the domain
|
| 393 |
+
if abs(x) >= 1:
|
| 394 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 395 |
+
else:
|
| 396 |
+
return interval(np.arctanh(x))
|
| 397 |
+
elif isinstance(x, interval):
|
| 398 |
+
#outside the domain
|
| 399 |
+
if x.is_valid is False or x.start >= 1 or x.end <= -1:
|
| 400 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 401 |
+
#partly outside the domain
|
| 402 |
+
elif x.start <= -1 or x.end >= 1:
|
| 403 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 404 |
+
else:
|
| 405 |
+
start = np.arctanh(x.start)
|
| 406 |
+
end = np.arctanh(x.end)
|
| 407 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 408 |
+
else:
|
| 409 |
+
return NotImplementedError
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
#Three valued logic for interval plotting.
|
| 413 |
+
|
| 414 |
+
def And(*args):
|
| 415 |
+
"""Defines the three valued ``And`` behaviour for a 2-tuple of
|
| 416 |
+
three valued logic values"""
|
| 417 |
+
def reduce_and(cmp_intervala, cmp_intervalb):
|
| 418 |
+
if cmp_intervala[0] is False or cmp_intervalb[0] is False:
|
| 419 |
+
first = False
|
| 420 |
+
elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
|
| 421 |
+
first = None
|
| 422 |
+
else:
|
| 423 |
+
first = True
|
| 424 |
+
if cmp_intervala[1] is False or cmp_intervalb[1] is False:
|
| 425 |
+
second = False
|
| 426 |
+
elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
|
| 427 |
+
second = None
|
| 428 |
+
else:
|
| 429 |
+
second = True
|
| 430 |
+
return (first, second)
|
| 431 |
+
return reduce(reduce_and, args)
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def Or(*args):
|
| 435 |
+
"""Defines the three valued ``Or`` behaviour for a 2-tuple of
|
| 436 |
+
three valued logic values"""
|
| 437 |
+
def reduce_or(cmp_intervala, cmp_intervalb):
|
| 438 |
+
if cmp_intervala[0] is True or cmp_intervalb[0] is True:
|
| 439 |
+
first = True
|
| 440 |
+
elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
|
| 441 |
+
first = None
|
| 442 |
+
else:
|
| 443 |
+
first = False
|
| 444 |
+
|
| 445 |
+
if cmp_intervala[1] is True or cmp_intervalb[1] is True:
|
| 446 |
+
second = True
|
| 447 |
+
elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
|
| 448 |
+
second = None
|
| 449 |
+
else:
|
| 450 |
+
second = False
|
| 451 |
+
return (first, second)
|
| 452 |
+
return reduce(reduce_or, args)
|
venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/__init__.py
ADDED
|
File without changes
|
venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/test_interval_functions.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.external import import_module
|
| 2 |
+
from sympy.plotting.intervalmath import (
|
| 3 |
+
Abs, acos, acosh, And, asin, asinh, atan, atanh, ceil, cos, cosh,
|
| 4 |
+
exp, floor, imax, imin, interval, log, log10, Or, sin, sinh, sqrt,
|
| 5 |
+
tan, tanh,
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
np = import_module('numpy')
|
| 9 |
+
if not np:
|
| 10 |
+
disabled = True
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
#requires Numpy. Hence included in interval_functions
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def test_interval_pow():
|
| 17 |
+
a = 2**interval(1, 2) == interval(2, 4)
|
| 18 |
+
assert a == (True, True)
|
| 19 |
+
a = interval(1, 2)**interval(1, 2) == interval(1, 4)
|
| 20 |
+
assert a == (True, True)
|
| 21 |
+
a = interval(-1, 1)**interval(0.5, 2)
|
| 22 |
+
assert a.is_valid is None
|
| 23 |
+
a = interval(-2, -1) ** interval(1, 2)
|
| 24 |
+
assert a.is_valid is False
|
| 25 |
+
a = interval(-2, -1) ** (1.0 / 2)
|
| 26 |
+
assert a.is_valid is False
|
| 27 |
+
a = interval(-1, 1)**(1.0 / 2)
|
| 28 |
+
assert a.is_valid is None
|
| 29 |
+
a = interval(-1, 1)**(1.0 / 3) == interval(-1, 1)
|
| 30 |
+
assert a == (True, True)
|
| 31 |
+
a = interval(-1, 1)**2 == interval(0, 1)
|
| 32 |
+
assert a == (True, True)
|
| 33 |
+
a = interval(-1, 1) ** (1.0 / 29) == interval(-1, 1)
|
| 34 |
+
assert a == (True, True)
|
| 35 |
+
a = -2**interval(1, 1) == interval(-2, -2)
|
| 36 |
+
assert a == (True, True)
|
| 37 |
+
|
| 38 |
+
a = interval(1, 2, is_valid=False)**2
|
| 39 |
+
assert a.is_valid is False
|
| 40 |
+
|
| 41 |
+
a = (-3)**interval(1, 2)
|
| 42 |
+
assert a.is_valid is False
|
| 43 |
+
a = (-4)**interval(0.5, 0.5)
|
| 44 |
+
assert a.is_valid is False
|
| 45 |
+
assert ((-3)**interval(1, 1) == interval(-3, -3)) == (True, True)
|
| 46 |
+
|
| 47 |
+
a = interval(8, 64)**(2.0 / 3)
|
| 48 |
+
assert abs(a.start - 4) < 1e-10 # eps
|
| 49 |
+
assert abs(a.end - 16) < 1e-10
|
| 50 |
+
a = interval(-8, 64)**(2.0 / 3)
|
| 51 |
+
assert abs(a.start - 4) < 1e-10 # eps
|
| 52 |
+
assert abs(a.end - 16) < 1e-10
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def test_exp():
|
| 56 |
+
a = exp(interval(-np.inf, 0))
|
| 57 |
+
assert a.start == np.exp(-np.inf)
|
| 58 |
+
assert a.end == np.exp(0)
|
| 59 |
+
a = exp(interval(1, 2))
|
| 60 |
+
assert a.start == np.exp(1)
|
| 61 |
+
assert a.end == np.exp(2)
|
| 62 |
+
a = exp(1)
|
| 63 |
+
assert a.start == np.exp(1)
|
| 64 |
+
assert a.end == np.exp(1)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def test_log():
|
| 68 |
+
a = log(interval(1, 2))
|
| 69 |
+
assert a.start == 0
|
| 70 |
+
assert a.end == np.log(2)
|
| 71 |
+
a = log(interval(-1, 1))
|
| 72 |
+
assert a.is_valid is None
|
| 73 |
+
a = log(interval(-3, -1))
|
| 74 |
+
assert a.is_valid is False
|
| 75 |
+
a = log(-3)
|
| 76 |
+
assert a.is_valid is False
|
| 77 |
+
a = log(2)
|
| 78 |
+
assert a.start == np.log(2)
|
| 79 |
+
assert a.end == np.log(2)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def test_log10():
|
| 83 |
+
a = log10(interval(1, 2))
|
| 84 |
+
assert a.start == 0
|
| 85 |
+
assert a.end == np.log10(2)
|
| 86 |
+
a = log10(interval(-1, 1))
|
| 87 |
+
assert a.is_valid is None
|
| 88 |
+
a = log10(interval(-3, -1))
|
| 89 |
+
assert a.is_valid is False
|
| 90 |
+
a = log10(-3)
|
| 91 |
+
assert a.is_valid is False
|
| 92 |
+
a = log10(2)
|
| 93 |
+
assert a.start == np.log10(2)
|
| 94 |
+
assert a.end == np.log10(2)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def test_atan():
|
| 98 |
+
a = atan(interval(0, 1))
|
| 99 |
+
assert a.start == np.arctan(0)
|
| 100 |
+
assert a.end == np.arctan(1)
|
| 101 |
+
a = atan(1)
|
| 102 |
+
assert a.start == np.arctan(1)
|
| 103 |
+
assert a.end == np.arctan(1)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def test_sin():
|
| 107 |
+
a = sin(interval(0, np.pi / 4))
|
| 108 |
+
assert a.start == np.sin(0)
|
| 109 |
+
assert a.end == np.sin(np.pi / 4)
|
| 110 |
+
|
| 111 |
+
a = sin(interval(-np.pi / 4, np.pi / 4))
|
| 112 |
+
assert a.start == np.sin(-np.pi / 4)
|
| 113 |
+
assert a.end == np.sin(np.pi / 4)
|
| 114 |
+
|
| 115 |
+
a = sin(interval(np.pi / 4, 3 * np.pi / 4))
|
| 116 |
+
assert a.start == np.sin(np.pi / 4)
|
| 117 |
+
assert a.end == 1
|
| 118 |
+
|
| 119 |
+
a = sin(interval(7 * np.pi / 6, 7 * np.pi / 4))
|
| 120 |
+
assert a.start == -1
|
| 121 |
+
assert a.end == np.sin(7 * np.pi / 6)
|
| 122 |
+
|
| 123 |
+
a = sin(interval(0, 3 * np.pi))
|
| 124 |
+
assert a.start == -1
|
| 125 |
+
assert a.end == 1
|
| 126 |
+
|
| 127 |
+
a = sin(interval(np.pi / 3, 7 * np.pi / 4))
|
| 128 |
+
assert a.start == -1
|
| 129 |
+
assert a.end == 1
|
| 130 |
+
|
| 131 |
+
a = sin(np.pi / 4)
|
| 132 |
+
assert a.start == np.sin(np.pi / 4)
|
| 133 |
+
assert a.end == np.sin(np.pi / 4)
|
| 134 |
+
|
| 135 |
+
a = sin(interval(1, 2, is_valid=False))
|
| 136 |
+
assert a.is_valid is False
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_cos():
|
| 140 |
+
a = cos(interval(0, np.pi / 4))
|
| 141 |
+
assert a.start == np.cos(np.pi / 4)
|
| 142 |
+
assert a.end == 1
|
| 143 |
+
|
| 144 |
+
a = cos(interval(-np.pi / 4, np.pi / 4))
|
| 145 |
+
assert a.start == np.cos(-np.pi / 4)
|
| 146 |
+
assert a.end == 1
|
| 147 |
+
|
| 148 |
+
a = cos(interval(np.pi / 4, 3 * np.pi / 4))
|
| 149 |
+
assert a.start == np.cos(3 * np.pi / 4)
|
| 150 |
+
assert a.end == np.cos(np.pi / 4)
|
| 151 |
+
|
| 152 |
+
a = cos(interval(3 * np.pi / 4, 5 * np.pi / 4))
|
| 153 |
+
assert a.start == -1
|
| 154 |
+
assert a.end == np.cos(3 * np.pi / 4)
|
| 155 |
+
|
| 156 |
+
a = cos(interval(0, 3 * np.pi))
|
| 157 |
+
assert a.start == -1
|
| 158 |
+
assert a.end == 1
|
| 159 |
+
|
| 160 |
+
a = cos(interval(- np.pi / 3, 5 * np.pi / 4))
|
| 161 |
+
assert a.start == -1
|
| 162 |
+
assert a.end == 1
|
| 163 |
+
|
| 164 |
+
a = cos(interval(1, 2, is_valid=False))
|
| 165 |
+
assert a.is_valid is False
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def test_tan():
|
| 169 |
+
a = tan(interval(0, np.pi / 4))
|
| 170 |
+
assert a.start == 0
|
| 171 |
+
# must match lib_interval definition of tan:
|
| 172 |
+
assert a.end == np.sin(np.pi / 4)/np.cos(np.pi / 4)
|
| 173 |
+
|
| 174 |
+
a = tan(interval(np.pi / 4, 3 * np.pi / 4))
|
| 175 |
+
#discontinuity
|
| 176 |
+
assert a.is_valid is None
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def test_sqrt():
|
| 180 |
+
a = sqrt(interval(1, 4))
|
| 181 |
+
assert a.start == 1
|
| 182 |
+
assert a.end == 2
|
| 183 |
+
|
| 184 |
+
a = sqrt(interval(0.01, 1))
|
| 185 |
+
assert a.start == np.sqrt(0.01)
|
| 186 |
+
assert a.end == 1
|
| 187 |
+
|
| 188 |
+
a = sqrt(interval(-1, 1))
|
| 189 |
+
assert a.is_valid is None
|
| 190 |
+
|
| 191 |
+
a = sqrt(interval(-3, -1))
|
| 192 |
+
assert a.is_valid is False
|
| 193 |
+
|
| 194 |
+
a = sqrt(4)
|
| 195 |
+
assert (a == interval(2, 2)) == (True, True)
|
| 196 |
+
|
| 197 |
+
a = sqrt(-3)
|
| 198 |
+
assert a.is_valid is False
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def test_imin():
|
| 202 |
+
a = imin(interval(1, 3), interval(2, 5), interval(-1, 3))
|
| 203 |
+
assert a.start == -1
|
| 204 |
+
assert a.end == 3
|
| 205 |
+
|
| 206 |
+
a = imin(-2, interval(1, 4))
|
| 207 |
+
assert a.start == -2
|
| 208 |
+
assert a.end == -2
|
| 209 |
+
|
| 210 |
+
a = imin(5, interval(3, 4), interval(-2, 2, is_valid=False))
|
| 211 |
+
assert a.start == 3
|
| 212 |
+
assert a.end == 4
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def test_imax():
|
| 216 |
+
a = imax(interval(-2, 2), interval(2, 7), interval(-3, 9))
|
| 217 |
+
assert a.start == 2
|
| 218 |
+
assert a.end == 9
|
| 219 |
+
|
| 220 |
+
a = imax(8, interval(1, 4))
|
| 221 |
+
assert a.start == 8
|
| 222 |
+
assert a.end == 8
|
| 223 |
+
|
| 224 |
+
a = imax(interval(1, 2), interval(3, 4), interval(-2, 2, is_valid=False))
|
| 225 |
+
assert a.start == 3
|
| 226 |
+
assert a.end == 4
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def test_sinh():
|
| 230 |
+
a = sinh(interval(-1, 1))
|
| 231 |
+
assert a.start == np.sinh(-1)
|
| 232 |
+
assert a.end == np.sinh(1)
|
| 233 |
+
|
| 234 |
+
a = sinh(1)
|
| 235 |
+
assert a.start == np.sinh(1)
|
| 236 |
+
assert a.end == np.sinh(1)
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
def test_cosh():
|
| 240 |
+
a = cosh(interval(1, 2))
|
| 241 |
+
assert a.start == np.cosh(1)
|
| 242 |
+
assert a.end == np.cosh(2)
|
| 243 |
+
a = cosh(interval(-2, -1))
|
| 244 |
+
assert a.start == np.cosh(-1)
|
| 245 |
+
assert a.end == np.cosh(-2)
|
| 246 |
+
|
| 247 |
+
a = cosh(interval(-2, 1))
|
| 248 |
+
assert a.start == 1
|
| 249 |
+
assert a.end == np.cosh(-2)
|
| 250 |
+
|
| 251 |
+
a = cosh(1)
|
| 252 |
+
assert a.start == np.cosh(1)
|
| 253 |
+
assert a.end == np.cosh(1)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def test_tanh():
|
| 257 |
+
a = tanh(interval(-3, 3))
|
| 258 |
+
assert a.start == np.tanh(-3)
|
| 259 |
+
assert a.end == np.tanh(3)
|
| 260 |
+
|
| 261 |
+
a = tanh(3)
|
| 262 |
+
assert a.start == np.tanh(3)
|
| 263 |
+
assert a.end == np.tanh(3)
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
def test_asin():
|
| 267 |
+
a = asin(interval(-0.5, 0.5))
|
| 268 |
+
assert a.start == np.arcsin(-0.5)
|
| 269 |
+
assert a.end == np.arcsin(0.5)
|
| 270 |
+
|
| 271 |
+
a = asin(interval(-1.5, 1.5))
|
| 272 |
+
assert a.is_valid is None
|
| 273 |
+
a = asin(interval(-2, -1.5))
|
| 274 |
+
assert a.is_valid is False
|
| 275 |
+
|
| 276 |
+
a = asin(interval(0, 2))
|
| 277 |
+
assert a.is_valid is None
|
| 278 |
+
|
| 279 |
+
a = asin(interval(2, 5))
|
| 280 |
+
assert a.is_valid is False
|
| 281 |
+
|
| 282 |
+
a = asin(0.5)
|
| 283 |
+
assert a.start == np.arcsin(0.5)
|
| 284 |
+
assert a.end == np.arcsin(0.5)
|
| 285 |
+
|
| 286 |
+
a = asin(1.5)
|
| 287 |
+
assert a.is_valid is False
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def test_acos():
|
| 291 |
+
a = acos(interval(-0.5, 0.5))
|
| 292 |
+
assert a.start == np.arccos(0.5)
|
| 293 |
+
assert a.end == np.arccos(-0.5)
|
| 294 |
+
|
| 295 |
+
a = acos(interval(-1.5, 1.5))
|
| 296 |
+
assert a.is_valid is None
|
| 297 |
+
a = acos(interval(-2, -1.5))
|
| 298 |
+
assert a.is_valid is False
|
| 299 |
+
|
| 300 |
+
a = acos(interval(0, 2))
|
| 301 |
+
assert a.is_valid is None
|
| 302 |
+
|
| 303 |
+
a = acos(interval(2, 5))
|
| 304 |
+
assert a.is_valid is False
|
| 305 |
+
|
| 306 |
+
a = acos(0.5)
|
| 307 |
+
assert a.start == np.arccos(0.5)
|
| 308 |
+
assert a.end == np.arccos(0.5)
|
| 309 |
+
|
| 310 |
+
a = acos(1.5)
|
| 311 |
+
assert a.is_valid is False
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def test_ceil():
|
| 315 |
+
a = ceil(interval(0.2, 0.5))
|
| 316 |
+
assert a.start == 1
|
| 317 |
+
assert a.end == 1
|
| 318 |
+
|
| 319 |
+
a = ceil(interval(0.5, 1.5))
|
| 320 |
+
assert a.start == 1
|
| 321 |
+
assert a.end == 2
|
| 322 |
+
assert a.is_valid is None
|
| 323 |
+
|
| 324 |
+
a = ceil(interval(-5, 5))
|
| 325 |
+
assert a.is_valid is None
|
| 326 |
+
|
| 327 |
+
a = ceil(5.4)
|
| 328 |
+
assert a.start == 6
|
| 329 |
+
assert a.end == 6
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
def test_floor():
|
| 333 |
+
a = floor(interval(0.2, 0.5))
|
| 334 |
+
assert a.start == 0
|
| 335 |
+
assert a.end == 0
|
| 336 |
+
|
| 337 |
+
a = floor(interval(0.5, 1.5))
|
| 338 |
+
assert a.start == 0
|
| 339 |
+
assert a.end == 1
|
| 340 |
+
assert a.is_valid is None
|
| 341 |
+
|
| 342 |
+
a = floor(interval(-5, 5))
|
| 343 |
+
assert a.is_valid is None
|
| 344 |
+
|
| 345 |
+
a = floor(5.4)
|
| 346 |
+
assert a.start == 5
|
| 347 |
+
assert a.end == 5
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def test_asinh():
|
| 351 |
+
a = asinh(interval(1, 2))
|
| 352 |
+
assert a.start == np.arcsinh(1)
|
| 353 |
+
assert a.end == np.arcsinh(2)
|
| 354 |
+
|
| 355 |
+
a = asinh(0.5)
|
| 356 |
+
assert a.start == np.arcsinh(0.5)
|
| 357 |
+
assert a.end == np.arcsinh(0.5)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def test_acosh():
|
| 361 |
+
a = acosh(interval(3, 5))
|
| 362 |
+
assert a.start == np.arccosh(3)
|
| 363 |
+
assert a.end == np.arccosh(5)
|
| 364 |
+
|
| 365 |
+
a = acosh(interval(0, 3))
|
| 366 |
+
assert a.is_valid is None
|
| 367 |
+
a = acosh(interval(-3, 0.5))
|
| 368 |
+
assert a.is_valid is False
|
| 369 |
+
|
| 370 |
+
a = acosh(0.5)
|
| 371 |
+
assert a.is_valid is False
|
| 372 |
+
|
| 373 |
+
a = acosh(2)
|
| 374 |
+
assert a.start == np.arccosh(2)
|
| 375 |
+
assert a.end == np.arccosh(2)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def test_atanh():
|
| 379 |
+
a = atanh(interval(-0.5, 0.5))
|
| 380 |
+
assert a.start == np.arctanh(-0.5)
|
| 381 |
+
assert a.end == np.arctanh(0.5)
|
| 382 |
+
|
| 383 |
+
a = atanh(interval(0, 3))
|
| 384 |
+
assert a.is_valid is None
|
| 385 |
+
|
| 386 |
+
a = atanh(interval(-3, -2))
|
| 387 |
+
assert a.is_valid is False
|
| 388 |
+
|
| 389 |
+
a = atanh(0.5)
|
| 390 |
+
assert a.start == np.arctanh(0.5)
|
| 391 |
+
assert a.end == np.arctanh(0.5)
|
| 392 |
+
|
| 393 |
+
a = atanh(1.5)
|
| 394 |
+
assert a.is_valid is False
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def test_Abs():
|
| 398 |
+
assert (Abs(interval(-0.5, 0.5)) == interval(0, 0.5)) == (True, True)
|
| 399 |
+
assert (Abs(interval(-3, -2)) == interval(2, 3)) == (True, True)
|
| 400 |
+
assert (Abs(-3) == interval(3, 3)) == (True, True)
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def test_And():
|
| 404 |
+
args = [(True, True), (True, False), (True, None)]
|
| 405 |
+
assert And(*args) == (True, False)
|
| 406 |
+
|
| 407 |
+
args = [(False, True), (None, None), (True, True)]
|
| 408 |
+
assert And(*args) == (False, None)
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def test_Or():
|
| 412 |
+
args = [(True, True), (True, False), (False, None)]
|
| 413 |
+
assert Or(*args) == (True, True)
|
| 414 |
+
args = [(None, None), (False, None), (False, False)]
|
| 415 |
+
assert Or(*args) == (None, None)
|
venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/test_interval_membership.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.symbol import Symbol
|
| 2 |
+
from sympy.plotting.intervalmath import interval
|
| 3 |
+
from sympy.plotting.intervalmath.interval_membership import intervalMembership
|
| 4 |
+
from sympy.plotting.experimental_lambdify import experimental_lambdify
|
| 5 |
+
from sympy.testing.pytest import raises
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_creation():
|
| 9 |
+
assert intervalMembership(True, True)
|
| 10 |
+
raises(TypeError, lambda: intervalMembership(True))
|
| 11 |
+
raises(TypeError, lambda: intervalMembership(True, True, True))
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_getitem():
|
| 15 |
+
a = intervalMembership(True, False)
|
| 16 |
+
assert a[0] is True
|
| 17 |
+
assert a[1] is False
|
| 18 |
+
raises(IndexError, lambda: a[2])
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_str():
|
| 22 |
+
a = intervalMembership(True, False)
|
| 23 |
+
assert str(a) == 'intervalMembership(True, False)'
|
| 24 |
+
assert repr(a) == 'intervalMembership(True, False)'
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def test_equivalence():
|
| 28 |
+
a = intervalMembership(True, True)
|
| 29 |
+
b = intervalMembership(True, False)
|
| 30 |
+
assert (a == b) is False
|
| 31 |
+
assert (a != b) is True
|
| 32 |
+
|
| 33 |
+
a = intervalMembership(True, False)
|
| 34 |
+
b = intervalMembership(True, False)
|
| 35 |
+
assert (a == b) is True
|
| 36 |
+
assert (a != b) is False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_not():
|
| 40 |
+
x = Symbol('x')
|
| 41 |
+
|
| 42 |
+
r1 = x > -1
|
| 43 |
+
r2 = x <= -1
|
| 44 |
+
|
| 45 |
+
i = interval
|
| 46 |
+
|
| 47 |
+
f1 = experimental_lambdify((x,), r1)
|
| 48 |
+
f2 = experimental_lambdify((x,), r2)
|
| 49 |
+
|
| 50 |
+
tt = i(-0.1, 0.1, is_valid=True)
|
| 51 |
+
tn = i(-0.1, 0.1, is_valid=None)
|
| 52 |
+
tf = i(-0.1, 0.1, is_valid=False)
|
| 53 |
+
|
| 54 |
+
assert f1(tt) == ~f2(tt)
|
| 55 |
+
assert f1(tn) == ~f2(tn)
|
| 56 |
+
assert f1(tf) == ~f2(tf)
|
| 57 |
+
|
| 58 |
+
nt = i(0.9, 1.1, is_valid=True)
|
| 59 |
+
nn = i(0.9, 1.1, is_valid=None)
|
| 60 |
+
nf = i(0.9, 1.1, is_valid=False)
|
| 61 |
+
|
| 62 |
+
assert f1(nt) == ~f2(nt)
|
| 63 |
+
assert f1(nn) == ~f2(nn)
|
| 64 |
+
assert f1(nf) == ~f2(nf)
|
| 65 |
+
|
| 66 |
+
ft = i(1.9, 2.1, is_valid=True)
|
| 67 |
+
fn = i(1.9, 2.1, is_valid=None)
|
| 68 |
+
ff = i(1.9, 2.1, is_valid=False)
|
| 69 |
+
|
| 70 |
+
assert f1(ft) == ~f2(ft)
|
| 71 |
+
assert f1(fn) == ~f2(fn)
|
| 72 |
+
assert f1(ff) == ~f2(ff)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_boolean():
|
| 76 |
+
# There can be 9*9 test cases in full mapping of the cartesian product.
|
| 77 |
+
# But we only consider 3*3 cases for simplicity.
|
| 78 |
+
s = [
|
| 79 |
+
intervalMembership(False, False),
|
| 80 |
+
intervalMembership(None, None),
|
| 81 |
+
intervalMembership(True, True)
|
| 82 |
+
]
|
| 83 |
+
|
| 84 |
+
# Reduced tests for 'And'
|
| 85 |
+
a1 = [
|
| 86 |
+
intervalMembership(False, False),
|
| 87 |
+
intervalMembership(False, False),
|
| 88 |
+
intervalMembership(False, False),
|
| 89 |
+
intervalMembership(False, False),
|
| 90 |
+
intervalMembership(None, None),
|
| 91 |
+
intervalMembership(None, None),
|
| 92 |
+
intervalMembership(False, False),
|
| 93 |
+
intervalMembership(None, None),
|
| 94 |
+
intervalMembership(True, True)
|
| 95 |
+
]
|
| 96 |
+
a1_iter = iter(a1)
|
| 97 |
+
for i in range(len(s)):
|
| 98 |
+
for j in range(len(s)):
|
| 99 |
+
assert s[i] & s[j] == next(a1_iter)
|
| 100 |
+
|
| 101 |
+
# Reduced tests for 'Or'
|
| 102 |
+
a1 = [
|
| 103 |
+
intervalMembership(False, False),
|
| 104 |
+
intervalMembership(None, False),
|
| 105 |
+
intervalMembership(True, False),
|
| 106 |
+
intervalMembership(None, False),
|
| 107 |
+
intervalMembership(None, None),
|
| 108 |
+
intervalMembership(True, None),
|
| 109 |
+
intervalMembership(True, False),
|
| 110 |
+
intervalMembership(True, None),
|
| 111 |
+
intervalMembership(True, True)
|
| 112 |
+
]
|
| 113 |
+
a1_iter = iter(a1)
|
| 114 |
+
for i in range(len(s)):
|
| 115 |
+
for j in range(len(s)):
|
| 116 |
+
assert s[i] | s[j] == next(a1_iter)
|
| 117 |
+
|
| 118 |
+
# Reduced tests for 'Xor'
|
| 119 |
+
a1 = [
|
| 120 |
+
intervalMembership(False, False),
|
| 121 |
+
intervalMembership(None, False),
|
| 122 |
+
intervalMembership(True, False),
|
| 123 |
+
intervalMembership(None, False),
|
| 124 |
+
intervalMembership(None, None),
|
| 125 |
+
intervalMembership(None, None),
|
| 126 |
+
intervalMembership(True, False),
|
| 127 |
+
intervalMembership(None, None),
|
| 128 |
+
intervalMembership(False, True)
|
| 129 |
+
]
|
| 130 |
+
a1_iter = iter(a1)
|
| 131 |
+
for i in range(len(s)):
|
| 132 |
+
for j in range(len(s)):
|
| 133 |
+
assert s[i] ^ s[j] == next(a1_iter)
|
| 134 |
+
|
| 135 |
+
# Reduced tests for 'Not'
|
| 136 |
+
a1 = [
|
| 137 |
+
intervalMembership(True, False),
|
| 138 |
+
intervalMembership(None, None),
|
| 139 |
+
intervalMembership(False, True)
|
| 140 |
+
]
|
| 141 |
+
a1_iter = iter(a1)
|
| 142 |
+
for i in range(len(s)):
|
| 143 |
+
assert ~s[i] == next(a1_iter)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def test_boolean_errors():
|
| 147 |
+
a = intervalMembership(True, True)
|
| 148 |
+
raises(ValueError, lambda: a & 1)
|
| 149 |
+
raises(ValueError, lambda: a | 1)
|
| 150 |
+
raises(ValueError, lambda: a ^ 1)
|