Add files using upload-large-folder tool
Browse files- .venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/INSTALLER +1 -0
- .venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/METADATA +216 -0
- .venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/REQUESTED +0 -0
- .venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/WHEEL +6 -0
- .venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/RECORD +140 -0
- .venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/REQUESTED +0 -0
- .venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/zip-safe +1 -0
- .venv/lib/python3.13/site-packages/sympy/discrete/__init__.py +20 -0
- .venv/lib/python3.13/site-packages/sympy/discrete/convolutions.py +597 -0
- .venv/lib/python3.13/site-packages/sympy/discrete/recurrences.py +166 -0
- .venv/lib/python3.13/site-packages/sympy/discrete/transforms.py +425 -0
- .venv/lib/python3.13/site-packages/sympy/ntheory/modular.py +291 -0
- .venv/lib/python3.13/site-packages/sympy/printing/aesaracode.py +563 -0
- .venv/lib/python3.13/site-packages/sympy/printing/cxx.py +181 -0
- .venv/lib/python3.13/site-packages/sympy/printing/glsl.py +548 -0
- .venv/lib/python3.13/site-packages/sympy/printing/gtk.py +16 -0
- .venv/lib/python3.13/site-packages/sympy/printing/maple.py +311 -0
- .venv/lib/python3.13/site-packages/sympy/printing/preview.py +390 -0
- .venv/lib/python3.13/site-packages/sympy/printing/str.py +1021 -0
- .venv/lib/python3.13/site-packages/sympy/printing/tree.py +175 -0
.venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
.venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/METADATA
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: onnxruntime
|
| 3 |
+
Version: 1.24.1
|
| 4 |
+
Summary: ONNX Runtime is a runtime accelerator for Machine Learning models
|
| 5 |
+
Home-page: https://onnxruntime.ai
|
| 6 |
+
Download-URL: https://github.com/microsoft/onnxruntime/tags
|
| 7 |
+
Author: Microsoft Corporation
|
| 8 |
+
Author-email: onnxruntime@microsoft.com
|
| 9 |
+
License: MIT License
|
| 10 |
+
Keywords: onnx machine learning
|
| 11 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 12 |
+
Classifier: Intended Audience :: Developers
|
| 13 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 14 |
+
Classifier: Operating System :: POSIX :: Linux
|
| 15 |
+
Classifier: Operating System :: Microsoft :: Windows
|
| 16 |
+
Classifier: Operating System :: MacOS
|
| 17 |
+
Classifier: Topic :: Scientific/Engineering
|
| 18 |
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
| 19 |
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
| 20 |
+
Classifier: Topic :: Software Development
|
| 21 |
+
Classifier: Topic :: Software Development :: Libraries
|
| 22 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 23 |
+
Classifier: Programming Language :: Python
|
| 24 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 25 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 26 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 27 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 28 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 29 |
+
Requires-Python: >=3.10
|
| 30 |
+
Requires-Dist: flatbuffers
|
| 31 |
+
Requires-Dist: numpy>=1.21.6
|
| 32 |
+
Requires-Dist: packaging
|
| 33 |
+
Requires-Dist: protobuf
|
| 34 |
+
Requires-Dist: sympy
|
| 35 |
+
Dynamic: author
|
| 36 |
+
Dynamic: author-email
|
| 37 |
+
Dynamic: classifier
|
| 38 |
+
Dynamic: description
|
| 39 |
+
Dynamic: download-url
|
| 40 |
+
Dynamic: home-page
|
| 41 |
+
Dynamic: keywords
|
| 42 |
+
Dynamic: license
|
| 43 |
+
Dynamic: requires-dist
|
| 44 |
+
Dynamic: requires-python
|
| 45 |
+
Dynamic: summary
|
| 46 |
+
|
| 47 |
+
ONNX Runtime
|
| 48 |
+
============
|
| 49 |
+
|
| 50 |
+
ONNX Runtime is a performance-focused scoring engine for Open Neural Network Exchange (ONNX) models.
|
| 51 |
+
For more information on ONNX Runtime, please see `aka.ms/onnxruntime <https://aka.ms/onnxruntime/>`_ or the `Github project <https://github.com/microsoft/onnxruntime/>`_.
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
Changes
|
| 55 |
+
-------
|
| 56 |
+
|
| 57 |
+
1.24.1
|
| 58 |
+
^^^^^^
|
| 59 |
+
|
| 60 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.24.1
|
| 61 |
+
|
| 62 |
+
1.23.0
|
| 63 |
+
^^^^^^
|
| 64 |
+
|
| 65 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.23.0
|
| 66 |
+
|
| 67 |
+
1.22.0
|
| 68 |
+
^^^^^^
|
| 69 |
+
|
| 70 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.22.0
|
| 71 |
+
|
| 72 |
+
1.21.0
|
| 73 |
+
^^^^^^
|
| 74 |
+
|
| 75 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.21.0
|
| 76 |
+
|
| 77 |
+
1.20.0
|
| 78 |
+
^^^^^^
|
| 79 |
+
|
| 80 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.20.0
|
| 81 |
+
|
| 82 |
+
1.19.0
|
| 83 |
+
^^^^^^
|
| 84 |
+
|
| 85 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.19.0
|
| 86 |
+
|
| 87 |
+
1.18.0
|
| 88 |
+
^^^^^^
|
| 89 |
+
|
| 90 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.18.0
|
| 91 |
+
|
| 92 |
+
1.17.0
|
| 93 |
+
^^^^^^
|
| 94 |
+
|
| 95 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.17.0
|
| 96 |
+
|
| 97 |
+
1.16.0
|
| 98 |
+
^^^^^^
|
| 99 |
+
|
| 100 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.16.0
|
| 101 |
+
|
| 102 |
+
1.15.0
|
| 103 |
+
^^^^^^
|
| 104 |
+
|
| 105 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.15.0
|
| 106 |
+
|
| 107 |
+
1.14.0
|
| 108 |
+
^^^^^^
|
| 109 |
+
|
| 110 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.14.0
|
| 111 |
+
|
| 112 |
+
1.13.0
|
| 113 |
+
^^^^^^
|
| 114 |
+
|
| 115 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.13.0
|
| 116 |
+
|
| 117 |
+
1.12.0
|
| 118 |
+
^^^^^^
|
| 119 |
+
|
| 120 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.12.0
|
| 121 |
+
|
| 122 |
+
1.11.0
|
| 123 |
+
^^^^^^
|
| 124 |
+
|
| 125 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.11.0
|
| 126 |
+
|
| 127 |
+
1.10.0
|
| 128 |
+
^^^^^^
|
| 129 |
+
|
| 130 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.10.0
|
| 131 |
+
|
| 132 |
+
1.9.0
|
| 133 |
+
^^^^^
|
| 134 |
+
|
| 135 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.9.0
|
| 136 |
+
|
| 137 |
+
1.8.2
|
| 138 |
+
^^^^^
|
| 139 |
+
|
| 140 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.8.2
|
| 141 |
+
|
| 142 |
+
1.8.1
|
| 143 |
+
^^^^^
|
| 144 |
+
|
| 145 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.8.1
|
| 146 |
+
|
| 147 |
+
1.8.0
|
| 148 |
+
^^^^^
|
| 149 |
+
|
| 150 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.8.0
|
| 151 |
+
|
| 152 |
+
1.7.0
|
| 153 |
+
^^^^^
|
| 154 |
+
|
| 155 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.7.0
|
| 156 |
+
|
| 157 |
+
1.6.0
|
| 158 |
+
^^^^^
|
| 159 |
+
|
| 160 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.6.0
|
| 161 |
+
|
| 162 |
+
1.5.3
|
| 163 |
+
^^^^^
|
| 164 |
+
|
| 165 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.5.3
|
| 166 |
+
|
| 167 |
+
1.5.2
|
| 168 |
+
^^^^^
|
| 169 |
+
|
| 170 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.5.2
|
| 171 |
+
|
| 172 |
+
1.5.1
|
| 173 |
+
^^^^^
|
| 174 |
+
|
| 175 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.5.1
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
1.4.0
|
| 179 |
+
^^^^^
|
| 180 |
+
|
| 181 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.4.0
|
| 182 |
+
|
| 183 |
+
1.3.1
|
| 184 |
+
^^^^^
|
| 185 |
+
|
| 186 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.3.1
|
| 187 |
+
|
| 188 |
+
1.3.0
|
| 189 |
+
^^^^^
|
| 190 |
+
|
| 191 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.3.0
|
| 192 |
+
|
| 193 |
+
1.2.0
|
| 194 |
+
^^^^^
|
| 195 |
+
|
| 196 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.2.0
|
| 197 |
+
|
| 198 |
+
1.1.0
|
| 199 |
+
^^^^^
|
| 200 |
+
|
| 201 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.1.0
|
| 202 |
+
|
| 203 |
+
1.0.0
|
| 204 |
+
^^^^^
|
| 205 |
+
|
| 206 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v1.0.0
|
| 207 |
+
|
| 208 |
+
0.5.0
|
| 209 |
+
^^^^^
|
| 210 |
+
|
| 211 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v0.5.0
|
| 212 |
+
|
| 213 |
+
0.4.0
|
| 214 |
+
^^^^^
|
| 215 |
+
|
| 216 |
+
Release Notes : https://github.com/Microsoft/onnxruntime/releases/tag/v0.4.0
|
.venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/REQUESTED
ADDED
|
File without changes
|
.venv/lib/python3.13/site-packages/onnxruntime-1.24.1.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (78.1.1)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp313-cp313-macosx_14_0_arm64
|
| 5 |
+
Generator: delocate 0.13.0
|
| 6 |
+
|
.venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/RECORD
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
PIL/.dylibs/libXau.6.dylib,sha256=nQNsVba7r1Hf9j6VseTE0wbv8AJvbJJLfTnE57UgW_I,70048
|
| 2 |
+
PIL/.dylibs/libavif.16.3.0.dylib,sha256=4cZljlxPfQW9gaz65FMKe-prEnHaMjQXyV6j_gM7qAA,2983120
|
| 3 |
+
PIL/.dylibs/libbrotlicommon.1.2.0.dylib,sha256=hRywsOFrlwqmtT-_NtqmKeYvEOOpBlN6x3LMfJDYImU,201184
|
| 4 |
+
PIL/.dylibs/libbrotlidec.1.2.0.dylib,sha256=gAdn1-QeiaM6nOjUvNLiTwHL1GNNG1bRmaTec9DjT5k,104560
|
| 5 |
+
PIL/.dylibs/libfreetype.6.dylib,sha256=VhPqCWIaAjaBNexBQpDUsq-Cus-r2IQER5VAuOzRyMg,1143856
|
| 6 |
+
PIL/.dylibs/libharfbuzz.0.dylib,sha256=0iKmnPcd-gN5C4r-dX-73ko7dOxjM4Ji97O53z1I7mA,1779344
|
| 7 |
+
PIL/.dylibs/libjpeg.62.4.0.dylib,sha256=Fw3hf4BpBUzue53v5pR4BSBhyDBorHOH8P5WgwMO_QQ,653536
|
| 8 |
+
PIL/.dylibs/liblcms2.2.dylib,sha256=4ZedQZXRu_W41ptCeasVbRRUblNll6UJQ0HseHvusY8,524672
|
| 9 |
+
PIL/.dylibs/liblzma.5.dylib,sha256=sHGyixFEX0fbSFGxhSQUh8Zdgzc5YogeBgZ1JtwbtP4,325120
|
| 10 |
+
PIL/.dylibs/libopenjp2.2.5.4.dylib,sha256=8pbJacrtOUY0Y8ryJSz0yFGQMC0HlTKEWqTkzU3Tjik,666432
|
| 11 |
+
PIL/.dylibs/libpng16.16.dylib,sha256=GT1Et1gmboSSlVeNlXc6qO6KpGmtFXoc_ebGHhtEizA,344368
|
| 12 |
+
PIL/.dylibs/libsharpyuv.0.dylib,sha256=jSgehjdhBEKurMj0wkcWZIlZfrdCGpSpMAU61Y_-PqI,86240
|
| 13 |
+
PIL/.dylibs/libtiff.6.dylib,sha256=uJrxnUQYeLN22SXjr0vdTEsIVWsT_zaba_VBRwQ8gZc,770432
|
| 14 |
+
PIL/.dylibs/libwebp.7.dylib,sha256=INkaleVsCQanf2TWSVafPc_91bEE7QXdEO6fCHfewsk,515712
|
| 15 |
+
PIL/.dylibs/libwebpdemux.2.dylib,sha256=VuLKpYe2YZn6ko9ZjfIyEF0XglvAu5pqK5a8qCfBcTI,69904
|
| 16 |
+
PIL/.dylibs/libwebpmux.3.dylib,sha256=1AaRf97STlBUw4GZ5rzBguBPjvfvr2JPlLal2V4mdJE,106368
|
| 17 |
+
PIL/.dylibs/libxcb.1.1.0.dylib,sha256=blpsZKejMqA0kMAV_qg4Grwgm8LgwmVkx4XBJGipxTk,277664
|
| 18 |
+
PIL/.dylibs/libz.1.3.1.zlib-ng.dylib,sha256=2YUI2M3Hjgnt6W3OBSVE5AikV_5lElzLU-5Y8NP1OhY,177344
|
| 19 |
+
PIL/AvifImagePlugin.py,sha256=sHaYxONtVEDZ24y8Ad7qRlixD4iIPC2ipH1W79-uPbE,9030
|
| 20 |
+
PIL/BdfFontFile.py,sha256=PhlZfIRmEfmorbhZZeSM5eebGo1Ei7fL-lR9XlfTZZA,3285
|
| 21 |
+
PIL/BlpImagePlugin.py,sha256=tQxeJbpiyvp3-zSLjeWAQr8YdLS_HoKWmuLDQwRQl4w,16568
|
| 22 |
+
PIL/BmpImagePlugin.py,sha256=64PB3YE8kEL8QrAuUMlWoEaByYJUTYV6OQA254Uj4Lc,19925
|
| 23 |
+
PIL/BufrStubImagePlugin.py,sha256=gMbELNwEghE_IM2Ov1NT4jy-96CTpEvyuj_mXmckL-c,1765
|
| 24 |
+
PIL/ContainerIO.py,sha256=wkBqL2GDAb5fh3wrtfTGUfqioJipCl-lg2GxbjQrTZw,4604
|
| 25 |
+
PIL/CurImagePlugin.py,sha256=-WEsgwQbA9rQzXB0HG0LK1V_qbuwHosPZ0T2IjfN8r0,1791
|
| 26 |
+
PIL/DcxImagePlugin.py,sha256=OlCv6yDI0LLZNbuUI7s69FI1DuArPyGxo3YCzQ1n8Hg,2180
|
| 27 |
+
PIL/DdsImagePlugin.py,sha256=JRhDGFhKXvolAiD_X4_nYQ3DARogu2hmE2AhZuGc02Q,18931
|
| 28 |
+
PIL/EpsImagePlugin.py,sha256=jnT9Y01VEOdnU0Q3ZncyeTM81WYPpPABXT2BOgKW-mY,16626
|
| 29 |
+
PIL/ExifTags.py,sha256=zW6kVikCosiyoCo7J7R62evD3hoxjKPchnVh8po7CZc,9931
|
| 30 |
+
PIL/FitsImagePlugin.py,sha256=-oDJnAH113CK5qPvwz9lL81fkV1gla_tNfqLcq8zKgo,4644
|
| 31 |
+
PIL/FliImagePlugin.py,sha256=4zxH8IXBX9DGi6dJRM6Y5NMdbA1d99x696mcGZHxHzI,4929
|
| 32 |
+
PIL/FontFile.py,sha256=St7MxO5Q-oakCLWn3ZrgrtaT3wSsmAarxm8AU-G8Moc,3577
|
| 33 |
+
PIL/FpxImagePlugin.py,sha256=NnHQj6Ze1LUXqIOi7WHCKBEBftAkJZJJ1quHEpZaE14,7363
|
| 34 |
+
PIL/FtexImagePlugin.py,sha256=ZS0WqBHtItNoi0W52A4REOGrDIfh40dCGDLruaXLRFE,3570
|
| 35 |
+
PIL/GbrImagePlugin.py,sha256=DYyvhekFB1sOQLEWPwYbf0CEfGdgdax1ci-3DuSeIgQ,3053
|
| 36 |
+
PIL/GdImageFile.py,sha256=LP4Uxv3Y2ivGZIyOVuGJarDDVS7zK6F1Q6SNl4wyGuQ,2788
|
| 37 |
+
PIL/GifImagePlugin.py,sha256=Bu5YTg7YwLgJIxcE0RiDGHIGTApmGZxXvWQn0dPs674,42304
|
| 38 |
+
PIL/GimpGradientFile.py,sha256=gqqUkDbKVFCtBxt5VAhPS0HtLZDYFI6KWEaUhhTNNE8,3982
|
| 39 |
+
PIL/GimpPaletteFile.py,sha256=hIHQ9LJ5ri0hy1e_vZYeD-n67UWdhEDlKc4vDxgaUdg,1860
|
| 40 |
+
PIL/GribStubImagePlugin.py,sha256=C9YNx1hxBgYvRFzcs1ZG-srEG_3A3DIWBgk1j-nbl8I,1794
|
| 41 |
+
PIL/Hdf5StubImagePlugin.py,sha256=4JirZFt94qjH73NsWJi83OP-cEUMKDTsIhWKrdNGvuc,1776
|
| 42 |
+
PIL/IcnsImagePlugin.py,sha256=l3JdNkYQVAMeiGpK_tem2vtDD11KtAJ64wQGsA0E-JQ,12440
|
| 43 |
+
PIL/IcoImagePlugin.py,sha256=UGzIDlQ02ZB-VJEsoyI7YWmTx26ZiB2EHJZ1qvB28JQ,13103
|
| 44 |
+
PIL/ImImagePlugin.py,sha256=8sBOAy7xlqP722sXWhnJn4VoBhTBzbZQTtfW_XsWefU,11602
|
| 45 |
+
PIL/Image.py,sha256=Pl7NzD6IAHSZAeYcgQi9PEkGPTukRPoLYrDBivAiAH8,148899
|
| 46 |
+
PIL/ImageChops.py,sha256=GEjlymcoDtA5OOeIxQVIX96BD-s6AXhb7TmSLYn2tUg,7946
|
| 47 |
+
PIL/ImageCms.py,sha256=IuCm3gXKpb5Eu1kn-TB8cD9XJLZEa8fpkEjzVqAIKNk,40676
|
| 48 |
+
PIL/ImageColor.py,sha256=IGA9C2umeED_EzS2Cvj6KsU0VutC9RstWIYPe8uDsVk,9441
|
| 49 |
+
PIL/ImageDraw.py,sha256=FMn0AK_gxxJBYB8afOGF2FUP2KJPFvUf3UZp_KrJz7A,36287
|
| 50 |
+
PIL/ImageDraw2.py,sha256=pdVMW7bVw3KwhXvRZh28Md4y-2xFfuo5fHcDnaYqVK4,7227
|
| 51 |
+
PIL/ImageEnhance.py,sha256=4Elhz_lyyxLmx0GkSHrwOAmNJ2TkqVQPHejzGihZUMI,3627
|
| 52 |
+
PIL/ImageFile.py,sha256=W5_QULZWeY7Kqz04cLujVu0kSBnKZu7liLCCqoXO-GM,29972
|
| 53 |
+
PIL/ImageFilter.py,sha256=MO1MBrbXDiX2IAGESdGm_0087bwmSZ_14ecAj28ojCY,18729
|
| 54 |
+
PIL/ImageFont.py,sha256=tsIwpnCjWl57_sMcAFTt84Ikpf14EPrW8_IJ9XQ7z7E,63253
|
| 55 |
+
PIL/ImageGrab.py,sha256=oaeBn7UHjtxKPXJ9OSXPUt3cG1I0j-4yesbfvh0p_TY,7750
|
| 56 |
+
PIL/ImageMath.py,sha256=RQl6cRXGuszba4KwtbIudin_8U65shpWrajr9gTn1rw,10369
|
| 57 |
+
PIL/ImageMode.py,sha256=aaZVHAiCEanOA2K1jN3DlW3NPKa8Dm5nIXTXErzyFms,2395
|
| 58 |
+
PIL/ImageMorph.py,sha256=otf5f9A-Y4Aosfl1E4YWEVatgDBInGp6xWBJfAJ0NRs,10356
|
| 59 |
+
PIL/ImageOps.py,sha256=bIcQFK_MtovfNSYTcOesp4So9OgsGrwt3cGsB7xlGRM,25567
|
| 60 |
+
PIL/ImagePalette.py,sha256=uPP_qWWSTWitlv4WdXucZF4n-2ZNsHjg4H36g-JpuxU,9092
|
| 61 |
+
PIL/ImagePath.py,sha256=5yUG5XCUil1KKTTA_8PgGhcmg-mnue-GK0FwTBlhjw4,371
|
| 62 |
+
PIL/ImageQt.py,sha256=PTt5TPyngWL-Vuvx_bwnH17EOBe3tE7l4huVmvGQP5Y,6684
|
| 63 |
+
PIL/ImageSequence.py,sha256=Mphgkr79scmYBgmi9ZguhDfVwHvpLSX5uZVHDZlrn0I,2253
|
| 64 |
+
PIL/ImageShow.py,sha256=Ju0_Db2B4_n3yKJV9sDsF7_HAgciEdXlq6I1Eiw1YTo,10106
|
| 65 |
+
PIL/ImageStat.py,sha256=FVTiYWGCciPW1QD61b7DYZlcDqR0dS6hsLjq-gcKcG4,5495
|
| 66 |
+
PIL/ImageText.py,sha256=E3EEmIlP1FG-zgy9YddETN1HKSIXaRJSrPJIC67aAxY,12120
|
| 67 |
+
PIL/ImageTk.py,sha256=b5SntckGXs0ECsI2MmdJg3CSX6AtELsWh0Ohxu41u_k,8132
|
| 68 |
+
PIL/ImageTransform.py,sha256=-qek7P3lzLddcXt9cWt5w_L11JGp2yY3AJtOfmJAkDc,3916
|
| 69 |
+
PIL/ImageWin.py,sha256=LT05w8_vTfRrC3n9S9pM0TNbXrzZLEJHlCJil7Xv80k,8085
|
| 70 |
+
PIL/ImtImagePlugin.py,sha256=SL5IrsHcblltxtX4v_HVFhYnR6haJ0AOd2NHhZKMImY,2665
|
| 71 |
+
PIL/IptcImagePlugin.py,sha256=AV5HTo7kHo20IuF1rd4OhQcbf3wt1rbRPwpI3cK0xw4,6579
|
| 72 |
+
PIL/Jpeg2KImagePlugin.py,sha256=DugpqlY-L54ZSbOOr1mux_fT1APy73e9715NQx_zL9E,14002
|
| 73 |
+
PIL/JpegImagePlugin.py,sha256=kWLRMNTVrfcHaIpZpcLsaLtxaefrenWdILZWwZfYEZ8,31543
|
| 74 |
+
PIL/JpegPresets.py,sha256=lnqWHo4DLIHIulcdHp0NJ7CWexHt8T3w51kIKlLfkIA,12379
|
| 75 |
+
PIL/McIdasImagePlugin.py,sha256=baOIkD-CIIeCgBFTf8kos928PKBuCUqYYa38u3WES_8,1877
|
| 76 |
+
PIL/MicImagePlugin.py,sha256=j91UizvL-zgXZgOcq_4oWBbSX6LPq1J9kNHkdITM1Qw,2599
|
| 77 |
+
PIL/MpegImagePlugin.py,sha256=g7BZd93kWpFi41SG_wKFoi0yEPsioI4kj45b2F-3Vrw,2010
|
| 78 |
+
PIL/MpoImagePlugin.py,sha256=xRCA2R2lSpT76pqDiQX0Es0yyaHj5d4gdN26eV7aInU,6792
|
| 79 |
+
PIL/MspImagePlugin.py,sha256=oxk_MLUDvzJ4JDuOZCHkmqOPXniG42PHOyNGwe60slY,5892
|
| 80 |
+
PIL/PSDraw.py,sha256=KMBGj3vXaFpblaIcA9KjFFTpdal41AQggY-UgzqoMkQ,6918
|
| 81 |
+
PIL/PaletteFile.py,sha256=suDdAL6VMljXw4oEn1vhTt4DQ4vbpIHGd3A4oxOgE6s,1216
|
| 82 |
+
PIL/PalmImagePlugin.py,sha256=WJ1b8I1xTSAXYDJhIpkVFCLu2LlpbiBD5d1Hr-m2l08,8748
|
| 83 |
+
PIL/PcdImagePlugin.py,sha256=-gnMUqQH0R-aljsd3nZS9eBI1j75ijWD_HZfadE3RsQ,1774
|
| 84 |
+
PIL/PcfFontFile.py,sha256=DqcyydQgP2vtiPFzj57KYHLuF2v-0oMTB-VkgYYHKhE,7223
|
| 85 |
+
PIL/PcxImagePlugin.py,sha256=1xAq6CdH34cOsOgTPi4Wu2SKQCdQiTLVyqaMkYQZUP4,6245
|
| 86 |
+
PIL/PdfImagePlugin.py,sha256=6lZLoQMVbAE-x1ESrv6PgGSyM9Ueck7e6E6ps-YQ-vI,9321
|
| 87 |
+
PIL/PdfParser.py,sha256=Hr3ImLDSIKwUF6OrQ1GjlAnGi6ZpGVLWhGfKhqQ_DRM,37996
|
| 88 |
+
PIL/PixarImagePlugin.py,sha256=l_4GwBd0mATnIXYJbwmmODU2vP7wewLu6BRviHCB2EI,1758
|
| 89 |
+
PIL/PngImagePlugin.py,sha256=XaVZDKJIf37Cp9tSZr74kYWFTVfV5PYZ9Qeo8pVbeqo,51721
|
| 90 |
+
PIL/PpmImagePlugin.py,sha256=vb5SP0IjQPzDRDE8jSPtcJv9K3Rh1LczAlt0Pg26i90,12391
|
| 91 |
+
PIL/PsdImagePlugin.py,sha256=Dxyb0AwhIk7ngpuOluaJz3HtlQtSUmylLJao9CPiCII,8720
|
| 92 |
+
PIL/QoiImagePlugin.py,sha256=o3IJbJYiG6Qz_evDAixeTferNaXOA7Rf0TsoNQeFaZ4,8607
|
| 93 |
+
PIL/SgiImagePlugin.py,sha256=3Ql89s8vycNWjcxJwMw28iksV9Yj2xWoKBQ6c5DHXBg,6389
|
| 94 |
+
PIL/SpiderImagePlugin.py,sha256=etb3Q-rKGtBg_rrVmBfh-poM2AODqibtA3yq5zoUTSc,10306
|
| 95 |
+
PIL/SunImagePlugin.py,sha256=Hdxkhk0pxpBGxYhPJfCDLwsYcO1KjxjtplNMFYibIvk,4589
|
| 96 |
+
PIL/TarIO.py,sha256=BqYUChCBb9F7Sh-uZ86iz1Dtoy2D0obNwGm65z1rdc0,1442
|
| 97 |
+
PIL/TgaImagePlugin.py,sha256=2vDsFTcBUBHw1V80wpVv4tgpLDbPr6yVHi6Fvaqf0HY,6980
|
| 98 |
+
PIL/TiffImagePlugin.py,sha256=cIQ48x3zmm5PFSG01wqweC8DJUhJxrX1R62c8Edw1Jg,85002
|
| 99 |
+
PIL/TiffTags.py,sha256=dm3qNQQkuaLKqzUTvnlGiUA8K3IizQOABvac_wjttF0,17206
|
| 100 |
+
PIL/WalImageFile.py,sha256=P6Ue6JZGAwBKE-JAIUP-oHCC4MPPc2jyqOScnAEKK3Y,5761
|
| 101 |
+
PIL/WebPImagePlugin.py,sha256=fCHqpMBM5CV2pr26GzLEuBm7V58Q_Z9YJ-9zxc2jA4M,9854
|
| 102 |
+
PIL/WmfImagePlugin.py,sha256=B2pMdJxRildCITQdb4XNAJXEwjitr2ZZVKVl2c-G1xY,5316
|
| 103 |
+
PIL/XVThumbImagePlugin.py,sha256=XghmnRm8Y_8j0O28XPbHRrmK7CrGVuR844gv1hfF5ug,2126
|
| 104 |
+
PIL/XbmImagePlugin.py,sha256=Fd6GVDEo73nyFICA3Z3w4LjkwoZWvhHB6rKCm5yVrho,2669
|
| 105 |
+
PIL/XpmImagePlugin.py,sha256=jtUKavJCYwIAsJaJwSx8vJsx1oTbCywfDxePENmA93w,4400
|
| 106 |
+
PIL/__init__.py,sha256=Q4KOEpR7S_Xsj30fvOsvR94xEpX4KUsVeUwaVP1fU80,2031
|
| 107 |
+
PIL/__main__.py,sha256=Lpj4vef8mI7jA1sRCUAoVYaeePD_Uc898xF5c7XLx1A,133
|
| 108 |
+
PIL/_avif.cpython-313-darwin.so,sha256=GowXJE1Wvzm6-0zzM5vK1sdibOS6B1bbVS2glWwu3Fs,73968
|
| 109 |
+
PIL/_avif.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63
|
| 110 |
+
PIL/_binary.py,sha256=pcM6AL04GxgmGeLfcH1V1BZHENwIrQH0uxhJ7r0HIL0,2550
|
| 111 |
+
PIL/_deprecate.py,sha256=KhyLVmQYVVww0rORbU24Te3t8fOt9G2LeM6fJGWbl0o,2034
|
| 112 |
+
PIL/_imaging.cpython-313-darwin.so,sha256=TrGLRFVdweDxh397MnuNhXRZNCc11GCJg3gryXtdb7Y,572144
|
| 113 |
+
PIL/_imaging.pyi,sha256=fT-TTGQS0kym1gv77gXTKQtJrAmO1NynS5LKBtG3G6M,893
|
| 114 |
+
PIL/_imagingcms.cpython-313-darwin.so,sha256=4UL7ZNmzOS1zG1uOu30c_ClLLunjH5JAP2HbzeK0gwg,98512
|
| 115 |
+
PIL/_imagingcms.pyi,sha256=ZZ8iIoi6EHWLvgAdfm1hPD5CQmxi75LiJl5x8yGxYoU,4433
|
| 116 |
+
PIL/_imagingft.cpython-313-darwin.so,sha256=BHIs6R0Kx4BUwfXNaRxqsjD89cELRkzULOCzdiAuOI4,117728
|
| 117 |
+
PIL/_imagingft.pyi,sha256=cYySzvcKBCiHPBsvttMie9AdfUcEsqZR-3256YQtz2Q,1833
|
| 118 |
+
PIL/_imagingmath.cpython-313-darwin.so,sha256=7dFu3iMmAmC2oLt6qmohGO8fZi4rkPjlYE873GaYmIg,55904
|
| 119 |
+
PIL/_imagingmath.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63
|
| 120 |
+
PIL/_imagingmorph.cpython-313-darwin.so,sha256=gSUB6Tq15YR8QTfKdLDSWka61dI6VDBRk-G05fJ6e0w,51440
|
| 121 |
+
PIL/_imagingmorph.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63
|
| 122 |
+
PIL/_imagingtk.cpython-313-darwin.so,sha256=hGQDcrQdtLldqMvt1BMtBVovVlVzIpR8zv-Y7KLSRUU,52720
|
| 123 |
+
PIL/_imagingtk.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63
|
| 124 |
+
PIL/_tkinter_finder.py,sha256=GIZ4stmFhUosmHKSrdxcjStiocDNfyJn7RBie2SWxU0,538
|
| 125 |
+
PIL/_typing.py,sha256=2z33ZUp9aQnkSqXzNR3Zn7l04d2W-oAj1OiZhiyFF68,919
|
| 126 |
+
PIL/_util.py,sha256=fxhWdrLARyc2PsMgN3m9_U1dY3oUKbV7mkoHcXgoeeA,684
|
| 127 |
+
PIL/_version.py,sha256=28QchbjtLhowcIycQ-2YvK356_LZtiCuh-r1ELBCsEQ,87
|
| 128 |
+
PIL/_webp.cpython-313-darwin.so,sha256=a7erFcORHzjmuZp0nZ0CUGxvyeyW1zdrO-pKmpK9BrI,76256
|
| 129 |
+
PIL/_webp.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63
|
| 130 |
+
PIL/features.py,sha256=FPkEhjtBaRSqpkgHNYduwxiFtycu4NjZKwEMWxtemPU,10775
|
| 131 |
+
PIL/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 132 |
+
PIL/report.py,sha256=4JY6-IU7sH1RKuRbOvy1fUt0dAoi79FX4tYJN3p1DT0,100
|
| 133 |
+
pillow-12.1.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 134 |
+
pillow-12.1.1.dist-info/METADATA,sha256=F_8fLSrbwe5hjpfxSagcqvTDKT1aaHIntiiRmecPLRc,8808
|
| 135 |
+
pillow-12.1.1.dist-info/RECORD,,
|
| 136 |
+
pillow-12.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 137 |
+
pillow-12.1.1.dist-info/WHEEL,sha256=MhISwTXlcxsZliAvBTu_MYP_gZvKB8bGPOh0ufmy-Yo,136
|
| 138 |
+
pillow-12.1.1.dist-info/licenses/LICENSE,sha256=MBeL96_5-NyCr-01CGzTeKkGTnf8tDgEhfOLXaM3cFI,68061
|
| 139 |
+
pillow-12.1.1.dist-info/top_level.txt,sha256=riZqrk-hyZqh5f1Z0Zwii3dKfxEsByhu9cU9IODF-NY,4
|
| 140 |
+
pillow-12.1.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
.venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/REQUESTED
ADDED
|
File without changes
|
.venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/zip-safe
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
.venv/lib/python3.13/site-packages/sympy/discrete/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""This module contains functions which operate on discrete sequences.
|
| 2 |
+
|
| 3 |
+
Transforms - ``fft``, ``ifft``, ``ntt``, ``intt``, ``fwht``, ``ifwht``,
|
| 4 |
+
``mobius_transform``, ``inverse_mobius_transform``
|
| 5 |
+
|
| 6 |
+
Convolutions - ``convolution``, ``convolution_fft``, ``convolution_ntt``,
|
| 7 |
+
``convolution_fwht``, ``convolution_subset``,
|
| 8 |
+
``covering_product``, ``intersecting_product``
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from .transforms import (fft, ifft, ntt, intt, fwht, ifwht,
|
| 12 |
+
mobius_transform, inverse_mobius_transform)
|
| 13 |
+
from .convolutions import convolution, covering_product, intersecting_product
|
| 14 |
+
|
| 15 |
+
__all__ = [
|
| 16 |
+
'fft', 'ifft', 'ntt', 'intt', 'fwht', 'ifwht', 'mobius_transform',
|
| 17 |
+
'inverse_mobius_transform',
|
| 18 |
+
|
| 19 |
+
'convolution', 'covering_product', 'intersecting_product',
|
| 20 |
+
]
|
.venv/lib/python3.13/site-packages/sympy/discrete/convolutions.py
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Convolution (using **FFT**, **NTT**, **FWHT**), Subset Convolution,
|
| 3 |
+
Covering Product, Intersecting Product
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from sympy.core import S, sympify, Rational
|
| 7 |
+
from sympy.core.function import expand_mul
|
| 8 |
+
from sympy.discrete.transforms import (
|
| 9 |
+
fft, ifft, ntt, intt, fwht, ifwht,
|
| 10 |
+
mobius_transform, inverse_mobius_transform)
|
| 11 |
+
from sympy.external.gmpy import MPZ, lcm
|
| 12 |
+
from sympy.utilities.iterables import iterable
|
| 13 |
+
from sympy.utilities.misc import as_int
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def convolution(a, b, cycle=0, dps=None, prime=None, dyadic=None, subset=None):
|
| 17 |
+
"""
|
| 18 |
+
Performs convolution by determining the type of desired
|
| 19 |
+
convolution using hints.
|
| 20 |
+
|
| 21 |
+
Exactly one of ``dps``, ``prime``, ``dyadic``, ``subset`` arguments
|
| 22 |
+
should be specified explicitly for identifying the type of convolution,
|
| 23 |
+
and the argument ``cycle`` can be specified optionally.
|
| 24 |
+
|
| 25 |
+
For the default arguments, linear convolution is performed using **FFT**.
|
| 26 |
+
|
| 27 |
+
Parameters
|
| 28 |
+
==========
|
| 29 |
+
|
| 30 |
+
a, b : iterables
|
| 31 |
+
The sequences for which convolution is performed.
|
| 32 |
+
cycle : Integer
|
| 33 |
+
Specifies the length for doing cyclic convolution.
|
| 34 |
+
dps : Integer
|
| 35 |
+
Specifies the number of decimal digits for precision for
|
| 36 |
+
performing **FFT** on the sequence.
|
| 37 |
+
prime : Integer
|
| 38 |
+
Prime modulus of the form `(m 2^k + 1)` to be used for
|
| 39 |
+
performing **NTT** on the sequence.
|
| 40 |
+
dyadic : bool
|
| 41 |
+
Identifies the convolution type as dyadic (*bitwise-XOR*)
|
| 42 |
+
convolution, which is performed using **FWHT**.
|
| 43 |
+
subset : bool
|
| 44 |
+
Identifies the convolution type as subset convolution.
|
| 45 |
+
|
| 46 |
+
Examples
|
| 47 |
+
========
|
| 48 |
+
|
| 49 |
+
>>> from sympy import convolution, symbols, S, I
|
| 50 |
+
>>> u, v, w, x, y, z = symbols('u v w x y z')
|
| 51 |
+
|
| 52 |
+
>>> convolution([1 + 2*I, 4 + 3*I], [S(5)/4, 6], dps=3)
|
| 53 |
+
[1.25 + 2.5*I, 11.0 + 15.8*I, 24.0 + 18.0*I]
|
| 54 |
+
>>> convolution([1, 2, 3], [4, 5, 6], cycle=3)
|
| 55 |
+
[31, 31, 28]
|
| 56 |
+
|
| 57 |
+
>>> convolution([111, 777], [888, 444], prime=19*2**10 + 1)
|
| 58 |
+
[1283, 19351, 14219]
|
| 59 |
+
>>> convolution([111, 777], [888, 444], prime=19*2**10 + 1, cycle=2)
|
| 60 |
+
[15502, 19351]
|
| 61 |
+
|
| 62 |
+
>>> convolution([u, v], [x, y, z], dyadic=True)
|
| 63 |
+
[u*x + v*y, u*y + v*x, u*z, v*z]
|
| 64 |
+
>>> convolution([u, v], [x, y, z], dyadic=True, cycle=2)
|
| 65 |
+
[u*x + u*z + v*y, u*y + v*x + v*z]
|
| 66 |
+
|
| 67 |
+
>>> convolution([u, v, w], [x, y, z], subset=True)
|
| 68 |
+
[u*x, u*y + v*x, u*z + w*x, v*z + w*y]
|
| 69 |
+
>>> convolution([u, v, w], [x, y, z], subset=True, cycle=3)
|
| 70 |
+
[u*x + v*z + w*y, u*y + v*x, u*z + w*x]
|
| 71 |
+
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
c = as_int(cycle)
|
| 75 |
+
if c < 0:
|
| 76 |
+
raise ValueError("The length for cyclic convolution "
|
| 77 |
+
"must be non-negative")
|
| 78 |
+
|
| 79 |
+
dyadic = True if dyadic else None
|
| 80 |
+
subset = True if subset else None
|
| 81 |
+
if sum(x is not None for x in (prime, dps, dyadic, subset)) > 1:
|
| 82 |
+
raise TypeError("Ambiguity in determining the type of convolution")
|
| 83 |
+
|
| 84 |
+
if prime is not None:
|
| 85 |
+
ls = convolution_ntt(a, b, prime=prime)
|
| 86 |
+
return ls if not c else [sum(ls[i::c]) % prime for i in range(c)]
|
| 87 |
+
|
| 88 |
+
if dyadic:
|
| 89 |
+
ls = convolution_fwht(a, b)
|
| 90 |
+
elif subset:
|
| 91 |
+
ls = convolution_subset(a, b)
|
| 92 |
+
else:
|
| 93 |
+
def loop(a):
|
| 94 |
+
dens = []
|
| 95 |
+
for i in a:
|
| 96 |
+
if isinstance(i, Rational) and i.q - 1:
|
| 97 |
+
dens.append(i.q)
|
| 98 |
+
elif not isinstance(i, int):
|
| 99 |
+
return
|
| 100 |
+
if dens:
|
| 101 |
+
l = lcm(*dens)
|
| 102 |
+
return [i*l if type(i) is int else i.p*(l//i.q) for i in a], l
|
| 103 |
+
# no lcm of den to deal with
|
| 104 |
+
return a, 1
|
| 105 |
+
ls = None
|
| 106 |
+
da = loop(a)
|
| 107 |
+
if da is not None:
|
| 108 |
+
db = loop(b)
|
| 109 |
+
if db is not None:
|
| 110 |
+
(ia, ma), (ib, mb) = da, db
|
| 111 |
+
den = ma*mb
|
| 112 |
+
ls = convolution_int(ia, ib)
|
| 113 |
+
if den != 1:
|
| 114 |
+
ls = [Rational(i, den) for i in ls]
|
| 115 |
+
if ls is None:
|
| 116 |
+
ls = convolution_fft(a, b, dps)
|
| 117 |
+
|
| 118 |
+
return ls if not c else [sum(ls[i::c]) for i in range(c)]
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
#----------------------------------------------------------------------------#
|
| 122 |
+
# #
|
| 123 |
+
# Convolution for Complex domain #
|
| 124 |
+
# #
|
| 125 |
+
#----------------------------------------------------------------------------#
|
| 126 |
+
|
| 127 |
+
def convolution_fft(a, b, dps=None):
|
| 128 |
+
"""
|
| 129 |
+
Performs linear convolution using Fast Fourier Transform.
|
| 130 |
+
|
| 131 |
+
Parameters
|
| 132 |
+
==========
|
| 133 |
+
|
| 134 |
+
a, b : iterables
|
| 135 |
+
The sequences for which convolution is performed.
|
| 136 |
+
dps : Integer
|
| 137 |
+
Specifies the number of decimal digits for precision.
|
| 138 |
+
|
| 139 |
+
Examples
|
| 140 |
+
========
|
| 141 |
+
|
| 142 |
+
>>> from sympy import S, I
|
| 143 |
+
>>> from sympy.discrete.convolutions import convolution_fft
|
| 144 |
+
|
| 145 |
+
>>> convolution_fft([2, 3], [4, 5])
|
| 146 |
+
[8, 22, 15]
|
| 147 |
+
>>> convolution_fft([2, 5], [6, 7, 3])
|
| 148 |
+
[12, 44, 41, 15]
|
| 149 |
+
>>> convolution_fft([1 + 2*I, 4 + 3*I], [S(5)/4, 6])
|
| 150 |
+
[5/4 + 5*I/2, 11 + 63*I/4, 24 + 18*I]
|
| 151 |
+
|
| 152 |
+
References
|
| 153 |
+
==========
|
| 154 |
+
|
| 155 |
+
.. [1] https://en.wikipedia.org/wiki/Convolution_theorem
|
| 156 |
+
.. [2] https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
|
| 157 |
+
|
| 158 |
+
"""
|
| 159 |
+
|
| 160 |
+
a, b = a[:], b[:]
|
| 161 |
+
n = m = len(a) + len(b) - 1 # convolution size
|
| 162 |
+
|
| 163 |
+
if n > 0 and n&(n - 1): # not a power of 2
|
| 164 |
+
n = 2**n.bit_length()
|
| 165 |
+
|
| 166 |
+
# padding with zeros
|
| 167 |
+
a += [S.Zero]*(n - len(a))
|
| 168 |
+
b += [S.Zero]*(n - len(b))
|
| 169 |
+
|
| 170 |
+
a, b = fft(a, dps), fft(b, dps)
|
| 171 |
+
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
| 172 |
+
a = ifft(a, dps)[:m]
|
| 173 |
+
|
| 174 |
+
return a
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
#----------------------------------------------------------------------------#
|
| 178 |
+
# #
|
| 179 |
+
# Convolution for GF(p) #
|
| 180 |
+
# #
|
| 181 |
+
#----------------------------------------------------------------------------#
|
| 182 |
+
|
| 183 |
+
def convolution_ntt(a, b, prime):
|
| 184 |
+
"""
|
| 185 |
+
Performs linear convolution using Number Theoretic Transform.
|
| 186 |
+
|
| 187 |
+
Parameters
|
| 188 |
+
==========
|
| 189 |
+
|
| 190 |
+
a, b : iterables
|
| 191 |
+
The sequences for which convolution is performed.
|
| 192 |
+
prime : Integer
|
| 193 |
+
Prime modulus of the form `(m 2^k + 1)` to be used for performing
|
| 194 |
+
**NTT** on the sequence.
|
| 195 |
+
|
| 196 |
+
Examples
|
| 197 |
+
========
|
| 198 |
+
|
| 199 |
+
>>> from sympy.discrete.convolutions import convolution_ntt
|
| 200 |
+
>>> convolution_ntt([2, 3], [4, 5], prime=19*2**10 + 1)
|
| 201 |
+
[8, 22, 15]
|
| 202 |
+
>>> convolution_ntt([2, 5], [6, 7, 3], prime=19*2**10 + 1)
|
| 203 |
+
[12, 44, 41, 15]
|
| 204 |
+
>>> convolution_ntt([333, 555], [222, 666], prime=19*2**10 + 1)
|
| 205 |
+
[15555, 14219, 19404]
|
| 206 |
+
|
| 207 |
+
References
|
| 208 |
+
==========
|
| 209 |
+
|
| 210 |
+
.. [1] https://en.wikipedia.org/wiki/Convolution_theorem
|
| 211 |
+
.. [2] https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
|
| 212 |
+
|
| 213 |
+
"""
|
| 214 |
+
|
| 215 |
+
a, b, p = a[:], b[:], as_int(prime)
|
| 216 |
+
n = m = len(a) + len(b) - 1 # convolution size
|
| 217 |
+
|
| 218 |
+
if n > 0 and n&(n - 1): # not a power of 2
|
| 219 |
+
n = 2**n.bit_length()
|
| 220 |
+
|
| 221 |
+
# padding with zeros
|
| 222 |
+
a += [0]*(n - len(a))
|
| 223 |
+
b += [0]*(n - len(b))
|
| 224 |
+
|
| 225 |
+
a, b = ntt(a, p), ntt(b, p)
|
| 226 |
+
a = [x*y % p for x, y in zip(a, b)]
|
| 227 |
+
a = intt(a, p)[:m]
|
| 228 |
+
|
| 229 |
+
return a
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
#----------------------------------------------------------------------------#
|
| 233 |
+
# #
|
| 234 |
+
# Convolution for 2**n-group #
|
| 235 |
+
# #
|
| 236 |
+
#----------------------------------------------------------------------------#
|
| 237 |
+
|
| 238 |
+
def convolution_fwht(a, b):
|
| 239 |
+
"""
|
| 240 |
+
Performs dyadic (*bitwise-XOR*) convolution using Fast Walsh Hadamard
|
| 241 |
+
Transform.
|
| 242 |
+
|
| 243 |
+
The convolution is automatically padded to the right with zeros, as the
|
| 244 |
+
*radix-2 FWHT* requires the number of sample points to be a power of 2.
|
| 245 |
+
|
| 246 |
+
Parameters
|
| 247 |
+
==========
|
| 248 |
+
|
| 249 |
+
a, b : iterables
|
| 250 |
+
The sequences for which convolution is performed.
|
| 251 |
+
|
| 252 |
+
Examples
|
| 253 |
+
========
|
| 254 |
+
|
| 255 |
+
>>> from sympy import symbols, S, I
|
| 256 |
+
>>> from sympy.discrete.convolutions import convolution_fwht
|
| 257 |
+
|
| 258 |
+
>>> u, v, x, y = symbols('u v x y')
|
| 259 |
+
>>> convolution_fwht([u, v], [x, y])
|
| 260 |
+
[u*x + v*y, u*y + v*x]
|
| 261 |
+
|
| 262 |
+
>>> convolution_fwht([2, 3], [4, 5])
|
| 263 |
+
[23, 22]
|
| 264 |
+
>>> convolution_fwht([2, 5 + 4*I, 7], [6*I, 7, 3 + 4*I])
|
| 265 |
+
[56 + 68*I, -10 + 30*I, 6 + 50*I, 48 + 32*I]
|
| 266 |
+
|
| 267 |
+
>>> convolution_fwht([S(33)/7, S(55)/6, S(7)/4], [S(2)/3, 5])
|
| 268 |
+
[2057/42, 1870/63, 7/6, 35/4]
|
| 269 |
+
|
| 270 |
+
References
|
| 271 |
+
==========
|
| 272 |
+
|
| 273 |
+
.. [1] https://www.radioeng.cz/fulltexts/2002/02_03_40_42.pdf
|
| 274 |
+
.. [2] https://en.wikipedia.org/wiki/Hadamard_transform
|
| 275 |
+
|
| 276 |
+
"""
|
| 277 |
+
|
| 278 |
+
if not a or not b:
|
| 279 |
+
return []
|
| 280 |
+
|
| 281 |
+
a, b = a[:], b[:]
|
| 282 |
+
n = max(len(a), len(b))
|
| 283 |
+
|
| 284 |
+
if n&(n - 1): # not a power of 2
|
| 285 |
+
n = 2**n.bit_length()
|
| 286 |
+
|
| 287 |
+
# padding with zeros
|
| 288 |
+
a += [S.Zero]*(n - len(a))
|
| 289 |
+
b += [S.Zero]*(n - len(b))
|
| 290 |
+
|
| 291 |
+
a, b = fwht(a), fwht(b)
|
| 292 |
+
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
| 293 |
+
a = ifwht(a)
|
| 294 |
+
|
| 295 |
+
return a
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
#----------------------------------------------------------------------------#
|
| 299 |
+
# #
|
| 300 |
+
# Subset Convolution #
|
| 301 |
+
# #
|
| 302 |
+
#----------------------------------------------------------------------------#
|
| 303 |
+
|
| 304 |
+
def convolution_subset(a, b):
|
| 305 |
+
"""
|
| 306 |
+
Performs Subset Convolution of given sequences.
|
| 307 |
+
|
| 308 |
+
The indices of each argument, considered as bit strings, correspond to
|
| 309 |
+
subsets of a finite set.
|
| 310 |
+
|
| 311 |
+
The sequence is automatically padded to the right with zeros, as the
|
| 312 |
+
definition of subset based on bitmasks (indices) requires the size of
|
| 313 |
+
sequence to be a power of 2.
|
| 314 |
+
|
| 315 |
+
Parameters
|
| 316 |
+
==========
|
| 317 |
+
|
| 318 |
+
a, b : iterables
|
| 319 |
+
The sequences for which convolution is performed.
|
| 320 |
+
|
| 321 |
+
Examples
|
| 322 |
+
========
|
| 323 |
+
|
| 324 |
+
>>> from sympy import symbols, S
|
| 325 |
+
>>> from sympy.discrete.convolutions import convolution_subset
|
| 326 |
+
>>> u, v, x, y, z = symbols('u v x y z')
|
| 327 |
+
|
| 328 |
+
>>> convolution_subset([u, v], [x, y])
|
| 329 |
+
[u*x, u*y + v*x]
|
| 330 |
+
>>> convolution_subset([u, v, x], [y, z])
|
| 331 |
+
[u*y, u*z + v*y, x*y, x*z]
|
| 332 |
+
|
| 333 |
+
>>> convolution_subset([1, S(2)/3], [3, 4])
|
| 334 |
+
[3, 6]
|
| 335 |
+
>>> convolution_subset([1, 3, S(5)/7], [7])
|
| 336 |
+
[7, 21, 5, 0]
|
| 337 |
+
|
| 338 |
+
References
|
| 339 |
+
==========
|
| 340 |
+
|
| 341 |
+
.. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
| 342 |
+
|
| 343 |
+
"""
|
| 344 |
+
|
| 345 |
+
if not a or not b:
|
| 346 |
+
return []
|
| 347 |
+
|
| 348 |
+
if not iterable(a) or not iterable(b):
|
| 349 |
+
raise TypeError("Expected a sequence of coefficients for convolution")
|
| 350 |
+
|
| 351 |
+
a = [sympify(arg) for arg in a]
|
| 352 |
+
b = [sympify(arg) for arg in b]
|
| 353 |
+
n = max(len(a), len(b))
|
| 354 |
+
|
| 355 |
+
if n&(n - 1): # not a power of 2
|
| 356 |
+
n = 2**n.bit_length()
|
| 357 |
+
|
| 358 |
+
# padding with zeros
|
| 359 |
+
a += [S.Zero]*(n - len(a))
|
| 360 |
+
b += [S.Zero]*(n - len(b))
|
| 361 |
+
|
| 362 |
+
c = [S.Zero]*n
|
| 363 |
+
|
| 364 |
+
for mask in range(n):
|
| 365 |
+
smask = mask
|
| 366 |
+
while smask > 0:
|
| 367 |
+
c[mask] += expand_mul(a[smask] * b[mask^smask])
|
| 368 |
+
smask = (smask - 1)&mask
|
| 369 |
+
|
| 370 |
+
c[mask] += expand_mul(a[smask] * b[mask^smask])
|
| 371 |
+
|
| 372 |
+
return c
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
#----------------------------------------------------------------------------#
|
| 376 |
+
# #
|
| 377 |
+
# Covering Product #
|
| 378 |
+
# #
|
| 379 |
+
#----------------------------------------------------------------------------#
|
| 380 |
+
|
| 381 |
+
def covering_product(a, b):
|
| 382 |
+
"""
|
| 383 |
+
Returns the covering product of given sequences.
|
| 384 |
+
|
| 385 |
+
The indices of each argument, considered as bit strings, correspond to
|
| 386 |
+
subsets of a finite set.
|
| 387 |
+
|
| 388 |
+
The covering product of given sequences is a sequence which contains
|
| 389 |
+
the sum of products of the elements of the given sequences grouped by
|
| 390 |
+
the *bitwise-OR* of the corresponding indices.
|
| 391 |
+
|
| 392 |
+
The sequence is automatically padded to the right with zeros, as the
|
| 393 |
+
definition of subset based on bitmasks (indices) requires the size of
|
| 394 |
+
sequence to be a power of 2.
|
| 395 |
+
|
| 396 |
+
Parameters
|
| 397 |
+
==========
|
| 398 |
+
|
| 399 |
+
a, b : iterables
|
| 400 |
+
The sequences for which covering product is to be obtained.
|
| 401 |
+
|
| 402 |
+
Examples
|
| 403 |
+
========
|
| 404 |
+
|
| 405 |
+
>>> from sympy import symbols, S, I, covering_product
|
| 406 |
+
>>> u, v, x, y, z = symbols('u v x y z')
|
| 407 |
+
|
| 408 |
+
>>> covering_product([u, v], [x, y])
|
| 409 |
+
[u*x, u*y + v*x + v*y]
|
| 410 |
+
>>> covering_product([u, v, x], [y, z])
|
| 411 |
+
[u*y, u*z + v*y + v*z, x*y, x*z]
|
| 412 |
+
|
| 413 |
+
>>> covering_product([1, S(2)/3], [3, 4 + 5*I])
|
| 414 |
+
[3, 26/3 + 25*I/3]
|
| 415 |
+
>>> covering_product([1, 3, S(5)/7], [7, 8])
|
| 416 |
+
[7, 53, 5, 40/7]
|
| 417 |
+
|
| 418 |
+
References
|
| 419 |
+
==========
|
| 420 |
+
|
| 421 |
+
.. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
| 422 |
+
|
| 423 |
+
"""
|
| 424 |
+
|
| 425 |
+
if not a or not b:
|
| 426 |
+
return []
|
| 427 |
+
|
| 428 |
+
a, b = a[:], b[:]
|
| 429 |
+
n = max(len(a), len(b))
|
| 430 |
+
|
| 431 |
+
if n&(n - 1): # not a power of 2
|
| 432 |
+
n = 2**n.bit_length()
|
| 433 |
+
|
| 434 |
+
# padding with zeros
|
| 435 |
+
a += [S.Zero]*(n - len(a))
|
| 436 |
+
b += [S.Zero]*(n - len(b))
|
| 437 |
+
|
| 438 |
+
a, b = mobius_transform(a), mobius_transform(b)
|
| 439 |
+
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
| 440 |
+
a = inverse_mobius_transform(a)
|
| 441 |
+
|
| 442 |
+
return a
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
#----------------------------------------------------------------------------#
|
| 446 |
+
# #
|
| 447 |
+
# Intersecting Product #
|
| 448 |
+
# #
|
| 449 |
+
#----------------------------------------------------------------------------#
|
| 450 |
+
|
| 451 |
+
def intersecting_product(a, b):
|
| 452 |
+
"""
|
| 453 |
+
Returns the intersecting product of given sequences.
|
| 454 |
+
|
| 455 |
+
The indices of each argument, considered as bit strings, correspond to
|
| 456 |
+
subsets of a finite set.
|
| 457 |
+
|
| 458 |
+
The intersecting product of given sequences is the sequence which
|
| 459 |
+
contains the sum of products of the elements of the given sequences
|
| 460 |
+
grouped by the *bitwise-AND* of the corresponding indices.
|
| 461 |
+
|
| 462 |
+
The sequence is automatically padded to the right with zeros, as the
|
| 463 |
+
definition of subset based on bitmasks (indices) requires the size of
|
| 464 |
+
sequence to be a power of 2.
|
| 465 |
+
|
| 466 |
+
Parameters
|
| 467 |
+
==========
|
| 468 |
+
|
| 469 |
+
a, b : iterables
|
| 470 |
+
The sequences for which intersecting product is to be obtained.
|
| 471 |
+
|
| 472 |
+
Examples
|
| 473 |
+
========
|
| 474 |
+
|
| 475 |
+
>>> from sympy import symbols, S, I, intersecting_product
|
| 476 |
+
>>> u, v, x, y, z = symbols('u v x y z')
|
| 477 |
+
|
| 478 |
+
>>> intersecting_product([u, v], [x, y])
|
| 479 |
+
[u*x + u*y + v*x, v*y]
|
| 480 |
+
>>> intersecting_product([u, v, x], [y, z])
|
| 481 |
+
[u*y + u*z + v*y + x*y + x*z, v*z, 0, 0]
|
| 482 |
+
|
| 483 |
+
>>> intersecting_product([1, S(2)/3], [3, 4 + 5*I])
|
| 484 |
+
[9 + 5*I, 8/3 + 10*I/3]
|
| 485 |
+
>>> intersecting_product([1, 3, S(5)/7], [7, 8])
|
| 486 |
+
[327/7, 24, 0, 0]
|
| 487 |
+
|
| 488 |
+
References
|
| 489 |
+
==========
|
| 490 |
+
|
| 491 |
+
.. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
| 492 |
+
|
| 493 |
+
"""
|
| 494 |
+
|
| 495 |
+
if not a or not b:
|
| 496 |
+
return []
|
| 497 |
+
|
| 498 |
+
a, b = a[:], b[:]
|
| 499 |
+
n = max(len(a), len(b))
|
| 500 |
+
|
| 501 |
+
if n&(n - 1): # not a power of 2
|
| 502 |
+
n = 2**n.bit_length()
|
| 503 |
+
|
| 504 |
+
# padding with zeros
|
| 505 |
+
a += [S.Zero]*(n - len(a))
|
| 506 |
+
b += [S.Zero]*(n - len(b))
|
| 507 |
+
|
| 508 |
+
a, b = mobius_transform(a, subset=False), mobius_transform(b, subset=False)
|
| 509 |
+
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
| 510 |
+
a = inverse_mobius_transform(a, subset=False)
|
| 511 |
+
|
| 512 |
+
return a
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
#----------------------------------------------------------------------------#
|
| 516 |
+
# #
|
| 517 |
+
# Integer Convolutions #
|
| 518 |
+
# #
|
| 519 |
+
#----------------------------------------------------------------------------#
|
| 520 |
+
|
| 521 |
+
def convolution_int(a, b):
|
| 522 |
+
"""Return the convolution of two sequences as a list.
|
| 523 |
+
|
| 524 |
+
The iterables must consist solely of integers.
|
| 525 |
+
|
| 526 |
+
Parameters
|
| 527 |
+
==========
|
| 528 |
+
|
| 529 |
+
a, b : Sequence
|
| 530 |
+
The sequences for which convolution is performed.
|
| 531 |
+
|
| 532 |
+
Explanation
|
| 533 |
+
===========
|
| 534 |
+
|
| 535 |
+
This function performs the convolution of ``a`` and ``b`` by packing
|
| 536 |
+
each into a single integer, multiplying them together, and then
|
| 537 |
+
unpacking the result from the product. The intuition behind this is
|
| 538 |
+
that if we evaluate some polynomial [1]:
|
| 539 |
+
|
| 540 |
+
.. math ::
|
| 541 |
+
1156x^6 + 3808x^5 + 8440x^4 + 14856x^3 + 16164x^2 + 14040x + 8100
|
| 542 |
+
|
| 543 |
+
at say $x = 10^5$ we obtain $1156038080844014856161641404008100$.
|
| 544 |
+
Note we can read of the coefficients for each term every five digits.
|
| 545 |
+
If the $x$ we chose to evaluate at is large enough, the same will hold
|
| 546 |
+
for the product.
|
| 547 |
+
|
| 548 |
+
The idea now is since big integer multiplication in libraries such
|
| 549 |
+
as GMP is highly optimised, this will be reasonably fast.
|
| 550 |
+
|
| 551 |
+
Examples
|
| 552 |
+
========
|
| 553 |
+
|
| 554 |
+
>>> from sympy.discrete.convolutions import convolution_int
|
| 555 |
+
|
| 556 |
+
>>> convolution_int([2, 3], [4, 5])
|
| 557 |
+
[8, 22, 15]
|
| 558 |
+
>>> convolution_int([1, 1, -1], [1, 1])
|
| 559 |
+
[1, 2, 0, -1]
|
| 560 |
+
|
| 561 |
+
References
|
| 562 |
+
==========
|
| 563 |
+
|
| 564 |
+
.. [1] Fateman, Richard J.
|
| 565 |
+
Can you save time in multiplying polynomials by encoding them as integers?
|
| 566 |
+
University of California, Berkeley, California (2004).
|
| 567 |
+
https://people.eecs.berkeley.edu/~fateman/papers/polysbyGMP.pdf
|
| 568 |
+
"""
|
| 569 |
+
# An upper bound on the largest coefficient in p(x)q(x) is given by (1 + min(dp, dq))N(p)N(q)
|
| 570 |
+
# where dp = deg(p), dq = deg(q), N(f) denotes the coefficient of largest modulus in f [1]
|
| 571 |
+
B = max(abs(c) for c in a)*max(abs(c) for c in b)*(1 + min(len(a) - 1, len(b) - 1))
|
| 572 |
+
x, power = MPZ(1), 0
|
| 573 |
+
while x <= (2*B): # multiply by two for negative coefficients, see [1]
|
| 574 |
+
x <<= 1
|
| 575 |
+
power += 1
|
| 576 |
+
|
| 577 |
+
def to_integer(poly):
|
| 578 |
+
n, mul = MPZ(0), 0
|
| 579 |
+
for c in reversed(poly):
|
| 580 |
+
if c and not mul: mul = -1 if c < 0 else 1
|
| 581 |
+
n <<= power
|
| 582 |
+
n += mul*int(c)
|
| 583 |
+
return mul, n
|
| 584 |
+
|
| 585 |
+
# Perform packing and multiplication
|
| 586 |
+
(a_mul, a_packed), (b_mul, b_packed) = to_integer(a), to_integer(b)
|
| 587 |
+
result = a_packed * b_packed
|
| 588 |
+
|
| 589 |
+
# Perform unpacking
|
| 590 |
+
mul = a_mul * b_mul
|
| 591 |
+
mask, half, borrow, poly = x - 1, x >> 1, 0, []
|
| 592 |
+
while result or borrow:
|
| 593 |
+
coeff = (result & mask) + borrow
|
| 594 |
+
result >>= power
|
| 595 |
+
borrow = coeff >= half
|
| 596 |
+
poly.append(mul * int(coeff if coeff < half else coeff - x))
|
| 597 |
+
return poly or [0]
|
.venv/lib/python3.13/site-packages/sympy/discrete/recurrences.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Recurrences
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from sympy.core import S, sympify
|
| 6 |
+
from sympy.utilities.iterables import iterable
|
| 7 |
+
from sympy.utilities.misc import as_int
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def linrec(coeffs, init, n):
|
| 11 |
+
r"""
|
| 12 |
+
Evaluation of univariate linear recurrences of homogeneous type
|
| 13 |
+
having coefficients independent of the recurrence variable.
|
| 14 |
+
|
| 15 |
+
Parameters
|
| 16 |
+
==========
|
| 17 |
+
|
| 18 |
+
coeffs : iterable
|
| 19 |
+
Coefficients of the recurrence
|
| 20 |
+
init : iterable
|
| 21 |
+
Initial values of the recurrence
|
| 22 |
+
n : Integer
|
| 23 |
+
Point of evaluation for the recurrence
|
| 24 |
+
|
| 25 |
+
Notes
|
| 26 |
+
=====
|
| 27 |
+
|
| 28 |
+
Let `y(n)` be the recurrence of given type, ``c`` be the sequence
|
| 29 |
+
of coefficients, ``b`` be the sequence of initial/base values of the
|
| 30 |
+
recurrence and ``k`` (equal to ``len(c)``) be the order of recurrence.
|
| 31 |
+
Then,
|
| 32 |
+
|
| 33 |
+
.. math :: y(n) = \begin{cases} b_n & 0 \le n < k \\
|
| 34 |
+
c_0 y(n-1) + c_1 y(n-2) + \cdots + c_{k-1} y(n-k) & n \ge k
|
| 35 |
+
\end{cases}
|
| 36 |
+
|
| 37 |
+
Let `x_0, x_1, \ldots, x_n` be a sequence and consider the transformation
|
| 38 |
+
that maps each polynomial `f(x)` to `T(f(x))` where each power `x^i` is
|
| 39 |
+
replaced by the corresponding value `x_i`. The sequence is then a solution
|
| 40 |
+
of the recurrence if and only if `T(x^i p(x)) = 0` for each `i \ge 0` where
|
| 41 |
+
`p(x) = x^k - c_0 x^(k-1) - \cdots - c_{k-1}` is the characteristic
|
| 42 |
+
polynomial.
|
| 43 |
+
|
| 44 |
+
Then `T(f(x)p(x)) = 0` for each polynomial `f(x)` (as it is a linear
|
| 45 |
+
combination of powers `x^i`). Now, if `x^n` is congruent to
|
| 46 |
+
`g(x) = a_0 x^0 + a_1 x^1 + \cdots + a_{k-1} x^{k-1}` modulo `p(x)`, then
|
| 47 |
+
`T(x^n) = x_n` is equal to
|
| 48 |
+
`T(g(x)) = a_0 x_0 + a_1 x_1 + \cdots + a_{k-1} x_{k-1}`.
|
| 49 |
+
|
| 50 |
+
Computation of `x^n`,
|
| 51 |
+
given `x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`
|
| 52 |
+
is performed using exponentiation by squaring (refer to [1_]) with
|
| 53 |
+
an additional reduction step performed to retain only first `k` powers
|
| 54 |
+
of `x` in the representation of `x^n`.
|
| 55 |
+
|
| 56 |
+
Examples
|
| 57 |
+
========
|
| 58 |
+
|
| 59 |
+
>>> from sympy.discrete.recurrences import linrec
|
| 60 |
+
>>> from sympy.abc import x, y, z
|
| 61 |
+
|
| 62 |
+
>>> linrec(coeffs=[1, 1], init=[0, 1], n=10)
|
| 63 |
+
55
|
| 64 |
+
|
| 65 |
+
>>> linrec(coeffs=[1, 1], init=[x, y], n=10)
|
| 66 |
+
34*x + 55*y
|
| 67 |
+
|
| 68 |
+
>>> linrec(coeffs=[x, y], init=[0, 1], n=5)
|
| 69 |
+
x**2*y + x*(x**3 + 2*x*y) + y**2
|
| 70 |
+
|
| 71 |
+
>>> linrec(coeffs=[1, 2, 3, 0, 0, 4], init=[x, y, z], n=16)
|
| 72 |
+
13576*x + 5676*y + 2356*z
|
| 73 |
+
|
| 74 |
+
References
|
| 75 |
+
==========
|
| 76 |
+
|
| 77 |
+
.. [1] https://en.wikipedia.org/wiki/Exponentiation_by_squaring
|
| 78 |
+
.. [2] https://en.wikipedia.org/w/index.php?title=Modular_exponentiation§ion=6#Matrices
|
| 79 |
+
|
| 80 |
+
See Also
|
| 81 |
+
========
|
| 82 |
+
|
| 83 |
+
sympy.polys.agca.extensions.ExtensionElement.__pow__
|
| 84 |
+
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
if not coeffs:
|
| 88 |
+
return S.Zero
|
| 89 |
+
|
| 90 |
+
if not iterable(coeffs):
|
| 91 |
+
raise TypeError("Expected a sequence of coefficients for"
|
| 92 |
+
" the recurrence")
|
| 93 |
+
|
| 94 |
+
if not iterable(init):
|
| 95 |
+
raise TypeError("Expected a sequence of values for the initialization"
|
| 96 |
+
" of the recurrence")
|
| 97 |
+
|
| 98 |
+
n = as_int(n)
|
| 99 |
+
if n < 0:
|
| 100 |
+
raise ValueError("Point of evaluation of recurrence must be a "
|
| 101 |
+
"non-negative integer")
|
| 102 |
+
|
| 103 |
+
c = [sympify(arg) for arg in coeffs]
|
| 104 |
+
b = [sympify(arg) for arg in init]
|
| 105 |
+
k = len(c)
|
| 106 |
+
|
| 107 |
+
if len(b) > k:
|
| 108 |
+
raise TypeError("Count of initial values should not exceed the "
|
| 109 |
+
"order of the recurrence")
|
| 110 |
+
else:
|
| 111 |
+
b += [S.Zero]*(k - len(b)) # remaining initial values default to zero
|
| 112 |
+
|
| 113 |
+
if n < k:
|
| 114 |
+
return b[n]
|
| 115 |
+
terms = [u*v for u, v in zip(linrec_coeffs(c, n), b)]
|
| 116 |
+
return sum(terms[:-1], terms[-1])
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def linrec_coeffs(c, n):
|
| 120 |
+
r"""
|
| 121 |
+
Compute the coefficients of n'th term in linear recursion
|
| 122 |
+
sequence defined by c.
|
| 123 |
+
|
| 124 |
+
`x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`.
|
| 125 |
+
|
| 126 |
+
It computes the coefficients by using binary exponentiation.
|
| 127 |
+
This function is used by `linrec` and `_eval_pow_by_cayley`.
|
| 128 |
+
|
| 129 |
+
Parameters
|
| 130 |
+
==========
|
| 131 |
+
|
| 132 |
+
c = coefficients of the divisor polynomial
|
| 133 |
+
n = exponent of x, so dividend is x^n
|
| 134 |
+
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
k = len(c)
|
| 138 |
+
|
| 139 |
+
def _square_and_reduce(u, offset):
|
| 140 |
+
# squares `(u_0 + u_1 x + u_2 x^2 + \cdots + u_{k-1} x^k)` (and
|
| 141 |
+
# multiplies by `x` if offset is 1) and reduces the above result of
|
| 142 |
+
# length upto `2k` to `k` using the characteristic equation of the
|
| 143 |
+
# recurrence given by, `x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`
|
| 144 |
+
|
| 145 |
+
w = [S.Zero]*(2*len(u) - 1 + offset)
|
| 146 |
+
for i, p in enumerate(u):
|
| 147 |
+
for j, q in enumerate(u):
|
| 148 |
+
w[offset + i + j] += p*q
|
| 149 |
+
|
| 150 |
+
for j in range(len(w) - 1, k - 1, -1):
|
| 151 |
+
for i in range(k):
|
| 152 |
+
w[j - i - 1] += w[j]*c[i]
|
| 153 |
+
|
| 154 |
+
return w[:k]
|
| 155 |
+
|
| 156 |
+
def _final_coeffs(n):
|
| 157 |
+
# computes the final coefficient list - `cf` corresponding to the
|
| 158 |
+
# point at which recurrence is to be evalauted - `n`, such that,
|
| 159 |
+
# `y(n) = cf_0 y(k-1) + cf_1 y(k-2) + \cdots + cf_{k-1} y(0)`
|
| 160 |
+
|
| 161 |
+
if n < k:
|
| 162 |
+
return [S.Zero]*n + [S.One] + [S.Zero]*(k - n - 1)
|
| 163 |
+
else:
|
| 164 |
+
return _square_and_reduce(_final_coeffs(n // 2), n % 2)
|
| 165 |
+
|
| 166 |
+
return _final_coeffs(n)
|
.venv/lib/python3.13/site-packages/sympy/discrete/transforms.py
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Discrete Fourier Transform, Number Theoretic Transform,
|
| 3 |
+
Walsh Hadamard Transform, Mobius Transform
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from sympy.core import S, Symbol, sympify
|
| 7 |
+
from sympy.core.function import expand_mul
|
| 8 |
+
from sympy.core.numbers import pi, I
|
| 9 |
+
from sympy.functions.elementary.trigonometric import sin, cos
|
| 10 |
+
from sympy.ntheory import isprime, primitive_root
|
| 11 |
+
from sympy.utilities.iterables import ibin, iterable
|
| 12 |
+
from sympy.utilities.misc import as_int
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
#----------------------------------------------------------------------------#
|
| 16 |
+
# #
|
| 17 |
+
# Discrete Fourier Transform #
|
| 18 |
+
# #
|
| 19 |
+
#----------------------------------------------------------------------------#
|
| 20 |
+
|
| 21 |
+
def _fourier_transform(seq, dps, inverse=False):
|
| 22 |
+
"""Utility function for the Discrete Fourier Transform"""
|
| 23 |
+
|
| 24 |
+
if not iterable(seq):
|
| 25 |
+
raise TypeError("Expected a sequence of numeric coefficients "
|
| 26 |
+
"for Fourier Transform")
|
| 27 |
+
|
| 28 |
+
a = [sympify(arg) for arg in seq]
|
| 29 |
+
if any(x.has(Symbol) for x in a):
|
| 30 |
+
raise ValueError("Expected non-symbolic coefficients")
|
| 31 |
+
|
| 32 |
+
n = len(a)
|
| 33 |
+
if n < 2:
|
| 34 |
+
return a
|
| 35 |
+
|
| 36 |
+
b = n.bit_length() - 1
|
| 37 |
+
if n&(n - 1): # not a power of 2
|
| 38 |
+
b += 1
|
| 39 |
+
n = 2**b
|
| 40 |
+
|
| 41 |
+
a += [S.Zero]*(n - len(a))
|
| 42 |
+
for i in range(1, n):
|
| 43 |
+
j = int(ibin(i, b, str=True)[::-1], 2)
|
| 44 |
+
if i < j:
|
| 45 |
+
a[i], a[j] = a[j], a[i]
|
| 46 |
+
|
| 47 |
+
ang = -2*pi/n if inverse else 2*pi/n
|
| 48 |
+
|
| 49 |
+
if dps is not None:
|
| 50 |
+
ang = ang.evalf(dps + 2)
|
| 51 |
+
|
| 52 |
+
w = [cos(ang*i) + I*sin(ang*i) for i in range(n // 2)]
|
| 53 |
+
|
| 54 |
+
h = 2
|
| 55 |
+
while h <= n:
|
| 56 |
+
hf, ut = h // 2, n // h
|
| 57 |
+
for i in range(0, n, h):
|
| 58 |
+
for j in range(hf):
|
| 59 |
+
u, v = a[i + j], expand_mul(a[i + j + hf]*w[ut * j])
|
| 60 |
+
a[i + j], a[i + j + hf] = u + v, u - v
|
| 61 |
+
h *= 2
|
| 62 |
+
|
| 63 |
+
if inverse:
|
| 64 |
+
a = [(x/n).evalf(dps) for x in a] if dps is not None \
|
| 65 |
+
else [x/n for x in a]
|
| 66 |
+
|
| 67 |
+
return a
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def fft(seq, dps=None):
|
| 71 |
+
r"""
|
| 72 |
+
Performs the Discrete Fourier Transform (**DFT**) in the complex domain.
|
| 73 |
+
|
| 74 |
+
The sequence is automatically padded to the right with zeros, as the
|
| 75 |
+
*radix-2 FFT* requires the number of sample points to be a power of 2.
|
| 76 |
+
|
| 77 |
+
This method should be used with default arguments only for short sequences
|
| 78 |
+
as the complexity of expressions increases with the size of the sequence.
|
| 79 |
+
|
| 80 |
+
Parameters
|
| 81 |
+
==========
|
| 82 |
+
|
| 83 |
+
seq : iterable
|
| 84 |
+
The sequence on which **DFT** is to be applied.
|
| 85 |
+
dps : Integer
|
| 86 |
+
Specifies the number of decimal digits for precision.
|
| 87 |
+
|
| 88 |
+
Examples
|
| 89 |
+
========
|
| 90 |
+
|
| 91 |
+
>>> from sympy import fft, ifft
|
| 92 |
+
|
| 93 |
+
>>> fft([1, 2, 3, 4])
|
| 94 |
+
[10, -2 - 2*I, -2, -2 + 2*I]
|
| 95 |
+
>>> ifft(_)
|
| 96 |
+
[1, 2, 3, 4]
|
| 97 |
+
|
| 98 |
+
>>> ifft([1, 2, 3, 4])
|
| 99 |
+
[5/2, -1/2 + I/2, -1/2, -1/2 - I/2]
|
| 100 |
+
>>> fft(_)
|
| 101 |
+
[1, 2, 3, 4]
|
| 102 |
+
|
| 103 |
+
>>> ifft([1, 7, 3, 4], dps=15)
|
| 104 |
+
[3.75, -0.5 - 0.75*I, -1.75, -0.5 + 0.75*I]
|
| 105 |
+
>>> fft(_)
|
| 106 |
+
[1.0, 7.0, 3.0, 4.0]
|
| 107 |
+
|
| 108 |
+
References
|
| 109 |
+
==========
|
| 110 |
+
|
| 111 |
+
.. [1] https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm
|
| 112 |
+
.. [2] https://mathworld.wolfram.com/FastFourierTransform.html
|
| 113 |
+
|
| 114 |
+
"""
|
| 115 |
+
|
| 116 |
+
return _fourier_transform(seq, dps=dps)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def ifft(seq, dps=None):
|
| 120 |
+
return _fourier_transform(seq, dps=dps, inverse=True)
|
| 121 |
+
|
| 122 |
+
ifft.__doc__ = fft.__doc__
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
#----------------------------------------------------------------------------#
|
| 126 |
+
# #
|
| 127 |
+
# Number Theoretic Transform #
|
| 128 |
+
# #
|
| 129 |
+
#----------------------------------------------------------------------------#
|
| 130 |
+
|
| 131 |
+
def _number_theoretic_transform(seq, prime, inverse=False):
|
| 132 |
+
"""Utility function for the Number Theoretic Transform"""
|
| 133 |
+
|
| 134 |
+
if not iterable(seq):
|
| 135 |
+
raise TypeError("Expected a sequence of integer coefficients "
|
| 136 |
+
"for Number Theoretic Transform")
|
| 137 |
+
|
| 138 |
+
p = as_int(prime)
|
| 139 |
+
if not isprime(p):
|
| 140 |
+
raise ValueError("Expected prime modulus for "
|
| 141 |
+
"Number Theoretic Transform")
|
| 142 |
+
|
| 143 |
+
a = [as_int(x) % p for x in seq]
|
| 144 |
+
|
| 145 |
+
n = len(a)
|
| 146 |
+
if n < 1:
|
| 147 |
+
return a
|
| 148 |
+
|
| 149 |
+
b = n.bit_length() - 1
|
| 150 |
+
if n&(n - 1):
|
| 151 |
+
b += 1
|
| 152 |
+
n = 2**b
|
| 153 |
+
|
| 154 |
+
if (p - 1) % n:
|
| 155 |
+
raise ValueError("Expected prime modulus of the form (m*2**k + 1)")
|
| 156 |
+
|
| 157 |
+
a += [0]*(n - len(a))
|
| 158 |
+
for i in range(1, n):
|
| 159 |
+
j = int(ibin(i, b, str=True)[::-1], 2)
|
| 160 |
+
if i < j:
|
| 161 |
+
a[i], a[j] = a[j], a[i]
|
| 162 |
+
|
| 163 |
+
pr = primitive_root(p)
|
| 164 |
+
|
| 165 |
+
rt = pow(pr, (p - 1) // n, p)
|
| 166 |
+
if inverse:
|
| 167 |
+
rt = pow(rt, p - 2, p)
|
| 168 |
+
|
| 169 |
+
w = [1]*(n // 2)
|
| 170 |
+
for i in range(1, n // 2):
|
| 171 |
+
w[i] = w[i - 1]*rt % p
|
| 172 |
+
|
| 173 |
+
h = 2
|
| 174 |
+
while h <= n:
|
| 175 |
+
hf, ut = h // 2, n // h
|
| 176 |
+
for i in range(0, n, h):
|
| 177 |
+
for j in range(hf):
|
| 178 |
+
u, v = a[i + j], a[i + j + hf]*w[ut * j]
|
| 179 |
+
a[i + j], a[i + j + hf] = (u + v) % p, (u - v) % p
|
| 180 |
+
h *= 2
|
| 181 |
+
|
| 182 |
+
if inverse:
|
| 183 |
+
rv = pow(n, p - 2, p)
|
| 184 |
+
a = [x*rv % p for x in a]
|
| 185 |
+
|
| 186 |
+
return a
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def ntt(seq, prime):
|
| 190 |
+
r"""
|
| 191 |
+
Performs the Number Theoretic Transform (**NTT**), which specializes the
|
| 192 |
+
Discrete Fourier Transform (**DFT**) over quotient ring `Z/pZ` for prime
|
| 193 |
+
`p` instead of complex numbers `C`.
|
| 194 |
+
|
| 195 |
+
The sequence is automatically padded to the right with zeros, as the
|
| 196 |
+
*radix-2 NTT* requires the number of sample points to be a power of 2.
|
| 197 |
+
|
| 198 |
+
Parameters
|
| 199 |
+
==========
|
| 200 |
+
|
| 201 |
+
seq : iterable
|
| 202 |
+
The sequence on which **DFT** is to be applied.
|
| 203 |
+
prime : Integer
|
| 204 |
+
Prime modulus of the form `(m 2^k + 1)` to be used for performing
|
| 205 |
+
**NTT** on the sequence.
|
| 206 |
+
|
| 207 |
+
Examples
|
| 208 |
+
========
|
| 209 |
+
|
| 210 |
+
>>> from sympy import ntt, intt
|
| 211 |
+
>>> ntt([1, 2, 3, 4], prime=3*2**8 + 1)
|
| 212 |
+
[10, 643, 767, 122]
|
| 213 |
+
>>> intt(_, 3*2**8 + 1)
|
| 214 |
+
[1, 2, 3, 4]
|
| 215 |
+
>>> intt([1, 2, 3, 4], prime=3*2**8 + 1)
|
| 216 |
+
[387, 415, 384, 353]
|
| 217 |
+
>>> ntt(_, prime=3*2**8 + 1)
|
| 218 |
+
[1, 2, 3, 4]
|
| 219 |
+
|
| 220 |
+
References
|
| 221 |
+
==========
|
| 222 |
+
|
| 223 |
+
.. [1] http://www.apfloat.org/ntt.html
|
| 224 |
+
.. [2] https://mathworld.wolfram.com/NumberTheoreticTransform.html
|
| 225 |
+
.. [3] https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
|
| 226 |
+
|
| 227 |
+
"""
|
| 228 |
+
|
| 229 |
+
return _number_theoretic_transform(seq, prime=prime)
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def intt(seq, prime):
|
| 233 |
+
return _number_theoretic_transform(seq, prime=prime, inverse=True)
|
| 234 |
+
|
| 235 |
+
intt.__doc__ = ntt.__doc__
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
#----------------------------------------------------------------------------#
|
| 239 |
+
# #
|
| 240 |
+
# Walsh Hadamard Transform #
|
| 241 |
+
# #
|
| 242 |
+
#----------------------------------------------------------------------------#
|
| 243 |
+
|
| 244 |
+
def _walsh_hadamard_transform(seq, inverse=False):
|
| 245 |
+
"""Utility function for the Walsh Hadamard Transform"""
|
| 246 |
+
|
| 247 |
+
if not iterable(seq):
|
| 248 |
+
raise TypeError("Expected a sequence of coefficients "
|
| 249 |
+
"for Walsh Hadamard Transform")
|
| 250 |
+
|
| 251 |
+
a = [sympify(arg) for arg in seq]
|
| 252 |
+
n = len(a)
|
| 253 |
+
if n < 2:
|
| 254 |
+
return a
|
| 255 |
+
|
| 256 |
+
if n&(n - 1):
|
| 257 |
+
n = 2**n.bit_length()
|
| 258 |
+
|
| 259 |
+
a += [S.Zero]*(n - len(a))
|
| 260 |
+
h = 2
|
| 261 |
+
while h <= n:
|
| 262 |
+
hf = h // 2
|
| 263 |
+
for i in range(0, n, h):
|
| 264 |
+
for j in range(hf):
|
| 265 |
+
u, v = a[i + j], a[i + j + hf]
|
| 266 |
+
a[i + j], a[i + j + hf] = u + v, u - v
|
| 267 |
+
h *= 2
|
| 268 |
+
|
| 269 |
+
if inverse:
|
| 270 |
+
a = [x/n for x in a]
|
| 271 |
+
|
| 272 |
+
return a
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def fwht(seq):
|
| 276 |
+
r"""
|
| 277 |
+
Performs the Walsh Hadamard Transform (**WHT**), and uses Hadamard
|
| 278 |
+
ordering for the sequence.
|
| 279 |
+
|
| 280 |
+
The sequence is automatically padded to the right with zeros, as the
|
| 281 |
+
*radix-2 FWHT* requires the number of sample points to be a power of 2.
|
| 282 |
+
|
| 283 |
+
Parameters
|
| 284 |
+
==========
|
| 285 |
+
|
| 286 |
+
seq : iterable
|
| 287 |
+
The sequence on which WHT is to be applied.
|
| 288 |
+
|
| 289 |
+
Examples
|
| 290 |
+
========
|
| 291 |
+
|
| 292 |
+
>>> from sympy import fwht, ifwht
|
| 293 |
+
>>> fwht([4, 2, 2, 0, 0, 2, -2, 0])
|
| 294 |
+
[8, 0, 8, 0, 8, 8, 0, 0]
|
| 295 |
+
>>> ifwht(_)
|
| 296 |
+
[4, 2, 2, 0, 0, 2, -2, 0]
|
| 297 |
+
|
| 298 |
+
>>> ifwht([19, -1, 11, -9, -7, 13, -15, 5])
|
| 299 |
+
[2, 0, 4, 0, 3, 10, 0, 0]
|
| 300 |
+
>>> fwht(_)
|
| 301 |
+
[19, -1, 11, -9, -7, 13, -15, 5]
|
| 302 |
+
|
| 303 |
+
References
|
| 304 |
+
==========
|
| 305 |
+
|
| 306 |
+
.. [1] https://en.wikipedia.org/wiki/Hadamard_transform
|
| 307 |
+
.. [2] https://en.wikipedia.org/wiki/Fast_Walsh%E2%80%93Hadamard_transform
|
| 308 |
+
|
| 309 |
+
"""
|
| 310 |
+
|
| 311 |
+
return _walsh_hadamard_transform(seq)
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def ifwht(seq):
|
| 315 |
+
return _walsh_hadamard_transform(seq, inverse=True)
|
| 316 |
+
|
| 317 |
+
ifwht.__doc__ = fwht.__doc__
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
#----------------------------------------------------------------------------#
|
| 321 |
+
# #
|
| 322 |
+
# Mobius Transform for Subset Lattice #
|
| 323 |
+
# #
|
| 324 |
+
#----------------------------------------------------------------------------#
|
| 325 |
+
|
| 326 |
+
def _mobius_transform(seq, sgn, subset):
|
| 327 |
+
r"""Utility function for performing Mobius Transform using
|
| 328 |
+
Yate's Dynamic Programming method"""
|
| 329 |
+
|
| 330 |
+
if not iterable(seq):
|
| 331 |
+
raise TypeError("Expected a sequence of coefficients")
|
| 332 |
+
|
| 333 |
+
a = [sympify(arg) for arg in seq]
|
| 334 |
+
|
| 335 |
+
n = len(a)
|
| 336 |
+
if n < 2:
|
| 337 |
+
return a
|
| 338 |
+
|
| 339 |
+
if n&(n - 1):
|
| 340 |
+
n = 2**n.bit_length()
|
| 341 |
+
|
| 342 |
+
a += [S.Zero]*(n - len(a))
|
| 343 |
+
|
| 344 |
+
if subset:
|
| 345 |
+
i = 1
|
| 346 |
+
while i < n:
|
| 347 |
+
for j in range(n):
|
| 348 |
+
if j & i:
|
| 349 |
+
a[j] += sgn*a[j ^ i]
|
| 350 |
+
i *= 2
|
| 351 |
+
|
| 352 |
+
else:
|
| 353 |
+
i = 1
|
| 354 |
+
while i < n:
|
| 355 |
+
for j in range(n):
|
| 356 |
+
if j & i:
|
| 357 |
+
continue
|
| 358 |
+
a[j] += sgn*a[j ^ i]
|
| 359 |
+
i *= 2
|
| 360 |
+
|
| 361 |
+
return a
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def mobius_transform(seq, subset=True):
|
| 365 |
+
r"""
|
| 366 |
+
Performs the Mobius Transform for subset lattice with indices of
|
| 367 |
+
sequence as bitmasks.
|
| 368 |
+
|
| 369 |
+
The indices of each argument, considered as bit strings, correspond
|
| 370 |
+
to subsets of a finite set.
|
| 371 |
+
|
| 372 |
+
The sequence is automatically padded to the right with zeros, as the
|
| 373 |
+
definition of subset/superset based on bitmasks (indices) requires
|
| 374 |
+
the size of sequence to be a power of 2.
|
| 375 |
+
|
| 376 |
+
Parameters
|
| 377 |
+
==========
|
| 378 |
+
|
| 379 |
+
seq : iterable
|
| 380 |
+
The sequence on which Mobius Transform is to be applied.
|
| 381 |
+
subset : bool
|
| 382 |
+
Specifies if Mobius Transform is applied by enumerating subsets
|
| 383 |
+
or supersets of the given set.
|
| 384 |
+
|
| 385 |
+
Examples
|
| 386 |
+
========
|
| 387 |
+
|
| 388 |
+
>>> from sympy import symbols
|
| 389 |
+
>>> from sympy import mobius_transform, inverse_mobius_transform
|
| 390 |
+
>>> x, y, z = symbols('x y z')
|
| 391 |
+
|
| 392 |
+
>>> mobius_transform([x, y, z])
|
| 393 |
+
[x, x + y, x + z, x + y + z]
|
| 394 |
+
>>> inverse_mobius_transform(_)
|
| 395 |
+
[x, y, z, 0]
|
| 396 |
+
|
| 397 |
+
>>> mobius_transform([x, y, z], subset=False)
|
| 398 |
+
[x + y + z, y, z, 0]
|
| 399 |
+
>>> inverse_mobius_transform(_, subset=False)
|
| 400 |
+
[x, y, z, 0]
|
| 401 |
+
|
| 402 |
+
>>> mobius_transform([1, 2, 3, 4])
|
| 403 |
+
[1, 3, 4, 10]
|
| 404 |
+
>>> inverse_mobius_transform(_)
|
| 405 |
+
[1, 2, 3, 4]
|
| 406 |
+
>>> mobius_transform([1, 2, 3, 4], subset=False)
|
| 407 |
+
[10, 6, 7, 4]
|
| 408 |
+
>>> inverse_mobius_transform(_, subset=False)
|
| 409 |
+
[1, 2, 3, 4]
|
| 410 |
+
|
| 411 |
+
References
|
| 412 |
+
==========
|
| 413 |
+
|
| 414 |
+
.. [1] https://en.wikipedia.org/wiki/M%C3%B6bius_inversion_formula
|
| 415 |
+
.. [2] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
| 416 |
+
.. [3] https://arxiv.org/pdf/1211.0189.pdf
|
| 417 |
+
|
| 418 |
+
"""
|
| 419 |
+
|
| 420 |
+
return _mobius_transform(seq, sgn=+1, subset=subset)
|
| 421 |
+
|
| 422 |
+
def inverse_mobius_transform(seq, subset=True):
|
| 423 |
+
return _mobius_transform(seq, sgn=-1, subset=subset)
|
| 424 |
+
|
| 425 |
+
inverse_mobius_transform.__doc__ = mobius_transform.__doc__
|
.venv/lib/python3.13/site-packages/sympy/ntheory/modular.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from math import prod
|
| 2 |
+
|
| 3 |
+
from sympy.external.gmpy import gcd, gcdext
|
| 4 |
+
from sympy.ntheory.primetest import isprime
|
| 5 |
+
from sympy.polys.domains import ZZ
|
| 6 |
+
from sympy.polys.galoistools import gf_crt, gf_crt1, gf_crt2
|
| 7 |
+
from sympy.utilities.misc import as_int
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def symmetric_residue(a, m):
|
| 11 |
+
"""Return the residual mod m such that it is within half of the modulus.
|
| 12 |
+
|
| 13 |
+
>>> from sympy.ntheory.modular import symmetric_residue
|
| 14 |
+
>>> symmetric_residue(1, 6)
|
| 15 |
+
1
|
| 16 |
+
>>> symmetric_residue(4, 6)
|
| 17 |
+
-2
|
| 18 |
+
"""
|
| 19 |
+
if a <= m // 2:
|
| 20 |
+
return a
|
| 21 |
+
return a - m
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def crt(m, v, symmetric=False, check=True):
|
| 25 |
+
r"""Chinese Remainder Theorem.
|
| 26 |
+
|
| 27 |
+
The moduli in m are assumed to be pairwise coprime. The output
|
| 28 |
+
is then an integer f, such that f = v_i mod m_i for each pair out
|
| 29 |
+
of v and m. If ``symmetric`` is False a positive integer will be
|
| 30 |
+
returned, else \|f\| will be less than or equal to the LCM of the
|
| 31 |
+
moduli, and thus f may be negative.
|
| 32 |
+
|
| 33 |
+
If the moduli are not co-prime the correct result will be returned
|
| 34 |
+
if/when the test of the result is found to be incorrect. This result
|
| 35 |
+
will be None if there is no solution.
|
| 36 |
+
|
| 37 |
+
The keyword ``check`` can be set to False if it is known that the moduli
|
| 38 |
+
are coprime.
|
| 39 |
+
|
| 40 |
+
Examples
|
| 41 |
+
========
|
| 42 |
+
|
| 43 |
+
As an example consider a set of residues ``U = [49, 76, 65]``
|
| 44 |
+
and a set of moduli ``M = [99, 97, 95]``. Then we have::
|
| 45 |
+
|
| 46 |
+
>>> from sympy.ntheory.modular import crt
|
| 47 |
+
|
| 48 |
+
>>> crt([99, 97, 95], [49, 76, 65])
|
| 49 |
+
(639985, 912285)
|
| 50 |
+
|
| 51 |
+
This is the correct result because::
|
| 52 |
+
|
| 53 |
+
>>> [639985 % m for m in [99, 97, 95]]
|
| 54 |
+
[49, 76, 65]
|
| 55 |
+
|
| 56 |
+
If the moduli are not co-prime, you may receive an incorrect result
|
| 57 |
+
if you use ``check=False``:
|
| 58 |
+
|
| 59 |
+
>>> crt([12, 6, 17], [3, 4, 2], check=False)
|
| 60 |
+
(954, 1224)
|
| 61 |
+
>>> [954 % m for m in [12, 6, 17]]
|
| 62 |
+
[6, 0, 2]
|
| 63 |
+
>>> crt([12, 6, 17], [3, 4, 2]) is None
|
| 64 |
+
True
|
| 65 |
+
>>> crt([3, 6], [2, 5])
|
| 66 |
+
(5, 6)
|
| 67 |
+
|
| 68 |
+
Note: the order of gf_crt's arguments is reversed relative to crt,
|
| 69 |
+
and that solve_congruence takes residue, modulus pairs.
|
| 70 |
+
|
| 71 |
+
Programmer's note: rather than checking that all pairs of moduli share
|
| 72 |
+
no GCD (an O(n**2) test) and rather than factoring all moduli and seeing
|
| 73 |
+
that there is no factor in common, a check that the result gives the
|
| 74 |
+
indicated residuals is performed -- an O(n) operation.
|
| 75 |
+
|
| 76 |
+
See Also
|
| 77 |
+
========
|
| 78 |
+
|
| 79 |
+
solve_congruence
|
| 80 |
+
sympy.polys.galoistools.gf_crt : low level crt routine used by this routine
|
| 81 |
+
"""
|
| 82 |
+
if check:
|
| 83 |
+
m = list(map(as_int, m))
|
| 84 |
+
v = list(map(as_int, v))
|
| 85 |
+
|
| 86 |
+
result = gf_crt(v, m, ZZ)
|
| 87 |
+
mm = prod(m)
|
| 88 |
+
|
| 89 |
+
if check:
|
| 90 |
+
if not all(v % m == result % m for v, m in zip(v, m)):
|
| 91 |
+
result = solve_congruence(*list(zip(v, m)),
|
| 92 |
+
check=False, symmetric=symmetric)
|
| 93 |
+
if result is None:
|
| 94 |
+
return result
|
| 95 |
+
result, mm = result
|
| 96 |
+
|
| 97 |
+
if symmetric:
|
| 98 |
+
return int(symmetric_residue(result, mm)), int(mm)
|
| 99 |
+
return int(result), int(mm)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def crt1(m):
|
| 103 |
+
"""First part of Chinese Remainder Theorem, for multiple application.
|
| 104 |
+
|
| 105 |
+
Examples
|
| 106 |
+
========
|
| 107 |
+
|
| 108 |
+
>>> from sympy.ntheory.modular import crt, crt1, crt2
|
| 109 |
+
>>> m = [99, 97, 95]
|
| 110 |
+
>>> v = [49, 76, 65]
|
| 111 |
+
|
| 112 |
+
The following two codes have the same result.
|
| 113 |
+
|
| 114 |
+
>>> crt(m, v)
|
| 115 |
+
(639985, 912285)
|
| 116 |
+
|
| 117 |
+
>>> mm, e, s = crt1(m)
|
| 118 |
+
>>> crt2(m, v, mm, e, s)
|
| 119 |
+
(639985, 912285)
|
| 120 |
+
|
| 121 |
+
However, it is faster when we want to fix ``m`` and
|
| 122 |
+
compute for multiple ``v``, i.e. the following cases:
|
| 123 |
+
|
| 124 |
+
>>> mm, e, s = crt1(m)
|
| 125 |
+
>>> vs = [[52, 21, 37], [19, 46, 76]]
|
| 126 |
+
>>> for v in vs:
|
| 127 |
+
... print(crt2(m, v, mm, e, s))
|
| 128 |
+
(397042, 912285)
|
| 129 |
+
(803206, 912285)
|
| 130 |
+
|
| 131 |
+
See Also
|
| 132 |
+
========
|
| 133 |
+
|
| 134 |
+
sympy.polys.galoistools.gf_crt1 : low level crt routine used by this routine
|
| 135 |
+
sympy.ntheory.modular.crt
|
| 136 |
+
sympy.ntheory.modular.crt2
|
| 137 |
+
|
| 138 |
+
"""
|
| 139 |
+
|
| 140 |
+
return gf_crt1(m, ZZ)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def crt2(m, v, mm, e, s, symmetric=False):
|
| 144 |
+
"""Second part of Chinese Remainder Theorem, for multiple application.
|
| 145 |
+
|
| 146 |
+
See ``crt1`` for usage.
|
| 147 |
+
|
| 148 |
+
Examples
|
| 149 |
+
========
|
| 150 |
+
|
| 151 |
+
>>> from sympy.ntheory.modular import crt1, crt2
|
| 152 |
+
>>> mm, e, s = crt1([18, 42, 6])
|
| 153 |
+
>>> crt2([18, 42, 6], [0, 0, 0], mm, e, s)
|
| 154 |
+
(0, 4536)
|
| 155 |
+
|
| 156 |
+
See Also
|
| 157 |
+
========
|
| 158 |
+
|
| 159 |
+
sympy.polys.galoistools.gf_crt2 : low level crt routine used by this routine
|
| 160 |
+
sympy.ntheory.modular.crt
|
| 161 |
+
sympy.ntheory.modular.crt1
|
| 162 |
+
|
| 163 |
+
"""
|
| 164 |
+
|
| 165 |
+
result = gf_crt2(v, m, mm, e, s, ZZ)
|
| 166 |
+
|
| 167 |
+
if symmetric:
|
| 168 |
+
return int(symmetric_residue(result, mm)), int(mm)
|
| 169 |
+
return int(result), int(mm)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def solve_congruence(*remainder_modulus_pairs, **hint):
|
| 173 |
+
"""Compute the integer ``n`` that has the residual ``ai`` when it is
|
| 174 |
+
divided by ``mi`` where the ``ai`` and ``mi`` are given as pairs to
|
| 175 |
+
this function: ((a1, m1), (a2, m2), ...). If there is no solution,
|
| 176 |
+
return None. Otherwise return ``n`` and its modulus.
|
| 177 |
+
|
| 178 |
+
The ``mi`` values need not be co-prime. If it is known that the moduli are
|
| 179 |
+
not co-prime then the hint ``check`` can be set to False (default=True) and
|
| 180 |
+
the check for a quicker solution via crt() (valid when the moduli are
|
| 181 |
+
co-prime) will be skipped.
|
| 182 |
+
|
| 183 |
+
If the hint ``symmetric`` is True (default is False), the value of ``n``
|
| 184 |
+
will be within 1/2 of the modulus, possibly negative.
|
| 185 |
+
|
| 186 |
+
Examples
|
| 187 |
+
========
|
| 188 |
+
|
| 189 |
+
>>> from sympy.ntheory.modular import solve_congruence
|
| 190 |
+
|
| 191 |
+
What number is 2 mod 3, 3 mod 5 and 2 mod 7?
|
| 192 |
+
|
| 193 |
+
>>> solve_congruence((2, 3), (3, 5), (2, 7))
|
| 194 |
+
(23, 105)
|
| 195 |
+
>>> [23 % m for m in [3, 5, 7]]
|
| 196 |
+
[2, 3, 2]
|
| 197 |
+
|
| 198 |
+
If you prefer to work with all remainder in one list and
|
| 199 |
+
all moduli in another, send the arguments like this:
|
| 200 |
+
|
| 201 |
+
>>> solve_congruence(*zip((2, 3, 2), (3, 5, 7)))
|
| 202 |
+
(23, 105)
|
| 203 |
+
|
| 204 |
+
The moduli need not be co-prime; in this case there may or
|
| 205 |
+
may not be a solution:
|
| 206 |
+
|
| 207 |
+
>>> solve_congruence((2, 3), (4, 6)) is None
|
| 208 |
+
True
|
| 209 |
+
|
| 210 |
+
>>> solve_congruence((2, 3), (5, 6))
|
| 211 |
+
(5, 6)
|
| 212 |
+
|
| 213 |
+
The symmetric flag will make the result be within 1/2 of the modulus:
|
| 214 |
+
|
| 215 |
+
>>> solve_congruence((2, 3), (5, 6), symmetric=True)
|
| 216 |
+
(-1, 6)
|
| 217 |
+
|
| 218 |
+
See Also
|
| 219 |
+
========
|
| 220 |
+
|
| 221 |
+
crt : high level routine implementing the Chinese Remainder Theorem
|
| 222 |
+
|
| 223 |
+
"""
|
| 224 |
+
def combine(c1, c2):
|
| 225 |
+
"""Return the tuple (a, m) which satisfies the requirement
|
| 226 |
+
that n = a + i*m satisfy n = a1 + j*m1 and n = a2 = k*m2.
|
| 227 |
+
|
| 228 |
+
References
|
| 229 |
+
==========
|
| 230 |
+
|
| 231 |
+
.. [1] https://en.wikipedia.org/wiki/Method_of_successive_substitution
|
| 232 |
+
"""
|
| 233 |
+
a1, m1 = c1
|
| 234 |
+
a2, m2 = c2
|
| 235 |
+
a, b, c = m1, a2 - a1, m2
|
| 236 |
+
g = gcd(a, b, c)
|
| 237 |
+
a, b, c = [i//g for i in [a, b, c]]
|
| 238 |
+
if a != 1:
|
| 239 |
+
g, inv_a, _ = gcdext(a, c)
|
| 240 |
+
if g != 1:
|
| 241 |
+
return None
|
| 242 |
+
b *= inv_a
|
| 243 |
+
a, m = a1 + m1*b, m1*c
|
| 244 |
+
return a, m
|
| 245 |
+
|
| 246 |
+
rm = remainder_modulus_pairs
|
| 247 |
+
symmetric = hint.get('symmetric', False)
|
| 248 |
+
|
| 249 |
+
if hint.get('check', True):
|
| 250 |
+
rm = [(as_int(r), as_int(m)) for r, m in rm]
|
| 251 |
+
|
| 252 |
+
# ignore redundant pairs but raise an error otherwise; also
|
| 253 |
+
# make sure that a unique set of bases is sent to gf_crt if
|
| 254 |
+
# they are all prime.
|
| 255 |
+
#
|
| 256 |
+
# The routine will work out less-trivial violations and
|
| 257 |
+
# return None, e.g. for the pairs (1,3) and (14,42) there
|
| 258 |
+
# is no answer because 14 mod 42 (having a gcd of 14) implies
|
| 259 |
+
# (14/2) mod (42/2), (14/7) mod (42/7) and (14/14) mod (42/14)
|
| 260 |
+
# which, being 0 mod 3, is inconsistent with 1 mod 3. But to
|
| 261 |
+
# preprocess the input beyond checking of another pair with 42
|
| 262 |
+
# or 3 as the modulus (for this example) is not necessary.
|
| 263 |
+
uniq = {}
|
| 264 |
+
for r, m in rm:
|
| 265 |
+
r %= m
|
| 266 |
+
if m in uniq:
|
| 267 |
+
if r != uniq[m]:
|
| 268 |
+
return None
|
| 269 |
+
continue
|
| 270 |
+
uniq[m] = r
|
| 271 |
+
rm = [(r, m) for m, r in uniq.items()]
|
| 272 |
+
del uniq
|
| 273 |
+
|
| 274 |
+
# if the moduli are co-prime, the crt will be significantly faster;
|
| 275 |
+
# checking all pairs for being co-prime gets to be slow but a prime
|
| 276 |
+
# test is a good trade-off
|
| 277 |
+
if all(isprime(m) for r, m in rm):
|
| 278 |
+
r, m = list(zip(*rm))
|
| 279 |
+
return crt(m, r, symmetric=symmetric, check=False)
|
| 280 |
+
|
| 281 |
+
rv = (0, 1)
|
| 282 |
+
for rmi in rm:
|
| 283 |
+
rv = combine(rv, rmi)
|
| 284 |
+
if rv is None:
|
| 285 |
+
break
|
| 286 |
+
n, m = rv
|
| 287 |
+
n = n % m
|
| 288 |
+
else:
|
| 289 |
+
if symmetric:
|
| 290 |
+
return symmetric_residue(n, m), m
|
| 291 |
+
return n, m
|
.venv/lib/python3.13/site-packages/sympy/printing/aesaracode.py
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
import math
|
| 3 |
+
from typing import Any
|
| 4 |
+
|
| 5 |
+
from sympy.external import import_module
|
| 6 |
+
from sympy.printing.printer import Printer
|
| 7 |
+
from sympy.utilities.exceptions import sympy_deprecation_warning
|
| 8 |
+
from sympy.utilities.iterables import is_sequence
|
| 9 |
+
import sympy
|
| 10 |
+
from functools import partial
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
aesara = import_module('aesara')
|
| 14 |
+
|
| 15 |
+
if aesara:
|
| 16 |
+
aes = aesara.scalar
|
| 17 |
+
aet = aesara.tensor
|
| 18 |
+
from aesara.tensor import nlinalg
|
| 19 |
+
from aesara.tensor.elemwise import Elemwise
|
| 20 |
+
from aesara.tensor.elemwise import DimShuffle
|
| 21 |
+
|
| 22 |
+
# `true_divide` replaced `true_div` in Aesara 2.8.11 (released 2023) to
|
| 23 |
+
# match NumPy
|
| 24 |
+
# XXX: Remove this when not needed to support older versions.
|
| 25 |
+
true_divide = getattr(aet, 'true_divide', None)
|
| 26 |
+
if true_divide is None:
|
| 27 |
+
true_divide = aet.true_div
|
| 28 |
+
|
| 29 |
+
mapping = {
|
| 30 |
+
sympy.Add: aet.add,
|
| 31 |
+
sympy.Mul: aet.mul,
|
| 32 |
+
sympy.Abs: aet.abs,
|
| 33 |
+
sympy.sign: aet.sgn,
|
| 34 |
+
sympy.ceiling: aet.ceil,
|
| 35 |
+
sympy.floor: aet.floor,
|
| 36 |
+
sympy.log: aet.log,
|
| 37 |
+
sympy.exp: aet.exp,
|
| 38 |
+
sympy.sqrt: aet.sqrt,
|
| 39 |
+
sympy.cos: aet.cos,
|
| 40 |
+
sympy.acos: aet.arccos,
|
| 41 |
+
sympy.sin: aet.sin,
|
| 42 |
+
sympy.asin: aet.arcsin,
|
| 43 |
+
sympy.tan: aet.tan,
|
| 44 |
+
sympy.atan: aet.arctan,
|
| 45 |
+
sympy.atan2: aet.arctan2,
|
| 46 |
+
sympy.cosh: aet.cosh,
|
| 47 |
+
sympy.acosh: aet.arccosh,
|
| 48 |
+
sympy.sinh: aet.sinh,
|
| 49 |
+
sympy.asinh: aet.arcsinh,
|
| 50 |
+
sympy.tanh: aet.tanh,
|
| 51 |
+
sympy.atanh: aet.arctanh,
|
| 52 |
+
sympy.re: aet.real,
|
| 53 |
+
sympy.im: aet.imag,
|
| 54 |
+
sympy.arg: aet.angle,
|
| 55 |
+
sympy.erf: aet.erf,
|
| 56 |
+
sympy.gamma: aet.gamma,
|
| 57 |
+
sympy.loggamma: aet.gammaln,
|
| 58 |
+
sympy.Pow: aet.pow,
|
| 59 |
+
sympy.Eq: aet.eq,
|
| 60 |
+
sympy.StrictGreaterThan: aet.gt,
|
| 61 |
+
sympy.StrictLessThan: aet.lt,
|
| 62 |
+
sympy.LessThan: aet.le,
|
| 63 |
+
sympy.GreaterThan: aet.ge,
|
| 64 |
+
sympy.And: aet.bitwise_and, # bitwise
|
| 65 |
+
sympy.Or: aet.bitwise_or, # bitwise
|
| 66 |
+
sympy.Not: aet.invert, # bitwise
|
| 67 |
+
sympy.Xor: aet.bitwise_xor, # bitwise
|
| 68 |
+
sympy.Max: aet.maximum, # Sympy accept >2 inputs, Aesara only 2
|
| 69 |
+
sympy.Min: aet.minimum, # Sympy accept >2 inputs, Aesara only 2
|
| 70 |
+
sympy.conjugate: aet.conj,
|
| 71 |
+
sympy.core.numbers.ImaginaryUnit: lambda:aet.complex(0,1),
|
| 72 |
+
# Matrices
|
| 73 |
+
sympy.MatAdd: Elemwise(aes.add),
|
| 74 |
+
sympy.HadamardProduct: Elemwise(aes.mul),
|
| 75 |
+
sympy.Trace: nlinalg.trace,
|
| 76 |
+
sympy.Determinant : nlinalg.det,
|
| 77 |
+
sympy.Inverse: nlinalg.matrix_inverse,
|
| 78 |
+
sympy.Transpose: DimShuffle((False, False), [1, 0]),
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class AesaraPrinter(Printer):
|
| 83 |
+
"""
|
| 84 |
+
.. deprecated:: 1.14.
|
| 85 |
+
The ``Aesara Code printing`` is deprecated.See its documentation for
|
| 86 |
+
more information. See :ref:`deprecated-aesaraprinter` for details.
|
| 87 |
+
|
| 88 |
+
Code printer which creates Aesara symbolic expression graphs.
|
| 89 |
+
|
| 90 |
+
Parameters
|
| 91 |
+
==========
|
| 92 |
+
|
| 93 |
+
cache : dict
|
| 94 |
+
Cache dictionary to use. If None (default) will use
|
| 95 |
+
the global cache. To create a printer which does not depend on or alter
|
| 96 |
+
global state pass an empty dictionary. Note: the dictionary is not
|
| 97 |
+
copied on initialization of the printer and will be updated in-place,
|
| 98 |
+
so using the same dict object when creating multiple printers or making
|
| 99 |
+
multiple calls to :func:`.aesara_code` or :func:`.aesara_function` means
|
| 100 |
+
the cache is shared between all these applications.
|
| 101 |
+
|
| 102 |
+
Attributes
|
| 103 |
+
==========
|
| 104 |
+
|
| 105 |
+
cache : dict
|
| 106 |
+
A cache of Aesara variables which have been created for SymPy
|
| 107 |
+
symbol-like objects (e.g. :class:`sympy.core.symbol.Symbol` or
|
| 108 |
+
:class:`sympy.matrices.expressions.MatrixSymbol`). This is used to
|
| 109 |
+
ensure that all references to a given symbol in an expression (or
|
| 110 |
+
multiple expressions) are printed as the same Aesara variable, which is
|
| 111 |
+
created only once. Symbols are differentiated only by name and type. The
|
| 112 |
+
format of the cache's contents should be considered opaque to the user.
|
| 113 |
+
"""
|
| 114 |
+
printmethod = "_aesara"
|
| 115 |
+
|
| 116 |
+
def __init__(self, *args, **kwargs):
|
| 117 |
+
self.cache = kwargs.pop('cache', {})
|
| 118 |
+
super().__init__(*args, **kwargs)
|
| 119 |
+
|
| 120 |
+
def _get_key(self, s, name=None, dtype=None, broadcastable=None):
|
| 121 |
+
""" Get the cache key for a SymPy object.
|
| 122 |
+
|
| 123 |
+
Parameters
|
| 124 |
+
==========
|
| 125 |
+
|
| 126 |
+
s : sympy.core.basic.Basic
|
| 127 |
+
SymPy object to get key for.
|
| 128 |
+
|
| 129 |
+
name : str
|
| 130 |
+
Name of object, if it does not have a ``name`` attribute.
|
| 131 |
+
"""
|
| 132 |
+
|
| 133 |
+
if name is None:
|
| 134 |
+
name = s.name
|
| 135 |
+
|
| 136 |
+
return (name, type(s), s.args, dtype, broadcastable)
|
| 137 |
+
|
| 138 |
+
def _get_or_create(self, s, name=None, dtype=None, broadcastable=None):
|
| 139 |
+
"""
|
| 140 |
+
Get the Aesara variable for a SymPy symbol from the cache, or create it
|
| 141 |
+
if it does not exist.
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
# Defaults
|
| 145 |
+
if name is None:
|
| 146 |
+
name = s.name
|
| 147 |
+
if dtype is None:
|
| 148 |
+
dtype = 'floatX'
|
| 149 |
+
if broadcastable is None:
|
| 150 |
+
broadcastable = ()
|
| 151 |
+
|
| 152 |
+
key = self._get_key(s, name, dtype=dtype, broadcastable=broadcastable)
|
| 153 |
+
|
| 154 |
+
if key in self.cache:
|
| 155 |
+
return self.cache[key]
|
| 156 |
+
|
| 157 |
+
value = aet.tensor(name=name, dtype=dtype, shape=broadcastable)
|
| 158 |
+
self.cache[key] = value
|
| 159 |
+
return value
|
| 160 |
+
|
| 161 |
+
def _print_Symbol(self, s, **kwargs):
|
| 162 |
+
dtype = kwargs.get('dtypes', {}).get(s)
|
| 163 |
+
bc = kwargs.get('broadcastables', {}).get(s)
|
| 164 |
+
return self._get_or_create(s, dtype=dtype, broadcastable=bc)
|
| 165 |
+
|
| 166 |
+
def _print_AppliedUndef(self, s, **kwargs):
|
| 167 |
+
name = str(type(s)) + '_' + str(s.args[0])
|
| 168 |
+
dtype = kwargs.get('dtypes', {}).get(s)
|
| 169 |
+
bc = kwargs.get('broadcastables', {}).get(s)
|
| 170 |
+
return self._get_or_create(s, name=name, dtype=dtype, broadcastable=bc)
|
| 171 |
+
|
| 172 |
+
def _print_Basic(self, expr, **kwargs):
|
| 173 |
+
op = mapping[type(expr)]
|
| 174 |
+
children = [self._print(arg, **kwargs) for arg in expr.args]
|
| 175 |
+
return op(*children)
|
| 176 |
+
|
| 177 |
+
def _print_Number(self, n, **kwargs):
|
| 178 |
+
# Integers already taken care of below, interpret as float
|
| 179 |
+
return float(n.evalf())
|
| 180 |
+
|
| 181 |
+
def _print_MatrixSymbol(self, X, **kwargs):
|
| 182 |
+
dtype = kwargs.get('dtypes', {}).get(X)
|
| 183 |
+
return self._get_or_create(X, dtype=dtype, broadcastable=(None, None))
|
| 184 |
+
|
| 185 |
+
def _print_DenseMatrix(self, X, **kwargs):
|
| 186 |
+
if not hasattr(aet, 'stacklists'):
|
| 187 |
+
raise NotImplementedError(
|
| 188 |
+
"Matrix translation not yet supported in this version of Aesara")
|
| 189 |
+
|
| 190 |
+
return aet.stacklists([
|
| 191 |
+
[self._print(arg, **kwargs) for arg in L]
|
| 192 |
+
for L in X.tolist()
|
| 193 |
+
])
|
| 194 |
+
|
| 195 |
+
_print_ImmutableMatrix = _print_ImmutableDenseMatrix = _print_DenseMatrix
|
| 196 |
+
|
| 197 |
+
def _print_MatMul(self, expr, **kwargs):
|
| 198 |
+
children = [self._print(arg, **kwargs) for arg in expr.args]
|
| 199 |
+
result = children[0]
|
| 200 |
+
for child in children[1:]:
|
| 201 |
+
result = aet.dot(result, child)
|
| 202 |
+
return result
|
| 203 |
+
|
| 204 |
+
def _print_MatPow(self, expr, **kwargs):
|
| 205 |
+
children = [self._print(arg, **kwargs) for arg in expr.args]
|
| 206 |
+
result = 1
|
| 207 |
+
if isinstance(children[1], int) and children[1] > 0:
|
| 208 |
+
for i in range(children[1]):
|
| 209 |
+
result = aet.dot(result, children[0])
|
| 210 |
+
else:
|
| 211 |
+
raise NotImplementedError('''Only non-negative integer
|
| 212 |
+
powers of matrices can be handled by Aesara at the moment''')
|
| 213 |
+
return result
|
| 214 |
+
|
| 215 |
+
def _print_MatrixSlice(self, expr, **kwargs):
|
| 216 |
+
parent = self._print(expr.parent, **kwargs)
|
| 217 |
+
rowslice = self._print(slice(*expr.rowslice), **kwargs)
|
| 218 |
+
colslice = self._print(slice(*expr.colslice), **kwargs)
|
| 219 |
+
return parent[rowslice, colslice]
|
| 220 |
+
|
| 221 |
+
def _print_BlockMatrix(self, expr, **kwargs):
|
| 222 |
+
nrows, ncols = expr.blocks.shape
|
| 223 |
+
blocks = [[self._print(expr.blocks[r, c], **kwargs)
|
| 224 |
+
for c in range(ncols)]
|
| 225 |
+
for r in range(nrows)]
|
| 226 |
+
return aet.join(0, *[aet.join(1, *row) for row in blocks])
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def _print_slice(self, expr, **kwargs):
|
| 230 |
+
return slice(*[self._print(i, **kwargs)
|
| 231 |
+
if isinstance(i, sympy.Basic) else i
|
| 232 |
+
for i in (expr.start, expr.stop, expr.step)])
|
| 233 |
+
|
| 234 |
+
def _print_Pi(self, expr, **kwargs):
|
| 235 |
+
return math.pi
|
| 236 |
+
|
| 237 |
+
def _print_Piecewise(self, expr, **kwargs):
|
| 238 |
+
import numpy as np
|
| 239 |
+
e, cond = expr.args[0].args # First condition and corresponding value
|
| 240 |
+
|
| 241 |
+
# Print conditional expression and value for first condition
|
| 242 |
+
p_cond = self._print(cond, **kwargs)
|
| 243 |
+
p_e = self._print(e, **kwargs)
|
| 244 |
+
|
| 245 |
+
# One condition only
|
| 246 |
+
if len(expr.args) == 1:
|
| 247 |
+
# Return value if condition else NaN
|
| 248 |
+
return aet.switch(p_cond, p_e, np.nan)
|
| 249 |
+
|
| 250 |
+
# Return value_1 if condition_1 else evaluate remaining conditions
|
| 251 |
+
p_remaining = self._print(sympy.Piecewise(*expr.args[1:]), **kwargs)
|
| 252 |
+
return aet.switch(p_cond, p_e, p_remaining)
|
| 253 |
+
|
| 254 |
+
def _print_Rational(self, expr, **kwargs):
|
| 255 |
+
return true_divide(self._print(expr.p, **kwargs),
|
| 256 |
+
self._print(expr.q, **kwargs))
|
| 257 |
+
|
| 258 |
+
def _print_Integer(self, expr, **kwargs):
|
| 259 |
+
return expr.p
|
| 260 |
+
|
| 261 |
+
def _print_factorial(self, expr, **kwargs):
|
| 262 |
+
return self._print(sympy.gamma(expr.args[0] + 1), **kwargs)
|
| 263 |
+
|
| 264 |
+
def _print_Derivative(self, deriv, **kwargs):
|
| 265 |
+
from aesara.gradient import Rop
|
| 266 |
+
|
| 267 |
+
rv = self._print(deriv.expr, **kwargs)
|
| 268 |
+
for var in deriv.variables:
|
| 269 |
+
var = self._print(var, **kwargs)
|
| 270 |
+
rv = Rop(rv, var, aet.ones_like(var))
|
| 271 |
+
return rv
|
| 272 |
+
|
| 273 |
+
def emptyPrinter(self, expr):
|
| 274 |
+
return expr
|
| 275 |
+
|
| 276 |
+
def doprint(self, expr, dtypes=None, broadcastables=None):
|
| 277 |
+
""" Convert a SymPy expression to a Aesara graph variable.
|
| 278 |
+
|
| 279 |
+
The ``dtypes`` and ``broadcastables`` arguments are used to specify the
|
| 280 |
+
data type, dimension, and broadcasting behavior of the Aesara variables
|
| 281 |
+
corresponding to the free symbols in ``expr``. Each is a mapping from
|
| 282 |
+
SymPy symbols to the value of the corresponding argument to
|
| 283 |
+
``aesara.tensor.var.TensorVariable``.
|
| 284 |
+
|
| 285 |
+
See the corresponding `documentation page`__ for more information on
|
| 286 |
+
broadcasting in Aesara.
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
.. __: https://aesara.readthedocs.io/en/latest/reference/tensor/shapes.html#broadcasting
|
| 290 |
+
|
| 291 |
+
Parameters
|
| 292 |
+
==========
|
| 293 |
+
|
| 294 |
+
expr : sympy.core.expr.Expr
|
| 295 |
+
SymPy expression to print.
|
| 296 |
+
|
| 297 |
+
dtypes : dict
|
| 298 |
+
Mapping from SymPy symbols to Aesara datatypes to use when creating
|
| 299 |
+
new Aesara variables for those symbols. Corresponds to the ``dtype``
|
| 300 |
+
argument to ``aesara.tensor.var.TensorVariable``. Defaults to ``'floatX'``
|
| 301 |
+
for symbols not included in the mapping.
|
| 302 |
+
|
| 303 |
+
broadcastables : dict
|
| 304 |
+
Mapping from SymPy symbols to the value of the ``broadcastable``
|
| 305 |
+
argument to ``aesara.tensor.var.TensorVariable`` to use when creating Aesara
|
| 306 |
+
variables for those symbols. Defaults to the empty tuple for symbols
|
| 307 |
+
not included in the mapping (resulting in a scalar).
|
| 308 |
+
|
| 309 |
+
Returns
|
| 310 |
+
=======
|
| 311 |
+
|
| 312 |
+
aesara.graph.basic.Variable
|
| 313 |
+
A variable corresponding to the expression's value in a Aesara
|
| 314 |
+
symbolic expression graph.
|
| 315 |
+
|
| 316 |
+
"""
|
| 317 |
+
if dtypes is None:
|
| 318 |
+
dtypes = {}
|
| 319 |
+
if broadcastables is None:
|
| 320 |
+
broadcastables = {}
|
| 321 |
+
|
| 322 |
+
return self._print(expr, dtypes=dtypes, broadcastables=broadcastables)
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
global_cache: dict[Any, Any] = {}
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def aesara_code(expr, cache=None, **kwargs):
|
| 329 |
+
"""
|
| 330 |
+
Convert a SymPy expression into a Aesara graph variable.
|
| 331 |
+
|
| 332 |
+
Parameters
|
| 333 |
+
==========
|
| 334 |
+
|
| 335 |
+
expr : sympy.core.expr.Expr
|
| 336 |
+
SymPy expression object to convert.
|
| 337 |
+
|
| 338 |
+
cache : dict
|
| 339 |
+
Cached Aesara variables (see :class:`AesaraPrinter.cache
|
| 340 |
+
<AesaraPrinter>`). Defaults to the module-level global cache.
|
| 341 |
+
|
| 342 |
+
dtypes : dict
|
| 343 |
+
Passed to :meth:`.AesaraPrinter.doprint`.
|
| 344 |
+
|
| 345 |
+
broadcastables : dict
|
| 346 |
+
Passed to :meth:`.AesaraPrinter.doprint`.
|
| 347 |
+
|
| 348 |
+
Returns
|
| 349 |
+
=======
|
| 350 |
+
|
| 351 |
+
aesara.graph.basic.Variable
|
| 352 |
+
A variable corresponding to the expression's value in a Aesara symbolic
|
| 353 |
+
expression graph.
|
| 354 |
+
|
| 355 |
+
"""
|
| 356 |
+
sympy_deprecation_warning(
|
| 357 |
+
"""
|
| 358 |
+
The aesara_code function is deprecated.
|
| 359 |
+
""",
|
| 360 |
+
deprecated_since_version="1.14",
|
| 361 |
+
active_deprecations_target='deprecated-aesaraprinter',
|
| 362 |
+
)
|
| 363 |
+
|
| 364 |
+
if not aesara:
|
| 365 |
+
raise ImportError("aesara is required for aesara_code")
|
| 366 |
+
|
| 367 |
+
if cache is None:
|
| 368 |
+
cache = global_cache
|
| 369 |
+
|
| 370 |
+
return AesaraPrinter(cache=cache, settings={}).doprint(expr, **kwargs)
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
def dim_handling(inputs, dim=None, dims=None, broadcastables=None):
|
| 374 |
+
r"""
|
| 375 |
+
Get value of ``broadcastables`` argument to :func:`.aesara_code` from
|
| 376 |
+
keyword arguments to :func:`.aesara_function`.
|
| 377 |
+
|
| 378 |
+
Included for backwards compatibility.
|
| 379 |
+
|
| 380 |
+
Parameters
|
| 381 |
+
==========
|
| 382 |
+
|
| 383 |
+
inputs
|
| 384 |
+
Sequence of input symbols.
|
| 385 |
+
|
| 386 |
+
dim : int
|
| 387 |
+
Common number of dimensions for all inputs. Overrides other arguments
|
| 388 |
+
if given.
|
| 389 |
+
|
| 390 |
+
dims : dict
|
| 391 |
+
Mapping from input symbols to number of dimensions. Overrides
|
| 392 |
+
``broadcastables`` argument if given.
|
| 393 |
+
|
| 394 |
+
broadcastables : dict
|
| 395 |
+
Explicit value of ``broadcastables`` argument to
|
| 396 |
+
:meth:`.AesaraPrinter.doprint`. If not None function will return this value unchanged.
|
| 397 |
+
|
| 398 |
+
Returns
|
| 399 |
+
=======
|
| 400 |
+
dict
|
| 401 |
+
Dictionary mapping elements of ``inputs`` to their "broadcastable"
|
| 402 |
+
values (tuple of ``bool``\ s).
|
| 403 |
+
"""
|
| 404 |
+
if dim is not None:
|
| 405 |
+
return dict.fromkeys(inputs, (False,) * dim)
|
| 406 |
+
|
| 407 |
+
if dims is not None:
|
| 408 |
+
maxdim = max(dims.values())
|
| 409 |
+
return {
|
| 410 |
+
s: (False,) * d + (True,) * (maxdim - d)
|
| 411 |
+
for s, d in dims.items()
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
if broadcastables is not None:
|
| 415 |
+
return broadcastables
|
| 416 |
+
|
| 417 |
+
return {}
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
def aesara_function(inputs, outputs, scalar=False, *,
|
| 421 |
+
dim=None, dims=None, broadcastables=None, **kwargs):
|
| 422 |
+
"""
|
| 423 |
+
Create a Aesara function from SymPy expressions.
|
| 424 |
+
|
| 425 |
+
The inputs and outputs are converted to Aesara variables using
|
| 426 |
+
:func:`.aesara_code` and then passed to ``aesara.function``.
|
| 427 |
+
|
| 428 |
+
Parameters
|
| 429 |
+
==========
|
| 430 |
+
|
| 431 |
+
inputs
|
| 432 |
+
Sequence of symbols which constitute the inputs of the function.
|
| 433 |
+
|
| 434 |
+
outputs
|
| 435 |
+
Sequence of expressions which constitute the outputs(s) of the
|
| 436 |
+
function. The free symbols of each expression must be a subset of
|
| 437 |
+
``inputs``.
|
| 438 |
+
|
| 439 |
+
scalar : bool
|
| 440 |
+
Convert 0-dimensional arrays in output to scalars. This will return a
|
| 441 |
+
Python wrapper function around the Aesara function object.
|
| 442 |
+
|
| 443 |
+
cache : dict
|
| 444 |
+
Cached Aesara variables (see :class:`AesaraPrinter.cache
|
| 445 |
+
<AesaraPrinter>`). Defaults to the module-level global cache.
|
| 446 |
+
|
| 447 |
+
dtypes : dict
|
| 448 |
+
Passed to :meth:`.AesaraPrinter.doprint`.
|
| 449 |
+
|
| 450 |
+
broadcastables : dict
|
| 451 |
+
Passed to :meth:`.AesaraPrinter.doprint`.
|
| 452 |
+
|
| 453 |
+
dims : dict
|
| 454 |
+
Alternative to ``broadcastables`` argument. Mapping from elements of
|
| 455 |
+
``inputs`` to integers indicating the dimension of their associated
|
| 456 |
+
arrays/tensors. Overrides ``broadcastables`` argument if given.
|
| 457 |
+
|
| 458 |
+
dim : int
|
| 459 |
+
Another alternative to the ``broadcastables`` argument. Common number of
|
| 460 |
+
dimensions to use for all arrays/tensors.
|
| 461 |
+
``aesara_function([x, y], [...], dim=2)`` is equivalent to using
|
| 462 |
+
``broadcastables={x: (False, False), y: (False, False)}``.
|
| 463 |
+
|
| 464 |
+
Returns
|
| 465 |
+
=======
|
| 466 |
+
callable
|
| 467 |
+
A callable object which takes values of ``inputs`` as positional
|
| 468 |
+
arguments and returns an output array for each of the expressions
|
| 469 |
+
in ``outputs``. If ``outputs`` is a single expression the function will
|
| 470 |
+
return a Numpy array, if it is a list of multiple expressions the
|
| 471 |
+
function will return a list of arrays. See description of the ``squeeze``
|
| 472 |
+
argument above for the behavior when a single output is passed in a list.
|
| 473 |
+
The returned object will either be an instance of
|
| 474 |
+
``aesara.compile.function.types.Function`` or a Python wrapper
|
| 475 |
+
function around one. In both cases, the returned value will have a
|
| 476 |
+
``aesara_function`` attribute which points to the return value of
|
| 477 |
+
``aesara.function``.
|
| 478 |
+
|
| 479 |
+
Examples
|
| 480 |
+
========
|
| 481 |
+
|
| 482 |
+
>>> from sympy.abc import x, y, z
|
| 483 |
+
>>> from sympy.printing.aesaracode import aesara_function
|
| 484 |
+
|
| 485 |
+
A simple function with one input and one output:
|
| 486 |
+
|
| 487 |
+
>>> f1 = aesara_function([x], [x**2 - 1], scalar=True)
|
| 488 |
+
>>> f1(3)
|
| 489 |
+
8.0
|
| 490 |
+
|
| 491 |
+
A function with multiple inputs and one output:
|
| 492 |
+
|
| 493 |
+
>>> f2 = aesara_function([x, y, z], [(x**z + y**z)**(1/z)], scalar=True)
|
| 494 |
+
>>> f2(3, 4, 2)
|
| 495 |
+
5.0
|
| 496 |
+
|
| 497 |
+
A function with multiple inputs and multiple outputs:
|
| 498 |
+
|
| 499 |
+
>>> f3 = aesara_function([x, y], [x**2 + y**2, x**2 - y**2], scalar=True)
|
| 500 |
+
>>> f3(2, 3)
|
| 501 |
+
[13.0, -5.0]
|
| 502 |
+
|
| 503 |
+
See also
|
| 504 |
+
========
|
| 505 |
+
|
| 506 |
+
dim_handling
|
| 507 |
+
|
| 508 |
+
"""
|
| 509 |
+
sympy_deprecation_warning(
|
| 510 |
+
"""
|
| 511 |
+
The aesara_function function is deprecated.
|
| 512 |
+
""",
|
| 513 |
+
deprecated_since_version="1.14",
|
| 514 |
+
active_deprecations_target='deprecated-aesaraprinter',
|
| 515 |
+
)
|
| 516 |
+
|
| 517 |
+
if not aesara:
|
| 518 |
+
raise ImportError("Aesara is required for aesara_function")
|
| 519 |
+
|
| 520 |
+
# Pop off non-aesara keyword args
|
| 521 |
+
cache = kwargs.pop('cache', {})
|
| 522 |
+
dtypes = kwargs.pop('dtypes', {})
|
| 523 |
+
|
| 524 |
+
broadcastables = dim_handling(
|
| 525 |
+
inputs, dim=dim, dims=dims, broadcastables=broadcastables,
|
| 526 |
+
)
|
| 527 |
+
|
| 528 |
+
# Print inputs/outputs
|
| 529 |
+
code = partial(aesara_code, cache=cache, dtypes=dtypes,
|
| 530 |
+
broadcastables=broadcastables)
|
| 531 |
+
tinputs = list(map(code, inputs))
|
| 532 |
+
toutputs = list(map(code, outputs))
|
| 533 |
+
|
| 534 |
+
#fix constant expressions as variables
|
| 535 |
+
toutputs = [output if isinstance(output, aesara.graph.basic.Variable) else aet.as_tensor_variable(output) for output in toutputs]
|
| 536 |
+
|
| 537 |
+
if len(toutputs) == 1:
|
| 538 |
+
toutputs = toutputs[0]
|
| 539 |
+
|
| 540 |
+
# Compile aesara func
|
| 541 |
+
func = aesara.function(tinputs, toutputs, **kwargs)
|
| 542 |
+
|
| 543 |
+
is_0d = [len(o.variable.broadcastable) == 0 for o in func.outputs]
|
| 544 |
+
|
| 545 |
+
# No wrapper required
|
| 546 |
+
if not scalar or not any(is_0d):
|
| 547 |
+
func.aesara_function = func
|
| 548 |
+
return func
|
| 549 |
+
|
| 550 |
+
# Create wrapper to convert 0-dimensional outputs to scalars
|
| 551 |
+
def wrapper(*args):
|
| 552 |
+
out = func(*args)
|
| 553 |
+
# out can be array(1.0) or [array(1.0), array(2.0)]
|
| 554 |
+
|
| 555 |
+
if is_sequence(out):
|
| 556 |
+
return [o[()] if is_0d[i] else o for i, o in enumerate(out)]
|
| 557 |
+
else:
|
| 558 |
+
return out[()]
|
| 559 |
+
|
| 560 |
+
wrapper.__wrapped__ = func
|
| 561 |
+
wrapper.__doc__ = func.__doc__
|
| 562 |
+
wrapper.aesara_function = func
|
| 563 |
+
return wrapper
|
.venv/lib/python3.13/site-packages/sympy/printing/cxx.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
C++ code printer
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from itertools import chain
|
| 6 |
+
from sympy.codegen.ast import Type, none
|
| 7 |
+
from .codeprinter import requires
|
| 8 |
+
from .c import C89CodePrinter, C99CodePrinter
|
| 9 |
+
|
| 10 |
+
# These are defined in the other file so we can avoid importing sympy.codegen
|
| 11 |
+
# from the top-level 'import sympy'. Export them here as well.
|
| 12 |
+
from sympy.printing.codeprinter import cxxcode # noqa:F401
|
| 13 |
+
|
| 14 |
+
# from https://en.cppreference.com/w/cpp/keyword
|
| 15 |
+
reserved = {
|
| 16 |
+
'C++98': [
|
| 17 |
+
'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', 'bool', 'break',
|
| 18 |
+
'case', 'catch,', 'char', 'class', 'compl', 'const', 'const_cast',
|
| 19 |
+
'continue', 'default', 'delete', 'do', 'double', 'dynamic_cast',
|
| 20 |
+
'else', 'enum', 'explicit', 'export', 'extern', 'false', 'float',
|
| 21 |
+
'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable',
|
| 22 |
+
'namespace', 'new', 'not', 'not_eq', 'operator', 'or', 'or_eq',
|
| 23 |
+
'private', 'protected', 'public', 'register', 'reinterpret_cast',
|
| 24 |
+
'return', 'short', 'signed', 'sizeof', 'static', 'static_cast',
|
| 25 |
+
'struct', 'switch', 'template', 'this', 'throw', 'true', 'try',
|
| 26 |
+
'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using',
|
| 27 |
+
'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'
|
| 28 |
+
]
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
reserved['C++11'] = reserved['C++98'][:] + [
|
| 32 |
+
'alignas', 'alignof', 'char16_t', 'char32_t', 'constexpr', 'decltype',
|
| 33 |
+
'noexcept', 'nullptr', 'static_assert', 'thread_local'
|
| 34 |
+
]
|
| 35 |
+
reserved['C++17'] = reserved['C++11'][:]
|
| 36 |
+
reserved['C++17'].remove('register')
|
| 37 |
+
# TM TS: atomic_cancel, atomic_commit, atomic_noexcept, synchronized
|
| 38 |
+
# concepts TS: concept, requires
|
| 39 |
+
# module TS: import, module
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
_math_functions = {
|
| 43 |
+
'C++98': {
|
| 44 |
+
'Mod': 'fmod',
|
| 45 |
+
'ceiling': 'ceil',
|
| 46 |
+
},
|
| 47 |
+
'C++11': {
|
| 48 |
+
'gamma': 'tgamma',
|
| 49 |
+
},
|
| 50 |
+
'C++17': {
|
| 51 |
+
'beta': 'beta',
|
| 52 |
+
'Ei': 'expint',
|
| 53 |
+
'zeta': 'riemann_zeta',
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
# from https://en.cppreference.com/w/cpp/header/cmath
|
| 58 |
+
for k in ('Abs', 'exp', 'log', 'log10', 'sqrt', 'sin', 'cos', 'tan', # 'Pow'
|
| 59 |
+
'asin', 'acos', 'atan', 'atan2', 'sinh', 'cosh', 'tanh', 'floor'):
|
| 60 |
+
_math_functions['C++98'][k] = k.lower()
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
for k in ('asinh', 'acosh', 'atanh', 'erf', 'erfc'):
|
| 64 |
+
_math_functions['C++11'][k] = k.lower()
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def _attach_print_method(cls, sympy_name, func_name):
|
| 68 |
+
meth_name = '_print_%s' % sympy_name
|
| 69 |
+
if hasattr(cls, meth_name):
|
| 70 |
+
raise ValueError("Edit method (or subclass) instead of overwriting.")
|
| 71 |
+
def _print_method(self, expr):
|
| 72 |
+
return '{}{}({})'.format(self._ns, func_name, ', '.join(map(self._print, expr.args)))
|
| 73 |
+
_print_method.__doc__ = "Prints code for %s" % k
|
| 74 |
+
setattr(cls, meth_name, _print_method)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def _attach_print_methods(cls, cont):
|
| 78 |
+
for sympy_name, cxx_name in cont[cls.standard].items():
|
| 79 |
+
_attach_print_method(cls, sympy_name, cxx_name)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class _CXXCodePrinterBase:
|
| 83 |
+
printmethod = "_cxxcode"
|
| 84 |
+
language = 'C++'
|
| 85 |
+
_ns = 'std::' # namespace
|
| 86 |
+
|
| 87 |
+
def __init__(self, settings=None):
|
| 88 |
+
super().__init__(settings or {})
|
| 89 |
+
|
| 90 |
+
@requires(headers={'algorithm'})
|
| 91 |
+
def _print_Max(self, expr):
|
| 92 |
+
from sympy.functions.elementary.miscellaneous import Max
|
| 93 |
+
if len(expr.args) == 1:
|
| 94 |
+
return self._print(expr.args[0])
|
| 95 |
+
return "%smax(%s, %s)" % (self._ns, self._print(expr.args[0]),
|
| 96 |
+
self._print(Max(*expr.args[1:])))
|
| 97 |
+
|
| 98 |
+
@requires(headers={'algorithm'})
|
| 99 |
+
def _print_Min(self, expr):
|
| 100 |
+
from sympy.functions.elementary.miscellaneous import Min
|
| 101 |
+
if len(expr.args) == 1:
|
| 102 |
+
return self._print(expr.args[0])
|
| 103 |
+
return "%smin(%s, %s)" % (self._ns, self._print(expr.args[0]),
|
| 104 |
+
self._print(Min(*expr.args[1:])))
|
| 105 |
+
|
| 106 |
+
def _print_using(self, expr):
|
| 107 |
+
if expr.alias == none:
|
| 108 |
+
return 'using %s' % expr.type
|
| 109 |
+
else:
|
| 110 |
+
raise ValueError("C++98 does not support type aliases")
|
| 111 |
+
|
| 112 |
+
def _print_Raise(self, rs):
|
| 113 |
+
arg, = rs.args
|
| 114 |
+
return 'throw %s' % self._print(arg)
|
| 115 |
+
|
| 116 |
+
@requires(headers={'stdexcept'})
|
| 117 |
+
def _print_RuntimeError_(self, re):
|
| 118 |
+
message, = re.args
|
| 119 |
+
return "%sruntime_error(%s)" % (self._ns, self._print(message))
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class CXX98CodePrinter(_CXXCodePrinterBase, C89CodePrinter):
|
| 123 |
+
standard = 'C++98'
|
| 124 |
+
reserved_words = set(reserved['C++98'])
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
# _attach_print_methods(CXX98CodePrinter, _math_functions)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
class CXX11CodePrinter(_CXXCodePrinterBase, C99CodePrinter):
|
| 131 |
+
standard = 'C++11'
|
| 132 |
+
reserved_words = set(reserved['C++11'])
|
| 133 |
+
type_mappings = dict(chain(
|
| 134 |
+
CXX98CodePrinter.type_mappings.items(),
|
| 135 |
+
{
|
| 136 |
+
Type('int8'): ('int8_t', {'cstdint'}),
|
| 137 |
+
Type('int16'): ('int16_t', {'cstdint'}),
|
| 138 |
+
Type('int32'): ('int32_t', {'cstdint'}),
|
| 139 |
+
Type('int64'): ('int64_t', {'cstdint'}),
|
| 140 |
+
Type('uint8'): ('uint8_t', {'cstdint'}),
|
| 141 |
+
Type('uint16'): ('uint16_t', {'cstdint'}),
|
| 142 |
+
Type('uint32'): ('uint32_t', {'cstdint'}),
|
| 143 |
+
Type('uint64'): ('uint64_t', {'cstdint'}),
|
| 144 |
+
Type('complex64'): ('std::complex<float>', {'complex'}),
|
| 145 |
+
Type('complex128'): ('std::complex<double>', {'complex'}),
|
| 146 |
+
Type('bool'): ('bool', None),
|
| 147 |
+
}.items()
|
| 148 |
+
))
|
| 149 |
+
|
| 150 |
+
def _print_using(self, expr):
|
| 151 |
+
if expr.alias == none:
|
| 152 |
+
return super()._print_using(expr)
|
| 153 |
+
else:
|
| 154 |
+
return 'using %(alias)s = %(type)s' % expr.kwargs(apply=self._print)
|
| 155 |
+
|
| 156 |
+
# _attach_print_methods(CXX11CodePrinter, _math_functions)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
class CXX17CodePrinter(_CXXCodePrinterBase, C99CodePrinter):
|
| 160 |
+
standard = 'C++17'
|
| 161 |
+
reserved_words = set(reserved['C++17'])
|
| 162 |
+
|
| 163 |
+
_kf = dict(C99CodePrinter._kf, **_math_functions['C++17'])
|
| 164 |
+
|
| 165 |
+
def _print_beta(self, expr):
|
| 166 |
+
return self._print_math_func(expr)
|
| 167 |
+
|
| 168 |
+
def _print_Ei(self, expr):
|
| 169 |
+
return self._print_math_func(expr)
|
| 170 |
+
|
| 171 |
+
def _print_zeta(self, expr):
|
| 172 |
+
return self._print_math_func(expr)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
# _attach_print_methods(CXX17CodePrinter, _math_functions)
|
| 176 |
+
|
| 177 |
+
cxx_code_printers = {
|
| 178 |
+
'c++98': CXX98CodePrinter,
|
| 179 |
+
'c++11': CXX11CodePrinter,
|
| 180 |
+
'c++17': CXX17CodePrinter
|
| 181 |
+
}
|
.venv/lib/python3.13/site-packages/sympy/printing/glsl.py
ADDED
|
@@ -0,0 +1,548 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from sympy.core import Basic, S
|
| 4 |
+
from sympy.core.function import Lambda
|
| 5 |
+
from sympy.core.numbers import equal_valued
|
| 6 |
+
from sympy.printing.codeprinter import CodePrinter
|
| 7 |
+
from sympy.printing.precedence import precedence
|
| 8 |
+
from functools import reduce
|
| 9 |
+
|
| 10 |
+
known_functions = {
|
| 11 |
+
'Abs': 'abs',
|
| 12 |
+
'sin': 'sin',
|
| 13 |
+
'cos': 'cos',
|
| 14 |
+
'tan': 'tan',
|
| 15 |
+
'acos': 'acos',
|
| 16 |
+
'asin': 'asin',
|
| 17 |
+
'atan': 'atan',
|
| 18 |
+
'atan2': 'atan',
|
| 19 |
+
'ceiling': 'ceil',
|
| 20 |
+
'floor': 'floor',
|
| 21 |
+
'sign': 'sign',
|
| 22 |
+
'exp': 'exp',
|
| 23 |
+
'log': 'log',
|
| 24 |
+
'add': 'add',
|
| 25 |
+
'sub': 'sub',
|
| 26 |
+
'mul': 'mul',
|
| 27 |
+
'pow': 'pow'
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
class GLSLPrinter(CodePrinter):
|
| 31 |
+
"""
|
| 32 |
+
Rudimentary, generic GLSL printing tools.
|
| 33 |
+
|
| 34 |
+
Additional settings:
|
| 35 |
+
'use_operators': Boolean (should the printer use operators for +,-,*, or functions?)
|
| 36 |
+
"""
|
| 37 |
+
_not_supported: set[Basic] = set()
|
| 38 |
+
printmethod = "_glsl"
|
| 39 |
+
language = "GLSL"
|
| 40 |
+
|
| 41 |
+
_default_settings = dict(CodePrinter._default_settings, **{
|
| 42 |
+
'use_operators': True,
|
| 43 |
+
'zero': 0,
|
| 44 |
+
'mat_nested': False,
|
| 45 |
+
'mat_separator': ',\n',
|
| 46 |
+
'mat_transpose': False,
|
| 47 |
+
'array_type': 'float',
|
| 48 |
+
'glsl_types': True,
|
| 49 |
+
|
| 50 |
+
'precision': 9,
|
| 51 |
+
'user_functions': {},
|
| 52 |
+
'contract': True,
|
| 53 |
+
})
|
| 54 |
+
|
| 55 |
+
def __init__(self, settings={}):
|
| 56 |
+
CodePrinter.__init__(self, settings)
|
| 57 |
+
self.known_functions = dict(known_functions)
|
| 58 |
+
userfuncs = settings.get('user_functions', {})
|
| 59 |
+
self.known_functions.update(userfuncs)
|
| 60 |
+
|
| 61 |
+
def _rate_index_position(self, p):
|
| 62 |
+
return p*5
|
| 63 |
+
|
| 64 |
+
def _get_statement(self, codestring):
|
| 65 |
+
return "%s;" % codestring
|
| 66 |
+
|
| 67 |
+
def _get_comment(self, text):
|
| 68 |
+
return "// {}".format(text)
|
| 69 |
+
|
| 70 |
+
def _declare_number_const(self, name, value):
|
| 71 |
+
return "float {} = {};".format(name, value)
|
| 72 |
+
|
| 73 |
+
def _format_code(self, lines):
|
| 74 |
+
return self.indent_code(lines)
|
| 75 |
+
|
| 76 |
+
def indent_code(self, code):
|
| 77 |
+
"""Accepts a string of code or a list of code lines"""
|
| 78 |
+
|
| 79 |
+
if isinstance(code, str):
|
| 80 |
+
code_lines = self.indent_code(code.splitlines(True))
|
| 81 |
+
return ''.join(code_lines)
|
| 82 |
+
|
| 83 |
+
tab = " "
|
| 84 |
+
inc_token = ('{', '(', '{\n', '(\n')
|
| 85 |
+
dec_token = ('}', ')')
|
| 86 |
+
|
| 87 |
+
code = [line.lstrip(' \t') for line in code]
|
| 88 |
+
|
| 89 |
+
increase = [int(any(map(line.endswith, inc_token))) for line in code]
|
| 90 |
+
decrease = [int(any(map(line.startswith, dec_token))) for line in code]
|
| 91 |
+
|
| 92 |
+
pretty = []
|
| 93 |
+
level = 0
|
| 94 |
+
for n, line in enumerate(code):
|
| 95 |
+
if line in ('', '\n'):
|
| 96 |
+
pretty.append(line)
|
| 97 |
+
continue
|
| 98 |
+
level -= decrease[n]
|
| 99 |
+
pretty.append("%s%s" % (tab*level, line))
|
| 100 |
+
level += increase[n]
|
| 101 |
+
return pretty
|
| 102 |
+
|
| 103 |
+
def _print_MatrixBase(self, mat):
|
| 104 |
+
mat_separator = self._settings['mat_separator']
|
| 105 |
+
mat_transpose = self._settings['mat_transpose']
|
| 106 |
+
column_vector = (mat.rows == 1) if mat_transpose else (mat.cols == 1)
|
| 107 |
+
A = mat.transpose() if mat_transpose != column_vector else mat
|
| 108 |
+
|
| 109 |
+
glsl_types = self._settings['glsl_types']
|
| 110 |
+
array_type = self._settings['array_type']
|
| 111 |
+
array_size = A.cols*A.rows
|
| 112 |
+
array_constructor = "{}[{}]".format(array_type, array_size)
|
| 113 |
+
|
| 114 |
+
if A.cols == 1:
|
| 115 |
+
return self._print(A[0])
|
| 116 |
+
if A.rows <= 4 and A.cols <= 4 and glsl_types:
|
| 117 |
+
if A.rows == 1:
|
| 118 |
+
return "vec{}{}".format(
|
| 119 |
+
A.cols, A.table(self,rowstart='(',rowend=')')
|
| 120 |
+
)
|
| 121 |
+
elif A.rows == A.cols:
|
| 122 |
+
return "mat{}({})".format(
|
| 123 |
+
A.rows, A.table(self,rowsep=', ',
|
| 124 |
+
rowstart='',rowend='')
|
| 125 |
+
)
|
| 126 |
+
else:
|
| 127 |
+
return "mat{}x{}({})".format(
|
| 128 |
+
A.cols, A.rows,
|
| 129 |
+
A.table(self,rowsep=', ',
|
| 130 |
+
rowstart='',rowend='')
|
| 131 |
+
)
|
| 132 |
+
elif S.One in A.shape:
|
| 133 |
+
return "{}({})".format(
|
| 134 |
+
array_constructor,
|
| 135 |
+
A.table(self,rowsep=mat_separator,rowstart='',rowend='')
|
| 136 |
+
)
|
| 137 |
+
elif not self._settings['mat_nested']:
|
| 138 |
+
return "{}(\n{}\n) /* a {}x{} matrix */".format(
|
| 139 |
+
array_constructor,
|
| 140 |
+
A.table(self,rowsep=mat_separator,rowstart='',rowend=''),
|
| 141 |
+
A.rows, A.cols
|
| 142 |
+
)
|
| 143 |
+
elif self._settings['mat_nested']:
|
| 144 |
+
return "{}[{}][{}](\n{}\n)".format(
|
| 145 |
+
array_type, A.rows, A.cols,
|
| 146 |
+
A.table(self,rowsep=mat_separator,rowstart='float[](',rowend=')')
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
def _print_SparseRepMatrix(self, mat):
|
| 150 |
+
# do not allow sparse matrices to be made dense
|
| 151 |
+
return self._print_not_supported(mat)
|
| 152 |
+
|
| 153 |
+
def _traverse_matrix_indices(self, mat):
|
| 154 |
+
mat_transpose = self._settings['mat_transpose']
|
| 155 |
+
if mat_transpose:
|
| 156 |
+
rows,cols = mat.shape
|
| 157 |
+
else:
|
| 158 |
+
cols,rows = mat.shape
|
| 159 |
+
return ((i, j) for i in range(cols) for j in range(rows))
|
| 160 |
+
|
| 161 |
+
def _print_MatrixElement(self, expr):
|
| 162 |
+
# print('begin _print_MatrixElement')
|
| 163 |
+
nest = self._settings['mat_nested']
|
| 164 |
+
glsl_types = self._settings['glsl_types']
|
| 165 |
+
mat_transpose = self._settings['mat_transpose']
|
| 166 |
+
if mat_transpose:
|
| 167 |
+
cols,rows = expr.parent.shape
|
| 168 |
+
i,j = expr.j,expr.i
|
| 169 |
+
else:
|
| 170 |
+
rows,cols = expr.parent.shape
|
| 171 |
+
i,j = expr.i,expr.j
|
| 172 |
+
pnt = self._print(expr.parent)
|
| 173 |
+
if glsl_types and ((rows <= 4 and cols <=4) or nest):
|
| 174 |
+
return "{}[{}][{}]".format(pnt, i, j)
|
| 175 |
+
else:
|
| 176 |
+
return "{}[{}]".format(pnt, i + j*rows)
|
| 177 |
+
|
| 178 |
+
def _print_list(self, expr):
|
| 179 |
+
l = ', '.join(self._print(item) for item in expr)
|
| 180 |
+
glsl_types = self._settings['glsl_types']
|
| 181 |
+
array_type = self._settings['array_type']
|
| 182 |
+
array_size = len(expr)
|
| 183 |
+
array_constructor = '{}[{}]'.format(array_type, array_size)
|
| 184 |
+
|
| 185 |
+
if array_size <= 4 and glsl_types:
|
| 186 |
+
return 'vec{}({})'.format(array_size, l)
|
| 187 |
+
else:
|
| 188 |
+
return '{}({})'.format(array_constructor, l)
|
| 189 |
+
|
| 190 |
+
_print_tuple = _print_list
|
| 191 |
+
_print_Tuple = _print_list
|
| 192 |
+
|
| 193 |
+
def _get_loop_opening_ending(self, indices):
|
| 194 |
+
open_lines = []
|
| 195 |
+
close_lines = []
|
| 196 |
+
loopstart = "for (int %(varble)s=%(start)s; %(varble)s<%(end)s; %(varble)s++){"
|
| 197 |
+
for i in indices:
|
| 198 |
+
# GLSL arrays start at 0 and end at dimension-1
|
| 199 |
+
open_lines.append(loopstart % {
|
| 200 |
+
'varble': self._print(i.label),
|
| 201 |
+
'start': self._print(i.lower),
|
| 202 |
+
'end': self._print(i.upper + 1)})
|
| 203 |
+
close_lines.append("}")
|
| 204 |
+
return open_lines, close_lines
|
| 205 |
+
|
| 206 |
+
def _print_Function_with_args(self, func, func_args):
|
| 207 |
+
if func in self.known_functions:
|
| 208 |
+
cond_func = self.known_functions[func]
|
| 209 |
+
func = None
|
| 210 |
+
if isinstance(cond_func, str):
|
| 211 |
+
func = cond_func
|
| 212 |
+
else:
|
| 213 |
+
for cond, func in cond_func:
|
| 214 |
+
if cond(func_args):
|
| 215 |
+
break
|
| 216 |
+
if func is not None:
|
| 217 |
+
try:
|
| 218 |
+
return func(*[self.parenthesize(item, 0) for item in func_args])
|
| 219 |
+
except TypeError:
|
| 220 |
+
return '{}({})'.format(func, self.stringify(func_args, ", "))
|
| 221 |
+
elif isinstance(func, Lambda):
|
| 222 |
+
# inlined function
|
| 223 |
+
return self._print(func(*func_args))
|
| 224 |
+
else:
|
| 225 |
+
return self._print_not_supported(func)
|
| 226 |
+
|
| 227 |
+
def _print_Piecewise(self, expr):
|
| 228 |
+
from sympy.codegen.ast import Assignment
|
| 229 |
+
if expr.args[-1].cond != True:
|
| 230 |
+
# We need the last conditional to be a True, otherwise the resulting
|
| 231 |
+
# function may not return a result.
|
| 232 |
+
raise ValueError("All Piecewise expressions must contain an "
|
| 233 |
+
"(expr, True) statement to be used as a default "
|
| 234 |
+
"condition. Without one, the generated "
|
| 235 |
+
"expression may not evaluate to anything under "
|
| 236 |
+
"some condition.")
|
| 237 |
+
lines = []
|
| 238 |
+
if expr.has(Assignment):
|
| 239 |
+
for i, (e, c) in enumerate(expr.args):
|
| 240 |
+
if i == 0:
|
| 241 |
+
lines.append("if (%s) {" % self._print(c))
|
| 242 |
+
elif i == len(expr.args) - 1 and c == True:
|
| 243 |
+
lines.append("else {")
|
| 244 |
+
else:
|
| 245 |
+
lines.append("else if (%s) {" % self._print(c))
|
| 246 |
+
code0 = self._print(e)
|
| 247 |
+
lines.append(code0)
|
| 248 |
+
lines.append("}")
|
| 249 |
+
return "\n".join(lines)
|
| 250 |
+
else:
|
| 251 |
+
# The piecewise was used in an expression, need to do inline
|
| 252 |
+
# operators. This has the downside that inline operators will
|
| 253 |
+
# not work for statements that span multiple lines (Matrix or
|
| 254 |
+
# Indexed expressions).
|
| 255 |
+
ecpairs = ["((%s) ? (\n%s\n)\n" % (self._print(c),
|
| 256 |
+
self._print(e))
|
| 257 |
+
for e, c in expr.args[:-1]]
|
| 258 |
+
last_line = ": (\n%s\n)" % self._print(expr.args[-1].expr)
|
| 259 |
+
return ": ".join(ecpairs) + last_line + " ".join([")"*len(ecpairs)])
|
| 260 |
+
|
| 261 |
+
def _print_Indexed(self, expr):
|
| 262 |
+
# calculate index for 1d array
|
| 263 |
+
dims = expr.shape
|
| 264 |
+
elem = S.Zero
|
| 265 |
+
offset = S.One
|
| 266 |
+
for i in reversed(range(expr.rank)):
|
| 267 |
+
elem += expr.indices[i]*offset
|
| 268 |
+
offset *= dims[i]
|
| 269 |
+
return "{}[{}]".format(
|
| 270 |
+
self._print(expr.base.label),
|
| 271 |
+
self._print(elem)
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
def _print_Pow(self, expr):
|
| 275 |
+
PREC = precedence(expr)
|
| 276 |
+
if equal_valued(expr.exp, -1):
|
| 277 |
+
return '1.0/%s' % (self.parenthesize(expr.base, PREC))
|
| 278 |
+
elif equal_valued(expr.exp, 0.5):
|
| 279 |
+
return 'sqrt(%s)' % self._print(expr.base)
|
| 280 |
+
else:
|
| 281 |
+
try:
|
| 282 |
+
e = self._print(float(expr.exp))
|
| 283 |
+
except TypeError:
|
| 284 |
+
e = self._print(expr.exp)
|
| 285 |
+
return self._print_Function_with_args('pow', (
|
| 286 |
+
self._print(expr.base),
|
| 287 |
+
e
|
| 288 |
+
))
|
| 289 |
+
|
| 290 |
+
def _print_int(self, expr):
|
| 291 |
+
return str(float(expr))
|
| 292 |
+
|
| 293 |
+
def _print_Rational(self, expr):
|
| 294 |
+
return "{}.0/{}.0".format(expr.p, expr.q)
|
| 295 |
+
|
| 296 |
+
def _print_Relational(self, expr):
|
| 297 |
+
lhs_code = self._print(expr.lhs)
|
| 298 |
+
rhs_code = self._print(expr.rhs)
|
| 299 |
+
op = expr.rel_op
|
| 300 |
+
return "{} {} {}".format(lhs_code, op, rhs_code)
|
| 301 |
+
|
| 302 |
+
def _print_Add(self, expr, order=None):
|
| 303 |
+
if self._settings['use_operators']:
|
| 304 |
+
return CodePrinter._print_Add(self, expr, order=order)
|
| 305 |
+
|
| 306 |
+
terms = expr.as_ordered_terms()
|
| 307 |
+
|
| 308 |
+
def partition(p,l):
|
| 309 |
+
return reduce(lambda x, y: (x[0]+[y], x[1]) if p(y) else (x[0], x[1]+[y]), l, ([], []))
|
| 310 |
+
def add(a,b):
|
| 311 |
+
return self._print_Function_with_args('add', (a, b))
|
| 312 |
+
# return self.known_functions['add']+'(%s, %s)' % (a,b)
|
| 313 |
+
neg, pos = partition(lambda arg: arg.could_extract_minus_sign(), terms)
|
| 314 |
+
if pos:
|
| 315 |
+
s = pos = reduce(lambda a,b: add(a,b), (self._print(t) for t in pos))
|
| 316 |
+
else:
|
| 317 |
+
s = pos = self._print(self._settings['zero'])
|
| 318 |
+
|
| 319 |
+
if neg:
|
| 320 |
+
# sum the absolute values of the negative terms
|
| 321 |
+
neg = reduce(lambda a,b: add(a,b), (self._print(-n) for n in neg))
|
| 322 |
+
# then subtract them from the positive terms
|
| 323 |
+
s = self._print_Function_with_args('sub', (pos,neg))
|
| 324 |
+
# s = self.known_functions['sub']+'(%s, %s)' % (pos,neg)
|
| 325 |
+
return s
|
| 326 |
+
|
| 327 |
+
def _print_Mul(self, expr, **kwargs):
|
| 328 |
+
if self._settings['use_operators']:
|
| 329 |
+
return CodePrinter._print_Mul(self, expr, **kwargs)
|
| 330 |
+
terms = expr.as_ordered_factors()
|
| 331 |
+
def mul(a,b):
|
| 332 |
+
# return self.known_functions['mul']+'(%s, %s)' % (a,b)
|
| 333 |
+
return self._print_Function_with_args('mul', (a,b))
|
| 334 |
+
|
| 335 |
+
s = reduce(lambda a,b: mul(a,b), (self._print(t) for t in terms))
|
| 336 |
+
return s
|
| 337 |
+
|
| 338 |
+
def glsl_code(expr,assign_to=None,**settings):
|
| 339 |
+
"""Converts an expr to a string of GLSL code
|
| 340 |
+
|
| 341 |
+
Parameters
|
| 342 |
+
==========
|
| 343 |
+
|
| 344 |
+
expr : Expr
|
| 345 |
+
A SymPy expression to be converted.
|
| 346 |
+
assign_to : optional
|
| 347 |
+
When given, the argument is used for naming the variable or variables
|
| 348 |
+
to which the expression is assigned. Can be a string, ``Symbol``,
|
| 349 |
+
``MatrixSymbol`` or ``Indexed`` type object. In cases where ``expr``
|
| 350 |
+
would be printed as an array, a list of string or ``Symbol`` objects
|
| 351 |
+
can also be passed.
|
| 352 |
+
|
| 353 |
+
This is helpful in case of line-wrapping, or for expressions that
|
| 354 |
+
generate multi-line statements. It can also be used to spread an array-like
|
| 355 |
+
expression into multiple assignments.
|
| 356 |
+
use_operators: bool, optional
|
| 357 |
+
If set to False, then *,/,+,- operators will be replaced with functions
|
| 358 |
+
mul, add, and sub, which must be implemented by the user, e.g. for
|
| 359 |
+
implementing non-standard rings or emulated quad/octal precision.
|
| 360 |
+
[default=True]
|
| 361 |
+
glsl_types: bool, optional
|
| 362 |
+
Set this argument to ``False`` in order to avoid using the ``vec`` and ``mat``
|
| 363 |
+
types. The printer will instead use arrays (or nested arrays).
|
| 364 |
+
[default=True]
|
| 365 |
+
mat_nested: bool, optional
|
| 366 |
+
GLSL version 4.3 and above support nested arrays (arrays of arrays). Set this to ``True``
|
| 367 |
+
to render matrices as nested arrays.
|
| 368 |
+
[default=False]
|
| 369 |
+
mat_separator: str, optional
|
| 370 |
+
By default, matrices are rendered with newlines using this separator,
|
| 371 |
+
making them easier to read, but less compact. By removing the newline
|
| 372 |
+
this option can be used to make them more vertically compact.
|
| 373 |
+
[default=',\n']
|
| 374 |
+
mat_transpose: bool, optional
|
| 375 |
+
GLSL's matrix multiplication implementation assumes column-major indexing.
|
| 376 |
+
By default, this printer ignores that convention. Setting this option to
|
| 377 |
+
``True`` transposes all matrix output.
|
| 378 |
+
[default=False]
|
| 379 |
+
array_type: str, optional
|
| 380 |
+
The GLSL array constructor type.
|
| 381 |
+
[default='float']
|
| 382 |
+
precision : integer, optional
|
| 383 |
+
The precision for numbers such as pi [default=15].
|
| 384 |
+
user_functions : dict, optional
|
| 385 |
+
A dictionary where keys are ``FunctionClass`` instances and values are
|
| 386 |
+
their string representations. Alternatively, the dictionary value can
|
| 387 |
+
be a list of tuples i.e. [(argument_test, js_function_string)]. See
|
| 388 |
+
below for examples.
|
| 389 |
+
human : bool, optional
|
| 390 |
+
If True, the result is a single string that may contain some constant
|
| 391 |
+
declarations for the number symbols. If False, the same information is
|
| 392 |
+
returned in a tuple of (symbols_to_declare, not_supported_functions,
|
| 393 |
+
code_text). [default=True].
|
| 394 |
+
contract: bool, optional
|
| 395 |
+
If True, ``Indexed`` instances are assumed to obey tensor contraction
|
| 396 |
+
rules and the corresponding nested loops over indices are generated.
|
| 397 |
+
Setting contract=False will not generate loops, instead the user is
|
| 398 |
+
responsible to provide values for the indices in the code.
|
| 399 |
+
[default=True].
|
| 400 |
+
|
| 401 |
+
Examples
|
| 402 |
+
========
|
| 403 |
+
|
| 404 |
+
>>> from sympy import glsl_code, symbols, Rational, sin, ceiling, Abs
|
| 405 |
+
>>> x, tau = symbols("x, tau")
|
| 406 |
+
>>> glsl_code((2*tau)**Rational(7, 2))
|
| 407 |
+
'8*sqrt(2)*pow(tau, 3.5)'
|
| 408 |
+
>>> glsl_code(sin(x), assign_to="float y")
|
| 409 |
+
'float y = sin(x);'
|
| 410 |
+
|
| 411 |
+
Various GLSL types are supported:
|
| 412 |
+
>>> from sympy import Matrix, glsl_code
|
| 413 |
+
>>> glsl_code(Matrix([1,2,3]))
|
| 414 |
+
'vec3(1, 2, 3)'
|
| 415 |
+
|
| 416 |
+
>>> glsl_code(Matrix([[1, 2],[3, 4]]))
|
| 417 |
+
'mat2(1, 2, 3, 4)'
|
| 418 |
+
|
| 419 |
+
Pass ``mat_transpose = True`` to switch to column-major indexing:
|
| 420 |
+
>>> glsl_code(Matrix([[1, 2],[3, 4]]), mat_transpose = True)
|
| 421 |
+
'mat2(1, 3, 2, 4)'
|
| 422 |
+
|
| 423 |
+
By default, larger matrices get collapsed into float arrays:
|
| 424 |
+
>>> print(glsl_code( Matrix([[1,2,3,4,5],[6,7,8,9,10]]) ))
|
| 425 |
+
float[10](
|
| 426 |
+
1, 2, 3, 4, 5,
|
| 427 |
+
6, 7, 8, 9, 10
|
| 428 |
+
) /* a 2x5 matrix */
|
| 429 |
+
|
| 430 |
+
The type of array constructor used to print GLSL arrays can be controlled
|
| 431 |
+
via the ``array_type`` parameter:
|
| 432 |
+
>>> glsl_code(Matrix([1,2,3,4,5]), array_type='int')
|
| 433 |
+
'int[5](1, 2, 3, 4, 5)'
|
| 434 |
+
|
| 435 |
+
Passing a list of strings or ``symbols`` to the ``assign_to`` parameter will yield
|
| 436 |
+
a multi-line assignment for each item in an array-like expression:
|
| 437 |
+
>>> x_struct_members = symbols('x.a x.b x.c x.d')
|
| 438 |
+
>>> print(glsl_code(Matrix([1,2,3,4]), assign_to=x_struct_members))
|
| 439 |
+
x.a = 1;
|
| 440 |
+
x.b = 2;
|
| 441 |
+
x.c = 3;
|
| 442 |
+
x.d = 4;
|
| 443 |
+
|
| 444 |
+
This could be useful in cases where it's desirable to modify members of a
|
| 445 |
+
GLSL ``Struct``. It could also be used to spread items from an array-like
|
| 446 |
+
expression into various miscellaneous assignments:
|
| 447 |
+
>>> misc_assignments = ('x[0]', 'x[1]', 'float y', 'float z')
|
| 448 |
+
>>> print(glsl_code(Matrix([1,2,3,4]), assign_to=misc_assignments))
|
| 449 |
+
x[0] = 1;
|
| 450 |
+
x[1] = 2;
|
| 451 |
+
float y = 3;
|
| 452 |
+
float z = 4;
|
| 453 |
+
|
| 454 |
+
Passing ``mat_nested = True`` instead prints out nested float arrays, which are
|
| 455 |
+
supported in GLSL 4.3 and above.
|
| 456 |
+
>>> mat = Matrix([
|
| 457 |
+
... [ 0, 1, 2],
|
| 458 |
+
... [ 3, 4, 5],
|
| 459 |
+
... [ 6, 7, 8],
|
| 460 |
+
... [ 9, 10, 11],
|
| 461 |
+
... [12, 13, 14]])
|
| 462 |
+
>>> print(glsl_code( mat, mat_nested = True ))
|
| 463 |
+
float[5][3](
|
| 464 |
+
float[]( 0, 1, 2),
|
| 465 |
+
float[]( 3, 4, 5),
|
| 466 |
+
float[]( 6, 7, 8),
|
| 467 |
+
float[]( 9, 10, 11),
|
| 468 |
+
float[](12, 13, 14)
|
| 469 |
+
)
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
Custom printing can be defined for certain types by passing a dictionary of
|
| 474 |
+
"type" : "function" to the ``user_functions`` kwarg. Alternatively, the
|
| 475 |
+
dictionary value can be a list of tuples i.e. [(argument_test,
|
| 476 |
+
js_function_string)].
|
| 477 |
+
|
| 478 |
+
>>> custom_functions = {
|
| 479 |
+
... "ceiling": "CEIL",
|
| 480 |
+
... "Abs": [(lambda x: not x.is_integer, "fabs"),
|
| 481 |
+
... (lambda x: x.is_integer, "ABS")]
|
| 482 |
+
... }
|
| 483 |
+
>>> glsl_code(Abs(x) + ceiling(x), user_functions=custom_functions)
|
| 484 |
+
'fabs(x) + CEIL(x)'
|
| 485 |
+
|
| 486 |
+
If further control is needed, addition, subtraction, multiplication and
|
| 487 |
+
division operators can be replaced with ``add``, ``sub``, and ``mul``
|
| 488 |
+
functions. This is done by passing ``use_operators = False``:
|
| 489 |
+
|
| 490 |
+
>>> x,y,z = symbols('x,y,z')
|
| 491 |
+
>>> glsl_code(x*(y+z), use_operators = False)
|
| 492 |
+
'mul(x, add(y, z))'
|
| 493 |
+
>>> glsl_code(x*(y+z*(x-y)**z), use_operators = False)
|
| 494 |
+
'mul(x, add(y, mul(z, pow(sub(x, y), z))))'
|
| 495 |
+
|
| 496 |
+
``Piecewise`` expressions are converted into conditionals. If an
|
| 497 |
+
``assign_to`` variable is provided an if statement is created, otherwise
|
| 498 |
+
the ternary operator is used. Note that if the ``Piecewise`` lacks a
|
| 499 |
+
default term, represented by ``(expr, True)`` then an error will be thrown.
|
| 500 |
+
This is to prevent generating an expression that may not evaluate to
|
| 501 |
+
anything.
|
| 502 |
+
|
| 503 |
+
>>> from sympy import Piecewise
|
| 504 |
+
>>> expr = Piecewise((x + 1, x > 0), (x, True))
|
| 505 |
+
>>> print(glsl_code(expr, tau))
|
| 506 |
+
if (x > 0) {
|
| 507 |
+
tau = x + 1;
|
| 508 |
+
}
|
| 509 |
+
else {
|
| 510 |
+
tau = x;
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
Support for loops is provided through ``Indexed`` types. With
|
| 514 |
+
``contract=True`` these expressions will be turned into loops, whereas
|
| 515 |
+
``contract=False`` will just print the assignment expression that should be
|
| 516 |
+
looped over:
|
| 517 |
+
|
| 518 |
+
>>> from sympy import Eq, IndexedBase, Idx
|
| 519 |
+
>>> len_y = 5
|
| 520 |
+
>>> y = IndexedBase('y', shape=(len_y,))
|
| 521 |
+
>>> t = IndexedBase('t', shape=(len_y,))
|
| 522 |
+
>>> Dy = IndexedBase('Dy', shape=(len_y-1,))
|
| 523 |
+
>>> i = Idx('i', len_y-1)
|
| 524 |
+
>>> e=Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i]))
|
| 525 |
+
>>> glsl_code(e.rhs, assign_to=e.lhs, contract=False)
|
| 526 |
+
'Dy[i] = (y[i + 1] - y[i])/(t[i + 1] - t[i]);'
|
| 527 |
+
|
| 528 |
+
>>> from sympy import Matrix, MatrixSymbol
|
| 529 |
+
>>> mat = Matrix([x**2, Piecewise((x + 1, x > 0), (x, True)), sin(x)])
|
| 530 |
+
>>> A = MatrixSymbol('A', 3, 1)
|
| 531 |
+
>>> print(glsl_code(mat, A))
|
| 532 |
+
A[0][0] = pow(x, 2.0);
|
| 533 |
+
if (x > 0) {
|
| 534 |
+
A[1][0] = x + 1;
|
| 535 |
+
}
|
| 536 |
+
else {
|
| 537 |
+
A[1][0] = x;
|
| 538 |
+
}
|
| 539 |
+
A[2][0] = sin(x);
|
| 540 |
+
"""
|
| 541 |
+
return GLSLPrinter(settings).doprint(expr,assign_to)
|
| 542 |
+
|
| 543 |
+
def print_glsl(expr, **settings):
|
| 544 |
+
"""Prints the GLSL representation of the given expression.
|
| 545 |
+
|
| 546 |
+
See GLSLPrinter init function for settings.
|
| 547 |
+
"""
|
| 548 |
+
print(glsl_code(expr, **settings))
|
.venv/lib/python3.13/site-packages/sympy/printing/gtk.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.printing.mathml import mathml
|
| 2 |
+
from sympy.utilities.mathml import c2p
|
| 3 |
+
import tempfile
|
| 4 |
+
import subprocess
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def print_gtk(x, start_viewer=True):
|
| 8 |
+
"""Print to Gtkmathview, a gtk widget capable of rendering MathML.
|
| 9 |
+
|
| 10 |
+
Needs libgtkmathview-bin"""
|
| 11 |
+
with tempfile.NamedTemporaryFile('w') as file:
|
| 12 |
+
file.write(c2p(mathml(x), simple=True))
|
| 13 |
+
file.flush()
|
| 14 |
+
|
| 15 |
+
if start_viewer:
|
| 16 |
+
subprocess.check_call(('mathmlviewer', file.name))
|
.venv/lib/python3.13/site-packages/sympy/printing/maple.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Maple code printer
|
| 3 |
+
|
| 4 |
+
The MapleCodePrinter converts single SymPy expressions into single
|
| 5 |
+
Maple expressions, using the functions defined in the Maple objects where possible.
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
FIXME: This module is still under actively developed. Some functions may be not completed.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from sympy.core import S
|
| 12 |
+
from sympy.core.numbers import Integer, IntegerConstant, equal_valued
|
| 13 |
+
from sympy.printing.codeprinter import CodePrinter
|
| 14 |
+
from sympy.printing.precedence import precedence, PRECEDENCE
|
| 15 |
+
|
| 16 |
+
import sympy
|
| 17 |
+
|
| 18 |
+
_known_func_same_name = (
|
| 19 |
+
'sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'sinh', 'cosh', 'tanh', 'sech',
|
| 20 |
+
'csch', 'coth', 'exp', 'floor', 'factorial', 'bernoulli', 'euler',
|
| 21 |
+
'fibonacci', 'gcd', 'lcm', 'conjugate', 'Ci', 'Chi', 'Ei', 'Li', 'Si', 'Shi',
|
| 22 |
+
'erf', 'erfc', 'harmonic', 'LambertW',
|
| 23 |
+
'sqrt', # For automatic rewrites
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
known_functions = {
|
| 27 |
+
# SymPy -> Maple
|
| 28 |
+
'Abs': 'abs',
|
| 29 |
+
'log': 'ln',
|
| 30 |
+
'asin': 'arcsin',
|
| 31 |
+
'acos': 'arccos',
|
| 32 |
+
'atan': 'arctan',
|
| 33 |
+
'asec': 'arcsec',
|
| 34 |
+
'acsc': 'arccsc',
|
| 35 |
+
'acot': 'arccot',
|
| 36 |
+
'asinh': 'arcsinh',
|
| 37 |
+
'acosh': 'arccosh',
|
| 38 |
+
'atanh': 'arctanh',
|
| 39 |
+
'asech': 'arcsech',
|
| 40 |
+
'acsch': 'arccsch',
|
| 41 |
+
'acoth': 'arccoth',
|
| 42 |
+
'ceiling': 'ceil',
|
| 43 |
+
'Max' : 'max',
|
| 44 |
+
'Min' : 'min',
|
| 45 |
+
|
| 46 |
+
'factorial2': 'doublefactorial',
|
| 47 |
+
'RisingFactorial': 'pochhammer',
|
| 48 |
+
'besseli': 'BesselI',
|
| 49 |
+
'besselj': 'BesselJ',
|
| 50 |
+
'besselk': 'BesselK',
|
| 51 |
+
'bessely': 'BesselY',
|
| 52 |
+
'hankelh1': 'HankelH1',
|
| 53 |
+
'hankelh2': 'HankelH2',
|
| 54 |
+
'airyai': 'AiryAi',
|
| 55 |
+
'airybi': 'AiryBi',
|
| 56 |
+
'appellf1': 'AppellF1',
|
| 57 |
+
'fresnelc': 'FresnelC',
|
| 58 |
+
'fresnels': 'FresnelS',
|
| 59 |
+
'lerchphi' : 'LerchPhi',
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
for _func in _known_func_same_name:
|
| 63 |
+
known_functions[_func] = _func
|
| 64 |
+
|
| 65 |
+
number_symbols = {
|
| 66 |
+
# SymPy -> Maple
|
| 67 |
+
S.Pi: 'Pi',
|
| 68 |
+
S.Exp1: 'exp(1)',
|
| 69 |
+
S.Catalan: 'Catalan',
|
| 70 |
+
S.EulerGamma: 'gamma',
|
| 71 |
+
S.GoldenRatio: '(1/2 + (1/2)*sqrt(5))'
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
spec_relational_ops = {
|
| 75 |
+
# SymPy -> Maple
|
| 76 |
+
'==': '=',
|
| 77 |
+
'!=': '<>'
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
not_supported_symbol = [
|
| 81 |
+
S.ComplexInfinity
|
| 82 |
+
]
|
| 83 |
+
|
| 84 |
+
class MapleCodePrinter(CodePrinter):
|
| 85 |
+
"""
|
| 86 |
+
Printer which converts a SymPy expression into a maple code.
|
| 87 |
+
"""
|
| 88 |
+
printmethod = "_maple"
|
| 89 |
+
language = "maple"
|
| 90 |
+
|
| 91 |
+
_operators = {
|
| 92 |
+
'and': 'and',
|
| 93 |
+
'or': 'or',
|
| 94 |
+
'not': 'not ',
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
_default_settings = dict(CodePrinter._default_settings, **{
|
| 98 |
+
'inline': True,
|
| 99 |
+
'allow_unknown_functions': True,
|
| 100 |
+
})
|
| 101 |
+
|
| 102 |
+
def __init__(self, settings=None):
|
| 103 |
+
if settings is None:
|
| 104 |
+
settings = {}
|
| 105 |
+
super().__init__(settings)
|
| 106 |
+
self.known_functions = dict(known_functions)
|
| 107 |
+
userfuncs = settings.get('user_functions', {})
|
| 108 |
+
self.known_functions.update(userfuncs)
|
| 109 |
+
|
| 110 |
+
def _get_statement(self, codestring):
|
| 111 |
+
return "%s;" % codestring
|
| 112 |
+
|
| 113 |
+
def _get_comment(self, text):
|
| 114 |
+
return "# {}".format(text)
|
| 115 |
+
|
| 116 |
+
def _declare_number_const(self, name, value):
|
| 117 |
+
return "{} := {};".format(name,
|
| 118 |
+
value.evalf(self._settings['precision']))
|
| 119 |
+
|
| 120 |
+
def _format_code(self, lines):
|
| 121 |
+
return lines
|
| 122 |
+
|
| 123 |
+
def _print_tuple(self, expr):
|
| 124 |
+
return self._print(list(expr))
|
| 125 |
+
|
| 126 |
+
def _print_Tuple(self, expr):
|
| 127 |
+
return self._print(list(expr))
|
| 128 |
+
|
| 129 |
+
def _print_Assignment(self, expr):
|
| 130 |
+
lhs = self._print(expr.lhs)
|
| 131 |
+
rhs = self._print(expr.rhs)
|
| 132 |
+
return "{lhs} := {rhs}".format(lhs=lhs, rhs=rhs)
|
| 133 |
+
|
| 134 |
+
def _print_Pow(self, expr, **kwargs):
|
| 135 |
+
PREC = precedence(expr)
|
| 136 |
+
if equal_valued(expr.exp, -1):
|
| 137 |
+
return '1/%s' % (self.parenthesize(expr.base, PREC))
|
| 138 |
+
elif equal_valued(expr.exp, 0.5):
|
| 139 |
+
return 'sqrt(%s)' % self._print(expr.base)
|
| 140 |
+
elif equal_valued(expr.exp, -0.5):
|
| 141 |
+
return '1/sqrt(%s)' % self._print(expr.base)
|
| 142 |
+
else:
|
| 143 |
+
return '{base}^{exp}'.format(
|
| 144 |
+
base=self.parenthesize(expr.base, PREC),
|
| 145 |
+
exp=self.parenthesize(expr.exp, PREC))
|
| 146 |
+
|
| 147 |
+
def _print_Piecewise(self, expr):
|
| 148 |
+
if (expr.args[-1].cond is not True) and (expr.args[-1].cond != S.BooleanTrue):
|
| 149 |
+
# We need the last conditional to be a True, otherwise the resulting
|
| 150 |
+
# function may not return a result.
|
| 151 |
+
raise ValueError("All Piecewise expressions must contain an "
|
| 152 |
+
"(expr, True) statement to be used as a default "
|
| 153 |
+
"condition. Without one, the generated "
|
| 154 |
+
"expression may not evaluate to anything under "
|
| 155 |
+
"some condition.")
|
| 156 |
+
_coup_list = [
|
| 157 |
+
("{c}, {e}".format(c=self._print(c),
|
| 158 |
+
e=self._print(e)) if c is not True and c is not S.BooleanTrue else "{e}".format(
|
| 159 |
+
e=self._print(e)))
|
| 160 |
+
for e, c in expr.args]
|
| 161 |
+
_inbrace = ', '.join(_coup_list)
|
| 162 |
+
return 'piecewise({_inbrace})'.format(_inbrace=_inbrace)
|
| 163 |
+
|
| 164 |
+
def _print_Rational(self, expr):
|
| 165 |
+
p, q = int(expr.p), int(expr.q)
|
| 166 |
+
return "{p}/{q}".format(p=str(p), q=str(q))
|
| 167 |
+
|
| 168 |
+
def _print_Relational(self, expr):
|
| 169 |
+
PREC=precedence(expr)
|
| 170 |
+
lhs_code = self.parenthesize(expr.lhs, PREC)
|
| 171 |
+
rhs_code = self.parenthesize(expr.rhs, PREC)
|
| 172 |
+
op = expr.rel_op
|
| 173 |
+
if op in spec_relational_ops:
|
| 174 |
+
op = spec_relational_ops[op]
|
| 175 |
+
return "{lhs} {rel_op} {rhs}".format(lhs=lhs_code, rel_op=op, rhs=rhs_code)
|
| 176 |
+
|
| 177 |
+
def _print_NumberSymbol(self, expr):
|
| 178 |
+
return number_symbols[expr]
|
| 179 |
+
|
| 180 |
+
def _print_NegativeInfinity(self, expr):
|
| 181 |
+
return '-infinity'
|
| 182 |
+
|
| 183 |
+
def _print_Infinity(self, expr):
|
| 184 |
+
return 'infinity'
|
| 185 |
+
|
| 186 |
+
def _print_BooleanTrue(self, expr):
|
| 187 |
+
return "true"
|
| 188 |
+
|
| 189 |
+
def _print_BooleanFalse(self, expr):
|
| 190 |
+
return "false"
|
| 191 |
+
|
| 192 |
+
def _print_bool(self, expr):
|
| 193 |
+
return 'true' if expr else 'false'
|
| 194 |
+
|
| 195 |
+
def _print_NaN(self, expr):
|
| 196 |
+
return 'undefined'
|
| 197 |
+
|
| 198 |
+
def _get_matrix(self, expr, sparse=False):
|
| 199 |
+
if S.Zero in expr.shape:
|
| 200 |
+
_strM = 'Matrix([], storage = {storage})'.format(
|
| 201 |
+
storage='sparse' if sparse else 'rectangular')
|
| 202 |
+
else:
|
| 203 |
+
_strM = 'Matrix({list}, storage = {storage})'.format(
|
| 204 |
+
list=self._print(expr.tolist()),
|
| 205 |
+
storage='sparse' if sparse else 'rectangular')
|
| 206 |
+
return _strM
|
| 207 |
+
|
| 208 |
+
def _print_MatrixElement(self, expr):
|
| 209 |
+
return "{parent}[{i_maple}, {j_maple}]".format(
|
| 210 |
+
parent=self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True),
|
| 211 |
+
i_maple=self._print(expr.i + 1),
|
| 212 |
+
j_maple=self._print(expr.j + 1))
|
| 213 |
+
|
| 214 |
+
def _print_MatrixBase(self, expr):
|
| 215 |
+
return self._get_matrix(expr, sparse=False)
|
| 216 |
+
|
| 217 |
+
def _print_SparseRepMatrix(self, expr):
|
| 218 |
+
return self._get_matrix(expr, sparse=True)
|
| 219 |
+
|
| 220 |
+
def _print_Identity(self, expr):
|
| 221 |
+
if isinstance(expr.rows, (Integer, IntegerConstant)):
|
| 222 |
+
return self._print(sympy.SparseMatrix(expr))
|
| 223 |
+
else:
|
| 224 |
+
return "Matrix({var_size}, shape = identity)".format(var_size=self._print(expr.rows))
|
| 225 |
+
|
| 226 |
+
def _print_MatMul(self, expr):
|
| 227 |
+
PREC=precedence(expr)
|
| 228 |
+
_fact_list = list(expr.args)
|
| 229 |
+
_const = None
|
| 230 |
+
if not isinstance(_fact_list[0], (sympy.MatrixBase, sympy.MatrixExpr,
|
| 231 |
+
sympy.MatrixSlice, sympy.MatrixSymbol)):
|
| 232 |
+
_const, _fact_list = _fact_list[0], _fact_list[1:]
|
| 233 |
+
|
| 234 |
+
if _const is None or _const == 1:
|
| 235 |
+
return '.'.join(self.parenthesize(_m, PREC) for _m in _fact_list)
|
| 236 |
+
else:
|
| 237 |
+
return '{c}*{m}'.format(c=_const, m='.'.join(self.parenthesize(_m, PREC) for _m in _fact_list))
|
| 238 |
+
|
| 239 |
+
def _print_MatPow(self, expr):
|
| 240 |
+
# This function requires LinearAlgebra Function in Maple
|
| 241 |
+
return 'MatrixPower({A}, {n})'.format(A=self._print(expr.base), n=self._print(expr.exp))
|
| 242 |
+
|
| 243 |
+
def _print_HadamardProduct(self, expr):
|
| 244 |
+
PREC = precedence(expr)
|
| 245 |
+
_fact_list = list(expr.args)
|
| 246 |
+
return '*'.join(self.parenthesize(_m, PREC) for _m in _fact_list)
|
| 247 |
+
|
| 248 |
+
def _print_Derivative(self, expr):
|
| 249 |
+
_f, (_var, _order) = expr.args
|
| 250 |
+
|
| 251 |
+
if _order != 1:
|
| 252 |
+
_second_arg = '{var}${order}'.format(var=self._print(_var),
|
| 253 |
+
order=self._print(_order))
|
| 254 |
+
else:
|
| 255 |
+
_second_arg = '{var}'.format(var=self._print(_var))
|
| 256 |
+
return 'diff({func_expr}, {sec_arg})'.format(func_expr=self._print(_f), sec_arg=_second_arg)
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
def maple_code(expr, assign_to=None, **settings):
|
| 260 |
+
r"""Converts ``expr`` to a string of Maple code.
|
| 261 |
+
|
| 262 |
+
Parameters
|
| 263 |
+
==========
|
| 264 |
+
|
| 265 |
+
expr : Expr
|
| 266 |
+
A SymPy expression to be converted.
|
| 267 |
+
assign_to : optional
|
| 268 |
+
When given, the argument is used as the name of the variable to which
|
| 269 |
+
the expression is assigned. Can be a string, ``Symbol``,
|
| 270 |
+
``MatrixSymbol``, or ``Indexed`` type. This can be helpful for
|
| 271 |
+
expressions that generate multi-line statements.
|
| 272 |
+
precision : integer, optional
|
| 273 |
+
The precision for numbers such as pi [default=16].
|
| 274 |
+
user_functions : dict, optional
|
| 275 |
+
A dictionary where keys are ``FunctionClass`` instances and values are
|
| 276 |
+
their string representations. Alternatively, the dictionary value can
|
| 277 |
+
be a list of tuples i.e. [(argument_test, cfunction_string)]. See
|
| 278 |
+
below for examples.
|
| 279 |
+
human : bool, optional
|
| 280 |
+
If True, the result is a single string that may contain some constant
|
| 281 |
+
declarations for the number symbols. If False, the same information is
|
| 282 |
+
returned in a tuple of (symbols_to_declare, not_supported_functions,
|
| 283 |
+
code_text). [default=True].
|
| 284 |
+
contract: bool, optional
|
| 285 |
+
If True, ``Indexed`` instances are assumed to obey tensor contraction
|
| 286 |
+
rules and the corresponding nested loops over indices are generated.
|
| 287 |
+
Setting contract=False will not generate loops, instead the user is
|
| 288 |
+
responsible to provide values for the indices in the code.
|
| 289 |
+
[default=True].
|
| 290 |
+
inline: bool, optional
|
| 291 |
+
If True, we try to create single-statement code instead of multiple
|
| 292 |
+
statements. [default=True].
|
| 293 |
+
|
| 294 |
+
"""
|
| 295 |
+
return MapleCodePrinter(settings).doprint(expr, assign_to)
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def print_maple_code(expr, **settings):
|
| 299 |
+
"""Prints the Maple representation of the given expression.
|
| 300 |
+
|
| 301 |
+
See :func:`maple_code` for the meaning of the optional arguments.
|
| 302 |
+
|
| 303 |
+
Examples
|
| 304 |
+
========
|
| 305 |
+
|
| 306 |
+
>>> from sympy import print_maple_code, symbols
|
| 307 |
+
>>> x, y = symbols('x y')
|
| 308 |
+
>>> print_maple_code(x, assign_to=y)
|
| 309 |
+
y := x
|
| 310 |
+
"""
|
| 311 |
+
print(maple_code(expr, **settings))
|
.venv/lib/python3.13/site-packages/sympy/printing/preview.py
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from os.path import join
|
| 3 |
+
import shutil
|
| 4 |
+
import tempfile
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
from subprocess import STDOUT, CalledProcessError, check_output
|
| 9 |
+
except ImportError:
|
| 10 |
+
pass
|
| 11 |
+
|
| 12 |
+
from sympy.utilities.decorator import doctest_depends_on
|
| 13 |
+
from sympy.utilities.misc import debug
|
| 14 |
+
from .latex import latex
|
| 15 |
+
|
| 16 |
+
__doctest_requires__ = {('preview',): ['pyglet']}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _check_output_no_window(*args, **kwargs):
|
| 20 |
+
# Avoid showing a cmd.exe window when running this
|
| 21 |
+
# on Windows
|
| 22 |
+
if os.name == 'nt':
|
| 23 |
+
creation_flag = 0x08000000 # CREATE_NO_WINDOW
|
| 24 |
+
else:
|
| 25 |
+
creation_flag = 0 # Default value
|
| 26 |
+
return check_output(*args, creationflags=creation_flag, **kwargs)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def system_default_viewer(fname, fmt):
|
| 30 |
+
""" Open fname with the default system viewer.
|
| 31 |
+
|
| 32 |
+
In practice, it is impossible for python to know when the system viewer is
|
| 33 |
+
done. For this reason, we ensure the passed file will not be deleted under
|
| 34 |
+
it, and this function does not attempt to block.
|
| 35 |
+
"""
|
| 36 |
+
# copy to a new temporary file that will not be deleted
|
| 37 |
+
with tempfile.NamedTemporaryFile(prefix='sympy-preview-',
|
| 38 |
+
suffix=os.path.splitext(fname)[1],
|
| 39 |
+
delete=False) as temp_f:
|
| 40 |
+
with open(fname, 'rb') as f:
|
| 41 |
+
shutil.copyfileobj(f, temp_f)
|
| 42 |
+
|
| 43 |
+
import platform
|
| 44 |
+
if platform.system() == 'Darwin':
|
| 45 |
+
import subprocess
|
| 46 |
+
subprocess.call(('open', temp_f.name))
|
| 47 |
+
elif platform.system() == 'Windows':
|
| 48 |
+
os.startfile(temp_f.name)
|
| 49 |
+
else:
|
| 50 |
+
import subprocess
|
| 51 |
+
subprocess.call(('xdg-open', temp_f.name))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def pyglet_viewer(fname, fmt):
|
| 55 |
+
try:
|
| 56 |
+
from pyglet import window, image, gl
|
| 57 |
+
from pyglet.window import key
|
| 58 |
+
from pyglet.image.codecs import ImageDecodeException
|
| 59 |
+
except ImportError:
|
| 60 |
+
raise ImportError("pyglet is required for preview.\n visit https://pyglet.org/")
|
| 61 |
+
|
| 62 |
+
try:
|
| 63 |
+
img = image.load(fname)
|
| 64 |
+
except ImageDecodeException:
|
| 65 |
+
raise ValueError("pyglet preview does not work for '{}' files.".format(fmt))
|
| 66 |
+
|
| 67 |
+
offset = 25
|
| 68 |
+
|
| 69 |
+
config = gl.Config(double_buffer=False)
|
| 70 |
+
win = window.Window(
|
| 71 |
+
width=img.width + 2*offset,
|
| 72 |
+
height=img.height + 2*offset,
|
| 73 |
+
caption="SymPy",
|
| 74 |
+
resizable=False,
|
| 75 |
+
config=config
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
win.set_vsync(False)
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
def on_close():
|
| 82 |
+
win.has_exit = True
|
| 83 |
+
|
| 84 |
+
win.on_close = on_close
|
| 85 |
+
|
| 86 |
+
def on_key_press(symbol, modifiers):
|
| 87 |
+
if symbol in [key.Q, key.ESCAPE]:
|
| 88 |
+
on_close()
|
| 89 |
+
|
| 90 |
+
win.on_key_press = on_key_press
|
| 91 |
+
|
| 92 |
+
def on_expose():
|
| 93 |
+
gl.glClearColor(1.0, 1.0, 1.0, 1.0)
|
| 94 |
+
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
| 95 |
+
|
| 96 |
+
img.blit(
|
| 97 |
+
(win.width - img.width) / 2,
|
| 98 |
+
(win.height - img.height) / 2
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
win.on_expose = on_expose
|
| 102 |
+
|
| 103 |
+
while not win.has_exit:
|
| 104 |
+
win.dispatch_events()
|
| 105 |
+
win.flip()
|
| 106 |
+
except KeyboardInterrupt:
|
| 107 |
+
pass
|
| 108 |
+
|
| 109 |
+
win.close()
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def _get_latex_main(expr, *, preamble=None, packages=(), extra_preamble=None,
|
| 113 |
+
euler=True, fontsize=None, **latex_settings):
|
| 114 |
+
"""
|
| 115 |
+
Generate string of a LaTeX document rendering ``expr``.
|
| 116 |
+
"""
|
| 117 |
+
if preamble is None:
|
| 118 |
+
actual_packages = packages + ("amsmath", "amsfonts")
|
| 119 |
+
if euler:
|
| 120 |
+
actual_packages += ("euler",)
|
| 121 |
+
package_includes = "\n" + "\n".join(["\\usepackage{%s}" % p
|
| 122 |
+
for p in actual_packages])
|
| 123 |
+
if extra_preamble:
|
| 124 |
+
package_includes += extra_preamble
|
| 125 |
+
|
| 126 |
+
if not fontsize:
|
| 127 |
+
fontsize = "12pt"
|
| 128 |
+
elif isinstance(fontsize, int):
|
| 129 |
+
fontsize = "{}pt".format(fontsize)
|
| 130 |
+
preamble = r"""\documentclass[varwidth,%s]{standalone}
|
| 131 |
+
%s
|
| 132 |
+
|
| 133 |
+
\begin{document}
|
| 134 |
+
""" % (fontsize, package_includes)
|
| 135 |
+
else:
|
| 136 |
+
if packages or extra_preamble:
|
| 137 |
+
raise ValueError("The \"packages\" or \"extra_preamble\" keywords"
|
| 138 |
+
"must not be set if a "
|
| 139 |
+
"custom LaTeX preamble was specified")
|
| 140 |
+
|
| 141 |
+
if isinstance(expr, str):
|
| 142 |
+
latex_string = expr
|
| 143 |
+
else:
|
| 144 |
+
latex_string = ('$\\displaystyle ' +
|
| 145 |
+
latex(expr, mode='plain', **latex_settings) +
|
| 146 |
+
'$')
|
| 147 |
+
|
| 148 |
+
return preamble + '\n' + latex_string + '\n\n' + r"\end{document}"
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
@doctest_depends_on(exe=('latex', 'dvipng'), modules=('pyglet',),
|
| 152 |
+
disable_viewers=('evince', 'gimp', 'superior-dvi-viewer'))
|
| 153 |
+
def preview(expr, output='png', viewer=None, euler=True, packages=(),
|
| 154 |
+
filename=None, outputbuffer=None, preamble=None, dvioptions=None,
|
| 155 |
+
outputTexFile=None, extra_preamble=None, fontsize=None,
|
| 156 |
+
**latex_settings):
|
| 157 |
+
r"""
|
| 158 |
+
View expression or LaTeX markup in PNG, DVI, PostScript or PDF form.
|
| 159 |
+
|
| 160 |
+
If the expr argument is an expression, it will be exported to LaTeX and
|
| 161 |
+
then compiled using the available TeX distribution. The first argument,
|
| 162 |
+
'expr', may also be a LaTeX string. The function will then run the
|
| 163 |
+
appropriate viewer for the given output format or use the user defined
|
| 164 |
+
one. By default png output is generated.
|
| 165 |
+
|
| 166 |
+
By default pretty Euler fonts are used for typesetting (they were used to
|
| 167 |
+
typeset the well known "Concrete Mathematics" book). For that to work, you
|
| 168 |
+
need the 'eulervm.sty' LaTeX style (in Debian/Ubuntu, install the
|
| 169 |
+
texlive-fonts-extra package). If you prefer default AMS fonts or your
|
| 170 |
+
system lacks 'eulervm' LaTeX package then unset the 'euler' keyword
|
| 171 |
+
argument.
|
| 172 |
+
|
| 173 |
+
To use viewer auto-detection, lets say for 'png' output, issue
|
| 174 |
+
|
| 175 |
+
>>> from sympy import symbols, preview, Symbol
|
| 176 |
+
>>> x, y = symbols("x,y")
|
| 177 |
+
|
| 178 |
+
>>> preview(x + y, output='png')
|
| 179 |
+
|
| 180 |
+
This will choose 'pyglet' by default. To select a different one, do
|
| 181 |
+
|
| 182 |
+
>>> preview(x + y, output='png', viewer='gimp')
|
| 183 |
+
|
| 184 |
+
The 'png' format is considered special. For all other formats the rules
|
| 185 |
+
are slightly different. As an example we will take 'dvi' output format. If
|
| 186 |
+
you would run
|
| 187 |
+
|
| 188 |
+
>>> preview(x + y, output='dvi')
|
| 189 |
+
|
| 190 |
+
then 'view' will look for available 'dvi' viewers on your system
|
| 191 |
+
(predefined in the function, so it will try evince, first, then kdvi and
|
| 192 |
+
xdvi). If nothing is found, it will fall back to using a system file
|
| 193 |
+
association (via ``open`` and ``xdg-open``). To always use your system file
|
| 194 |
+
association without searching for the above readers, use
|
| 195 |
+
|
| 196 |
+
>>> from sympy.printing.preview import system_default_viewer
|
| 197 |
+
>>> preview(x + y, output='dvi', viewer=system_default_viewer)
|
| 198 |
+
|
| 199 |
+
If this still does not find the viewer you want, it can be set explicitly.
|
| 200 |
+
|
| 201 |
+
>>> preview(x + y, output='dvi', viewer='superior-dvi-viewer')
|
| 202 |
+
|
| 203 |
+
This will skip auto-detection and will run user specified
|
| 204 |
+
'superior-dvi-viewer'. If ``view`` fails to find it on your system it will
|
| 205 |
+
gracefully raise an exception.
|
| 206 |
+
|
| 207 |
+
You may also enter ``'file'`` for the viewer argument. Doing so will cause
|
| 208 |
+
this function to return a file object in read-only mode, if ``filename``
|
| 209 |
+
is unset. However, if it was set, then 'preview' writes the generated
|
| 210 |
+
file to this filename instead.
|
| 211 |
+
|
| 212 |
+
There is also support for writing to a ``io.BytesIO`` like object, which
|
| 213 |
+
needs to be passed to the ``outputbuffer`` argument.
|
| 214 |
+
|
| 215 |
+
>>> from io import BytesIO
|
| 216 |
+
>>> obj = BytesIO()
|
| 217 |
+
>>> preview(x + y, output='png', viewer='BytesIO',
|
| 218 |
+
... outputbuffer=obj)
|
| 219 |
+
|
| 220 |
+
The LaTeX preamble can be customized by setting the 'preamble' keyword
|
| 221 |
+
argument. This can be used, e.g., to set a different font size, use a
|
| 222 |
+
custom documentclass or import certain set of LaTeX packages.
|
| 223 |
+
|
| 224 |
+
>>> preamble = "\\documentclass[10pt]{article}\n" \
|
| 225 |
+
... "\\usepackage{amsmath,amsfonts}\\begin{document}"
|
| 226 |
+
>>> preview(x + y, output='png', preamble=preamble)
|
| 227 |
+
|
| 228 |
+
It is also possible to use the standard preamble and provide additional
|
| 229 |
+
information to the preamble using the ``extra_preamble`` keyword argument.
|
| 230 |
+
|
| 231 |
+
>>> from sympy import sin
|
| 232 |
+
>>> extra_preamble = "\\renewcommand{\\sin}{\\cos}"
|
| 233 |
+
>>> preview(sin(x), output='png', extra_preamble=extra_preamble)
|
| 234 |
+
|
| 235 |
+
If the value of 'output' is different from 'dvi' then command line
|
| 236 |
+
options can be set ('dvioptions' argument) for the execution of the
|
| 237 |
+
'dvi'+output conversion tool. These options have to be in the form of a
|
| 238 |
+
list of strings (see ``subprocess.Popen``).
|
| 239 |
+
|
| 240 |
+
Additional keyword args will be passed to the :func:`~sympy.printing.latex.latex` call,
|
| 241 |
+
e.g., the ``symbol_names`` flag.
|
| 242 |
+
|
| 243 |
+
>>> phidd = Symbol('phidd')
|
| 244 |
+
>>> preview(phidd, symbol_names={phidd: r'\ddot{\varphi}'})
|
| 245 |
+
|
| 246 |
+
For post-processing the generated TeX File can be written to a file by
|
| 247 |
+
passing the desired filename to the 'outputTexFile' keyword
|
| 248 |
+
argument. To write the TeX code to a file named
|
| 249 |
+
``"sample.tex"`` and run the default png viewer to display the resulting
|
| 250 |
+
bitmap, do
|
| 251 |
+
|
| 252 |
+
>>> preview(x + y, outputTexFile="sample.tex")
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
"""
|
| 256 |
+
# pyglet is the default for png
|
| 257 |
+
if viewer is None and output == "png":
|
| 258 |
+
try:
|
| 259 |
+
import pyglet # noqa: F401
|
| 260 |
+
except ImportError:
|
| 261 |
+
pass
|
| 262 |
+
else:
|
| 263 |
+
viewer = pyglet_viewer
|
| 264 |
+
|
| 265 |
+
# look up a known application
|
| 266 |
+
if viewer is None:
|
| 267 |
+
# sorted in order from most pretty to most ugly
|
| 268 |
+
# very discussable, but indeed 'gv' looks awful :)
|
| 269 |
+
candidates = {
|
| 270 |
+
"dvi": [ "evince", "okular", "kdvi", "xdvi" ],
|
| 271 |
+
"ps": [ "evince", "okular", "gsview", "gv" ],
|
| 272 |
+
"pdf": [ "evince", "okular", "kpdf", "acroread", "xpdf", "gv" ],
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
for candidate in candidates.get(output, []):
|
| 276 |
+
path = shutil.which(candidate)
|
| 277 |
+
if path is not None:
|
| 278 |
+
viewer = path
|
| 279 |
+
break
|
| 280 |
+
|
| 281 |
+
# otherwise, use the system default for file association
|
| 282 |
+
if viewer is None:
|
| 283 |
+
viewer = system_default_viewer
|
| 284 |
+
|
| 285 |
+
if viewer == "file":
|
| 286 |
+
if filename is None:
|
| 287 |
+
raise ValueError("filename has to be specified if viewer=\"file\"")
|
| 288 |
+
elif viewer == "BytesIO":
|
| 289 |
+
if outputbuffer is None:
|
| 290 |
+
raise ValueError("outputbuffer has to be a BytesIO "
|
| 291 |
+
"compatible object if viewer=\"BytesIO\"")
|
| 292 |
+
elif not callable(viewer) and not shutil.which(viewer):
|
| 293 |
+
raise OSError("Unrecognized viewer: %s" % viewer)
|
| 294 |
+
|
| 295 |
+
latex_main = _get_latex_main(expr, preamble=preamble, packages=packages,
|
| 296 |
+
euler=euler, extra_preamble=extra_preamble,
|
| 297 |
+
fontsize=fontsize, **latex_settings)
|
| 298 |
+
|
| 299 |
+
debug("Latex code:")
|
| 300 |
+
debug(latex_main)
|
| 301 |
+
with tempfile.TemporaryDirectory() as workdir:
|
| 302 |
+
Path(join(workdir, 'texput.tex')).write_text(latex_main, encoding='utf-8')
|
| 303 |
+
|
| 304 |
+
if outputTexFile is not None:
|
| 305 |
+
shutil.copyfile(join(workdir, 'texput.tex'), outputTexFile)
|
| 306 |
+
|
| 307 |
+
if not shutil.which('latex'):
|
| 308 |
+
raise RuntimeError("latex program is not installed")
|
| 309 |
+
|
| 310 |
+
try:
|
| 311 |
+
_check_output_no_window(
|
| 312 |
+
['latex', '-halt-on-error', '-interaction=nonstopmode',
|
| 313 |
+
'texput.tex'],
|
| 314 |
+
cwd=workdir,
|
| 315 |
+
stderr=STDOUT)
|
| 316 |
+
except CalledProcessError as e:
|
| 317 |
+
raise RuntimeError(
|
| 318 |
+
"'latex' exited abnormally with the following output:\n%s" %
|
| 319 |
+
e.output)
|
| 320 |
+
|
| 321 |
+
src = "texput.%s" % (output)
|
| 322 |
+
|
| 323 |
+
if output != "dvi":
|
| 324 |
+
# in order of preference
|
| 325 |
+
commandnames = {
|
| 326 |
+
"ps": ["dvips"],
|
| 327 |
+
"pdf": ["dvipdfmx", "dvipdfm", "dvipdf"],
|
| 328 |
+
"png": ["dvipng"],
|
| 329 |
+
"svg": ["dvisvgm"],
|
| 330 |
+
}
|
| 331 |
+
try:
|
| 332 |
+
cmd_variants = commandnames[output]
|
| 333 |
+
except KeyError:
|
| 334 |
+
raise ValueError("Invalid output format: %s" % output) from None
|
| 335 |
+
|
| 336 |
+
# find an appropriate command
|
| 337 |
+
for cmd_variant in cmd_variants:
|
| 338 |
+
cmd_path = shutil.which(cmd_variant)
|
| 339 |
+
if cmd_path:
|
| 340 |
+
cmd = [cmd_path]
|
| 341 |
+
break
|
| 342 |
+
else:
|
| 343 |
+
if len(cmd_variants) > 1:
|
| 344 |
+
raise RuntimeError("None of %s are installed" % ", ".join(cmd_variants))
|
| 345 |
+
else:
|
| 346 |
+
raise RuntimeError("%s is not installed" % cmd_variants[0])
|
| 347 |
+
|
| 348 |
+
defaultoptions = {
|
| 349 |
+
"dvipng": ["-T", "tight", "-z", "9", "--truecolor"],
|
| 350 |
+
"dvisvgm": ["--no-fonts"],
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
commandend = {
|
| 354 |
+
"dvips": ["-o", src, "texput.dvi"],
|
| 355 |
+
"dvipdf": ["texput.dvi", src],
|
| 356 |
+
"dvipdfm": ["-o", src, "texput.dvi"],
|
| 357 |
+
"dvipdfmx": ["-o", src, "texput.dvi"],
|
| 358 |
+
"dvipng": ["-o", src, "texput.dvi"],
|
| 359 |
+
"dvisvgm": ["-o", src, "texput.dvi"],
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
if dvioptions is not None:
|
| 363 |
+
cmd.extend(dvioptions)
|
| 364 |
+
else:
|
| 365 |
+
cmd.extend(defaultoptions.get(cmd_variant, []))
|
| 366 |
+
cmd.extend(commandend[cmd_variant])
|
| 367 |
+
|
| 368 |
+
try:
|
| 369 |
+
_check_output_no_window(cmd, cwd=workdir, stderr=STDOUT)
|
| 370 |
+
except CalledProcessError as e:
|
| 371 |
+
raise RuntimeError(
|
| 372 |
+
"'%s' exited abnormally with the following output:\n%s" %
|
| 373 |
+
(' '.join(cmd), e.output))
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
if viewer == "file":
|
| 377 |
+
shutil.move(join(workdir, src), filename)
|
| 378 |
+
elif viewer == "BytesIO":
|
| 379 |
+
s = Path(join(workdir, src)).read_bytes()
|
| 380 |
+
outputbuffer.write(s)
|
| 381 |
+
elif callable(viewer):
|
| 382 |
+
viewer(join(workdir, src), fmt=output)
|
| 383 |
+
else:
|
| 384 |
+
try:
|
| 385 |
+
_check_output_no_window(
|
| 386 |
+
[viewer, src], cwd=workdir, stderr=STDOUT)
|
| 387 |
+
except CalledProcessError as e:
|
| 388 |
+
raise RuntimeError(
|
| 389 |
+
"'%s %s' exited abnormally with the following output:\n%s" %
|
| 390 |
+
(viewer, src, e.output))
|
.venv/lib/python3.13/site-packages/sympy/printing/str.py
ADDED
|
@@ -0,0 +1,1021 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
A Printer for generating readable representation of most SymPy classes.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
from typing import Any
|
| 7 |
+
|
| 8 |
+
from sympy.core import S, Rational, Pow, Basic, Mul, Number
|
| 9 |
+
from sympy.core.mul import _keep_coeff
|
| 10 |
+
from sympy.core.numbers import Integer
|
| 11 |
+
from sympy.core.relational import Relational
|
| 12 |
+
from sympy.core.sorting import default_sort_key
|
| 13 |
+
from sympy.utilities.iterables import sift
|
| 14 |
+
from .precedence import precedence, PRECEDENCE
|
| 15 |
+
from .printer import Printer, print_function
|
| 16 |
+
|
| 17 |
+
from mpmath.libmp import prec_to_dps, to_str as mlib_to_str
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class StrPrinter(Printer):
|
| 21 |
+
printmethod = "_sympystr"
|
| 22 |
+
_default_settings: dict[str, Any] = {
|
| 23 |
+
"order": None,
|
| 24 |
+
"full_prec": "auto",
|
| 25 |
+
"sympy_integers": False,
|
| 26 |
+
"abbrev": False,
|
| 27 |
+
"perm_cyclic": True,
|
| 28 |
+
"min": None,
|
| 29 |
+
"max": None,
|
| 30 |
+
"dps" : None
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
_relationals: dict[str, str] = {}
|
| 34 |
+
|
| 35 |
+
def parenthesize(self, item, level, strict=False):
|
| 36 |
+
if (precedence(item) < level) or ((not strict) and precedence(item) <= level):
|
| 37 |
+
return "(%s)" % self._print(item)
|
| 38 |
+
else:
|
| 39 |
+
return self._print(item)
|
| 40 |
+
|
| 41 |
+
def stringify(self, args, sep, level=0):
|
| 42 |
+
return sep.join([self.parenthesize(item, level) for item in args])
|
| 43 |
+
|
| 44 |
+
def emptyPrinter(self, expr):
|
| 45 |
+
if isinstance(expr, str):
|
| 46 |
+
return expr
|
| 47 |
+
elif isinstance(expr, Basic):
|
| 48 |
+
return repr(expr)
|
| 49 |
+
else:
|
| 50 |
+
return str(expr)
|
| 51 |
+
|
| 52 |
+
def _print_Add(self, expr, order=None):
|
| 53 |
+
terms = self._as_ordered_terms(expr, order=order)
|
| 54 |
+
|
| 55 |
+
prec = precedence(expr)
|
| 56 |
+
l = []
|
| 57 |
+
for term in terms:
|
| 58 |
+
t = self._print(term)
|
| 59 |
+
if t.startswith('-') and not term.is_Add:
|
| 60 |
+
sign = "-"
|
| 61 |
+
t = t[1:]
|
| 62 |
+
else:
|
| 63 |
+
sign = "+"
|
| 64 |
+
if precedence(term) < prec or term.is_Add:
|
| 65 |
+
l.extend([sign, "(%s)" % t])
|
| 66 |
+
else:
|
| 67 |
+
l.extend([sign, t])
|
| 68 |
+
sign = l.pop(0)
|
| 69 |
+
if sign == '+':
|
| 70 |
+
sign = ""
|
| 71 |
+
return sign + ' '.join(l)
|
| 72 |
+
|
| 73 |
+
def _print_BooleanTrue(self, expr):
|
| 74 |
+
return "True"
|
| 75 |
+
|
| 76 |
+
def _print_BooleanFalse(self, expr):
|
| 77 |
+
return "False"
|
| 78 |
+
|
| 79 |
+
def _print_Not(self, expr):
|
| 80 |
+
return '~%s' %(self.parenthesize(expr.args[0],PRECEDENCE["Not"]))
|
| 81 |
+
|
| 82 |
+
def _print_And(self, expr):
|
| 83 |
+
args = list(expr.args)
|
| 84 |
+
for j, i in enumerate(args):
|
| 85 |
+
if isinstance(i, Relational) and (
|
| 86 |
+
i.canonical.rhs is S.NegativeInfinity):
|
| 87 |
+
args.insert(0, args.pop(j))
|
| 88 |
+
return self.stringify(args, " & ", PRECEDENCE["BitwiseAnd"])
|
| 89 |
+
|
| 90 |
+
def _print_Or(self, expr):
|
| 91 |
+
return self.stringify(expr.args, " | ", PRECEDENCE["BitwiseOr"])
|
| 92 |
+
|
| 93 |
+
def _print_Xor(self, expr):
|
| 94 |
+
return self.stringify(expr.args, " ^ ", PRECEDENCE["BitwiseXor"])
|
| 95 |
+
|
| 96 |
+
def _print_AppliedPredicate(self, expr):
|
| 97 |
+
return '%s(%s)' % (
|
| 98 |
+
self._print(expr.function), self.stringify(expr.arguments, ", "))
|
| 99 |
+
|
| 100 |
+
def _print_Basic(self, expr):
|
| 101 |
+
l = [self._print(o) for o in expr.args]
|
| 102 |
+
return expr.__class__.__name__ + "(%s)" % ", ".join(l)
|
| 103 |
+
|
| 104 |
+
def _print_BlockMatrix(self, B):
|
| 105 |
+
if B.blocks.shape == (1, 1):
|
| 106 |
+
self._print(B.blocks[0, 0])
|
| 107 |
+
return self._print(B.blocks)
|
| 108 |
+
|
| 109 |
+
def _print_Catalan(self, expr):
|
| 110 |
+
return 'Catalan'
|
| 111 |
+
|
| 112 |
+
def _print_ComplexInfinity(self, expr):
|
| 113 |
+
return 'zoo'
|
| 114 |
+
|
| 115 |
+
def _print_ConditionSet(self, s):
|
| 116 |
+
args = tuple([self._print(i) for i in (s.sym, s.condition)])
|
| 117 |
+
if s.base_set is S.UniversalSet:
|
| 118 |
+
return 'ConditionSet(%s, %s)' % args
|
| 119 |
+
args += (self._print(s.base_set),)
|
| 120 |
+
return 'ConditionSet(%s, %s, %s)' % args
|
| 121 |
+
|
| 122 |
+
def _print_Derivative(self, expr):
|
| 123 |
+
dexpr = expr.expr
|
| 124 |
+
dvars = [i[0] if i[1] == 1 else i for i in expr.variable_count]
|
| 125 |
+
return 'Derivative(%s)' % ", ".join((self._print(arg) for arg in [dexpr] + dvars))
|
| 126 |
+
|
| 127 |
+
def _print_dict(self, d):
|
| 128 |
+
keys = sorted(d.keys(), key=default_sort_key)
|
| 129 |
+
items = []
|
| 130 |
+
|
| 131 |
+
for key in keys:
|
| 132 |
+
item = "%s: %s" % (self._print(key), self._print(d[key]))
|
| 133 |
+
items.append(item)
|
| 134 |
+
|
| 135 |
+
return "{%s}" % ", ".join(items)
|
| 136 |
+
|
| 137 |
+
def _print_Dict(self, expr):
|
| 138 |
+
return self._print_dict(expr)
|
| 139 |
+
|
| 140 |
+
def _print_RandomDomain(self, d):
|
| 141 |
+
if hasattr(d, 'as_boolean'):
|
| 142 |
+
return 'Domain: ' + self._print(d.as_boolean())
|
| 143 |
+
elif hasattr(d, 'set'):
|
| 144 |
+
return ('Domain: ' + self._print(d.symbols) + ' in ' +
|
| 145 |
+
self._print(d.set))
|
| 146 |
+
else:
|
| 147 |
+
return 'Domain on ' + self._print(d.symbols)
|
| 148 |
+
|
| 149 |
+
def _print_Dummy(self, expr):
|
| 150 |
+
return '_' + expr.name
|
| 151 |
+
|
| 152 |
+
def _print_EulerGamma(self, expr):
|
| 153 |
+
return 'EulerGamma'
|
| 154 |
+
|
| 155 |
+
def _print_Exp1(self, expr):
|
| 156 |
+
return 'E'
|
| 157 |
+
|
| 158 |
+
def _print_ExprCondPair(self, expr):
|
| 159 |
+
return '(%s, %s)' % (self._print(expr.expr), self._print(expr.cond))
|
| 160 |
+
|
| 161 |
+
def _print_Function(self, expr):
|
| 162 |
+
return expr.func.__name__ + "(%s)" % self.stringify(expr.args, ", ")
|
| 163 |
+
|
| 164 |
+
def _print_GoldenRatio(self, expr):
|
| 165 |
+
return 'GoldenRatio'
|
| 166 |
+
|
| 167 |
+
def _print_Heaviside(self, expr):
|
| 168 |
+
# Same as _print_Function but uses pargs to suppress default 1/2 for
|
| 169 |
+
# 2nd args
|
| 170 |
+
return expr.func.__name__ + "(%s)" % self.stringify(expr.pargs, ", ")
|
| 171 |
+
|
| 172 |
+
def _print_TribonacciConstant(self, expr):
|
| 173 |
+
return 'TribonacciConstant'
|
| 174 |
+
|
| 175 |
+
def _print_ImaginaryUnit(self, expr):
|
| 176 |
+
return 'I'
|
| 177 |
+
|
| 178 |
+
def _print_Infinity(self, expr):
|
| 179 |
+
return 'oo'
|
| 180 |
+
|
| 181 |
+
def _print_Integral(self, expr):
|
| 182 |
+
def _xab_tostr(xab):
|
| 183 |
+
if len(xab) == 1:
|
| 184 |
+
return self._print(xab[0])
|
| 185 |
+
else:
|
| 186 |
+
return self._print((xab[0],) + tuple(xab[1:]))
|
| 187 |
+
L = ', '.join([_xab_tostr(l) for l in expr.limits])
|
| 188 |
+
return 'Integral(%s, %s)' % (self._print(expr.function), L)
|
| 189 |
+
|
| 190 |
+
def _print_Interval(self, i):
|
| 191 |
+
fin = 'Interval{m}({a}, {b})'
|
| 192 |
+
a, b, l, r = i.args
|
| 193 |
+
if a.is_infinite and b.is_infinite:
|
| 194 |
+
m = ''
|
| 195 |
+
elif a.is_infinite and not r:
|
| 196 |
+
m = ''
|
| 197 |
+
elif b.is_infinite and not l:
|
| 198 |
+
m = ''
|
| 199 |
+
elif not l and not r:
|
| 200 |
+
m = ''
|
| 201 |
+
elif l and r:
|
| 202 |
+
m = '.open'
|
| 203 |
+
elif l:
|
| 204 |
+
m = '.Lopen'
|
| 205 |
+
else:
|
| 206 |
+
m = '.Ropen'
|
| 207 |
+
return fin.format(**{'a': a, 'b': b, 'm': m})
|
| 208 |
+
|
| 209 |
+
def _print_AccumulationBounds(self, i):
|
| 210 |
+
return "AccumBounds(%s, %s)" % (self._print(i.min),
|
| 211 |
+
self._print(i.max))
|
| 212 |
+
|
| 213 |
+
def _print_Inverse(self, I):
|
| 214 |
+
return "%s**(-1)" % self.parenthesize(I.arg, PRECEDENCE["Pow"])
|
| 215 |
+
|
| 216 |
+
def _print_Lambda(self, obj):
|
| 217 |
+
expr = obj.expr
|
| 218 |
+
sig = obj.signature
|
| 219 |
+
if len(sig) == 1 and sig[0].is_symbol:
|
| 220 |
+
sig = sig[0]
|
| 221 |
+
return "Lambda(%s, %s)" % (self._print(sig), self._print(expr))
|
| 222 |
+
|
| 223 |
+
def _print_LatticeOp(self, expr):
|
| 224 |
+
args = sorted(expr.args, key=default_sort_key)
|
| 225 |
+
return expr.func.__name__ + "(%s)" % ", ".join(self._print(arg) for arg in args)
|
| 226 |
+
|
| 227 |
+
def _print_Limit(self, expr):
|
| 228 |
+
e, z, z0, dir = expr.args
|
| 229 |
+
return "Limit(%s, %s, %s, dir='%s')" % tuple(map(self._print, (e, z, z0, dir)))
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def _print_list(self, expr):
|
| 233 |
+
return "[%s]" % self.stringify(expr, ", ")
|
| 234 |
+
|
| 235 |
+
def _print_List(self, expr):
|
| 236 |
+
return self._print_list(expr)
|
| 237 |
+
|
| 238 |
+
def _print_MatrixBase(self, expr):
|
| 239 |
+
return expr._format_str(self)
|
| 240 |
+
|
| 241 |
+
def _print_MatrixElement(self, expr):
|
| 242 |
+
return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) \
|
| 243 |
+
+ '[%s, %s]' % (self._print(expr.i), self._print(expr.j))
|
| 244 |
+
|
| 245 |
+
def _print_MatrixSlice(self, expr):
|
| 246 |
+
def strslice(x, dim):
|
| 247 |
+
x = list(x)
|
| 248 |
+
if x[2] == 1:
|
| 249 |
+
del x[2]
|
| 250 |
+
if x[0] == 0:
|
| 251 |
+
x[0] = ''
|
| 252 |
+
if x[1] == dim:
|
| 253 |
+
x[1] = ''
|
| 254 |
+
return ':'.join((self._print(arg) for arg in x))
|
| 255 |
+
return (self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) + '[' +
|
| 256 |
+
strslice(expr.rowslice, expr.parent.rows) + ', ' +
|
| 257 |
+
strslice(expr.colslice, expr.parent.cols) + ']')
|
| 258 |
+
|
| 259 |
+
def _print_DeferredVector(self, expr):
|
| 260 |
+
return expr.name
|
| 261 |
+
|
| 262 |
+
def _print_Mul(self, expr):
|
| 263 |
+
|
| 264 |
+
prec = precedence(expr)
|
| 265 |
+
|
| 266 |
+
# Check for unevaluated Mul. In this case we need to make sure the
|
| 267 |
+
# identities are visible, multiple Rational factors are not combined
|
| 268 |
+
# etc so we display in a straight-forward form that fully preserves all
|
| 269 |
+
# args and their order.
|
| 270 |
+
args = expr.args
|
| 271 |
+
if args[0] is S.One or any(
|
| 272 |
+
isinstance(a, Number) or
|
| 273 |
+
a.is_Pow and all(ai.is_Integer for ai in a.args)
|
| 274 |
+
for a in args[1:]):
|
| 275 |
+
d, n = sift(args, lambda x:
|
| 276 |
+
isinstance(x, Pow) and bool(x.exp.as_coeff_Mul()[0] < 0),
|
| 277 |
+
binary=True)
|
| 278 |
+
for i, di in enumerate(d):
|
| 279 |
+
if di.exp.is_Number:
|
| 280 |
+
e = -di.exp
|
| 281 |
+
else:
|
| 282 |
+
dargs = list(di.exp.args)
|
| 283 |
+
dargs[0] = -dargs[0]
|
| 284 |
+
e = Mul._from_args(dargs)
|
| 285 |
+
d[i] = Pow(di.base, e, evaluate=False) if e - 1 else di.base
|
| 286 |
+
|
| 287 |
+
pre = []
|
| 288 |
+
# don't parenthesize first factor if negative
|
| 289 |
+
if n and not n[0].is_Add and n[0].could_extract_minus_sign():
|
| 290 |
+
pre = [self._print(n.pop(0))]
|
| 291 |
+
|
| 292 |
+
nfactors = pre + [self.parenthesize(a, prec, strict=False)
|
| 293 |
+
for a in n]
|
| 294 |
+
if not nfactors:
|
| 295 |
+
nfactors = ['1']
|
| 296 |
+
|
| 297 |
+
# don't parenthesize first of denominator unless singleton
|
| 298 |
+
if len(d) > 1 and d[0].could_extract_minus_sign():
|
| 299 |
+
pre = [self._print(d.pop(0))]
|
| 300 |
+
else:
|
| 301 |
+
pre = []
|
| 302 |
+
dfactors = pre + [self.parenthesize(a, prec, strict=False)
|
| 303 |
+
for a in d]
|
| 304 |
+
|
| 305 |
+
n = '*'.join(nfactors)
|
| 306 |
+
d = '*'.join(dfactors)
|
| 307 |
+
if len(dfactors) > 1:
|
| 308 |
+
return '%s/(%s)' % (n, d)
|
| 309 |
+
elif dfactors:
|
| 310 |
+
return '%s/%s' % (n, d)
|
| 311 |
+
return n
|
| 312 |
+
|
| 313 |
+
c, e = expr.as_coeff_Mul()
|
| 314 |
+
if c < 0:
|
| 315 |
+
expr = _keep_coeff(-c, e)
|
| 316 |
+
sign = "-"
|
| 317 |
+
else:
|
| 318 |
+
sign = ""
|
| 319 |
+
|
| 320 |
+
a = [] # items in the numerator
|
| 321 |
+
b = [] # items that are in the denominator (if any)
|
| 322 |
+
|
| 323 |
+
pow_paren = [] # Will collect all pow with more than one base element and exp = -1
|
| 324 |
+
|
| 325 |
+
if self.order not in ('old', 'none'):
|
| 326 |
+
args = expr.as_ordered_factors()
|
| 327 |
+
else:
|
| 328 |
+
# use make_args in case expr was something like -x -> x
|
| 329 |
+
args = Mul.make_args(expr)
|
| 330 |
+
|
| 331 |
+
# Gather args for numerator/denominator
|
| 332 |
+
def apow(i):
|
| 333 |
+
b, e = i.as_base_exp()
|
| 334 |
+
eargs = list(Mul.make_args(e))
|
| 335 |
+
if eargs[0] is S.NegativeOne:
|
| 336 |
+
eargs = eargs[1:]
|
| 337 |
+
else:
|
| 338 |
+
eargs[0] = -eargs[0]
|
| 339 |
+
e = Mul._from_args(eargs)
|
| 340 |
+
if isinstance(i, Pow):
|
| 341 |
+
return i.func(b, e, evaluate=False)
|
| 342 |
+
return i.func(e, evaluate=False)
|
| 343 |
+
for item in args:
|
| 344 |
+
if (item.is_commutative and
|
| 345 |
+
isinstance(item, Pow) and
|
| 346 |
+
bool(item.exp.as_coeff_Mul()[0] < 0)):
|
| 347 |
+
if item.exp is not S.NegativeOne:
|
| 348 |
+
b.append(apow(item))
|
| 349 |
+
else:
|
| 350 |
+
if (len(item.args[0].args) != 1 and
|
| 351 |
+
isinstance(item.base, (Mul, Pow))):
|
| 352 |
+
# To avoid situations like #14160
|
| 353 |
+
pow_paren.append(item)
|
| 354 |
+
b.append(item.base)
|
| 355 |
+
elif item.is_Rational and item is not S.Infinity:
|
| 356 |
+
if item.p != 1:
|
| 357 |
+
a.append(Rational(item.p))
|
| 358 |
+
if item.q != 1:
|
| 359 |
+
b.append(Rational(item.q))
|
| 360 |
+
else:
|
| 361 |
+
a.append(item)
|
| 362 |
+
|
| 363 |
+
a = a or [S.One]
|
| 364 |
+
|
| 365 |
+
a_str = [self.parenthesize(x, prec, strict=False) for x in a]
|
| 366 |
+
b_str = [self.parenthesize(x, prec, strict=False) for x in b]
|
| 367 |
+
|
| 368 |
+
# To parenthesize Pow with exp = -1 and having more than one Symbol
|
| 369 |
+
for item in pow_paren:
|
| 370 |
+
if item.base in b:
|
| 371 |
+
b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)]
|
| 372 |
+
|
| 373 |
+
if not b:
|
| 374 |
+
return sign + '*'.join(a_str)
|
| 375 |
+
elif len(b) == 1:
|
| 376 |
+
return sign + '*'.join(a_str) + "/" + b_str[0]
|
| 377 |
+
else:
|
| 378 |
+
return sign + '*'.join(a_str) + "/(%s)" % '*'.join(b_str)
|
| 379 |
+
|
| 380 |
+
def _print_MatMul(self, expr):
|
| 381 |
+
c, m = expr.as_coeff_mmul()
|
| 382 |
+
|
| 383 |
+
sign = ""
|
| 384 |
+
if c.is_number:
|
| 385 |
+
re, im = c.as_real_imag()
|
| 386 |
+
if im.is_zero and re.is_negative:
|
| 387 |
+
expr = _keep_coeff(-c, m)
|
| 388 |
+
sign = "-"
|
| 389 |
+
elif re.is_zero and im.is_negative:
|
| 390 |
+
expr = _keep_coeff(-c, m)
|
| 391 |
+
sign = "-"
|
| 392 |
+
|
| 393 |
+
return sign + '*'.join(
|
| 394 |
+
[self.parenthesize(arg, precedence(expr)) for arg in expr.args]
|
| 395 |
+
)
|
| 396 |
+
|
| 397 |
+
def _print_ElementwiseApplyFunction(self, expr):
|
| 398 |
+
return "{}.({})".format(
|
| 399 |
+
expr.function,
|
| 400 |
+
self._print(expr.expr),
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
def _print_NaN(self, expr):
|
| 404 |
+
return 'nan'
|
| 405 |
+
|
| 406 |
+
def _print_NegativeInfinity(self, expr):
|
| 407 |
+
return '-oo'
|
| 408 |
+
|
| 409 |
+
def _print_Order(self, expr):
|
| 410 |
+
if not expr.variables or all(p is S.Zero for p in expr.point):
|
| 411 |
+
if len(expr.variables) <= 1:
|
| 412 |
+
return 'O(%s)' % self._print(expr.expr)
|
| 413 |
+
else:
|
| 414 |
+
return 'O(%s)' % self.stringify((expr.expr,) + expr.variables, ', ', 0)
|
| 415 |
+
else:
|
| 416 |
+
return 'O(%s)' % self.stringify(expr.args, ', ', 0)
|
| 417 |
+
|
| 418 |
+
def _print_Ordinal(self, expr):
|
| 419 |
+
return expr.__str__()
|
| 420 |
+
|
| 421 |
+
def _print_Cycle(self, expr):
|
| 422 |
+
return expr.__str__()
|
| 423 |
+
|
| 424 |
+
def _print_Permutation(self, expr):
|
| 425 |
+
from sympy.combinatorics.permutations import Permutation, Cycle
|
| 426 |
+
from sympy.utilities.exceptions import sympy_deprecation_warning
|
| 427 |
+
|
| 428 |
+
perm_cyclic = Permutation.print_cyclic
|
| 429 |
+
if perm_cyclic is not None:
|
| 430 |
+
sympy_deprecation_warning(
|
| 431 |
+
f"""
|
| 432 |
+
Setting Permutation.print_cyclic is deprecated. Instead use
|
| 433 |
+
init_printing(perm_cyclic={perm_cyclic}).
|
| 434 |
+
""",
|
| 435 |
+
deprecated_since_version="1.6",
|
| 436 |
+
active_deprecations_target="deprecated-permutation-print_cyclic",
|
| 437 |
+
stacklevel=7,
|
| 438 |
+
)
|
| 439 |
+
else:
|
| 440 |
+
perm_cyclic = self._settings.get("perm_cyclic", True)
|
| 441 |
+
|
| 442 |
+
if perm_cyclic:
|
| 443 |
+
if not expr.size:
|
| 444 |
+
return '()'
|
| 445 |
+
# before taking Cycle notation, see if the last element is
|
| 446 |
+
# a singleton and move it to the head of the string
|
| 447 |
+
s = Cycle(expr)(expr.size - 1).__repr__()[len('Cycle'):]
|
| 448 |
+
last = s.rfind('(')
|
| 449 |
+
if not last == 0 and ',' not in s[last:]:
|
| 450 |
+
s = s[last:] + s[:last]
|
| 451 |
+
s = s.replace(',', '')
|
| 452 |
+
return s
|
| 453 |
+
else:
|
| 454 |
+
s = expr.support()
|
| 455 |
+
if not s:
|
| 456 |
+
if expr.size < 5:
|
| 457 |
+
return 'Permutation(%s)' % self._print(expr.array_form)
|
| 458 |
+
return 'Permutation([], size=%s)' % self._print(expr.size)
|
| 459 |
+
trim = self._print(expr.array_form[:s[-1] + 1]) + ', size=%s' % self._print(expr.size)
|
| 460 |
+
use = full = self._print(expr.array_form)
|
| 461 |
+
if len(trim) < len(full):
|
| 462 |
+
use = trim
|
| 463 |
+
return 'Permutation(%s)' % use
|
| 464 |
+
|
| 465 |
+
def _print_Subs(self, obj):
|
| 466 |
+
expr, old, new = obj.args
|
| 467 |
+
if len(obj.point) == 1:
|
| 468 |
+
old = old[0]
|
| 469 |
+
new = new[0]
|
| 470 |
+
return "Subs(%s, %s, %s)" % (
|
| 471 |
+
self._print(expr), self._print(old), self._print(new))
|
| 472 |
+
|
| 473 |
+
def _print_TensorIndex(self, expr):
|
| 474 |
+
return expr._print()
|
| 475 |
+
|
| 476 |
+
def _print_TensorHead(self, expr):
|
| 477 |
+
return expr._print()
|
| 478 |
+
|
| 479 |
+
def _print_Tensor(self, expr):
|
| 480 |
+
return expr._print()
|
| 481 |
+
|
| 482 |
+
def _print_TensMul(self, expr):
|
| 483 |
+
# prints expressions like "A(a)", "3*A(a)", "(1+x)*A(a)"
|
| 484 |
+
sign, args = expr._get_args_for_traditional_printer()
|
| 485 |
+
return sign + "*".join(
|
| 486 |
+
[self.parenthesize(arg, precedence(expr)) for arg in args]
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
def _print_TensAdd(self, expr):
|
| 490 |
+
return expr._print()
|
| 491 |
+
|
| 492 |
+
def _print_ArraySymbol(self, expr):
|
| 493 |
+
return self._print(expr.name)
|
| 494 |
+
|
| 495 |
+
def _print_ArrayElement(self, expr):
|
| 496 |
+
return "%s[%s]" % (
|
| 497 |
+
self.parenthesize(expr.name, PRECEDENCE["Func"], True), ", ".join([self._print(i) for i in expr.indices]))
|
| 498 |
+
|
| 499 |
+
def _print_PermutationGroup(self, expr):
|
| 500 |
+
p = [' %s' % self._print(a) for a in expr.args]
|
| 501 |
+
return 'PermutationGroup([\n%s])' % ',\n'.join(p)
|
| 502 |
+
|
| 503 |
+
def _print_Pi(self, expr):
|
| 504 |
+
return 'pi'
|
| 505 |
+
|
| 506 |
+
def _print_PolyRing(self, ring):
|
| 507 |
+
return "Polynomial ring in %s over %s with %s order" % \
|
| 508 |
+
(", ".join((self._print(rs) for rs in ring.symbols)),
|
| 509 |
+
self._print(ring.domain), self._print(ring.order))
|
| 510 |
+
|
| 511 |
+
def _print_FracField(self, field):
|
| 512 |
+
return "Rational function field in %s over %s with %s order" % \
|
| 513 |
+
(", ".join((self._print(fs) for fs in field.symbols)),
|
| 514 |
+
self._print(field.domain), self._print(field.order))
|
| 515 |
+
|
| 516 |
+
def _print_FreeGroupElement(self, elm):
|
| 517 |
+
return elm.__str__()
|
| 518 |
+
|
| 519 |
+
def _print_GaussianElement(self, poly):
|
| 520 |
+
return "(%s + %s*I)" % (poly.x, poly.y)
|
| 521 |
+
|
| 522 |
+
def _print_PolyElement(self, poly):
|
| 523 |
+
return poly.str(self, PRECEDENCE, "%s**%s", "*")
|
| 524 |
+
|
| 525 |
+
def _print_FracElement(self, frac):
|
| 526 |
+
if frac.denom == 1:
|
| 527 |
+
return self._print(frac.numer)
|
| 528 |
+
else:
|
| 529 |
+
numer = self.parenthesize(frac.numer, PRECEDENCE["Mul"], strict=True)
|
| 530 |
+
denom = self.parenthesize(frac.denom, PRECEDENCE["Atom"], strict=True)
|
| 531 |
+
return numer + "/" + denom
|
| 532 |
+
|
| 533 |
+
def _print_Poly(self, expr):
|
| 534 |
+
ATOM_PREC = PRECEDENCE["Atom"] - 1
|
| 535 |
+
terms, gens = [], [ self.parenthesize(s, ATOM_PREC) for s in expr.gens ]
|
| 536 |
+
|
| 537 |
+
for monom, coeff in expr.terms():
|
| 538 |
+
s_monom = []
|
| 539 |
+
|
| 540 |
+
for i, e in enumerate(monom):
|
| 541 |
+
if e > 0:
|
| 542 |
+
if e == 1:
|
| 543 |
+
s_monom.append(gens[i])
|
| 544 |
+
else:
|
| 545 |
+
s_monom.append(gens[i] + "**%d" % e)
|
| 546 |
+
|
| 547 |
+
s_monom = "*".join(s_monom)
|
| 548 |
+
|
| 549 |
+
if coeff.is_Add:
|
| 550 |
+
if s_monom:
|
| 551 |
+
s_coeff = "(" + self._print(coeff) + ")"
|
| 552 |
+
else:
|
| 553 |
+
s_coeff = self._print(coeff)
|
| 554 |
+
else:
|
| 555 |
+
if s_monom:
|
| 556 |
+
if coeff is S.One:
|
| 557 |
+
terms.extend(['+', s_monom])
|
| 558 |
+
continue
|
| 559 |
+
|
| 560 |
+
if coeff is S.NegativeOne:
|
| 561 |
+
terms.extend(['-', s_monom])
|
| 562 |
+
continue
|
| 563 |
+
|
| 564 |
+
s_coeff = self._print(coeff)
|
| 565 |
+
|
| 566 |
+
if not s_monom:
|
| 567 |
+
s_term = s_coeff
|
| 568 |
+
else:
|
| 569 |
+
s_term = s_coeff + "*" + s_monom
|
| 570 |
+
|
| 571 |
+
if s_term.startswith('-'):
|
| 572 |
+
terms.extend(['-', s_term[1:]])
|
| 573 |
+
else:
|
| 574 |
+
terms.extend(['+', s_term])
|
| 575 |
+
|
| 576 |
+
if terms[0] in ('-', '+'):
|
| 577 |
+
modifier = terms.pop(0)
|
| 578 |
+
|
| 579 |
+
if modifier == '-':
|
| 580 |
+
terms[0] = '-' + terms[0]
|
| 581 |
+
|
| 582 |
+
format = expr.__class__.__name__ + "(%s, %s"
|
| 583 |
+
|
| 584 |
+
from sympy.polys.polyerrors import PolynomialError
|
| 585 |
+
|
| 586 |
+
try:
|
| 587 |
+
format += ", modulus=%s" % expr.get_modulus()
|
| 588 |
+
except PolynomialError:
|
| 589 |
+
format += ", domain='%s'" % expr.get_domain()
|
| 590 |
+
|
| 591 |
+
format += ")"
|
| 592 |
+
|
| 593 |
+
for index, item in enumerate(gens):
|
| 594 |
+
if len(item) > 2 and (item[:1] == "(" and item[len(item) - 1:] == ")"):
|
| 595 |
+
gens[index] = item[1:len(item) - 1]
|
| 596 |
+
|
| 597 |
+
return format % (' '.join(terms), ', '.join(gens))
|
| 598 |
+
|
| 599 |
+
def _print_UniversalSet(self, p):
|
| 600 |
+
return 'UniversalSet'
|
| 601 |
+
|
| 602 |
+
def _print_AlgebraicNumber(self, expr):
|
| 603 |
+
if expr.is_aliased:
|
| 604 |
+
return self._print(expr.as_poly().as_expr())
|
| 605 |
+
else:
|
| 606 |
+
return self._print(expr.as_expr())
|
| 607 |
+
|
| 608 |
+
def _print_Pow(self, expr, rational=False):
|
| 609 |
+
"""Printing helper function for ``Pow``
|
| 610 |
+
|
| 611 |
+
Parameters
|
| 612 |
+
==========
|
| 613 |
+
|
| 614 |
+
rational : bool, optional
|
| 615 |
+
If ``True``, it will not attempt printing ``sqrt(x)`` or
|
| 616 |
+
``x**S.Half`` as ``sqrt``, and will use ``x**(1/2)``
|
| 617 |
+
instead.
|
| 618 |
+
|
| 619 |
+
See examples for additional details
|
| 620 |
+
|
| 621 |
+
Examples
|
| 622 |
+
========
|
| 623 |
+
|
| 624 |
+
>>> from sympy import sqrt, StrPrinter
|
| 625 |
+
>>> from sympy.abc import x
|
| 626 |
+
|
| 627 |
+
How ``rational`` keyword works with ``sqrt``:
|
| 628 |
+
|
| 629 |
+
>>> printer = StrPrinter()
|
| 630 |
+
>>> printer._print_Pow(sqrt(x), rational=True)
|
| 631 |
+
'x**(1/2)'
|
| 632 |
+
>>> printer._print_Pow(sqrt(x), rational=False)
|
| 633 |
+
'sqrt(x)'
|
| 634 |
+
>>> printer._print_Pow(1/sqrt(x), rational=True)
|
| 635 |
+
'x**(-1/2)'
|
| 636 |
+
>>> printer._print_Pow(1/sqrt(x), rational=False)
|
| 637 |
+
'1/sqrt(x)'
|
| 638 |
+
|
| 639 |
+
Notes
|
| 640 |
+
=====
|
| 641 |
+
|
| 642 |
+
``sqrt(x)`` is canonicalized as ``Pow(x, S.Half)`` in SymPy,
|
| 643 |
+
so there is no need of defining a separate printer for ``sqrt``.
|
| 644 |
+
Instead, it should be handled here as well.
|
| 645 |
+
"""
|
| 646 |
+
PREC = precedence(expr)
|
| 647 |
+
|
| 648 |
+
if expr.exp is S.Half and not rational:
|
| 649 |
+
return "sqrt(%s)" % self._print(expr.base)
|
| 650 |
+
|
| 651 |
+
if expr.is_commutative:
|
| 652 |
+
if -expr.exp is S.Half and not rational:
|
| 653 |
+
# Note: Don't test "expr.exp == -S.Half" here, because that will
|
| 654 |
+
# match -0.5, which we don't want.
|
| 655 |
+
return "%s/sqrt(%s)" % tuple((self._print(arg) for arg in (S.One, expr.base)))
|
| 656 |
+
if expr.exp is -S.One:
|
| 657 |
+
# Similarly to the S.Half case, don't test with "==" here.
|
| 658 |
+
return '%s/%s' % (self._print(S.One),
|
| 659 |
+
self.parenthesize(expr.base, PREC, strict=False))
|
| 660 |
+
|
| 661 |
+
e = self.parenthesize(expr.exp, PREC, strict=False)
|
| 662 |
+
if self.printmethod == '_sympyrepr' and expr.exp.is_Rational and expr.exp.q != 1:
|
| 663 |
+
# the parenthesized exp should be '(Rational(a, b))' so strip parens,
|
| 664 |
+
# but just check to be sure.
|
| 665 |
+
if e.startswith('(Rational'):
|
| 666 |
+
return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False), e[1:-1])
|
| 667 |
+
return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False), e)
|
| 668 |
+
|
| 669 |
+
def _print_UnevaluatedExpr(self, expr):
|
| 670 |
+
return self._print(expr.args[0])
|
| 671 |
+
|
| 672 |
+
def _print_MatPow(self, expr):
|
| 673 |
+
PREC = precedence(expr)
|
| 674 |
+
return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False),
|
| 675 |
+
self.parenthesize(expr.exp, PREC, strict=False))
|
| 676 |
+
|
| 677 |
+
def _print_Integer(self, expr):
|
| 678 |
+
if self._settings.get("sympy_integers", False):
|
| 679 |
+
return "S(%s)" % (expr)
|
| 680 |
+
return str(expr.p)
|
| 681 |
+
|
| 682 |
+
def _print_Integers(self, expr):
|
| 683 |
+
return 'Integers'
|
| 684 |
+
|
| 685 |
+
def _print_Naturals(self, expr):
|
| 686 |
+
return 'Naturals'
|
| 687 |
+
|
| 688 |
+
def _print_Naturals0(self, expr):
|
| 689 |
+
return 'Naturals0'
|
| 690 |
+
|
| 691 |
+
def _print_Rationals(self, expr):
|
| 692 |
+
return 'Rationals'
|
| 693 |
+
|
| 694 |
+
def _print_Reals(self, expr):
|
| 695 |
+
return 'Reals'
|
| 696 |
+
|
| 697 |
+
def _print_Complexes(self, expr):
|
| 698 |
+
return 'Complexes'
|
| 699 |
+
|
| 700 |
+
def _print_EmptySet(self, expr):
|
| 701 |
+
return 'EmptySet'
|
| 702 |
+
|
| 703 |
+
def _print_EmptySequence(self, expr):
|
| 704 |
+
return 'EmptySequence'
|
| 705 |
+
|
| 706 |
+
def _print_int(self, expr):
|
| 707 |
+
return str(expr)
|
| 708 |
+
|
| 709 |
+
def _print_mpz(self, expr):
|
| 710 |
+
return str(expr)
|
| 711 |
+
|
| 712 |
+
def _print_Rational(self, expr):
|
| 713 |
+
if expr.q == 1:
|
| 714 |
+
return str(expr.p)
|
| 715 |
+
else:
|
| 716 |
+
if self._settings.get("sympy_integers", False):
|
| 717 |
+
return "S(%s)/%s" % (expr.p, expr.q)
|
| 718 |
+
return "%s/%s" % (expr.p, expr.q)
|
| 719 |
+
|
| 720 |
+
def _print_PythonRational(self, expr):
|
| 721 |
+
if expr.q == 1:
|
| 722 |
+
return str(expr.p)
|
| 723 |
+
else:
|
| 724 |
+
return "%d/%d" % (expr.p, expr.q)
|
| 725 |
+
|
| 726 |
+
def _print_Fraction(self, expr):
|
| 727 |
+
if expr.denominator == 1:
|
| 728 |
+
return str(expr.numerator)
|
| 729 |
+
else:
|
| 730 |
+
return "%s/%s" % (expr.numerator, expr.denominator)
|
| 731 |
+
|
| 732 |
+
def _print_mpq(self, expr):
|
| 733 |
+
if expr.denominator == 1:
|
| 734 |
+
return str(expr.numerator)
|
| 735 |
+
else:
|
| 736 |
+
return "%s/%s" % (expr.numerator, expr.denominator)
|
| 737 |
+
|
| 738 |
+
def _print_Float(self, expr):
|
| 739 |
+
prec = expr._prec
|
| 740 |
+
dps = self._settings.get('dps', None)
|
| 741 |
+
if dps is None:
|
| 742 |
+
dps = 0 if prec < 5 else prec_to_dps(expr._prec)
|
| 743 |
+
if self._settings["full_prec"] is True:
|
| 744 |
+
strip = False
|
| 745 |
+
elif self._settings["full_prec"] is False:
|
| 746 |
+
strip = True
|
| 747 |
+
elif self._settings["full_prec"] == "auto":
|
| 748 |
+
strip = self._print_level > 1
|
| 749 |
+
low = self._settings["min"] if "min" in self._settings else None
|
| 750 |
+
high = self._settings["max"] if "max" in self._settings else None
|
| 751 |
+
rv = mlib_to_str(expr._mpf_, dps, strip_zeros=strip, min_fixed=low, max_fixed=high)
|
| 752 |
+
if rv.startswith('-.0'):
|
| 753 |
+
rv = '-0.' + rv[3:]
|
| 754 |
+
elif rv.startswith('.0'):
|
| 755 |
+
rv = '0.' + rv[2:]
|
| 756 |
+
rv = rv.removeprefix('+') # e.g., +inf -> inf
|
| 757 |
+
return rv
|
| 758 |
+
|
| 759 |
+
def _print_Relational(self, expr):
|
| 760 |
+
|
| 761 |
+
charmap = {
|
| 762 |
+
"==": "Eq",
|
| 763 |
+
"!=": "Ne",
|
| 764 |
+
":=": "Assignment",
|
| 765 |
+
'+=': "AddAugmentedAssignment",
|
| 766 |
+
"-=": "SubAugmentedAssignment",
|
| 767 |
+
"*=": "MulAugmentedAssignment",
|
| 768 |
+
"/=": "DivAugmentedAssignment",
|
| 769 |
+
"%=": "ModAugmentedAssignment",
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
if expr.rel_op in charmap:
|
| 773 |
+
return '%s(%s, %s)' % (charmap[expr.rel_op], self._print(expr.lhs),
|
| 774 |
+
self._print(expr.rhs))
|
| 775 |
+
|
| 776 |
+
return '%s %s %s' % (self.parenthesize(expr.lhs, precedence(expr)),
|
| 777 |
+
self._relationals.get(expr.rel_op) or expr.rel_op,
|
| 778 |
+
self.parenthesize(expr.rhs, precedence(expr)))
|
| 779 |
+
|
| 780 |
+
def _print_ComplexRootOf(self, expr):
|
| 781 |
+
return "CRootOf(%s, %d)" % (self._print_Add(expr.expr, order='lex'),
|
| 782 |
+
expr.index)
|
| 783 |
+
|
| 784 |
+
def _print_RootSum(self, expr):
|
| 785 |
+
args = [self._print_Add(expr.expr, order='lex')]
|
| 786 |
+
|
| 787 |
+
if expr.fun is not S.IdentityFunction:
|
| 788 |
+
args.append(self._print(expr.fun))
|
| 789 |
+
|
| 790 |
+
return "RootSum(%s)" % ", ".join(args)
|
| 791 |
+
|
| 792 |
+
def _print_GroebnerBasis(self, basis):
|
| 793 |
+
cls = basis.__class__.__name__
|
| 794 |
+
|
| 795 |
+
exprs = [self._print_Add(arg, order=basis.order) for arg in basis.exprs]
|
| 796 |
+
exprs = "[%s]" % ", ".join(exprs)
|
| 797 |
+
|
| 798 |
+
gens = [ self._print(gen) for gen in basis.gens ]
|
| 799 |
+
domain = "domain='%s'" % self._print(basis.domain)
|
| 800 |
+
order = "order='%s'" % self._print(basis.order)
|
| 801 |
+
|
| 802 |
+
args = [exprs] + gens + [domain, order]
|
| 803 |
+
|
| 804 |
+
return "%s(%s)" % (cls, ", ".join(args))
|
| 805 |
+
|
| 806 |
+
def _print_set(self, s):
|
| 807 |
+
items = sorted(s, key=default_sort_key)
|
| 808 |
+
|
| 809 |
+
args = ', '.join(self._print(item) for item in items)
|
| 810 |
+
if not args:
|
| 811 |
+
return "set()"
|
| 812 |
+
return '{%s}' % args
|
| 813 |
+
|
| 814 |
+
def _print_FiniteSet(self, s):
|
| 815 |
+
from sympy.sets.sets import FiniteSet
|
| 816 |
+
items = sorted(s, key=default_sort_key)
|
| 817 |
+
|
| 818 |
+
args = ', '.join(self._print(item) for item in items)
|
| 819 |
+
if any(item.has(FiniteSet) for item in items):
|
| 820 |
+
return 'FiniteSet({})'.format(args)
|
| 821 |
+
return '{{{}}}'.format(args)
|
| 822 |
+
|
| 823 |
+
def _print_Partition(self, s):
|
| 824 |
+
items = sorted(s, key=default_sort_key)
|
| 825 |
+
|
| 826 |
+
args = ', '.join(self._print(arg) for arg in items)
|
| 827 |
+
return 'Partition({})'.format(args)
|
| 828 |
+
|
| 829 |
+
def _print_frozenset(self, s):
|
| 830 |
+
if not s:
|
| 831 |
+
return "frozenset()"
|
| 832 |
+
return "frozenset(%s)" % self._print_set(s)
|
| 833 |
+
|
| 834 |
+
def _print_Sum(self, expr):
|
| 835 |
+
def _xab_tostr(xab):
|
| 836 |
+
if len(xab) == 1:
|
| 837 |
+
return self._print(xab[0])
|
| 838 |
+
else:
|
| 839 |
+
return self._print((xab[0],) + tuple(xab[1:]))
|
| 840 |
+
L = ', '.join([_xab_tostr(l) for l in expr.limits])
|
| 841 |
+
return 'Sum(%s, %s)' % (self._print(expr.function), L)
|
| 842 |
+
|
| 843 |
+
def _print_Symbol(self, expr):
|
| 844 |
+
return expr.name
|
| 845 |
+
_print_MatrixSymbol = _print_Symbol
|
| 846 |
+
_print_RandomSymbol = _print_Symbol
|
| 847 |
+
|
| 848 |
+
def _print_Identity(self, expr):
|
| 849 |
+
return "I"
|
| 850 |
+
|
| 851 |
+
def _print_ZeroMatrix(self, expr):
|
| 852 |
+
return "0"
|
| 853 |
+
|
| 854 |
+
def _print_OneMatrix(self, expr):
|
| 855 |
+
return "1"
|
| 856 |
+
|
| 857 |
+
def _print_Predicate(self, expr):
|
| 858 |
+
return "Q.%s" % expr.name
|
| 859 |
+
|
| 860 |
+
def _print_str(self, expr):
|
| 861 |
+
return str(expr)
|
| 862 |
+
|
| 863 |
+
def _print_tuple(self, expr):
|
| 864 |
+
if len(expr) == 1:
|
| 865 |
+
return "(%s,)" % self._print(expr[0])
|
| 866 |
+
else:
|
| 867 |
+
return "(%s)" % self.stringify(expr, ", ")
|
| 868 |
+
|
| 869 |
+
def _print_Tuple(self, expr):
|
| 870 |
+
return self._print_tuple(expr)
|
| 871 |
+
|
| 872 |
+
def _print_Transpose(self, T):
|
| 873 |
+
return "%s.T" % self.parenthesize(T.arg, PRECEDENCE["Pow"])
|
| 874 |
+
|
| 875 |
+
def _print_Uniform(self, expr):
|
| 876 |
+
return "Uniform(%s, %s)" % (self._print(expr.a), self._print(expr.b))
|
| 877 |
+
|
| 878 |
+
def _print_Quantity(self, expr):
|
| 879 |
+
if self._settings.get("abbrev", False):
|
| 880 |
+
return "%s" % expr.abbrev
|
| 881 |
+
return "%s" % expr.name
|
| 882 |
+
|
| 883 |
+
def _print_Quaternion(self, expr):
|
| 884 |
+
s = [self.parenthesize(i, PRECEDENCE["Mul"], strict=True) for i in expr.args]
|
| 885 |
+
a = [s[0]] + [i+"*"+j for i, j in zip(s[1:], "ijk")]
|
| 886 |
+
return " + ".join(a)
|
| 887 |
+
|
| 888 |
+
def _print_Dimension(self, expr):
|
| 889 |
+
return str(expr)
|
| 890 |
+
|
| 891 |
+
def _print_Wild(self, expr):
|
| 892 |
+
return expr.name + '_'
|
| 893 |
+
|
| 894 |
+
def _print_WildFunction(self, expr):
|
| 895 |
+
return expr.name + '_'
|
| 896 |
+
|
| 897 |
+
def _print_WildDot(self, expr):
|
| 898 |
+
return expr.name
|
| 899 |
+
|
| 900 |
+
def _print_WildPlus(self, expr):
|
| 901 |
+
return expr.name
|
| 902 |
+
|
| 903 |
+
def _print_WildStar(self, expr):
|
| 904 |
+
return expr.name
|
| 905 |
+
|
| 906 |
+
def _print_Zero(self, expr):
|
| 907 |
+
if self._settings.get("sympy_integers", False):
|
| 908 |
+
return "S(0)"
|
| 909 |
+
return self._print_Integer(Integer(0))
|
| 910 |
+
|
| 911 |
+
def _print_DMP(self, p):
|
| 912 |
+
cls = p.__class__.__name__
|
| 913 |
+
rep = self._print(p.to_list())
|
| 914 |
+
dom = self._print(p.dom)
|
| 915 |
+
|
| 916 |
+
return "%s(%s, %s)" % (cls, rep, dom)
|
| 917 |
+
|
| 918 |
+
def _print_DMF(self, expr):
|
| 919 |
+
cls = expr.__class__.__name__
|
| 920 |
+
num = self._print(expr.num)
|
| 921 |
+
den = self._print(expr.den)
|
| 922 |
+
dom = self._print(expr.dom)
|
| 923 |
+
|
| 924 |
+
return "%s(%s, %s, %s)" % (cls, num, den, dom)
|
| 925 |
+
|
| 926 |
+
def _print_Object(self, obj):
|
| 927 |
+
return 'Object("%s")' % obj.name
|
| 928 |
+
|
| 929 |
+
def _print_IdentityMorphism(self, morphism):
|
| 930 |
+
return 'IdentityMorphism(%s)' % morphism.domain
|
| 931 |
+
|
| 932 |
+
def _print_NamedMorphism(self, morphism):
|
| 933 |
+
return 'NamedMorphism(%s, %s, "%s")' % \
|
| 934 |
+
(morphism.domain, morphism.codomain, morphism.name)
|
| 935 |
+
|
| 936 |
+
def _print_Category(self, category):
|
| 937 |
+
return 'Category("%s")' % category.name
|
| 938 |
+
|
| 939 |
+
def _print_Manifold(self, manifold):
|
| 940 |
+
return manifold.name.name
|
| 941 |
+
|
| 942 |
+
def _print_Patch(self, patch):
|
| 943 |
+
return patch.name.name
|
| 944 |
+
|
| 945 |
+
def _print_CoordSystem(self, coords):
|
| 946 |
+
return coords.name.name
|
| 947 |
+
|
| 948 |
+
def _print_BaseScalarField(self, field):
|
| 949 |
+
return field._coord_sys.symbols[field._index].name
|
| 950 |
+
|
| 951 |
+
def _print_BaseVectorField(self, field):
|
| 952 |
+
return 'e_%s' % field._coord_sys.symbols[field._index].name
|
| 953 |
+
|
| 954 |
+
def _print_Differential(self, diff):
|
| 955 |
+
field = diff._form_field
|
| 956 |
+
if hasattr(field, '_coord_sys'):
|
| 957 |
+
return 'd%s' % field._coord_sys.symbols[field._index].name
|
| 958 |
+
else:
|
| 959 |
+
return 'd(%s)' % self._print(field)
|
| 960 |
+
|
| 961 |
+
def _print_Tr(self, expr):
|
| 962 |
+
#TODO : Handle indices
|
| 963 |
+
return "%s(%s)" % ("Tr", self._print(expr.args[0]))
|
| 964 |
+
|
| 965 |
+
def _print_Str(self, s):
|
| 966 |
+
return self._print(s.name)
|
| 967 |
+
|
| 968 |
+
def _print_AppliedBinaryRelation(self, expr):
|
| 969 |
+
rel = expr.function
|
| 970 |
+
return '%s(%s, %s)' % (self._print(rel),
|
| 971 |
+
self._print(expr.lhs),
|
| 972 |
+
self._print(expr.rhs))
|
| 973 |
+
|
| 974 |
+
|
| 975 |
+
@print_function(StrPrinter)
|
| 976 |
+
def sstr(expr, **settings):
|
| 977 |
+
"""Returns the expression as a string.
|
| 978 |
+
|
| 979 |
+
For large expressions where speed is a concern, use the setting
|
| 980 |
+
order='none'. If abbrev=True setting is used then units are printed in
|
| 981 |
+
abbreviated form.
|
| 982 |
+
|
| 983 |
+
Examples
|
| 984 |
+
========
|
| 985 |
+
|
| 986 |
+
>>> from sympy import symbols, Eq, sstr
|
| 987 |
+
>>> a, b = symbols('a b')
|
| 988 |
+
>>> sstr(Eq(a + b, 0))
|
| 989 |
+
'Eq(a + b, 0)'
|
| 990 |
+
"""
|
| 991 |
+
|
| 992 |
+
p = StrPrinter(settings)
|
| 993 |
+
s = p.doprint(expr)
|
| 994 |
+
|
| 995 |
+
return s
|
| 996 |
+
|
| 997 |
+
|
| 998 |
+
class StrReprPrinter(StrPrinter):
|
| 999 |
+
"""(internal) -- see sstrrepr"""
|
| 1000 |
+
|
| 1001 |
+
def _print_str(self, s):
|
| 1002 |
+
return repr(s)
|
| 1003 |
+
|
| 1004 |
+
def _print_Str(self, s):
|
| 1005 |
+
# Str does not to be printed same as str here
|
| 1006 |
+
return "%s(%s)" % (s.__class__.__name__, self._print(s.name))
|
| 1007 |
+
|
| 1008 |
+
@print_function(StrReprPrinter)
|
| 1009 |
+
def sstrrepr(expr, **settings):
|
| 1010 |
+
"""return expr in mixed str/repr form
|
| 1011 |
+
|
| 1012 |
+
i.e. strings are returned in repr form with quotes, and everything else
|
| 1013 |
+
is returned in str form.
|
| 1014 |
+
|
| 1015 |
+
This function could be useful for hooking into sys.displayhook
|
| 1016 |
+
"""
|
| 1017 |
+
|
| 1018 |
+
p = StrReprPrinter(settings)
|
| 1019 |
+
s = p.doprint(expr)
|
| 1020 |
+
|
| 1021 |
+
return s
|
.venv/lib/python3.13/site-packages/sympy/printing/tree.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def pprint_nodes(subtrees):
|
| 2 |
+
"""
|
| 3 |
+
Prettyprints systems of nodes.
|
| 4 |
+
|
| 5 |
+
Examples
|
| 6 |
+
========
|
| 7 |
+
|
| 8 |
+
>>> from sympy.printing.tree import pprint_nodes
|
| 9 |
+
>>> print(pprint_nodes(["a", "b1\\nb2", "c"]))
|
| 10 |
+
+-a
|
| 11 |
+
+-b1
|
| 12 |
+
| b2
|
| 13 |
+
+-c
|
| 14 |
+
|
| 15 |
+
"""
|
| 16 |
+
def indent(s, type=1):
|
| 17 |
+
x = s.split("\n")
|
| 18 |
+
r = "+-%s\n" % x[0]
|
| 19 |
+
for a in x[1:]:
|
| 20 |
+
if a == "":
|
| 21 |
+
continue
|
| 22 |
+
if type == 1:
|
| 23 |
+
r += "| %s\n" % a
|
| 24 |
+
else:
|
| 25 |
+
r += " %s\n" % a
|
| 26 |
+
return r
|
| 27 |
+
if not subtrees:
|
| 28 |
+
return ""
|
| 29 |
+
f = ""
|
| 30 |
+
for a in subtrees[:-1]:
|
| 31 |
+
f += indent(a)
|
| 32 |
+
f += indent(subtrees[-1], 2)
|
| 33 |
+
return f
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def print_node(node, assumptions=True):
|
| 37 |
+
"""
|
| 38 |
+
Returns information about the "node".
|
| 39 |
+
|
| 40 |
+
This includes class name, string representation and assumptions.
|
| 41 |
+
|
| 42 |
+
Parameters
|
| 43 |
+
==========
|
| 44 |
+
|
| 45 |
+
assumptions : bool, optional
|
| 46 |
+
See the ``assumptions`` keyword in ``tree``
|
| 47 |
+
"""
|
| 48 |
+
s = "%s: %s\n" % (node.__class__.__name__, str(node))
|
| 49 |
+
|
| 50 |
+
if assumptions:
|
| 51 |
+
d = node._assumptions
|
| 52 |
+
else:
|
| 53 |
+
d = None
|
| 54 |
+
|
| 55 |
+
if d:
|
| 56 |
+
for a in sorted(d):
|
| 57 |
+
v = d[a]
|
| 58 |
+
if v is None:
|
| 59 |
+
continue
|
| 60 |
+
s += "%s: %s\n" % (a, v)
|
| 61 |
+
|
| 62 |
+
return s
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def tree(node, assumptions=True):
|
| 66 |
+
"""
|
| 67 |
+
Returns a tree representation of "node" as a string.
|
| 68 |
+
|
| 69 |
+
It uses print_node() together with pprint_nodes() on node.args recursively.
|
| 70 |
+
|
| 71 |
+
Parameters
|
| 72 |
+
==========
|
| 73 |
+
|
| 74 |
+
assumptions : bool, optional
|
| 75 |
+
The flag to decide whether to print out all the assumption data
|
| 76 |
+
(such as ``is_integer`, ``is_real``) associated with the
|
| 77 |
+
expression or not.
|
| 78 |
+
|
| 79 |
+
Enabling the flag makes the result verbose, and the printed
|
| 80 |
+
result may not be deterministic because of the randomness used
|
| 81 |
+
in backtracing the assumptions.
|
| 82 |
+
|
| 83 |
+
See Also
|
| 84 |
+
========
|
| 85 |
+
|
| 86 |
+
print_tree
|
| 87 |
+
|
| 88 |
+
"""
|
| 89 |
+
subtrees = []
|
| 90 |
+
for arg in node.args:
|
| 91 |
+
subtrees.append(tree(arg, assumptions=assumptions))
|
| 92 |
+
s = print_node(node, assumptions=assumptions) + pprint_nodes(subtrees)
|
| 93 |
+
return s
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def print_tree(node, assumptions=True):
|
| 97 |
+
"""
|
| 98 |
+
Prints a tree representation of "node".
|
| 99 |
+
|
| 100 |
+
Parameters
|
| 101 |
+
==========
|
| 102 |
+
|
| 103 |
+
assumptions : bool, optional
|
| 104 |
+
The flag to decide whether to print out all the assumption data
|
| 105 |
+
(such as ``is_integer`, ``is_real``) associated with the
|
| 106 |
+
expression or not.
|
| 107 |
+
|
| 108 |
+
Enabling the flag makes the result verbose, and the printed
|
| 109 |
+
result may not be deterministic because of the randomness used
|
| 110 |
+
in backtracing the assumptions.
|
| 111 |
+
|
| 112 |
+
Examples
|
| 113 |
+
========
|
| 114 |
+
|
| 115 |
+
>>> from sympy.printing import print_tree
|
| 116 |
+
>>> from sympy import Symbol
|
| 117 |
+
>>> x = Symbol('x', odd=True)
|
| 118 |
+
>>> y = Symbol('y', even=True)
|
| 119 |
+
|
| 120 |
+
Printing with full assumptions information:
|
| 121 |
+
|
| 122 |
+
>>> print_tree(y**x)
|
| 123 |
+
Pow: y**x
|
| 124 |
+
+-Symbol: y
|
| 125 |
+
| algebraic: True
|
| 126 |
+
| commutative: True
|
| 127 |
+
| complex: True
|
| 128 |
+
| even: True
|
| 129 |
+
| extended_real: True
|
| 130 |
+
| finite: True
|
| 131 |
+
| hermitian: True
|
| 132 |
+
| imaginary: False
|
| 133 |
+
| infinite: False
|
| 134 |
+
| integer: True
|
| 135 |
+
| irrational: False
|
| 136 |
+
| noninteger: False
|
| 137 |
+
| odd: False
|
| 138 |
+
| rational: True
|
| 139 |
+
| real: True
|
| 140 |
+
| transcendental: False
|
| 141 |
+
+-Symbol: x
|
| 142 |
+
algebraic: True
|
| 143 |
+
commutative: True
|
| 144 |
+
complex: True
|
| 145 |
+
even: False
|
| 146 |
+
extended_nonzero: True
|
| 147 |
+
extended_real: True
|
| 148 |
+
finite: True
|
| 149 |
+
hermitian: True
|
| 150 |
+
imaginary: False
|
| 151 |
+
infinite: False
|
| 152 |
+
integer: True
|
| 153 |
+
irrational: False
|
| 154 |
+
noninteger: False
|
| 155 |
+
nonzero: True
|
| 156 |
+
odd: True
|
| 157 |
+
rational: True
|
| 158 |
+
real: True
|
| 159 |
+
transcendental: False
|
| 160 |
+
zero: False
|
| 161 |
+
|
| 162 |
+
Hiding the assumptions:
|
| 163 |
+
|
| 164 |
+
>>> print_tree(y**x, assumptions=False)
|
| 165 |
+
Pow: y**x
|
| 166 |
+
+-Symbol: y
|
| 167 |
+
+-Symbol: x
|
| 168 |
+
|
| 169 |
+
See Also
|
| 170 |
+
========
|
| 171 |
+
|
| 172 |
+
tree
|
| 173 |
+
|
| 174 |
+
"""
|
| 175 |
+
print(tree(node, assumptions=assumptions))
|