nanaj commited on
Commit
da7af1a
·
verified ·
1 Parent(s): bc2e44d

Upload folder using huggingface_hub (part 20)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/INSTALLER +1 -0
  3. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/METADATA +319 -0
  4. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/RECORD +0 -0
  5. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/WHEEL +5 -0
  6. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/entry_points.txt +2 -0
  7. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/licenses/AUTHORS +1379 -0
  8. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/licenses/LICENSE +153 -0
  9. venv/lib/python3.11/site-packages/sympy-1.14.0.dist-info/top_level.txt +2 -0
  10. venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_dimensionsystem.py +95 -0
  11. venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_prefixes.py +86 -0
  12. venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_quantities.py +575 -0
  13. venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_unit_system_cgs_gauss.py +55 -0
  14. venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_unitsystem.py +86 -0
  15. venv/lib/python3.11/site-packages/sympy/physics/units/tests/test_util.py +178 -0
  16. venv/lib/python3.11/site-packages/sympy/physics/units/unitsystem.py +204 -0
  17. venv/lib/python3.11/site-packages/sympy/physics/units/util.py +265 -0
  18. venv/lib/python3.11/site-packages/sympy/physics/vector/__init__.py +36 -0
  19. venv/lib/python3.11/site-packages/sympy/physics/vector/dyadic.py +545 -0
  20. venv/lib/python3.11/site-packages/sympy/physics/vector/fieldfunctions.py +313 -0
  21. venv/lib/python3.11/site-packages/sympy/physics/vector/frame.py +1575 -0
  22. venv/lib/python3.11/site-packages/sympy/physics/vector/functions.py +650 -0
  23. venv/lib/python3.11/site-packages/sympy/physics/vector/point.py +635 -0
  24. venv/lib/python3.11/site-packages/sympy/physics/vector/printing.py +371 -0
  25. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/__init__.py +0 -0
  26. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_dyadic.py +123 -0
  27. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_fieldfunctions.py +133 -0
  28. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_frame.py +761 -0
  29. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_functions.py +509 -0
  30. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_output.py +75 -0
  31. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_point.py +382 -0
  32. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_printing.py +353 -0
  33. venv/lib/python3.11/site-packages/sympy/physics/vector/tests/test_vector.py +274 -0
  34. venv/lib/python3.11/site-packages/sympy/physics/vector/vector.py +806 -0
  35. venv/lib/python3.11/site-packages/sympy/physics/wigner.py +1213 -0
  36. venv/lib/python3.11/site-packages/sympy/plotting/__init__.py +22 -0
  37. venv/lib/python3.11/site-packages/sympy/plotting/backends/__init__.py +0 -0
  38. venv/lib/python3.11/site-packages/sympy/plotting/backends/base_backend.py +419 -0
  39. venv/lib/python3.11/site-packages/sympy/plotting/backends/matplotlibbackend/__init__.py +5 -0
  40. venv/lib/python3.11/site-packages/sympy/plotting/backends/matplotlibbackend/matplotlib.py +318 -0
  41. venv/lib/python3.11/site-packages/sympy/plotting/backends/textbackend/__init__.py +3 -0
  42. venv/lib/python3.11/site-packages/sympy/plotting/backends/textbackend/text.py +24 -0
  43. venv/lib/python3.11/site-packages/sympy/plotting/experimental_lambdify.py +641 -0
  44. venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/__init__.py +12 -0
  45. venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py +413 -0
  46. venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/interval_membership.py +78 -0
  47. venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/lib_interval.py +452 -0
  48. venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/__init__.py +0 -0
  49. venv/lib/python3.11/site-packages/sympy/plotting/intervalmath/tests/test_interval_functions.py +415 -0
  50. 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
+ [![pypi version](https://img.shields.io/pypi/v/sympy.svg)](https://pypi.python.org/pypi/sympy)
52
+ [![Join the chat at https://gitter.im/sympy/sympy](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sympy/sympy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
53
+ [![Zenodo Badge](https://zenodo.org/badge/18918/sympy/sympy.svg)](https://zenodo.org/badge/latestdoi/18918/sympy/sympy)
54
+ [![Downloads](https://pepy.tech/badge/sympy/month)](https://pepy.tech/project/sympy)
55
+ [![GitHub Issues](https://img.shields.io/badge/issue_tracking-github-blue.svg)](https://github.com/sympy/sympy/issues)
56
+ [![Git Tutorial](https://img.shields.io/badge/PR-Welcome-%23FF8300.svg?)](https://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project)
57
+ [![Powered by NumFocus](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org)
58
+ [![Commits since last release](https://img.shields.io/github/commits-since/sympy/sympy/latest.svg?longCache=true&style=flat-square&logo=git&logoColor=fff)](https://github.com/sympy/sympy/releases)
59
+
60
+ [![SymPy Banner](https://github.com/sympy/sympy/raw/master/banner.svg)](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)