Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/__init__.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/delta.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/expr_with_limits.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/guess.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_delta.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_gosper.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_guess.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_products.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/__pycache__/__init__.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/combinatorial/__init__.py +1 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/combinatorial/factorials.py +1133 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/combinatorial/numbers.py +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/__init__.py +1 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/_trigonometric_special.py +261 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/complexes.py +1492 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/exponential.py +1286 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/hyperbolic.py +2285 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/integers.py +710 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/miscellaneous.py +915 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/piecewise.py +1517 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/tests/test_complexes.py +1030 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/tests/test_miscellaneous.py +504 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/trigonometric.py +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/__init__.py +1 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/bessel.py +2208 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/beta_functions.py +389 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/bsplines.py +348 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/delta_functions.py +664 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/elliptic_integrals.py +445 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/error_functions.py +2801 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/gamma_functions.py +1344 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/hyper.py +1185 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/mathieu_functions.py +269 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/polynomials.py +1447 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/singularity_functions.py +235 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/spherical_harmonics.py +334 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/tensor_functions.py +474 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/zeta_functions.py +786 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/__init__.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/experimental_lambdify.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/plot.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/plot_implicit.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/plotgrid.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/textplot.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/utils.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/backends/textbackend/__init__.py +3 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/backends/textbackend/text.py +24 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/intervalmath/__init__.py +12 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py +413 -0
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (372 Bytes). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/delta.cpython-312.pyc
ADDED
|
Binary file (13.6 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/expr_with_limits.cpython-312.pyc
ADDED
|
Binary file (26.9 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/__pycache__/guess.cpython-312.pyc
ADDED
|
Binary file (23.6 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (212 Bytes). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_delta.cpython-312.pyc
ADDED
|
Binary file (75.8 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_gosper.cpython-312.pyc
ADDED
|
Binary file (20.1 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_guess.cpython-312.pyc
ADDED
|
Binary file (6.58 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/concrete/tests/__pycache__/test_products.cpython-312.pyc
ADDED
|
Binary file (30.8 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (6.17 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/combinatorial/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Stub __init__.py for sympy.functions.combinatorial
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/combinatorial/factorials.py
ADDED
|
@@ -0,0 +1,1133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
from functools import reduce
|
| 3 |
+
|
| 4 |
+
from sympy.core import S, sympify, Dummy, Mod
|
| 5 |
+
from sympy.core.cache import cacheit
|
| 6 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError, PoleError
|
| 7 |
+
from sympy.core.logic import fuzzy_and
|
| 8 |
+
from sympy.core.numbers import Integer, pi, I
|
| 9 |
+
from sympy.core.relational import Eq
|
| 10 |
+
from sympy.external.gmpy import gmpy as _gmpy
|
| 11 |
+
from sympy.ntheory import sieve
|
| 12 |
+
from sympy.ntheory.residue_ntheory import binomial_mod
|
| 13 |
+
from sympy.polys.polytools import Poly
|
| 14 |
+
|
| 15 |
+
from math import factorial as _factorial, prod, sqrt as _sqrt
|
| 16 |
+
|
| 17 |
+
class CombinatorialFunction(DefinedFunction):
|
| 18 |
+
"""Base class for combinatorial functions. """
|
| 19 |
+
|
| 20 |
+
def _eval_simplify(self, **kwargs):
|
| 21 |
+
from sympy.simplify.combsimp import combsimp
|
| 22 |
+
# combinatorial function with non-integer arguments is
|
| 23 |
+
# automatically passed to gammasimp
|
| 24 |
+
expr = combsimp(self)
|
| 25 |
+
measure = kwargs['measure']
|
| 26 |
+
if measure(expr) <= kwargs['ratio']*measure(self):
|
| 27 |
+
return expr
|
| 28 |
+
return self
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
###############################################################################
|
| 32 |
+
######################## FACTORIAL and MULTI-FACTORIAL ########################
|
| 33 |
+
###############################################################################
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class factorial(CombinatorialFunction):
|
| 37 |
+
r"""Implementation of factorial function over nonnegative integers.
|
| 38 |
+
By convention (consistent with the gamma function and the binomial
|
| 39 |
+
coefficients), factorial of a negative integer is complex infinity.
|
| 40 |
+
|
| 41 |
+
The factorial is very important in combinatorics where it gives
|
| 42 |
+
the number of ways in which `n` objects can be permuted. It also
|
| 43 |
+
arises in calculus, probability, number theory, etc.
|
| 44 |
+
|
| 45 |
+
There is strict relation of factorial with gamma function. In
|
| 46 |
+
fact `n! = gamma(n+1)` for nonnegative integers. Rewrite of this
|
| 47 |
+
kind is very useful in case of combinatorial simplification.
|
| 48 |
+
|
| 49 |
+
Computation of the factorial is done using two algorithms. For
|
| 50 |
+
small arguments a precomputed look up table is used. However for bigger
|
| 51 |
+
input algorithm Prime-Swing is used. It is the fastest algorithm
|
| 52 |
+
known and computes `n!` via prime factorization of special class
|
| 53 |
+
of numbers, called here the 'Swing Numbers'.
|
| 54 |
+
|
| 55 |
+
Examples
|
| 56 |
+
========
|
| 57 |
+
|
| 58 |
+
>>> from sympy import Symbol, factorial, S
|
| 59 |
+
>>> n = Symbol('n', integer=True)
|
| 60 |
+
|
| 61 |
+
>>> factorial(0)
|
| 62 |
+
1
|
| 63 |
+
|
| 64 |
+
>>> factorial(7)
|
| 65 |
+
5040
|
| 66 |
+
|
| 67 |
+
>>> factorial(-2)
|
| 68 |
+
zoo
|
| 69 |
+
|
| 70 |
+
>>> factorial(n)
|
| 71 |
+
factorial(n)
|
| 72 |
+
|
| 73 |
+
>>> factorial(2*n)
|
| 74 |
+
factorial(2*n)
|
| 75 |
+
|
| 76 |
+
>>> factorial(S(1)/2)
|
| 77 |
+
factorial(1/2)
|
| 78 |
+
|
| 79 |
+
See Also
|
| 80 |
+
========
|
| 81 |
+
|
| 82 |
+
factorial2, RisingFactorial, FallingFactorial
|
| 83 |
+
"""
|
| 84 |
+
|
| 85 |
+
def fdiff(self, argindex=1):
|
| 86 |
+
from sympy.functions.special.gamma_functions import (gamma, polygamma)
|
| 87 |
+
if argindex == 1:
|
| 88 |
+
return gamma(self.args[0] + 1)*polygamma(0, self.args[0] + 1)
|
| 89 |
+
else:
|
| 90 |
+
raise ArgumentIndexError(self, argindex)
|
| 91 |
+
|
| 92 |
+
_small_swing = [
|
| 93 |
+
1, 1, 1, 3, 3, 15, 5, 35, 35, 315, 63, 693, 231, 3003, 429, 6435, 6435, 109395,
|
| 94 |
+
12155, 230945, 46189, 969969, 88179, 2028117, 676039, 16900975, 1300075,
|
| 95 |
+
35102025, 5014575, 145422675, 9694845, 300540195, 300540195
|
| 96 |
+
]
|
| 97 |
+
|
| 98 |
+
_small_factorials: list[int] = []
|
| 99 |
+
|
| 100 |
+
@classmethod
|
| 101 |
+
def _swing(cls, n):
|
| 102 |
+
if n < 33:
|
| 103 |
+
return cls._small_swing[n]
|
| 104 |
+
else:
|
| 105 |
+
N, primes = int(_sqrt(n)), []
|
| 106 |
+
|
| 107 |
+
for prime in sieve.primerange(3, N + 1):
|
| 108 |
+
p, q = 1, n
|
| 109 |
+
|
| 110 |
+
while True:
|
| 111 |
+
q //= prime
|
| 112 |
+
|
| 113 |
+
if q > 0:
|
| 114 |
+
if q & 1 == 1:
|
| 115 |
+
p *= prime
|
| 116 |
+
else:
|
| 117 |
+
break
|
| 118 |
+
|
| 119 |
+
if p > 1:
|
| 120 |
+
primes.append(p)
|
| 121 |
+
|
| 122 |
+
for prime in sieve.primerange(N + 1, n//3 + 1):
|
| 123 |
+
if (n // prime) & 1 == 1:
|
| 124 |
+
primes.append(prime)
|
| 125 |
+
|
| 126 |
+
L_product = prod(sieve.primerange(n//2 + 1, n + 1))
|
| 127 |
+
R_product = prod(primes)
|
| 128 |
+
|
| 129 |
+
return L_product*R_product
|
| 130 |
+
|
| 131 |
+
@classmethod
|
| 132 |
+
def _recursive(cls, n):
|
| 133 |
+
if n < 2:
|
| 134 |
+
return 1
|
| 135 |
+
else:
|
| 136 |
+
return (cls._recursive(n//2)**2)*cls._swing(n)
|
| 137 |
+
|
| 138 |
+
@classmethod
|
| 139 |
+
def eval(cls, n):
|
| 140 |
+
n = sympify(n)
|
| 141 |
+
|
| 142 |
+
if n.is_Number:
|
| 143 |
+
if n.is_zero:
|
| 144 |
+
return S.One
|
| 145 |
+
elif n is S.Infinity:
|
| 146 |
+
return S.Infinity
|
| 147 |
+
elif n.is_Integer:
|
| 148 |
+
if n.is_negative:
|
| 149 |
+
return S.ComplexInfinity
|
| 150 |
+
else:
|
| 151 |
+
n = n.p
|
| 152 |
+
|
| 153 |
+
if n < 20:
|
| 154 |
+
if not cls._small_factorials:
|
| 155 |
+
result = 1
|
| 156 |
+
for i in range(1, 20):
|
| 157 |
+
result *= i
|
| 158 |
+
cls._small_factorials.append(result)
|
| 159 |
+
result = cls._small_factorials[n-1]
|
| 160 |
+
|
| 161 |
+
# GMPY factorial is faster, use it when available
|
| 162 |
+
#
|
| 163 |
+
# XXX: There is a sympy.external.gmpy.factorial function
|
| 164 |
+
# which provides gmpy.fac if available or the flint version
|
| 165 |
+
# if flint is used. It could be used here to avoid the
|
| 166 |
+
# conditional logic but it needs to be checked whether the
|
| 167 |
+
# pure Python fallback used there is as fast as the
|
| 168 |
+
# fallback used here (perhaps the fallback here should be
|
| 169 |
+
# moved to sympy.external.ntheory).
|
| 170 |
+
elif _gmpy is not None:
|
| 171 |
+
result = _gmpy.fac(n)
|
| 172 |
+
|
| 173 |
+
else:
|
| 174 |
+
bits = bin(n).count('1')
|
| 175 |
+
result = cls._recursive(n)*2**(n - bits)
|
| 176 |
+
|
| 177 |
+
return Integer(result)
|
| 178 |
+
|
| 179 |
+
def _facmod(self, n, q):
|
| 180 |
+
res, N = 1, int(_sqrt(n))
|
| 181 |
+
|
| 182 |
+
# Exponent of prime p in n! is e_p(n) = [n/p] + [n/p**2] + ...
|
| 183 |
+
# for p > sqrt(n), e_p(n) < sqrt(n), the primes with [n/p] = m,
|
| 184 |
+
# occur consecutively and are grouped together in pw[m] for
|
| 185 |
+
# simultaneous exponentiation at a later stage
|
| 186 |
+
pw = [1]*N
|
| 187 |
+
|
| 188 |
+
m = 2 # to initialize the if condition below
|
| 189 |
+
for prime in sieve.primerange(2, n + 1):
|
| 190 |
+
if m > 1:
|
| 191 |
+
m, y = 0, n // prime
|
| 192 |
+
while y:
|
| 193 |
+
m += y
|
| 194 |
+
y //= prime
|
| 195 |
+
if m < N:
|
| 196 |
+
pw[m] = pw[m]*prime % q
|
| 197 |
+
else:
|
| 198 |
+
res = res*pow(prime, m, q) % q
|
| 199 |
+
|
| 200 |
+
for ex, bs in enumerate(pw):
|
| 201 |
+
if ex == 0 or bs == 1:
|
| 202 |
+
continue
|
| 203 |
+
if bs == 0:
|
| 204 |
+
return 0
|
| 205 |
+
res = res*pow(bs, ex, q) % q
|
| 206 |
+
|
| 207 |
+
return res
|
| 208 |
+
|
| 209 |
+
def _eval_Mod(self, q):
|
| 210 |
+
n = self.args[0]
|
| 211 |
+
if n.is_integer and n.is_nonnegative and q.is_integer:
|
| 212 |
+
aq = abs(q)
|
| 213 |
+
d = aq - n
|
| 214 |
+
if d.is_nonpositive:
|
| 215 |
+
return S.Zero
|
| 216 |
+
else:
|
| 217 |
+
isprime = aq.is_prime
|
| 218 |
+
if d == 1:
|
| 219 |
+
# Apply Wilson's theorem (if a natural number n > 1
|
| 220 |
+
# is a prime number, then (n-1)! = -1 mod n) and
|
| 221 |
+
# its inverse (if n > 4 is a composite number, then
|
| 222 |
+
# (n-1)! = 0 mod n)
|
| 223 |
+
if isprime:
|
| 224 |
+
return -1 % q
|
| 225 |
+
elif isprime is False and (aq - 6).is_nonnegative:
|
| 226 |
+
return S.Zero
|
| 227 |
+
elif n.is_Integer and q.is_Integer:
|
| 228 |
+
n, d, aq = map(int, (n, d, aq))
|
| 229 |
+
if isprime and (d - 1 < n):
|
| 230 |
+
fc = self._facmod(d - 1, aq)
|
| 231 |
+
fc = pow(fc, aq - 2, aq)
|
| 232 |
+
if d%2:
|
| 233 |
+
fc = -fc
|
| 234 |
+
else:
|
| 235 |
+
fc = self._facmod(n, aq)
|
| 236 |
+
|
| 237 |
+
return fc % q
|
| 238 |
+
|
| 239 |
+
def _eval_rewrite_as_gamma(self, n, piecewise=True, **kwargs):
|
| 240 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 241 |
+
return gamma(n + 1)
|
| 242 |
+
|
| 243 |
+
def _eval_rewrite_as_Product(self, n, **kwargs):
|
| 244 |
+
from sympy.concrete.products import Product
|
| 245 |
+
if n.is_nonnegative and n.is_integer:
|
| 246 |
+
i = Dummy('i', integer=True)
|
| 247 |
+
return Product(i, (i, 1, n))
|
| 248 |
+
|
| 249 |
+
def _eval_is_integer(self):
|
| 250 |
+
if self.args[0].is_integer and self.args[0].is_nonnegative:
|
| 251 |
+
return True
|
| 252 |
+
|
| 253 |
+
def _eval_is_positive(self):
|
| 254 |
+
if self.args[0].is_integer and self.args[0].is_nonnegative:
|
| 255 |
+
return True
|
| 256 |
+
|
| 257 |
+
def _eval_is_even(self):
|
| 258 |
+
x = self.args[0]
|
| 259 |
+
if x.is_integer and x.is_nonnegative:
|
| 260 |
+
return (x - 2).is_nonnegative
|
| 261 |
+
|
| 262 |
+
def _eval_is_composite(self):
|
| 263 |
+
x = self.args[0]
|
| 264 |
+
if x.is_integer and x.is_nonnegative:
|
| 265 |
+
return (x - 3).is_nonnegative
|
| 266 |
+
|
| 267 |
+
def _eval_is_real(self):
|
| 268 |
+
x = self.args[0]
|
| 269 |
+
if x.is_nonnegative or x.is_noninteger:
|
| 270 |
+
return True
|
| 271 |
+
|
| 272 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 273 |
+
arg = self.args[0].as_leading_term(x)
|
| 274 |
+
arg0 = arg.subs(x, 0)
|
| 275 |
+
if arg0.is_zero:
|
| 276 |
+
return S.One
|
| 277 |
+
elif not arg0.is_infinite:
|
| 278 |
+
return self.func(arg)
|
| 279 |
+
raise PoleError("Cannot expand %s around 0" % (self))
|
| 280 |
+
|
| 281 |
+
class MultiFactorial(CombinatorialFunction):
|
| 282 |
+
pass
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
class subfactorial(CombinatorialFunction):
|
| 286 |
+
r"""The subfactorial counts the derangements of $n$ items and is
|
| 287 |
+
defined for non-negative integers as:
|
| 288 |
+
|
| 289 |
+
.. math:: !n = \begin{cases} 1 & n = 0 \\ 0 & n = 1 \\
|
| 290 |
+
(n-1)(!(n-1) + !(n-2)) & n > 1 \end{cases}
|
| 291 |
+
|
| 292 |
+
It can also be written as ``int(round(n!/exp(1)))`` but the
|
| 293 |
+
recursive definition with caching is implemented for this function.
|
| 294 |
+
|
| 295 |
+
An interesting analytic expression is the following [2]_
|
| 296 |
+
|
| 297 |
+
.. math:: !x = \Gamma(x + 1, -1)/e
|
| 298 |
+
|
| 299 |
+
which is valid for non-negative integers `x`. The above formula
|
| 300 |
+
is not very useful in case of non-integers. `\Gamma(x + 1, -1)` is
|
| 301 |
+
single-valued only for integral arguments `x`, elsewhere on the positive
|
| 302 |
+
real axis it has an infinite number of branches none of which are real.
|
| 303 |
+
|
| 304 |
+
References
|
| 305 |
+
==========
|
| 306 |
+
|
| 307 |
+
.. [1] https://en.wikipedia.org/wiki/Subfactorial
|
| 308 |
+
.. [2] https://mathworld.wolfram.com/Subfactorial.html
|
| 309 |
+
|
| 310 |
+
Examples
|
| 311 |
+
========
|
| 312 |
+
|
| 313 |
+
>>> from sympy import subfactorial
|
| 314 |
+
>>> from sympy.abc import n
|
| 315 |
+
>>> subfactorial(n + 1)
|
| 316 |
+
subfactorial(n + 1)
|
| 317 |
+
>>> subfactorial(5)
|
| 318 |
+
44
|
| 319 |
+
|
| 320 |
+
See Also
|
| 321 |
+
========
|
| 322 |
+
|
| 323 |
+
factorial, uppergamma,
|
| 324 |
+
sympy.utilities.iterables.generate_derangements
|
| 325 |
+
"""
|
| 326 |
+
|
| 327 |
+
@classmethod
|
| 328 |
+
@cacheit
|
| 329 |
+
def _eval(self, n):
|
| 330 |
+
if not n:
|
| 331 |
+
return S.One
|
| 332 |
+
elif n == 1:
|
| 333 |
+
return S.Zero
|
| 334 |
+
else:
|
| 335 |
+
z1, z2 = 1, 0
|
| 336 |
+
for i in range(2, n + 1):
|
| 337 |
+
z1, z2 = z2, (i - 1)*(z2 + z1)
|
| 338 |
+
return z2
|
| 339 |
+
|
| 340 |
+
@classmethod
|
| 341 |
+
def eval(cls, arg):
|
| 342 |
+
if arg.is_Number:
|
| 343 |
+
if arg.is_Integer and arg.is_nonnegative:
|
| 344 |
+
return cls._eval(arg)
|
| 345 |
+
elif arg is S.NaN:
|
| 346 |
+
return S.NaN
|
| 347 |
+
elif arg is S.Infinity:
|
| 348 |
+
return S.Infinity
|
| 349 |
+
|
| 350 |
+
def _eval_is_even(self):
|
| 351 |
+
if self.args[0].is_odd and self.args[0].is_nonnegative:
|
| 352 |
+
return True
|
| 353 |
+
|
| 354 |
+
def _eval_is_integer(self):
|
| 355 |
+
if self.args[0].is_integer and self.args[0].is_nonnegative:
|
| 356 |
+
return True
|
| 357 |
+
|
| 358 |
+
def _eval_rewrite_as_factorial(self, arg, **kwargs):
|
| 359 |
+
from sympy.concrete.summations import summation
|
| 360 |
+
i = Dummy('i')
|
| 361 |
+
f = S.NegativeOne**i / factorial(i)
|
| 362 |
+
return factorial(arg) * summation(f, (i, 0, arg))
|
| 363 |
+
|
| 364 |
+
def _eval_rewrite_as_gamma(self, arg, piecewise=True, **kwargs):
|
| 365 |
+
from sympy.functions.elementary.exponential import exp
|
| 366 |
+
from sympy.functions.special.gamma_functions import (gamma, lowergamma)
|
| 367 |
+
return (S.NegativeOne**(arg + 1)*exp(-I*pi*arg)*lowergamma(arg + 1, -1)
|
| 368 |
+
+ gamma(arg + 1))*exp(-1)
|
| 369 |
+
|
| 370 |
+
def _eval_rewrite_as_uppergamma(self, arg, **kwargs):
|
| 371 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 372 |
+
return uppergamma(arg + 1, -1)/S.Exp1
|
| 373 |
+
|
| 374 |
+
def _eval_is_nonnegative(self):
|
| 375 |
+
if self.args[0].is_integer and self.args[0].is_nonnegative:
|
| 376 |
+
return True
|
| 377 |
+
|
| 378 |
+
def _eval_is_odd(self):
|
| 379 |
+
if self.args[0].is_even and self.args[0].is_nonnegative:
|
| 380 |
+
return True
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
class factorial2(CombinatorialFunction):
|
| 384 |
+
r"""The double factorial `n!!`, not to be confused with `(n!)!`
|
| 385 |
+
|
| 386 |
+
The double factorial is defined for nonnegative integers and for odd
|
| 387 |
+
negative integers as:
|
| 388 |
+
|
| 389 |
+
.. math:: n!! = \begin{cases} 1 & n = 0 \\
|
| 390 |
+
n(n-2)(n-4) \cdots 1 & n\ \text{positive odd} \\
|
| 391 |
+
n(n-2)(n-4) \cdots 2 & n\ \text{positive even} \\
|
| 392 |
+
(n+2)!!/(n+2) & n\ \text{negative odd} \end{cases}
|
| 393 |
+
|
| 394 |
+
References
|
| 395 |
+
==========
|
| 396 |
+
|
| 397 |
+
.. [1] https://en.wikipedia.org/wiki/Double_factorial
|
| 398 |
+
|
| 399 |
+
Examples
|
| 400 |
+
========
|
| 401 |
+
|
| 402 |
+
>>> from sympy import factorial2, var
|
| 403 |
+
>>> n = var('n')
|
| 404 |
+
>>> n
|
| 405 |
+
n
|
| 406 |
+
>>> factorial2(n + 1)
|
| 407 |
+
factorial2(n + 1)
|
| 408 |
+
>>> factorial2(5)
|
| 409 |
+
15
|
| 410 |
+
>>> factorial2(-1)
|
| 411 |
+
1
|
| 412 |
+
>>> factorial2(-5)
|
| 413 |
+
1/3
|
| 414 |
+
|
| 415 |
+
See Also
|
| 416 |
+
========
|
| 417 |
+
|
| 418 |
+
factorial, RisingFactorial, FallingFactorial
|
| 419 |
+
"""
|
| 420 |
+
|
| 421 |
+
@classmethod
|
| 422 |
+
def eval(cls, arg):
|
| 423 |
+
# TODO: extend this to complex numbers?
|
| 424 |
+
|
| 425 |
+
if arg.is_Number:
|
| 426 |
+
if not arg.is_Integer:
|
| 427 |
+
raise ValueError("argument must be nonnegative integer "
|
| 428 |
+
"or negative odd integer")
|
| 429 |
+
|
| 430 |
+
# This implementation is faster than the recursive one
|
| 431 |
+
# It also avoids "maximum recursion depth exceeded" runtime error
|
| 432 |
+
if arg.is_nonnegative:
|
| 433 |
+
if arg.is_even:
|
| 434 |
+
k = arg / 2
|
| 435 |
+
return 2**k * factorial(k)
|
| 436 |
+
return factorial(arg) / factorial2(arg - 1)
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
if arg.is_odd:
|
| 440 |
+
return arg*(S.NegativeOne)**((1 - arg)/2) / factorial2(-arg)
|
| 441 |
+
raise ValueError("argument must be nonnegative integer "
|
| 442 |
+
"or negative odd integer")
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
def _eval_is_even(self):
|
| 446 |
+
# Double factorial is even for every positive even input
|
| 447 |
+
n = self.args[0]
|
| 448 |
+
if n.is_integer:
|
| 449 |
+
if n.is_odd:
|
| 450 |
+
return False
|
| 451 |
+
if n.is_even:
|
| 452 |
+
if n.is_positive:
|
| 453 |
+
return True
|
| 454 |
+
if n.is_zero:
|
| 455 |
+
return False
|
| 456 |
+
|
| 457 |
+
def _eval_is_integer(self):
|
| 458 |
+
# Double factorial is an integer for every nonnegative input, and for
|
| 459 |
+
# -1 and -3
|
| 460 |
+
n = self.args[0]
|
| 461 |
+
if n.is_integer:
|
| 462 |
+
if (n + 1).is_nonnegative:
|
| 463 |
+
return True
|
| 464 |
+
if n.is_odd:
|
| 465 |
+
return (n + 3).is_nonnegative
|
| 466 |
+
|
| 467 |
+
def _eval_is_odd(self):
|
| 468 |
+
# Double factorial is odd for every odd input not smaller than -3, and
|
| 469 |
+
# for 0
|
| 470 |
+
n = self.args[0]
|
| 471 |
+
if n.is_odd:
|
| 472 |
+
return (n + 3).is_nonnegative
|
| 473 |
+
if n.is_even:
|
| 474 |
+
if n.is_positive:
|
| 475 |
+
return False
|
| 476 |
+
if n.is_zero:
|
| 477 |
+
return True
|
| 478 |
+
|
| 479 |
+
def _eval_is_positive(self):
|
| 480 |
+
# Double factorial is positive for every nonnegative input, and for
|
| 481 |
+
# every odd negative input which is of the form -1-4k for an
|
| 482 |
+
# nonnegative integer k
|
| 483 |
+
n = self.args[0]
|
| 484 |
+
if n.is_integer:
|
| 485 |
+
if (n + 1).is_nonnegative:
|
| 486 |
+
return True
|
| 487 |
+
if n.is_odd:
|
| 488 |
+
return ((n + 1) / 2).is_even
|
| 489 |
+
|
| 490 |
+
def _eval_rewrite_as_gamma(self, n, piecewise=True, **kwargs):
|
| 491 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 492 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 493 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 494 |
+
return 2**(n/2)*gamma(n/2 + 1) * Piecewise((1, Eq(Mod(n, 2), 0)),
|
| 495 |
+
(sqrt(2/pi), Eq(Mod(n, 2), 1)))
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
###############################################################################
|
| 499 |
+
######################## RISING and FALLING FACTORIALS ########################
|
| 500 |
+
###############################################################################
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
class RisingFactorial(CombinatorialFunction):
|
| 504 |
+
r"""
|
| 505 |
+
Rising factorial (also called Pochhammer symbol [1]_) is a double valued
|
| 506 |
+
function arising in concrete mathematics, hypergeometric functions
|
| 507 |
+
and series expansions. It is defined by:
|
| 508 |
+
|
| 509 |
+
.. math:: \texttt{rf(y, k)} = (x)^k = x \cdot (x+1) \cdots (x+k-1)
|
| 510 |
+
|
| 511 |
+
where `x` can be arbitrary expression and `k` is an integer. For
|
| 512 |
+
more information check "Concrete mathematics" by Graham, pp. 66
|
| 513 |
+
or visit https://mathworld.wolfram.com/RisingFactorial.html page.
|
| 514 |
+
|
| 515 |
+
When `x` is a `~.Poly` instance of degree $\ge 1$ with a single variable,
|
| 516 |
+
`(x)^k = x(y) \cdot x(y+1) \cdots x(y+k-1)`, where `y` is the
|
| 517 |
+
variable of `x`. This is as described in [2]_.
|
| 518 |
+
|
| 519 |
+
Examples
|
| 520 |
+
========
|
| 521 |
+
|
| 522 |
+
>>> from sympy import rf, Poly
|
| 523 |
+
>>> from sympy.abc import x
|
| 524 |
+
>>> rf(x, 0)
|
| 525 |
+
1
|
| 526 |
+
>>> rf(1, 5)
|
| 527 |
+
120
|
| 528 |
+
>>> rf(x, 5) == x*(1 + x)*(2 + x)*(3 + x)*(4 + x)
|
| 529 |
+
True
|
| 530 |
+
>>> rf(Poly(x**3, x), 2)
|
| 531 |
+
Poly(x**6 + 3*x**5 + 3*x**4 + x**3, x, domain='ZZ')
|
| 532 |
+
|
| 533 |
+
Rewriting is complicated unless the relationship between
|
| 534 |
+
the arguments is known, but rising factorial can
|
| 535 |
+
be rewritten in terms of gamma, factorial, binomial,
|
| 536 |
+
and falling factorial.
|
| 537 |
+
|
| 538 |
+
>>> from sympy import Symbol, factorial, ff, binomial, gamma
|
| 539 |
+
>>> n = Symbol('n', integer=True, positive=True)
|
| 540 |
+
>>> R = rf(n, n + 2)
|
| 541 |
+
>>> for i in (rf, ff, factorial, binomial, gamma):
|
| 542 |
+
... R.rewrite(i)
|
| 543 |
+
...
|
| 544 |
+
RisingFactorial(n, n + 2)
|
| 545 |
+
FallingFactorial(2*n + 1, n + 2)
|
| 546 |
+
factorial(2*n + 1)/factorial(n - 1)
|
| 547 |
+
binomial(2*n + 1, n + 2)*factorial(n + 2)
|
| 548 |
+
gamma(2*n + 2)/gamma(n)
|
| 549 |
+
|
| 550 |
+
See Also
|
| 551 |
+
========
|
| 552 |
+
|
| 553 |
+
factorial, factorial2, FallingFactorial
|
| 554 |
+
|
| 555 |
+
References
|
| 556 |
+
==========
|
| 557 |
+
|
| 558 |
+
.. [1] https://en.wikipedia.org/wiki/Pochhammer_symbol
|
| 559 |
+
.. [2] Peter Paule, "Greatest Factorial Factorization and Symbolic
|
| 560 |
+
Summation", Journal of Symbolic Computation, vol. 20, pp. 235-268,
|
| 561 |
+
1995.
|
| 562 |
+
|
| 563 |
+
"""
|
| 564 |
+
|
| 565 |
+
@classmethod
|
| 566 |
+
def eval(cls, x, k):
|
| 567 |
+
x = sympify(x)
|
| 568 |
+
k = sympify(k)
|
| 569 |
+
|
| 570 |
+
if x is S.NaN or k is S.NaN:
|
| 571 |
+
return S.NaN
|
| 572 |
+
elif x is S.One:
|
| 573 |
+
return factorial(k)
|
| 574 |
+
elif k.is_Integer:
|
| 575 |
+
if k.is_zero:
|
| 576 |
+
return S.One
|
| 577 |
+
else:
|
| 578 |
+
if k.is_positive:
|
| 579 |
+
if x is S.Infinity:
|
| 580 |
+
return S.Infinity
|
| 581 |
+
elif x is S.NegativeInfinity:
|
| 582 |
+
if k.is_odd:
|
| 583 |
+
return S.NegativeInfinity
|
| 584 |
+
else:
|
| 585 |
+
return S.Infinity
|
| 586 |
+
else:
|
| 587 |
+
if isinstance(x, Poly):
|
| 588 |
+
gens = x.gens
|
| 589 |
+
if len(gens)!= 1:
|
| 590 |
+
raise ValueError("rf only defined for "
|
| 591 |
+
"polynomials on one generator")
|
| 592 |
+
else:
|
| 593 |
+
return reduce(lambda r, i:
|
| 594 |
+
r*(x.shift(i)),
|
| 595 |
+
range(int(k)), 1)
|
| 596 |
+
else:
|
| 597 |
+
return reduce(lambda r, i: r*(x + i),
|
| 598 |
+
range(int(k)), 1)
|
| 599 |
+
|
| 600 |
+
else:
|
| 601 |
+
if x is S.Infinity:
|
| 602 |
+
return S.Infinity
|
| 603 |
+
elif x is S.NegativeInfinity:
|
| 604 |
+
return S.Infinity
|
| 605 |
+
else:
|
| 606 |
+
if isinstance(x, Poly):
|
| 607 |
+
gens = x.gens
|
| 608 |
+
if len(gens)!= 1:
|
| 609 |
+
raise ValueError("rf only defined for "
|
| 610 |
+
"polynomials on one generator")
|
| 611 |
+
else:
|
| 612 |
+
return 1/reduce(lambda r, i:
|
| 613 |
+
r*(x.shift(-i)),
|
| 614 |
+
range(1, abs(int(k)) + 1), 1)
|
| 615 |
+
else:
|
| 616 |
+
return 1/reduce(lambda r, i:
|
| 617 |
+
r*(x - i),
|
| 618 |
+
range(1, abs(int(k)) + 1), 1)
|
| 619 |
+
|
| 620 |
+
if k.is_integer == False:
|
| 621 |
+
if x.is_integer and x.is_negative:
|
| 622 |
+
return S.Zero
|
| 623 |
+
|
| 624 |
+
def _eval_rewrite_as_gamma(self, x, k, piecewise=True, **kwargs):
|
| 625 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 626 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 627 |
+
if not piecewise:
|
| 628 |
+
if (x <= 0) == True:
|
| 629 |
+
return S.NegativeOne**k*gamma(1 - x) / gamma(-k - x + 1)
|
| 630 |
+
return gamma(x + k) / gamma(x)
|
| 631 |
+
return Piecewise(
|
| 632 |
+
(gamma(x + k) / gamma(x), x > 0),
|
| 633 |
+
(S.NegativeOne**k*gamma(1 - x) / gamma(-k - x + 1), True))
|
| 634 |
+
|
| 635 |
+
def _eval_rewrite_as_FallingFactorial(self, x, k, **kwargs):
|
| 636 |
+
return FallingFactorial(x + k - 1, k)
|
| 637 |
+
|
| 638 |
+
def _eval_rewrite_as_factorial(self, x, k, **kwargs):
|
| 639 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 640 |
+
if x.is_integer and k.is_integer:
|
| 641 |
+
return Piecewise(
|
| 642 |
+
(factorial(k + x - 1)/factorial(x - 1), x > 0),
|
| 643 |
+
(S.NegativeOne**k*factorial(-x)/factorial(-k - x), True))
|
| 644 |
+
|
| 645 |
+
def _eval_rewrite_as_binomial(self, x, k, **kwargs):
|
| 646 |
+
if k.is_integer:
|
| 647 |
+
return factorial(k) * binomial(x + k - 1, k)
|
| 648 |
+
|
| 649 |
+
def _eval_rewrite_as_tractable(self, x, k, limitvar=None, **kwargs):
|
| 650 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 651 |
+
if limitvar:
|
| 652 |
+
k_lim = k.subs(limitvar, S.Infinity)
|
| 653 |
+
if k_lim is S.Infinity:
|
| 654 |
+
return (gamma(x + k).rewrite('tractable', deep=True) / gamma(x))
|
| 655 |
+
elif k_lim is S.NegativeInfinity:
|
| 656 |
+
return (S.NegativeOne**k*gamma(1 - x) / gamma(-k - x + 1).rewrite('tractable', deep=True))
|
| 657 |
+
return self.rewrite(gamma).rewrite('tractable', deep=True)
|
| 658 |
+
|
| 659 |
+
def _eval_is_integer(self):
|
| 660 |
+
return fuzzy_and((self.args[0].is_integer, self.args[1].is_integer,
|
| 661 |
+
self.args[1].is_nonnegative))
|
| 662 |
+
|
| 663 |
+
|
| 664 |
+
class FallingFactorial(CombinatorialFunction):
|
| 665 |
+
r"""
|
| 666 |
+
Falling factorial (related to rising factorial) is a double valued
|
| 667 |
+
function arising in concrete mathematics, hypergeometric functions
|
| 668 |
+
and series expansions. It is defined by
|
| 669 |
+
|
| 670 |
+
.. math:: \texttt{ff(x, k)} = (x)_k = x \cdot (x-1) \cdots (x-k+1)
|
| 671 |
+
|
| 672 |
+
where `x` can be arbitrary expression and `k` is an integer. For
|
| 673 |
+
more information check "Concrete mathematics" by Graham, pp. 66
|
| 674 |
+
or [1]_.
|
| 675 |
+
|
| 676 |
+
When `x` is a `~.Poly` instance of degree $\ge 1$ with single variable,
|
| 677 |
+
`(x)_k = x(y) \cdot x(y-1) \cdots x(y-k+1)`, where `y` is the
|
| 678 |
+
variable of `x`. This is as described in
|
| 679 |
+
|
| 680 |
+
>>> from sympy import ff, Poly, Symbol
|
| 681 |
+
>>> from sympy.abc import x
|
| 682 |
+
>>> n = Symbol('n', integer=True)
|
| 683 |
+
|
| 684 |
+
>>> ff(x, 0)
|
| 685 |
+
1
|
| 686 |
+
>>> ff(5, 5)
|
| 687 |
+
120
|
| 688 |
+
>>> ff(x, 5) == x*(x - 1)*(x - 2)*(x - 3)*(x - 4)
|
| 689 |
+
True
|
| 690 |
+
>>> ff(Poly(x**2, x), 2)
|
| 691 |
+
Poly(x**4 - 2*x**3 + x**2, x, domain='ZZ')
|
| 692 |
+
>>> ff(n, n)
|
| 693 |
+
factorial(n)
|
| 694 |
+
|
| 695 |
+
Rewriting is complicated unless the relationship between
|
| 696 |
+
the arguments is known, but falling factorial can
|
| 697 |
+
be rewritten in terms of gamma, factorial and binomial
|
| 698 |
+
and rising factorial.
|
| 699 |
+
|
| 700 |
+
>>> from sympy import factorial, rf, gamma, binomial, Symbol
|
| 701 |
+
>>> n = Symbol('n', integer=True, positive=True)
|
| 702 |
+
>>> F = ff(n, n - 2)
|
| 703 |
+
>>> for i in (rf, ff, factorial, binomial, gamma):
|
| 704 |
+
... F.rewrite(i)
|
| 705 |
+
...
|
| 706 |
+
RisingFactorial(3, n - 2)
|
| 707 |
+
FallingFactorial(n, n - 2)
|
| 708 |
+
factorial(n)/2
|
| 709 |
+
binomial(n, n - 2)*factorial(n - 2)
|
| 710 |
+
gamma(n + 1)/2
|
| 711 |
+
|
| 712 |
+
See Also
|
| 713 |
+
========
|
| 714 |
+
|
| 715 |
+
factorial, factorial2, RisingFactorial
|
| 716 |
+
|
| 717 |
+
References
|
| 718 |
+
==========
|
| 719 |
+
|
| 720 |
+
.. [1] https://mathworld.wolfram.com/FallingFactorial.html
|
| 721 |
+
.. [2] Peter Paule, "Greatest Factorial Factorization and Symbolic
|
| 722 |
+
Summation", Journal of Symbolic Computation, vol. 20, pp. 235-268,
|
| 723 |
+
1995.
|
| 724 |
+
|
| 725 |
+
"""
|
| 726 |
+
|
| 727 |
+
@classmethod
|
| 728 |
+
def eval(cls, x, k):
|
| 729 |
+
x = sympify(x)
|
| 730 |
+
k = sympify(k)
|
| 731 |
+
|
| 732 |
+
if x is S.NaN or k is S.NaN:
|
| 733 |
+
return S.NaN
|
| 734 |
+
elif k.is_integer and x == k:
|
| 735 |
+
return factorial(x)
|
| 736 |
+
elif k.is_Integer:
|
| 737 |
+
if k.is_zero:
|
| 738 |
+
return S.One
|
| 739 |
+
else:
|
| 740 |
+
if k.is_positive:
|
| 741 |
+
if x is S.Infinity:
|
| 742 |
+
return S.Infinity
|
| 743 |
+
elif x is S.NegativeInfinity:
|
| 744 |
+
if k.is_odd:
|
| 745 |
+
return S.NegativeInfinity
|
| 746 |
+
else:
|
| 747 |
+
return S.Infinity
|
| 748 |
+
else:
|
| 749 |
+
if isinstance(x, Poly):
|
| 750 |
+
gens = x.gens
|
| 751 |
+
if len(gens)!= 1:
|
| 752 |
+
raise ValueError("ff only defined for "
|
| 753 |
+
"polynomials on one generator")
|
| 754 |
+
else:
|
| 755 |
+
return reduce(lambda r, i:
|
| 756 |
+
r*(x.shift(-i)),
|
| 757 |
+
range(int(k)), 1)
|
| 758 |
+
else:
|
| 759 |
+
return reduce(lambda r, i: r*(x - i),
|
| 760 |
+
range(int(k)), 1)
|
| 761 |
+
else:
|
| 762 |
+
if x is S.Infinity:
|
| 763 |
+
return S.Infinity
|
| 764 |
+
elif x is S.NegativeInfinity:
|
| 765 |
+
return S.Infinity
|
| 766 |
+
else:
|
| 767 |
+
if isinstance(x, Poly):
|
| 768 |
+
gens = x.gens
|
| 769 |
+
if len(gens)!= 1:
|
| 770 |
+
raise ValueError("rf only defined for "
|
| 771 |
+
"polynomials on one generator")
|
| 772 |
+
else:
|
| 773 |
+
return 1/reduce(lambda r, i:
|
| 774 |
+
r*(x.shift(i)),
|
| 775 |
+
range(1, abs(int(k)) + 1), 1)
|
| 776 |
+
else:
|
| 777 |
+
return 1/reduce(lambda r, i: r*(x + i),
|
| 778 |
+
range(1, abs(int(k)) + 1), 1)
|
| 779 |
+
|
| 780 |
+
def _eval_rewrite_as_gamma(self, x, k, piecewise=True, **kwargs):
|
| 781 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 782 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 783 |
+
if not piecewise:
|
| 784 |
+
if (x < 0) == True:
|
| 785 |
+
return S.NegativeOne**k*gamma(k - x) / gamma(-x)
|
| 786 |
+
return gamma(x + 1) / gamma(x - k + 1)
|
| 787 |
+
return Piecewise(
|
| 788 |
+
(gamma(x + 1) / gamma(x - k + 1), x >= 0),
|
| 789 |
+
(S.NegativeOne**k*gamma(k - x) / gamma(-x), True))
|
| 790 |
+
|
| 791 |
+
def _eval_rewrite_as_RisingFactorial(self, x, k, **kwargs):
|
| 792 |
+
return rf(x - k + 1, k)
|
| 793 |
+
|
| 794 |
+
def _eval_rewrite_as_binomial(self, x, k, **kwargs):
|
| 795 |
+
if k.is_integer:
|
| 796 |
+
return factorial(k) * binomial(x, k)
|
| 797 |
+
|
| 798 |
+
def _eval_rewrite_as_factorial(self, x, k, **kwargs):
|
| 799 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 800 |
+
if x.is_integer and k.is_integer:
|
| 801 |
+
return Piecewise(
|
| 802 |
+
(factorial(x)/factorial(-k + x), x >= 0),
|
| 803 |
+
(S.NegativeOne**k*factorial(k - x - 1)/factorial(-x - 1), True))
|
| 804 |
+
|
| 805 |
+
def _eval_rewrite_as_tractable(self, x, k, limitvar=None, **kwargs):
|
| 806 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 807 |
+
if limitvar:
|
| 808 |
+
k_lim = k.subs(limitvar, S.Infinity)
|
| 809 |
+
if k_lim is S.Infinity:
|
| 810 |
+
return (S.NegativeOne**k*gamma(k - x).rewrite('tractable', deep=True) / gamma(-x))
|
| 811 |
+
elif k_lim is S.NegativeInfinity:
|
| 812 |
+
return (gamma(x + 1) / gamma(x - k + 1).rewrite('tractable', deep=True))
|
| 813 |
+
return self.rewrite(gamma).rewrite('tractable', deep=True)
|
| 814 |
+
|
| 815 |
+
def _eval_is_integer(self):
|
| 816 |
+
return fuzzy_and((self.args[0].is_integer, self.args[1].is_integer,
|
| 817 |
+
self.args[1].is_nonnegative))
|
| 818 |
+
|
| 819 |
+
|
| 820 |
+
rf = RisingFactorial
|
| 821 |
+
ff = FallingFactorial
|
| 822 |
+
|
| 823 |
+
###############################################################################
|
| 824 |
+
########################### BINOMIAL COEFFICIENTS #############################
|
| 825 |
+
###############################################################################
|
| 826 |
+
|
| 827 |
+
|
| 828 |
+
class binomial(CombinatorialFunction):
|
| 829 |
+
r"""Implementation of the binomial coefficient. It can be defined
|
| 830 |
+
in two ways depending on its desired interpretation:
|
| 831 |
+
|
| 832 |
+
.. math:: \binom{n}{k} = \frac{n!}{k!(n-k)!}\ \text{or}\
|
| 833 |
+
\binom{n}{k} = \frac{(n)_k}{k!}
|
| 834 |
+
|
| 835 |
+
First, in a strict combinatorial sense it defines the
|
| 836 |
+
number of ways we can choose `k` elements from a set of
|
| 837 |
+
`n` elements. In this case both arguments are nonnegative
|
| 838 |
+
integers and binomial is computed using an efficient
|
| 839 |
+
algorithm based on prime factorization.
|
| 840 |
+
|
| 841 |
+
The other definition is generalization for arbitrary `n`,
|
| 842 |
+
however `k` must also be nonnegative. This case is very
|
| 843 |
+
useful when evaluating summations.
|
| 844 |
+
|
| 845 |
+
For the sake of convenience, for negative integer `k` this function
|
| 846 |
+
will return zero no matter the other argument.
|
| 847 |
+
|
| 848 |
+
To expand the binomial when `n` is a symbol, use either
|
| 849 |
+
``expand_func()`` or ``expand(func=True)``. The former will keep
|
| 850 |
+
the polynomial in factored form while the latter will expand the
|
| 851 |
+
polynomial itself. See examples for details.
|
| 852 |
+
|
| 853 |
+
Examples
|
| 854 |
+
========
|
| 855 |
+
|
| 856 |
+
>>> from sympy import Symbol, Rational, binomial, expand_func
|
| 857 |
+
>>> n = Symbol('n', integer=True, positive=True)
|
| 858 |
+
|
| 859 |
+
>>> binomial(15, 8)
|
| 860 |
+
6435
|
| 861 |
+
|
| 862 |
+
>>> binomial(n, -1)
|
| 863 |
+
0
|
| 864 |
+
|
| 865 |
+
Rows of Pascal's triangle can be generated with the binomial function:
|
| 866 |
+
|
| 867 |
+
>>> for N in range(8):
|
| 868 |
+
... print([binomial(N, i) for i in range(N + 1)])
|
| 869 |
+
...
|
| 870 |
+
[1]
|
| 871 |
+
[1, 1]
|
| 872 |
+
[1, 2, 1]
|
| 873 |
+
[1, 3, 3, 1]
|
| 874 |
+
[1, 4, 6, 4, 1]
|
| 875 |
+
[1, 5, 10, 10, 5, 1]
|
| 876 |
+
[1, 6, 15, 20, 15, 6, 1]
|
| 877 |
+
[1, 7, 21, 35, 35, 21, 7, 1]
|
| 878 |
+
|
| 879 |
+
As can a given diagonal, e.g. the 4th diagonal:
|
| 880 |
+
|
| 881 |
+
>>> N = -4
|
| 882 |
+
>>> [binomial(N, i) for i in range(1 - N)]
|
| 883 |
+
[1, -4, 10, -20, 35]
|
| 884 |
+
|
| 885 |
+
>>> binomial(Rational(5, 4), 3)
|
| 886 |
+
-5/128
|
| 887 |
+
>>> binomial(Rational(-5, 4), 3)
|
| 888 |
+
-195/128
|
| 889 |
+
|
| 890 |
+
>>> binomial(n, 3)
|
| 891 |
+
binomial(n, 3)
|
| 892 |
+
|
| 893 |
+
>>> binomial(n, 3).expand(func=True)
|
| 894 |
+
n**3/6 - n**2/2 + n/3
|
| 895 |
+
|
| 896 |
+
>>> expand_func(binomial(n, 3))
|
| 897 |
+
n*(n - 2)*(n - 1)/6
|
| 898 |
+
|
| 899 |
+
In many cases, we can also compute binomial coefficients modulo a
|
| 900 |
+
prime p quickly using Lucas' Theorem [2]_, though we need to include
|
| 901 |
+
`evaluate=False` to postpone evaluation:
|
| 902 |
+
|
| 903 |
+
>>> from sympy import Mod
|
| 904 |
+
>>> Mod(binomial(156675, 4433, evaluate=False), 10**5 + 3)
|
| 905 |
+
28625
|
| 906 |
+
|
| 907 |
+
Using a generalisation of Lucas's Theorem given by Granville [3]_,
|
| 908 |
+
we can extend this to arbitrary n:
|
| 909 |
+
|
| 910 |
+
>>> Mod(binomial(10**18, 10**12, evaluate=False), (10**5 + 3)**2)
|
| 911 |
+
3744312326
|
| 912 |
+
|
| 913 |
+
References
|
| 914 |
+
==========
|
| 915 |
+
|
| 916 |
+
.. [1] https://www.johndcook.com/blog/binomial_coefficients/
|
| 917 |
+
.. [2] https://en.wikipedia.org/wiki/Lucas%27s_theorem
|
| 918 |
+
.. [3] Binomial coefficients modulo prime powers, Andrew Granville,
|
| 919 |
+
Available: https://web.archive.org/web/20170202003812/http://www.dms.umontreal.ca/~andrew/PDF/BinCoeff.pdf
|
| 920 |
+
"""
|
| 921 |
+
|
| 922 |
+
def fdiff(self, argindex=1):
|
| 923 |
+
from sympy.functions.special.gamma_functions import polygamma
|
| 924 |
+
if argindex == 1:
|
| 925 |
+
# https://functions.wolfram.com/GammaBetaErf/Binomial/20/01/01/
|
| 926 |
+
n, k = self.args
|
| 927 |
+
return binomial(n, k)*(polygamma(0, n + 1) - \
|
| 928 |
+
polygamma(0, n - k + 1))
|
| 929 |
+
elif argindex == 2:
|
| 930 |
+
# https://functions.wolfram.com/GammaBetaErf/Binomial/20/01/02/
|
| 931 |
+
n, k = self.args
|
| 932 |
+
return binomial(n, k)*(polygamma(0, n - k + 1) - \
|
| 933 |
+
polygamma(0, k + 1))
|
| 934 |
+
else:
|
| 935 |
+
raise ArgumentIndexError(self, argindex)
|
| 936 |
+
|
| 937 |
+
@classmethod
|
| 938 |
+
def _eval(self, n, k):
|
| 939 |
+
# n.is_Number and k.is_Integer and k != 1 and n != k
|
| 940 |
+
|
| 941 |
+
if k.is_Integer:
|
| 942 |
+
if n.is_Integer and n >= 0:
|
| 943 |
+
n, k = int(n), int(k)
|
| 944 |
+
|
| 945 |
+
if k > n:
|
| 946 |
+
return S.Zero
|
| 947 |
+
elif k > n // 2:
|
| 948 |
+
k = n - k
|
| 949 |
+
|
| 950 |
+
# XXX: This conditional logic should be moved to
|
| 951 |
+
# sympy.external.gmpy and the pure Python version of bincoef
|
| 952 |
+
# should be moved to sympy.external.ntheory.
|
| 953 |
+
if _gmpy is not None:
|
| 954 |
+
return Integer(_gmpy.bincoef(n, k))
|
| 955 |
+
|
| 956 |
+
d, result = n - k, 1
|
| 957 |
+
for i in range(1, k + 1):
|
| 958 |
+
d += 1
|
| 959 |
+
result = result * d // i
|
| 960 |
+
return Integer(result)
|
| 961 |
+
else:
|
| 962 |
+
d, result = n - k, 1
|
| 963 |
+
for i in range(1, k + 1):
|
| 964 |
+
d += 1
|
| 965 |
+
result *= d
|
| 966 |
+
return result / _factorial(k)
|
| 967 |
+
|
| 968 |
+
@classmethod
|
| 969 |
+
def eval(cls, n, k):
|
| 970 |
+
n, k = map(sympify, (n, k))
|
| 971 |
+
d = n - k
|
| 972 |
+
n_nonneg, n_isint = n.is_nonnegative, n.is_integer
|
| 973 |
+
if k.is_zero or ((n_nonneg or n_isint is False)
|
| 974 |
+
and d.is_zero):
|
| 975 |
+
return S.One
|
| 976 |
+
if (k - 1).is_zero or ((n_nonneg or n_isint is False)
|
| 977 |
+
and (d - 1).is_zero):
|
| 978 |
+
return n
|
| 979 |
+
if k.is_integer:
|
| 980 |
+
if k.is_negative or (n_nonneg and n_isint and d.is_negative):
|
| 981 |
+
return S.Zero
|
| 982 |
+
elif n.is_number:
|
| 983 |
+
res = cls._eval(n, k)
|
| 984 |
+
return res.expand(basic=True) if res else res
|
| 985 |
+
elif n_nonneg is False and n_isint:
|
| 986 |
+
# a special case when binomial evaluates to complex infinity
|
| 987 |
+
return S.ComplexInfinity
|
| 988 |
+
elif k.is_number:
|
| 989 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 990 |
+
return gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1))
|
| 991 |
+
|
| 992 |
+
def _eval_Mod(self, q):
|
| 993 |
+
n, k = self.args
|
| 994 |
+
|
| 995 |
+
if any(x.is_integer is False for x in (n, k, q)):
|
| 996 |
+
raise ValueError("Integers expected for binomial Mod")
|
| 997 |
+
|
| 998 |
+
if all(x.is_Integer for x in (n, k, q)):
|
| 999 |
+
n, k = map(int, (n, k))
|
| 1000 |
+
aq, res = abs(q), 1
|
| 1001 |
+
|
| 1002 |
+
# handle negative integers k or n
|
| 1003 |
+
if k < 0:
|
| 1004 |
+
return S.Zero
|
| 1005 |
+
if n < 0:
|
| 1006 |
+
n = -n + k - 1
|
| 1007 |
+
res = -1 if k%2 else 1
|
| 1008 |
+
|
| 1009 |
+
# non negative integers k and n
|
| 1010 |
+
if k > n:
|
| 1011 |
+
return S.Zero
|
| 1012 |
+
|
| 1013 |
+
isprime = aq.is_prime
|
| 1014 |
+
aq = int(aq)
|
| 1015 |
+
if isprime:
|
| 1016 |
+
if aq < n:
|
| 1017 |
+
# use Lucas Theorem
|
| 1018 |
+
N, K = n, k
|
| 1019 |
+
while N or K:
|
| 1020 |
+
res = res*binomial(N % aq, K % aq) % aq
|
| 1021 |
+
N, K = N // aq, K // aq
|
| 1022 |
+
|
| 1023 |
+
else:
|
| 1024 |
+
# use Factorial Modulo
|
| 1025 |
+
d = n - k
|
| 1026 |
+
if k > d:
|
| 1027 |
+
k, d = d, k
|
| 1028 |
+
kf = 1
|
| 1029 |
+
for i in range(2, k + 1):
|
| 1030 |
+
kf = kf*i % aq
|
| 1031 |
+
df = kf
|
| 1032 |
+
for i in range(k + 1, d + 1):
|
| 1033 |
+
df = df*i % aq
|
| 1034 |
+
res *= df
|
| 1035 |
+
for i in range(d + 1, n + 1):
|
| 1036 |
+
res = res*i % aq
|
| 1037 |
+
|
| 1038 |
+
res *= pow(kf*df % aq, aq - 2, aq)
|
| 1039 |
+
res %= aq
|
| 1040 |
+
|
| 1041 |
+
elif _sqrt(q) < k and q != 1:
|
| 1042 |
+
res = binomial_mod(n, k, q)
|
| 1043 |
+
|
| 1044 |
+
else:
|
| 1045 |
+
# Binomial Factorization is performed by calculating the
|
| 1046 |
+
# exponents of primes <= n in `n! /(k! (n - k)!)`,
|
| 1047 |
+
# for non-negative integers n and k. As the exponent of
|
| 1048 |
+
# prime in n! is e_p(n) = [n/p] + [n/p**2] + ...
|
| 1049 |
+
# the exponent of prime in binomial(n, k) would be
|
| 1050 |
+
# e_p(n) - e_p(k) - e_p(n - k)
|
| 1051 |
+
M = int(_sqrt(n))
|
| 1052 |
+
for prime in sieve.primerange(2, n + 1):
|
| 1053 |
+
if prime > n - k:
|
| 1054 |
+
res = res*prime % aq
|
| 1055 |
+
elif prime > n // 2:
|
| 1056 |
+
continue
|
| 1057 |
+
elif prime > M:
|
| 1058 |
+
if n % prime < k % prime:
|
| 1059 |
+
res = res*prime % aq
|
| 1060 |
+
else:
|
| 1061 |
+
N, K = n, k
|
| 1062 |
+
exp = a = 0
|
| 1063 |
+
|
| 1064 |
+
while N > 0:
|
| 1065 |
+
a = int((N % prime) < (K % prime + a))
|
| 1066 |
+
N, K = N // prime, K // prime
|
| 1067 |
+
exp += a
|
| 1068 |
+
|
| 1069 |
+
if exp > 0:
|
| 1070 |
+
res *= pow(prime, exp, aq)
|
| 1071 |
+
res %= aq
|
| 1072 |
+
|
| 1073 |
+
return S(res % q)
|
| 1074 |
+
|
| 1075 |
+
def _eval_expand_func(self, **hints):
|
| 1076 |
+
"""
|
| 1077 |
+
Function to expand binomial(n, k) when m is positive integer
|
| 1078 |
+
Also,
|
| 1079 |
+
n is self.args[0] and k is self.args[1] while using binomial(n, k)
|
| 1080 |
+
"""
|
| 1081 |
+
n = self.args[0]
|
| 1082 |
+
if n.is_Number:
|
| 1083 |
+
return binomial(*self.args)
|
| 1084 |
+
|
| 1085 |
+
k = self.args[1]
|
| 1086 |
+
if (n-k).is_Integer:
|
| 1087 |
+
k = n - k
|
| 1088 |
+
|
| 1089 |
+
if k.is_Integer:
|
| 1090 |
+
if k.is_zero:
|
| 1091 |
+
return S.One
|
| 1092 |
+
elif k.is_negative:
|
| 1093 |
+
return S.Zero
|
| 1094 |
+
else:
|
| 1095 |
+
n, result = self.args[0], 1
|
| 1096 |
+
for i in range(1, k + 1):
|
| 1097 |
+
result *= n - k + i
|
| 1098 |
+
return result / _factorial(k)
|
| 1099 |
+
else:
|
| 1100 |
+
return binomial(*self.args)
|
| 1101 |
+
|
| 1102 |
+
def _eval_rewrite_as_factorial(self, n, k, **kwargs):
|
| 1103 |
+
return factorial(n)/(factorial(k)*factorial(n - k))
|
| 1104 |
+
|
| 1105 |
+
def _eval_rewrite_as_gamma(self, n, k, piecewise=True, **kwargs):
|
| 1106 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 1107 |
+
return gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1))
|
| 1108 |
+
|
| 1109 |
+
def _eval_rewrite_as_tractable(self, n, k, limitvar=None, **kwargs):
|
| 1110 |
+
return self._eval_rewrite_as_gamma(n, k).rewrite('tractable')
|
| 1111 |
+
|
| 1112 |
+
def _eval_rewrite_as_FallingFactorial(self, n, k, **kwargs):
|
| 1113 |
+
if k.is_integer:
|
| 1114 |
+
return ff(n, k) / factorial(k)
|
| 1115 |
+
|
| 1116 |
+
def _eval_is_integer(self):
|
| 1117 |
+
n, k = self.args
|
| 1118 |
+
if n.is_integer and k.is_integer:
|
| 1119 |
+
return True
|
| 1120 |
+
elif k.is_integer is False:
|
| 1121 |
+
return False
|
| 1122 |
+
|
| 1123 |
+
def _eval_is_nonnegative(self):
|
| 1124 |
+
n, k = self.args
|
| 1125 |
+
if n.is_integer and k.is_integer:
|
| 1126 |
+
if n.is_nonnegative or k.is_negative or k.is_even:
|
| 1127 |
+
return True
|
| 1128 |
+
elif k.is_even is False:
|
| 1129 |
+
return False
|
| 1130 |
+
|
| 1131 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1132 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 1133 |
+
return self.rewrite(gamma)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/combinatorial/numbers.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Stub __init__.py for sympy.functions.elementary
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/_trigonometric_special.py
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""A module for special angle formulas for trigonometric functions
|
| 2 |
+
|
| 3 |
+
TODO
|
| 4 |
+
====
|
| 5 |
+
|
| 6 |
+
This module should be developed in the future to contain direct square root
|
| 7 |
+
representation of
|
| 8 |
+
|
| 9 |
+
.. math
|
| 10 |
+
F(\frac{n}{m} \pi)
|
| 11 |
+
|
| 12 |
+
for every
|
| 13 |
+
|
| 14 |
+
- $m \in \{ 3, 5, 17, 257, 65537 \}$
|
| 15 |
+
- $n \in \mathbb{N}$, $0 \le n < m$
|
| 16 |
+
- $F \in \{\sin, \cos, \tan, \csc, \sec, \cot\}$
|
| 17 |
+
|
| 18 |
+
Without multi-step rewrites
|
| 19 |
+
(e.g. $\tan \to \cos/\sin \to \cos/\sqrt \to \ sqrt$)
|
| 20 |
+
or using chebyshev identities
|
| 21 |
+
(e.g. $\cos \to \cos + \cos^2 + \cdots \to \sqrt{} + \sqrt{}^2 + \cdots $),
|
| 22 |
+
which are trivial to implement in sympy,
|
| 23 |
+
and had used to give overly complicated expressions.
|
| 24 |
+
|
| 25 |
+
The reference can be found below, if anyone may need help implementing them.
|
| 26 |
+
|
| 27 |
+
References
|
| 28 |
+
==========
|
| 29 |
+
|
| 30 |
+
.. [*] Gottlieb, Christian. (1999). The Simple and straightforward construction
|
| 31 |
+
of the regular 257-gon. The Mathematical Intelligencer. 21. 31-37.
|
| 32 |
+
10.1007/BF03024829.
|
| 33 |
+
.. [*] https://resources.wolframcloud.com/FunctionRepository/resources/Cos2PiOverFermatPrime
|
| 34 |
+
"""
|
| 35 |
+
from __future__ import annotations
|
| 36 |
+
from typing import Callable
|
| 37 |
+
from functools import reduce
|
| 38 |
+
from sympy.core.expr import Expr
|
| 39 |
+
from sympy.core.singleton import S
|
| 40 |
+
from sympy.core.intfunc import igcdex
|
| 41 |
+
from sympy.core.numbers import Integer
|
| 42 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 43 |
+
from sympy.core.cache import cacheit
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def migcdex(*x: int) -> tuple[tuple[int, ...], int]:
|
| 47 |
+
r"""Compute extended gcd for multiple integers.
|
| 48 |
+
|
| 49 |
+
Explanation
|
| 50 |
+
===========
|
| 51 |
+
|
| 52 |
+
Given the integers $x_1, \cdots, x_n$ and
|
| 53 |
+
an extended gcd for multiple arguments are defined as a solution
|
| 54 |
+
$(y_1, \cdots, y_n), g$ for the diophantine equation
|
| 55 |
+
$x_1 y_1 + \cdots + x_n y_n = g$ such that
|
| 56 |
+
$g = \gcd(x_1, \cdots, x_n)$.
|
| 57 |
+
|
| 58 |
+
Examples
|
| 59 |
+
========
|
| 60 |
+
|
| 61 |
+
>>> from sympy.functions.elementary._trigonometric_special import migcdex
|
| 62 |
+
>>> migcdex()
|
| 63 |
+
((), 0)
|
| 64 |
+
>>> migcdex(4)
|
| 65 |
+
((1,), 4)
|
| 66 |
+
>>> migcdex(4, 6)
|
| 67 |
+
((-1, 1), 2)
|
| 68 |
+
>>> migcdex(6, 10, 15)
|
| 69 |
+
((1, 1, -1), 1)
|
| 70 |
+
"""
|
| 71 |
+
if not x:
|
| 72 |
+
return (), 0
|
| 73 |
+
|
| 74 |
+
if len(x) == 1:
|
| 75 |
+
return (1,), x[0]
|
| 76 |
+
|
| 77 |
+
if len(x) == 2:
|
| 78 |
+
u, v, h = igcdex(x[0], x[1])
|
| 79 |
+
return (u, v), h
|
| 80 |
+
|
| 81 |
+
y, g = migcdex(*x[1:])
|
| 82 |
+
u, v, h = igcdex(x[0], g)
|
| 83 |
+
return (u, *(v * i for i in y)), h
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def ipartfrac(*denoms: int) -> tuple[int, ...]:
|
| 87 |
+
r"""Compute the partial fraction decomposition.
|
| 88 |
+
|
| 89 |
+
Explanation
|
| 90 |
+
===========
|
| 91 |
+
|
| 92 |
+
Given a rational number $\frac{1}{q_1 \cdots q_n}$ where all
|
| 93 |
+
$q_1, \cdots, q_n$ are pairwise coprime,
|
| 94 |
+
|
| 95 |
+
A partial fraction decomposition is defined as
|
| 96 |
+
|
| 97 |
+
.. math::
|
| 98 |
+
\frac{1}{q_1 \cdots q_n} = \frac{p_1}{q_1} + \cdots + \frac{p_n}{q_n}
|
| 99 |
+
|
| 100 |
+
And it can be derived from solving the following diophantine equation for
|
| 101 |
+
the $p_1, \cdots, p_n$
|
| 102 |
+
|
| 103 |
+
.. math::
|
| 104 |
+
1 = p_1 \prod_{i \ne 1}q_i + \cdots + p_n \prod_{i \ne n}q_i
|
| 105 |
+
|
| 106 |
+
Where $q_1, \cdots, q_n$ being pairwise coprime implies
|
| 107 |
+
$\gcd(\prod_{i \ne 1}q_i, \cdots, \prod_{i \ne n}q_i) = 1$,
|
| 108 |
+
which guarantees the existence of the solution.
|
| 109 |
+
|
| 110 |
+
It is sufficient to compute partial fraction decomposition only
|
| 111 |
+
for numerator $1$ because partial fraction decomposition for any
|
| 112 |
+
$\frac{n}{q_1 \cdots q_n}$ can be easily computed by multiplying
|
| 113 |
+
the result by $n$ afterwards.
|
| 114 |
+
|
| 115 |
+
Parameters
|
| 116 |
+
==========
|
| 117 |
+
|
| 118 |
+
denoms : int
|
| 119 |
+
The pairwise coprime integer denominators $q_i$ which defines the
|
| 120 |
+
rational number $\frac{1}{q_1 \cdots q_n}$
|
| 121 |
+
|
| 122 |
+
Returns
|
| 123 |
+
=======
|
| 124 |
+
|
| 125 |
+
tuple[int, ...]
|
| 126 |
+
The list of numerators which semantically corresponds to $p_i$ of the
|
| 127 |
+
partial fraction decomposition
|
| 128 |
+
$\frac{1}{q_1 \cdots q_n} = \frac{p_1}{q_1} + \cdots + \frac{p_n}{q_n}$
|
| 129 |
+
|
| 130 |
+
Examples
|
| 131 |
+
========
|
| 132 |
+
|
| 133 |
+
>>> from sympy import Rational, Mul
|
| 134 |
+
>>> from sympy.functions.elementary._trigonometric_special import ipartfrac
|
| 135 |
+
|
| 136 |
+
>>> denoms = 2, 3, 5
|
| 137 |
+
>>> numers = ipartfrac(2, 3, 5)
|
| 138 |
+
>>> numers
|
| 139 |
+
(1, 7, -14)
|
| 140 |
+
|
| 141 |
+
>>> Rational(1, Mul(*denoms))
|
| 142 |
+
1/30
|
| 143 |
+
>>> out = 0
|
| 144 |
+
>>> for n, d in zip(numers, denoms):
|
| 145 |
+
... out += Rational(n, d)
|
| 146 |
+
>>> out
|
| 147 |
+
1/30
|
| 148 |
+
"""
|
| 149 |
+
if not denoms:
|
| 150 |
+
return ()
|
| 151 |
+
|
| 152 |
+
def mul(x: int, y: int) -> int:
|
| 153 |
+
return x * y
|
| 154 |
+
|
| 155 |
+
denom = reduce(mul, denoms)
|
| 156 |
+
a = [denom // x for x in denoms]
|
| 157 |
+
h, _ = migcdex(*a)
|
| 158 |
+
return h
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def fermat_coords(n: int) -> list[int] | None:
|
| 162 |
+
"""If n can be factored in terms of Fermat primes with
|
| 163 |
+
multiplicity of each being 1, return those primes, else
|
| 164 |
+
None
|
| 165 |
+
"""
|
| 166 |
+
primes = []
|
| 167 |
+
for p in [3, 5, 17, 257, 65537]:
|
| 168 |
+
quotient, remainder = divmod(n, p)
|
| 169 |
+
if remainder == 0:
|
| 170 |
+
n = quotient
|
| 171 |
+
primes.append(p)
|
| 172 |
+
if n == 1:
|
| 173 |
+
return primes
|
| 174 |
+
return None
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
@cacheit
|
| 178 |
+
def cos_3() -> Expr:
|
| 179 |
+
r"""Computes $\cos \frac{\pi}{3}$ in square roots"""
|
| 180 |
+
return S.Half
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
@cacheit
|
| 184 |
+
def cos_5() -> Expr:
|
| 185 |
+
r"""Computes $\cos \frac{\pi}{5}$ in square roots"""
|
| 186 |
+
return (sqrt(5) + 1) / 4
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
@cacheit
|
| 190 |
+
def cos_17() -> Expr:
|
| 191 |
+
r"""Computes $\cos \frac{\pi}{17}$ in square roots"""
|
| 192 |
+
return sqrt(
|
| 193 |
+
(15 + sqrt(17)) / 32 + sqrt(2) * (sqrt(17 - sqrt(17)) +
|
| 194 |
+
sqrt(sqrt(2) * (-8 * sqrt(17 + sqrt(17)) - (1 - sqrt(17))
|
| 195 |
+
* sqrt(17 - sqrt(17))) + 6 * sqrt(17) + 34)) / 32)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
@cacheit
|
| 199 |
+
def cos_257() -> Expr:
|
| 200 |
+
r"""Computes $\cos \frac{\pi}{257}$ in square roots
|
| 201 |
+
|
| 202 |
+
References
|
| 203 |
+
==========
|
| 204 |
+
|
| 205 |
+
.. [*] https://math.stackexchange.com/questions/516142/how-does-cos2-pi-257-look-like-in-real-radicals
|
| 206 |
+
.. [*] https://r-knott.surrey.ac.uk/Fibonacci/simpleTrig.html
|
| 207 |
+
"""
|
| 208 |
+
def f1(a: Expr, b: Expr) -> tuple[Expr, Expr]:
|
| 209 |
+
return (a + sqrt(a**2 + b)) / 2, (a - sqrt(a**2 + b)) / 2
|
| 210 |
+
|
| 211 |
+
def f2(a: Expr, b: Expr) -> Expr:
|
| 212 |
+
return (a - sqrt(a**2 + b))/2
|
| 213 |
+
|
| 214 |
+
t1, t2 = f1(S.NegativeOne, Integer(256))
|
| 215 |
+
z1, z3 = f1(t1, Integer(64))
|
| 216 |
+
z2, z4 = f1(t2, Integer(64))
|
| 217 |
+
y1, y5 = f1(z1, 4*(5 + t1 + 2*z1))
|
| 218 |
+
y6, y2 = f1(z2, 4*(5 + t2 + 2*z2))
|
| 219 |
+
y3, y7 = f1(z3, 4*(5 + t1 + 2*z3))
|
| 220 |
+
y8, y4 = f1(z4, 4*(5 + t2 + 2*z4))
|
| 221 |
+
x1, x9 = f1(y1, -4*(t1 + y1 + y3 + 2*y6))
|
| 222 |
+
x2, x10 = f1(y2, -4*(t2 + y2 + y4 + 2*y7))
|
| 223 |
+
x3, x11 = f1(y3, -4*(t1 + y3 + y5 + 2*y8))
|
| 224 |
+
x4, x12 = f1(y4, -4*(t2 + y4 + y6 + 2*y1))
|
| 225 |
+
x5, x13 = f1(y5, -4*(t1 + y5 + y7 + 2*y2))
|
| 226 |
+
x6, x14 = f1(y6, -4*(t2 + y6 + y8 + 2*y3))
|
| 227 |
+
x15, x7 = f1(y7, -4*(t1 + y7 + y1 + 2*y4))
|
| 228 |
+
x8, x16 = f1(y8, -4*(t2 + y8 + y2 + 2*y5))
|
| 229 |
+
v1 = f2(x1, -4*(x1 + x2 + x3 + x6))
|
| 230 |
+
v2 = f2(x2, -4*(x2 + x3 + x4 + x7))
|
| 231 |
+
v3 = f2(x8, -4*(x8 + x9 + x10 + x13))
|
| 232 |
+
v4 = f2(x9, -4*(x9 + x10 + x11 + x14))
|
| 233 |
+
v5 = f2(x10, -4*(x10 + x11 + x12 + x15))
|
| 234 |
+
v6 = f2(x16, -4*(x16 + x1 + x2 + x5))
|
| 235 |
+
u1 = -f2(-v1, -4*(v2 + v3))
|
| 236 |
+
u2 = -f2(-v4, -4*(v5 + v6))
|
| 237 |
+
w1 = -2*f2(-u1, -4*u2)
|
| 238 |
+
return sqrt(sqrt(2)*sqrt(w1 + 4)/8 + S.Half)
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def cos_table() -> dict[int, Callable[[], Expr]]:
|
| 242 |
+
r"""Lazily evaluated table for $\cos \frac{\pi}{n}$ in square roots for
|
| 243 |
+
$n \in \{3, 5, 17, 257, 65537\}$.
|
| 244 |
+
|
| 245 |
+
Notes
|
| 246 |
+
=====
|
| 247 |
+
|
| 248 |
+
65537 is the only other known Fermat prime and it is nearly impossible to
|
| 249 |
+
build in the current SymPy due to performance issues.
|
| 250 |
+
|
| 251 |
+
References
|
| 252 |
+
==========
|
| 253 |
+
|
| 254 |
+
https://r-knott.surrey.ac.uk/Fibonacci/simpleTrig.html
|
| 255 |
+
"""
|
| 256 |
+
return {
|
| 257 |
+
3: cos_3,
|
| 258 |
+
5: cos_5,
|
| 259 |
+
17: cos_17,
|
| 260 |
+
257: cos_257
|
| 261 |
+
}
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/complexes.py
ADDED
|
@@ -0,0 +1,1492 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from sympy.core import S, Add, Mul, sympify, Symbol, Dummy, Basic
|
| 4 |
+
from sympy.core.expr import Expr
|
| 5 |
+
from sympy.core.exprtools import factor_terms
|
| 6 |
+
from sympy.core.function import (DefinedFunction, Derivative, ArgumentIndexError,
|
| 7 |
+
AppliedUndef, expand_mul, PoleError)
|
| 8 |
+
from sympy.core.logic import fuzzy_not, fuzzy_or
|
| 9 |
+
from sympy.core.numbers import pi, I, oo
|
| 10 |
+
from sympy.core.power import Pow
|
| 11 |
+
from sympy.core.relational import Eq
|
| 12 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 13 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 14 |
+
|
| 15 |
+
###############################################################################
|
| 16 |
+
######################### REAL and IMAGINARY PARTS ############################
|
| 17 |
+
###############################################################################
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class re(DefinedFunction):
|
| 21 |
+
"""
|
| 22 |
+
Returns real part of expression. This function performs only
|
| 23 |
+
elementary analysis and so it will fail to decompose properly
|
| 24 |
+
more complicated expressions. If completely simplified result
|
| 25 |
+
is needed then use ``Basic.as_real_imag()`` or perform complex
|
| 26 |
+
expansion on instance of this function.
|
| 27 |
+
|
| 28 |
+
Examples
|
| 29 |
+
========
|
| 30 |
+
|
| 31 |
+
>>> from sympy import re, im, I, E, symbols
|
| 32 |
+
>>> x, y = symbols('x y', real=True)
|
| 33 |
+
>>> re(2*E)
|
| 34 |
+
2*E
|
| 35 |
+
>>> re(2*I + 17)
|
| 36 |
+
17
|
| 37 |
+
>>> re(2*I)
|
| 38 |
+
0
|
| 39 |
+
>>> re(im(x) + x*I + 2)
|
| 40 |
+
2
|
| 41 |
+
>>> re(5 + I + 2)
|
| 42 |
+
7
|
| 43 |
+
|
| 44 |
+
Parameters
|
| 45 |
+
==========
|
| 46 |
+
|
| 47 |
+
arg : Expr
|
| 48 |
+
Real or complex expression.
|
| 49 |
+
|
| 50 |
+
Returns
|
| 51 |
+
=======
|
| 52 |
+
|
| 53 |
+
expr : Expr
|
| 54 |
+
Real part of expression.
|
| 55 |
+
|
| 56 |
+
See Also
|
| 57 |
+
========
|
| 58 |
+
|
| 59 |
+
im
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
args: tuple[Expr]
|
| 63 |
+
|
| 64 |
+
is_extended_real = True
|
| 65 |
+
unbranched = True # implicitly works on the projection to C
|
| 66 |
+
_singularities = True # non-holomorphic
|
| 67 |
+
|
| 68 |
+
@classmethod
|
| 69 |
+
def eval(cls, arg):
|
| 70 |
+
if arg is S.NaN:
|
| 71 |
+
return S.NaN
|
| 72 |
+
elif arg is S.ComplexInfinity:
|
| 73 |
+
return S.NaN
|
| 74 |
+
elif arg.is_extended_real:
|
| 75 |
+
return arg
|
| 76 |
+
elif arg.is_imaginary or (I*arg).is_extended_real:
|
| 77 |
+
return S.Zero
|
| 78 |
+
elif arg.is_Matrix:
|
| 79 |
+
return arg.as_real_imag()[0]
|
| 80 |
+
elif arg.is_Function and isinstance(arg, conjugate):
|
| 81 |
+
return re(arg.args[0])
|
| 82 |
+
else:
|
| 83 |
+
|
| 84 |
+
included, reverted, excluded = [], [], []
|
| 85 |
+
args = Add.make_args(arg)
|
| 86 |
+
for term in args:
|
| 87 |
+
coeff = term.as_coefficient(I)
|
| 88 |
+
|
| 89 |
+
if coeff is not None:
|
| 90 |
+
if not coeff.is_extended_real:
|
| 91 |
+
reverted.append(coeff)
|
| 92 |
+
elif not term.has(I) and term.is_extended_real:
|
| 93 |
+
excluded.append(term)
|
| 94 |
+
else:
|
| 95 |
+
# Try to do some advanced expansion. If
|
| 96 |
+
# impossible, don't try to do re(arg) again
|
| 97 |
+
# (because this is what we are trying to do now).
|
| 98 |
+
real_imag = term.as_real_imag(ignore=arg)
|
| 99 |
+
if real_imag:
|
| 100 |
+
excluded.append(real_imag[0])
|
| 101 |
+
else:
|
| 102 |
+
included.append(term)
|
| 103 |
+
|
| 104 |
+
if len(args) != len(included):
|
| 105 |
+
a, b, c = (Add(*xs) for xs in [included, reverted, excluded])
|
| 106 |
+
|
| 107 |
+
return cls(a) - im(b) + c
|
| 108 |
+
|
| 109 |
+
def as_real_imag(self, deep=True, **hints):
|
| 110 |
+
"""
|
| 111 |
+
Returns the real number with a zero imaginary part.
|
| 112 |
+
|
| 113 |
+
"""
|
| 114 |
+
return (self, S.Zero)
|
| 115 |
+
|
| 116 |
+
def _eval_derivative(self, x):
|
| 117 |
+
if x.is_extended_real or self.args[0].is_extended_real:
|
| 118 |
+
return re(Derivative(self.args[0], x, evaluate=True))
|
| 119 |
+
if x.is_imaginary or self.args[0].is_imaginary:
|
| 120 |
+
return -I \
|
| 121 |
+
* im(Derivative(self.args[0], x, evaluate=True))
|
| 122 |
+
|
| 123 |
+
def _eval_rewrite_as_im(self, arg, **kwargs):
|
| 124 |
+
return self.args[0] - I*im(self.args[0])
|
| 125 |
+
|
| 126 |
+
def _eval_is_algebraic(self):
|
| 127 |
+
return self.args[0].is_algebraic
|
| 128 |
+
|
| 129 |
+
def _eval_is_zero(self):
|
| 130 |
+
# is_imaginary implies nonzero
|
| 131 |
+
return fuzzy_or([self.args[0].is_imaginary, self.args[0].is_zero])
|
| 132 |
+
|
| 133 |
+
def _eval_is_finite(self):
|
| 134 |
+
if self.args[0].is_finite:
|
| 135 |
+
return True
|
| 136 |
+
|
| 137 |
+
def _eval_is_complex(self):
|
| 138 |
+
if self.args[0].is_finite:
|
| 139 |
+
return True
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
class im(DefinedFunction):
|
| 143 |
+
"""
|
| 144 |
+
Returns imaginary part of expression. This function performs only
|
| 145 |
+
elementary analysis and so it will fail to decompose properly more
|
| 146 |
+
complicated expressions. If completely simplified result is needed then
|
| 147 |
+
use ``Basic.as_real_imag()`` or perform complex expansion on instance of
|
| 148 |
+
this function.
|
| 149 |
+
|
| 150 |
+
Examples
|
| 151 |
+
========
|
| 152 |
+
|
| 153 |
+
>>> from sympy import re, im, E, I
|
| 154 |
+
>>> from sympy.abc import x, y
|
| 155 |
+
>>> im(2*E)
|
| 156 |
+
0
|
| 157 |
+
>>> im(2*I + 17)
|
| 158 |
+
2
|
| 159 |
+
>>> im(x*I)
|
| 160 |
+
re(x)
|
| 161 |
+
>>> im(re(x) + y)
|
| 162 |
+
im(y)
|
| 163 |
+
>>> im(2 + 3*I)
|
| 164 |
+
3
|
| 165 |
+
|
| 166 |
+
Parameters
|
| 167 |
+
==========
|
| 168 |
+
|
| 169 |
+
arg : Expr
|
| 170 |
+
Real or complex expression.
|
| 171 |
+
|
| 172 |
+
Returns
|
| 173 |
+
=======
|
| 174 |
+
|
| 175 |
+
expr : Expr
|
| 176 |
+
Imaginary part of expression.
|
| 177 |
+
|
| 178 |
+
See Also
|
| 179 |
+
========
|
| 180 |
+
|
| 181 |
+
re
|
| 182 |
+
"""
|
| 183 |
+
|
| 184 |
+
args: tuple[Expr]
|
| 185 |
+
|
| 186 |
+
is_extended_real = True
|
| 187 |
+
unbranched = True # implicitly works on the projection to C
|
| 188 |
+
_singularities = True # non-holomorphic
|
| 189 |
+
|
| 190 |
+
@classmethod
|
| 191 |
+
def eval(cls, arg):
|
| 192 |
+
if arg is S.NaN:
|
| 193 |
+
return S.NaN
|
| 194 |
+
elif arg is S.ComplexInfinity:
|
| 195 |
+
return S.NaN
|
| 196 |
+
elif arg.is_extended_real:
|
| 197 |
+
return S.Zero
|
| 198 |
+
elif arg.is_imaginary or (I*arg).is_extended_real:
|
| 199 |
+
return -I * arg
|
| 200 |
+
elif arg.is_Matrix:
|
| 201 |
+
return arg.as_real_imag()[1]
|
| 202 |
+
elif arg.is_Function and isinstance(arg, conjugate):
|
| 203 |
+
return -im(arg.args[0])
|
| 204 |
+
else:
|
| 205 |
+
included, reverted, excluded = [], [], []
|
| 206 |
+
args = Add.make_args(arg)
|
| 207 |
+
for term in args:
|
| 208 |
+
coeff = term.as_coefficient(I)
|
| 209 |
+
|
| 210 |
+
if coeff is not None:
|
| 211 |
+
if not coeff.is_extended_real:
|
| 212 |
+
reverted.append(coeff)
|
| 213 |
+
else:
|
| 214 |
+
excluded.append(coeff)
|
| 215 |
+
elif term.has(I) or not term.is_extended_real:
|
| 216 |
+
# Try to do some advanced expansion. If
|
| 217 |
+
# impossible, don't try to do im(arg) again
|
| 218 |
+
# (because this is what we are trying to do now).
|
| 219 |
+
real_imag = term.as_real_imag(ignore=arg)
|
| 220 |
+
if real_imag:
|
| 221 |
+
excluded.append(real_imag[1])
|
| 222 |
+
else:
|
| 223 |
+
included.append(term)
|
| 224 |
+
|
| 225 |
+
if len(args) != len(included):
|
| 226 |
+
a, b, c = (Add(*xs) for xs in [included, reverted, excluded])
|
| 227 |
+
|
| 228 |
+
return cls(a) + re(b) + c
|
| 229 |
+
|
| 230 |
+
def as_real_imag(self, deep=True, **hints):
|
| 231 |
+
"""
|
| 232 |
+
Return the imaginary part with a zero real part.
|
| 233 |
+
|
| 234 |
+
"""
|
| 235 |
+
return (self, S.Zero)
|
| 236 |
+
|
| 237 |
+
def _eval_derivative(self, x):
|
| 238 |
+
if x.is_extended_real or self.args[0].is_extended_real:
|
| 239 |
+
return im(Derivative(self.args[0], x, evaluate=True))
|
| 240 |
+
if x.is_imaginary or self.args[0].is_imaginary:
|
| 241 |
+
return -I \
|
| 242 |
+
* re(Derivative(self.args[0], x, evaluate=True))
|
| 243 |
+
|
| 244 |
+
def _eval_rewrite_as_re(self, arg, **kwargs):
|
| 245 |
+
return -I*(self.args[0] - re(self.args[0]))
|
| 246 |
+
|
| 247 |
+
def _eval_is_algebraic(self):
|
| 248 |
+
return self.args[0].is_algebraic
|
| 249 |
+
|
| 250 |
+
def _eval_is_zero(self):
|
| 251 |
+
return self.args[0].is_extended_real
|
| 252 |
+
|
| 253 |
+
def _eval_is_finite(self):
|
| 254 |
+
if self.args[0].is_finite:
|
| 255 |
+
return True
|
| 256 |
+
|
| 257 |
+
def _eval_is_complex(self):
|
| 258 |
+
if self.args[0].is_finite:
|
| 259 |
+
return True
|
| 260 |
+
|
| 261 |
+
###############################################################################
|
| 262 |
+
############### SIGN, ABSOLUTE VALUE, ARGUMENT and CONJUGATION ################
|
| 263 |
+
###############################################################################
|
| 264 |
+
|
| 265 |
+
class sign(DefinedFunction):
|
| 266 |
+
"""
|
| 267 |
+
Returns the complex sign of an expression:
|
| 268 |
+
|
| 269 |
+
Explanation
|
| 270 |
+
===========
|
| 271 |
+
|
| 272 |
+
If the expression is real the sign will be:
|
| 273 |
+
|
| 274 |
+
* $1$ if expression is positive
|
| 275 |
+
* $0$ if expression is equal to zero
|
| 276 |
+
* $-1$ if expression is negative
|
| 277 |
+
|
| 278 |
+
If the expression is imaginary the sign will be:
|
| 279 |
+
|
| 280 |
+
* $I$ if im(expression) is positive
|
| 281 |
+
* $-I$ if im(expression) is negative
|
| 282 |
+
|
| 283 |
+
Otherwise an unevaluated expression will be returned. When evaluated, the
|
| 284 |
+
result (in general) will be ``cos(arg(expr)) + I*sin(arg(expr))``.
|
| 285 |
+
|
| 286 |
+
Examples
|
| 287 |
+
========
|
| 288 |
+
|
| 289 |
+
>>> from sympy import sign, I
|
| 290 |
+
|
| 291 |
+
>>> sign(-1)
|
| 292 |
+
-1
|
| 293 |
+
>>> sign(0)
|
| 294 |
+
0
|
| 295 |
+
>>> sign(-3*I)
|
| 296 |
+
-I
|
| 297 |
+
>>> sign(1 + I)
|
| 298 |
+
sign(1 + I)
|
| 299 |
+
>>> _.evalf()
|
| 300 |
+
0.707106781186548 + 0.707106781186548*I
|
| 301 |
+
|
| 302 |
+
Parameters
|
| 303 |
+
==========
|
| 304 |
+
|
| 305 |
+
arg : Expr
|
| 306 |
+
Real or imaginary expression.
|
| 307 |
+
|
| 308 |
+
Returns
|
| 309 |
+
=======
|
| 310 |
+
|
| 311 |
+
expr : Expr
|
| 312 |
+
Complex sign of expression.
|
| 313 |
+
|
| 314 |
+
See Also
|
| 315 |
+
========
|
| 316 |
+
|
| 317 |
+
Abs, conjugate
|
| 318 |
+
"""
|
| 319 |
+
|
| 320 |
+
is_complex = True
|
| 321 |
+
_singularities = True
|
| 322 |
+
|
| 323 |
+
def doit(self, **hints):
|
| 324 |
+
s = super().doit()
|
| 325 |
+
if s == self and self.args[0].is_zero is False:
|
| 326 |
+
return self.args[0] / Abs(self.args[0])
|
| 327 |
+
return s
|
| 328 |
+
|
| 329 |
+
@classmethod
|
| 330 |
+
def eval(cls, arg):
|
| 331 |
+
# handle what we can
|
| 332 |
+
if arg.is_Mul:
|
| 333 |
+
c, args = arg.as_coeff_mul()
|
| 334 |
+
unk = []
|
| 335 |
+
s = sign(c)
|
| 336 |
+
for a in args:
|
| 337 |
+
if a.is_extended_negative:
|
| 338 |
+
s = -s
|
| 339 |
+
elif a.is_extended_positive:
|
| 340 |
+
pass
|
| 341 |
+
else:
|
| 342 |
+
if a.is_imaginary:
|
| 343 |
+
ai = im(a)
|
| 344 |
+
if ai.is_comparable: # i.e. a = I*real
|
| 345 |
+
s *= I
|
| 346 |
+
if ai.is_extended_negative:
|
| 347 |
+
# can't use sign(ai) here since ai might not be
|
| 348 |
+
# a Number
|
| 349 |
+
s = -s
|
| 350 |
+
else:
|
| 351 |
+
unk.append(a)
|
| 352 |
+
else:
|
| 353 |
+
unk.append(a)
|
| 354 |
+
if c is S.One and len(unk) == len(args):
|
| 355 |
+
return None
|
| 356 |
+
return s * cls(arg._new_rawargs(*unk))
|
| 357 |
+
if arg is S.NaN:
|
| 358 |
+
return S.NaN
|
| 359 |
+
if arg.is_zero: # it may be an Expr that is zero
|
| 360 |
+
return S.Zero
|
| 361 |
+
if arg.is_extended_positive:
|
| 362 |
+
return S.One
|
| 363 |
+
if arg.is_extended_negative:
|
| 364 |
+
return S.NegativeOne
|
| 365 |
+
if arg.is_Function:
|
| 366 |
+
if isinstance(arg, sign):
|
| 367 |
+
return arg
|
| 368 |
+
if arg.is_imaginary:
|
| 369 |
+
if arg.is_Pow and arg.exp is S.Half:
|
| 370 |
+
# we catch this because non-trivial sqrt args are not expanded
|
| 371 |
+
# e.g. sqrt(1-sqrt(2)) --x--> to I*sqrt(sqrt(2) - 1)
|
| 372 |
+
return I
|
| 373 |
+
arg2 = -I * arg
|
| 374 |
+
if arg2.is_extended_positive:
|
| 375 |
+
return I
|
| 376 |
+
if arg2.is_extended_negative:
|
| 377 |
+
return -I
|
| 378 |
+
|
| 379 |
+
def _eval_Abs(self):
|
| 380 |
+
if fuzzy_not(self.args[0].is_zero):
|
| 381 |
+
return S.One
|
| 382 |
+
|
| 383 |
+
def _eval_conjugate(self):
|
| 384 |
+
return sign(conjugate(self.args[0]))
|
| 385 |
+
|
| 386 |
+
def _eval_derivative(self, x):
|
| 387 |
+
if self.args[0].is_extended_real:
|
| 388 |
+
from sympy.functions.special.delta_functions import DiracDelta
|
| 389 |
+
return 2 * Derivative(self.args[0], x, evaluate=True) \
|
| 390 |
+
* DiracDelta(self.args[0])
|
| 391 |
+
elif self.args[0].is_imaginary:
|
| 392 |
+
from sympy.functions.special.delta_functions import DiracDelta
|
| 393 |
+
return 2 * Derivative(self.args[0], x, evaluate=True) \
|
| 394 |
+
* DiracDelta(-I * self.args[0])
|
| 395 |
+
|
| 396 |
+
def _eval_is_nonnegative(self):
|
| 397 |
+
if self.args[0].is_nonnegative:
|
| 398 |
+
return True
|
| 399 |
+
|
| 400 |
+
def _eval_is_nonpositive(self):
|
| 401 |
+
if self.args[0].is_nonpositive:
|
| 402 |
+
return True
|
| 403 |
+
|
| 404 |
+
def _eval_is_imaginary(self):
|
| 405 |
+
return self.args[0].is_imaginary
|
| 406 |
+
|
| 407 |
+
def _eval_is_integer(self):
|
| 408 |
+
return self.args[0].is_extended_real
|
| 409 |
+
|
| 410 |
+
def _eval_is_zero(self):
|
| 411 |
+
return self.args[0].is_zero
|
| 412 |
+
|
| 413 |
+
def _eval_power(self, other):
|
| 414 |
+
if (
|
| 415 |
+
fuzzy_not(self.args[0].is_zero) and
|
| 416 |
+
other.is_integer and
|
| 417 |
+
other.is_even
|
| 418 |
+
):
|
| 419 |
+
return S.One
|
| 420 |
+
|
| 421 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 422 |
+
arg0 = self.args[0]
|
| 423 |
+
x0 = arg0.subs(x, 0)
|
| 424 |
+
if x0 != 0:
|
| 425 |
+
return self.func(x0)
|
| 426 |
+
if cdir != 0:
|
| 427 |
+
cdir = arg0.dir(x, cdir)
|
| 428 |
+
return -S.One if re(cdir) < 0 else S.One
|
| 429 |
+
|
| 430 |
+
def _eval_rewrite_as_Piecewise(self, arg, **kwargs):
|
| 431 |
+
if arg.is_extended_real:
|
| 432 |
+
return Piecewise((1, arg > 0), (-1, arg < 0), (0, True))
|
| 433 |
+
|
| 434 |
+
def _eval_rewrite_as_Heaviside(self, arg, **kwargs):
|
| 435 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 436 |
+
if arg.is_extended_real:
|
| 437 |
+
return Heaviside(arg) * 2 - 1
|
| 438 |
+
|
| 439 |
+
def _eval_rewrite_as_Abs(self, arg, **kwargs):
|
| 440 |
+
return Piecewise((0, Eq(arg, 0)), (arg / Abs(arg), True))
|
| 441 |
+
|
| 442 |
+
def _eval_simplify(self, **kwargs):
|
| 443 |
+
return self.func(factor_terms(self.args[0])) # XXX include doit?
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
class Abs(DefinedFunction):
|
| 447 |
+
"""
|
| 448 |
+
Return the absolute value of the argument.
|
| 449 |
+
|
| 450 |
+
Explanation
|
| 451 |
+
===========
|
| 452 |
+
|
| 453 |
+
This is an extension of the built-in function ``abs()`` to accept symbolic
|
| 454 |
+
values. If you pass a SymPy expression to the built-in ``abs()``, it will
|
| 455 |
+
pass it automatically to ``Abs()``.
|
| 456 |
+
|
| 457 |
+
Examples
|
| 458 |
+
========
|
| 459 |
+
|
| 460 |
+
>>> from sympy import Abs, Symbol, S, I
|
| 461 |
+
>>> Abs(-1)
|
| 462 |
+
1
|
| 463 |
+
>>> x = Symbol('x', real=True)
|
| 464 |
+
>>> Abs(-x)
|
| 465 |
+
Abs(x)
|
| 466 |
+
>>> Abs(x**2)
|
| 467 |
+
x**2
|
| 468 |
+
>>> abs(-x) # The Python built-in
|
| 469 |
+
Abs(x)
|
| 470 |
+
>>> Abs(3*x + 2*I)
|
| 471 |
+
sqrt(9*x**2 + 4)
|
| 472 |
+
>>> Abs(8*I)
|
| 473 |
+
8
|
| 474 |
+
|
| 475 |
+
Note that the Python built-in will return either an Expr or int depending on
|
| 476 |
+
the argument::
|
| 477 |
+
|
| 478 |
+
>>> type(abs(-1))
|
| 479 |
+
<... 'int'>
|
| 480 |
+
>>> type(abs(S.NegativeOne))
|
| 481 |
+
<class 'sympy.core.numbers.One'>
|
| 482 |
+
|
| 483 |
+
Abs will always return a SymPy object.
|
| 484 |
+
|
| 485 |
+
Parameters
|
| 486 |
+
==========
|
| 487 |
+
|
| 488 |
+
arg : Expr
|
| 489 |
+
Real or complex expression.
|
| 490 |
+
|
| 491 |
+
Returns
|
| 492 |
+
=======
|
| 493 |
+
|
| 494 |
+
expr : Expr
|
| 495 |
+
Absolute value returned can be an expression or integer depending on
|
| 496 |
+
input arg.
|
| 497 |
+
|
| 498 |
+
See Also
|
| 499 |
+
========
|
| 500 |
+
|
| 501 |
+
sign, conjugate
|
| 502 |
+
"""
|
| 503 |
+
|
| 504 |
+
args: tuple[Expr]
|
| 505 |
+
|
| 506 |
+
is_extended_real = True
|
| 507 |
+
is_extended_negative = False
|
| 508 |
+
is_extended_nonnegative = True
|
| 509 |
+
unbranched = True
|
| 510 |
+
_singularities = True # non-holomorphic
|
| 511 |
+
|
| 512 |
+
def fdiff(self, argindex=1):
|
| 513 |
+
"""
|
| 514 |
+
Get the first derivative of the argument to Abs().
|
| 515 |
+
|
| 516 |
+
"""
|
| 517 |
+
if argindex == 1:
|
| 518 |
+
return sign(self.args[0])
|
| 519 |
+
else:
|
| 520 |
+
raise ArgumentIndexError(self, argindex)
|
| 521 |
+
|
| 522 |
+
@classmethod
|
| 523 |
+
def eval(cls, arg):
|
| 524 |
+
from sympy.simplify.simplify import signsimp
|
| 525 |
+
|
| 526 |
+
if hasattr(arg, '_eval_Abs'):
|
| 527 |
+
obj = arg._eval_Abs()
|
| 528 |
+
if obj is not None:
|
| 529 |
+
return obj
|
| 530 |
+
if not isinstance(arg, Expr):
|
| 531 |
+
raise TypeError("Bad argument type for Abs(): %s" % type(arg))
|
| 532 |
+
|
| 533 |
+
# handle what we can
|
| 534 |
+
arg = signsimp(arg, evaluate=False)
|
| 535 |
+
n, d = arg.as_numer_denom()
|
| 536 |
+
if d.free_symbols and not n.free_symbols:
|
| 537 |
+
return cls(n)/cls(d)
|
| 538 |
+
|
| 539 |
+
if arg.is_Mul:
|
| 540 |
+
known = []
|
| 541 |
+
unk = []
|
| 542 |
+
for t in arg.args:
|
| 543 |
+
if t.is_Pow and t.exp.is_integer and t.exp.is_negative:
|
| 544 |
+
bnew = cls(t.base)
|
| 545 |
+
if isinstance(bnew, cls):
|
| 546 |
+
unk.append(t)
|
| 547 |
+
else:
|
| 548 |
+
known.append(Pow(bnew, t.exp))
|
| 549 |
+
else:
|
| 550 |
+
tnew = cls(t)
|
| 551 |
+
if isinstance(tnew, cls):
|
| 552 |
+
unk.append(t)
|
| 553 |
+
else:
|
| 554 |
+
known.append(tnew)
|
| 555 |
+
known = Mul(*known)
|
| 556 |
+
unk = cls(Mul(*unk), evaluate=False) if unk else S.One
|
| 557 |
+
return known*unk
|
| 558 |
+
if arg is S.NaN:
|
| 559 |
+
return S.NaN
|
| 560 |
+
if arg is S.ComplexInfinity:
|
| 561 |
+
return oo
|
| 562 |
+
from sympy.functions.elementary.exponential import exp, log
|
| 563 |
+
|
| 564 |
+
if arg.is_Pow:
|
| 565 |
+
base, exponent = arg.as_base_exp()
|
| 566 |
+
if base.is_extended_real:
|
| 567 |
+
if exponent.is_integer:
|
| 568 |
+
if exponent.is_even:
|
| 569 |
+
return arg
|
| 570 |
+
if base is S.NegativeOne:
|
| 571 |
+
return S.One
|
| 572 |
+
return Abs(base)**exponent
|
| 573 |
+
if base.is_extended_nonnegative:
|
| 574 |
+
return base**re(exponent)
|
| 575 |
+
if base.is_extended_negative:
|
| 576 |
+
return (-base)**re(exponent)*exp(-pi*im(exponent))
|
| 577 |
+
return
|
| 578 |
+
elif not base.has(Symbol): # complex base
|
| 579 |
+
# express base**exponent as exp(exponent*log(base))
|
| 580 |
+
a, b = log(base).as_real_imag()
|
| 581 |
+
z = a + I*b
|
| 582 |
+
return exp(re(exponent*z))
|
| 583 |
+
if isinstance(arg, exp):
|
| 584 |
+
return exp(re(arg.args[0]))
|
| 585 |
+
if isinstance(arg, AppliedUndef):
|
| 586 |
+
if arg.is_positive:
|
| 587 |
+
return arg
|
| 588 |
+
elif arg.is_negative:
|
| 589 |
+
return -arg
|
| 590 |
+
return
|
| 591 |
+
if arg.is_Add and arg.has(oo, S.NegativeInfinity):
|
| 592 |
+
if any(a.is_infinite for a in arg.as_real_imag()):
|
| 593 |
+
return oo
|
| 594 |
+
if arg.is_zero:
|
| 595 |
+
return S.Zero
|
| 596 |
+
if arg.is_extended_nonnegative:
|
| 597 |
+
return arg
|
| 598 |
+
if arg.is_extended_nonpositive:
|
| 599 |
+
return -arg
|
| 600 |
+
if arg.is_imaginary:
|
| 601 |
+
arg2 = -I * arg
|
| 602 |
+
if arg2.is_extended_nonnegative:
|
| 603 |
+
return arg2
|
| 604 |
+
if arg.is_extended_real:
|
| 605 |
+
return
|
| 606 |
+
# reject result if all new conjugates are just wrappers around
|
| 607 |
+
# an expression that was already in the arg
|
| 608 |
+
conj = signsimp(arg.conjugate(), evaluate=False)
|
| 609 |
+
new_conj = conj.atoms(conjugate) - arg.atoms(conjugate)
|
| 610 |
+
if new_conj and all(arg.has(i.args[0]) for i in new_conj):
|
| 611 |
+
return
|
| 612 |
+
if arg != conj and arg != -conj:
|
| 613 |
+
ignore = arg.atoms(Abs)
|
| 614 |
+
abs_free_arg = arg.xreplace({i: Dummy(real=True) for i in ignore})
|
| 615 |
+
unk = [a for a in abs_free_arg.free_symbols if a.is_extended_real is None]
|
| 616 |
+
if not unk or not all(conj.has(conjugate(u)) for u in unk):
|
| 617 |
+
return sqrt(expand_mul(arg*conj))
|
| 618 |
+
|
| 619 |
+
def _eval_is_real(self):
|
| 620 |
+
if self.args[0].is_finite:
|
| 621 |
+
return True
|
| 622 |
+
|
| 623 |
+
def _eval_is_integer(self):
|
| 624 |
+
if self.args[0].is_extended_real:
|
| 625 |
+
return self.args[0].is_integer
|
| 626 |
+
|
| 627 |
+
def _eval_is_extended_nonzero(self):
|
| 628 |
+
return fuzzy_not(self._args[0].is_zero)
|
| 629 |
+
|
| 630 |
+
def _eval_is_zero(self):
|
| 631 |
+
return self._args[0].is_zero
|
| 632 |
+
|
| 633 |
+
def _eval_is_extended_positive(self):
|
| 634 |
+
return fuzzy_not(self._args[0].is_zero)
|
| 635 |
+
|
| 636 |
+
def _eval_is_rational(self):
|
| 637 |
+
if self.args[0].is_extended_real:
|
| 638 |
+
return self.args[0].is_rational
|
| 639 |
+
|
| 640 |
+
def _eval_is_even(self):
|
| 641 |
+
if self.args[0].is_extended_real:
|
| 642 |
+
return self.args[0].is_even
|
| 643 |
+
|
| 644 |
+
def _eval_is_odd(self):
|
| 645 |
+
if self.args[0].is_extended_real:
|
| 646 |
+
return self.args[0].is_odd
|
| 647 |
+
|
| 648 |
+
def _eval_is_algebraic(self):
|
| 649 |
+
return self.args[0].is_algebraic
|
| 650 |
+
|
| 651 |
+
def _eval_power(self, exponent):
|
| 652 |
+
if self.args[0].is_extended_real and exponent.is_integer:
|
| 653 |
+
if exponent.is_even:
|
| 654 |
+
return self.args[0]**exponent
|
| 655 |
+
elif exponent is not S.NegativeOne and exponent.is_Integer:
|
| 656 |
+
return self.args[0]**(exponent - 1)*self
|
| 657 |
+
return
|
| 658 |
+
|
| 659 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 660 |
+
from sympy.functions.elementary.exponential import log
|
| 661 |
+
direction = self.args[0].leadterm(x)[0]
|
| 662 |
+
if direction.has(log(x)):
|
| 663 |
+
direction = direction.subs(log(x), logx)
|
| 664 |
+
s = self.args[0]._eval_nseries(x, n=n, logx=logx)
|
| 665 |
+
return (sign(direction)*s).expand()
|
| 666 |
+
|
| 667 |
+
def _eval_derivative(self, x):
|
| 668 |
+
if self.args[0].is_extended_real or self.args[0].is_imaginary:
|
| 669 |
+
return Derivative(self.args[0], x, evaluate=True) \
|
| 670 |
+
* sign(conjugate(self.args[0]))
|
| 671 |
+
rv = (re(self.args[0]) * Derivative(re(self.args[0]), x,
|
| 672 |
+
evaluate=True) + im(self.args[0]) * Derivative(im(self.args[0]),
|
| 673 |
+
x, evaluate=True)) / Abs(self.args[0])
|
| 674 |
+
return rv.rewrite(sign)
|
| 675 |
+
|
| 676 |
+
def _eval_rewrite_as_Heaviside(self, arg, **kwargs):
|
| 677 |
+
# Note this only holds for real arg (since Heaviside is not defined
|
| 678 |
+
# for complex arguments).
|
| 679 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 680 |
+
if arg.is_extended_real:
|
| 681 |
+
return arg*(Heaviside(arg) - Heaviside(-arg))
|
| 682 |
+
|
| 683 |
+
def _eval_rewrite_as_Piecewise(self, arg, **kwargs):
|
| 684 |
+
if arg.is_extended_real:
|
| 685 |
+
return Piecewise((arg, arg >= 0), (-arg, True))
|
| 686 |
+
elif arg.is_imaginary:
|
| 687 |
+
return Piecewise((I*arg, I*arg >= 0), (-I*arg, True))
|
| 688 |
+
|
| 689 |
+
def _eval_rewrite_as_sign(self, arg, **kwargs):
|
| 690 |
+
return arg/sign(arg)
|
| 691 |
+
|
| 692 |
+
def _eval_rewrite_as_conjugate(self, arg, **kwargs):
|
| 693 |
+
return sqrt(arg*conjugate(arg))
|
| 694 |
+
|
| 695 |
+
|
| 696 |
+
class arg(DefinedFunction):
|
| 697 |
+
r"""
|
| 698 |
+
Returns the argument (in radians) of a complex number. The argument is
|
| 699 |
+
evaluated in consistent convention with ``atan2`` where the branch-cut is
|
| 700 |
+
taken along the negative real axis and ``arg(z)`` is in the interval
|
| 701 |
+
$(-\pi,\pi]$. For a positive number, the argument is always 0; the
|
| 702 |
+
argument of a negative number is $\pi$; and the argument of 0
|
| 703 |
+
is undefined and returns ``nan``. So the ``arg`` function will never nest
|
| 704 |
+
greater than 3 levels since at the 4th application, the result must be
|
| 705 |
+
nan; for a real number, nan is returned on the 3rd application.
|
| 706 |
+
|
| 707 |
+
Examples
|
| 708 |
+
========
|
| 709 |
+
|
| 710 |
+
>>> from sympy import arg, I, sqrt, Dummy
|
| 711 |
+
>>> from sympy.abc import x
|
| 712 |
+
>>> arg(2.0)
|
| 713 |
+
0
|
| 714 |
+
>>> arg(I)
|
| 715 |
+
pi/2
|
| 716 |
+
>>> arg(sqrt(2) + I*sqrt(2))
|
| 717 |
+
pi/4
|
| 718 |
+
>>> arg(sqrt(3)/2 + I/2)
|
| 719 |
+
pi/6
|
| 720 |
+
>>> arg(4 + 3*I)
|
| 721 |
+
atan(3/4)
|
| 722 |
+
>>> arg(0.8 + 0.6*I)
|
| 723 |
+
0.643501108793284
|
| 724 |
+
>>> arg(arg(arg(arg(x))))
|
| 725 |
+
nan
|
| 726 |
+
>>> real = Dummy(real=True)
|
| 727 |
+
>>> arg(arg(arg(real)))
|
| 728 |
+
nan
|
| 729 |
+
|
| 730 |
+
Parameters
|
| 731 |
+
==========
|
| 732 |
+
|
| 733 |
+
arg : Expr
|
| 734 |
+
Real or complex expression.
|
| 735 |
+
|
| 736 |
+
Returns
|
| 737 |
+
=======
|
| 738 |
+
|
| 739 |
+
value : Expr
|
| 740 |
+
Returns arc tangent of arg measured in radians.
|
| 741 |
+
|
| 742 |
+
"""
|
| 743 |
+
|
| 744 |
+
is_extended_real = True
|
| 745 |
+
is_real = True
|
| 746 |
+
is_finite = True
|
| 747 |
+
_singularities = True # non-holomorphic
|
| 748 |
+
|
| 749 |
+
@classmethod
|
| 750 |
+
def eval(cls, arg):
|
| 751 |
+
a = arg
|
| 752 |
+
for i in range(3):
|
| 753 |
+
if isinstance(a, cls):
|
| 754 |
+
a = a.args[0]
|
| 755 |
+
else:
|
| 756 |
+
if i == 2 and a.is_extended_real:
|
| 757 |
+
return S.NaN
|
| 758 |
+
break
|
| 759 |
+
else:
|
| 760 |
+
return S.NaN
|
| 761 |
+
from sympy.functions.elementary.exponential import exp, exp_polar
|
| 762 |
+
if isinstance(arg, exp_polar):
|
| 763 |
+
return periodic_argument(arg, oo)
|
| 764 |
+
elif isinstance(arg, exp):
|
| 765 |
+
i_ = im(arg.args[0])
|
| 766 |
+
if i_.is_comparable:
|
| 767 |
+
i_ %= 2*S.Pi
|
| 768 |
+
if i_ > S.Pi:
|
| 769 |
+
i_ -= 2*S.Pi
|
| 770 |
+
return i_
|
| 771 |
+
|
| 772 |
+
if not arg.is_Atom:
|
| 773 |
+
c, arg_ = factor_terms(arg).as_coeff_Mul()
|
| 774 |
+
if arg_.is_Mul:
|
| 775 |
+
arg_ = Mul(*[a if (sign(a) not in (-1, 1)) else
|
| 776 |
+
sign(a) for a in arg_.args])
|
| 777 |
+
arg_ = sign(c)*arg_
|
| 778 |
+
else:
|
| 779 |
+
arg_ = arg
|
| 780 |
+
if any(i.is_extended_positive is None for i in arg_.atoms(AppliedUndef)):
|
| 781 |
+
return
|
| 782 |
+
from sympy.functions.elementary.trigonometric import atan2
|
| 783 |
+
x, y = arg_.as_real_imag()
|
| 784 |
+
rv = atan2(y, x)
|
| 785 |
+
if rv.is_number:
|
| 786 |
+
return rv
|
| 787 |
+
if arg_ != arg:
|
| 788 |
+
return cls(arg_, evaluate=False)
|
| 789 |
+
|
| 790 |
+
def _eval_derivative(self, t):
|
| 791 |
+
x, y = self.args[0].as_real_imag()
|
| 792 |
+
return (x * Derivative(y, t, evaluate=True) - y *
|
| 793 |
+
Derivative(x, t, evaluate=True)) / (x**2 + y**2)
|
| 794 |
+
|
| 795 |
+
def _eval_rewrite_as_atan2(self, arg, **kwargs):
|
| 796 |
+
from sympy.functions.elementary.trigonometric import atan2
|
| 797 |
+
x, y = self.args[0].as_real_imag()
|
| 798 |
+
return atan2(y, x)
|
| 799 |
+
|
| 800 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 801 |
+
arg0 = self.args[0]
|
| 802 |
+
t = Dummy('t', positive=True)
|
| 803 |
+
if cdir == 0:
|
| 804 |
+
cdir = 1
|
| 805 |
+
z = arg0.subs(x, cdir*t)
|
| 806 |
+
if z.is_positive:
|
| 807 |
+
return S.Zero
|
| 808 |
+
elif z.is_negative:
|
| 809 |
+
return S.Pi
|
| 810 |
+
else:
|
| 811 |
+
raise PoleError("Cannot expand %s around 0" % (self))
|
| 812 |
+
|
| 813 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 814 |
+
from sympy.series.order import Order
|
| 815 |
+
if n <= 0:
|
| 816 |
+
return Order(1)
|
| 817 |
+
return self._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 818 |
+
|
| 819 |
+
|
| 820 |
+
class conjugate(DefinedFunction):
|
| 821 |
+
"""
|
| 822 |
+
Returns the *complex conjugate* [1]_ of an argument.
|
| 823 |
+
In mathematics, the complex conjugate of a complex number
|
| 824 |
+
is given by changing the sign of the imaginary part.
|
| 825 |
+
|
| 826 |
+
Thus, the conjugate of the complex number
|
| 827 |
+
:math:`a + ib` (where $a$ and $b$ are real numbers) is :math:`a - ib`
|
| 828 |
+
|
| 829 |
+
Examples
|
| 830 |
+
========
|
| 831 |
+
|
| 832 |
+
>>> from sympy import conjugate, I
|
| 833 |
+
>>> conjugate(2)
|
| 834 |
+
2
|
| 835 |
+
>>> conjugate(I)
|
| 836 |
+
-I
|
| 837 |
+
>>> conjugate(3 + 2*I)
|
| 838 |
+
3 - 2*I
|
| 839 |
+
>>> conjugate(5 - I)
|
| 840 |
+
5 + I
|
| 841 |
+
|
| 842 |
+
Parameters
|
| 843 |
+
==========
|
| 844 |
+
|
| 845 |
+
arg : Expr
|
| 846 |
+
Real or complex expression.
|
| 847 |
+
|
| 848 |
+
Returns
|
| 849 |
+
=======
|
| 850 |
+
|
| 851 |
+
arg : Expr
|
| 852 |
+
Complex conjugate of arg as real, imaginary or mixed expression.
|
| 853 |
+
|
| 854 |
+
See Also
|
| 855 |
+
========
|
| 856 |
+
|
| 857 |
+
sign, Abs
|
| 858 |
+
|
| 859 |
+
References
|
| 860 |
+
==========
|
| 861 |
+
|
| 862 |
+
.. [1] https://en.wikipedia.org/wiki/Complex_conjugation
|
| 863 |
+
"""
|
| 864 |
+
_singularities = True # non-holomorphic
|
| 865 |
+
|
| 866 |
+
@classmethod
|
| 867 |
+
def eval(cls, arg):
|
| 868 |
+
obj = arg._eval_conjugate()
|
| 869 |
+
if obj is not None:
|
| 870 |
+
return obj
|
| 871 |
+
|
| 872 |
+
def inverse(self):
|
| 873 |
+
return conjugate
|
| 874 |
+
|
| 875 |
+
def _eval_Abs(self):
|
| 876 |
+
return Abs(self.args[0], evaluate=True)
|
| 877 |
+
|
| 878 |
+
def _eval_adjoint(self):
|
| 879 |
+
return transpose(self.args[0])
|
| 880 |
+
|
| 881 |
+
def _eval_conjugate(self):
|
| 882 |
+
return self.args[0]
|
| 883 |
+
|
| 884 |
+
def _eval_derivative(self, x):
|
| 885 |
+
if x.is_real:
|
| 886 |
+
return conjugate(Derivative(self.args[0], x, evaluate=True))
|
| 887 |
+
elif x.is_imaginary:
|
| 888 |
+
return -conjugate(Derivative(self.args[0], x, evaluate=True))
|
| 889 |
+
|
| 890 |
+
def _eval_transpose(self):
|
| 891 |
+
return adjoint(self.args[0])
|
| 892 |
+
|
| 893 |
+
def _eval_is_algebraic(self):
|
| 894 |
+
return self.args[0].is_algebraic
|
| 895 |
+
|
| 896 |
+
|
| 897 |
+
class transpose(DefinedFunction):
|
| 898 |
+
"""
|
| 899 |
+
Linear map transposition.
|
| 900 |
+
|
| 901 |
+
Examples
|
| 902 |
+
========
|
| 903 |
+
|
| 904 |
+
>>> from sympy import transpose, Matrix, MatrixSymbol
|
| 905 |
+
>>> A = MatrixSymbol('A', 25, 9)
|
| 906 |
+
>>> transpose(A)
|
| 907 |
+
A.T
|
| 908 |
+
>>> B = MatrixSymbol('B', 9, 22)
|
| 909 |
+
>>> transpose(B)
|
| 910 |
+
B.T
|
| 911 |
+
>>> transpose(A*B)
|
| 912 |
+
B.T*A.T
|
| 913 |
+
>>> M = Matrix([[4, 5], [2, 1], [90, 12]])
|
| 914 |
+
>>> M
|
| 915 |
+
Matrix([
|
| 916 |
+
[ 4, 5],
|
| 917 |
+
[ 2, 1],
|
| 918 |
+
[90, 12]])
|
| 919 |
+
>>> transpose(M)
|
| 920 |
+
Matrix([
|
| 921 |
+
[4, 2, 90],
|
| 922 |
+
[5, 1, 12]])
|
| 923 |
+
|
| 924 |
+
Parameters
|
| 925 |
+
==========
|
| 926 |
+
|
| 927 |
+
arg : Matrix
|
| 928 |
+
Matrix or matrix expression to take the transpose of.
|
| 929 |
+
|
| 930 |
+
Returns
|
| 931 |
+
=======
|
| 932 |
+
|
| 933 |
+
value : Matrix
|
| 934 |
+
Transpose of arg.
|
| 935 |
+
|
| 936 |
+
"""
|
| 937 |
+
|
| 938 |
+
@classmethod
|
| 939 |
+
def eval(cls, arg):
|
| 940 |
+
obj = arg._eval_transpose()
|
| 941 |
+
if obj is not None:
|
| 942 |
+
return obj
|
| 943 |
+
|
| 944 |
+
def _eval_adjoint(self):
|
| 945 |
+
return conjugate(self.args[0])
|
| 946 |
+
|
| 947 |
+
def _eval_conjugate(self):
|
| 948 |
+
return adjoint(self.args[0])
|
| 949 |
+
|
| 950 |
+
def _eval_transpose(self):
|
| 951 |
+
return self.args[0]
|
| 952 |
+
|
| 953 |
+
|
| 954 |
+
class adjoint(DefinedFunction):
|
| 955 |
+
"""
|
| 956 |
+
Conjugate transpose or Hermite conjugation.
|
| 957 |
+
|
| 958 |
+
Examples
|
| 959 |
+
========
|
| 960 |
+
|
| 961 |
+
>>> from sympy import adjoint, MatrixSymbol
|
| 962 |
+
>>> A = MatrixSymbol('A', 10, 5)
|
| 963 |
+
>>> adjoint(A)
|
| 964 |
+
Adjoint(A)
|
| 965 |
+
|
| 966 |
+
Parameters
|
| 967 |
+
==========
|
| 968 |
+
|
| 969 |
+
arg : Matrix
|
| 970 |
+
Matrix or matrix expression to take the adjoint of.
|
| 971 |
+
|
| 972 |
+
Returns
|
| 973 |
+
=======
|
| 974 |
+
|
| 975 |
+
value : Matrix
|
| 976 |
+
Represents the conjugate transpose or Hermite
|
| 977 |
+
conjugation of arg.
|
| 978 |
+
|
| 979 |
+
"""
|
| 980 |
+
|
| 981 |
+
@classmethod
|
| 982 |
+
def eval(cls, arg):
|
| 983 |
+
obj = arg._eval_adjoint()
|
| 984 |
+
if obj is not None:
|
| 985 |
+
return obj
|
| 986 |
+
obj = arg._eval_transpose()
|
| 987 |
+
if obj is not None:
|
| 988 |
+
return conjugate(obj)
|
| 989 |
+
|
| 990 |
+
def _eval_adjoint(self):
|
| 991 |
+
return self.args[0]
|
| 992 |
+
|
| 993 |
+
def _eval_conjugate(self):
|
| 994 |
+
return transpose(self.args[0])
|
| 995 |
+
|
| 996 |
+
def _eval_transpose(self):
|
| 997 |
+
return conjugate(self.args[0])
|
| 998 |
+
|
| 999 |
+
def _latex(self, printer, exp=None, *args):
|
| 1000 |
+
arg = printer._print(self.args[0])
|
| 1001 |
+
tex = r'%s^{\dagger}' % arg
|
| 1002 |
+
if exp:
|
| 1003 |
+
tex = r'\left(%s\right)^{%s}' % (tex, exp)
|
| 1004 |
+
return tex
|
| 1005 |
+
|
| 1006 |
+
def _pretty(self, printer, *args):
|
| 1007 |
+
from sympy.printing.pretty.stringpict import prettyForm
|
| 1008 |
+
pform = printer._print(self.args[0], *args)
|
| 1009 |
+
if printer._use_unicode:
|
| 1010 |
+
pform = pform**prettyForm('\N{DAGGER}')
|
| 1011 |
+
else:
|
| 1012 |
+
pform = pform**prettyForm('+')
|
| 1013 |
+
return pform
|
| 1014 |
+
|
| 1015 |
+
###############################################################################
|
| 1016 |
+
############### HANDLING OF POLAR NUMBERS #####################################
|
| 1017 |
+
###############################################################################
|
| 1018 |
+
|
| 1019 |
+
|
| 1020 |
+
class polar_lift(DefinedFunction):
|
| 1021 |
+
"""
|
| 1022 |
+
Lift argument to the Riemann surface of the logarithm, using the
|
| 1023 |
+
standard branch.
|
| 1024 |
+
|
| 1025 |
+
Examples
|
| 1026 |
+
========
|
| 1027 |
+
|
| 1028 |
+
>>> from sympy import Symbol, polar_lift, I
|
| 1029 |
+
>>> p = Symbol('p', polar=True)
|
| 1030 |
+
>>> x = Symbol('x')
|
| 1031 |
+
>>> polar_lift(4)
|
| 1032 |
+
4*exp_polar(0)
|
| 1033 |
+
>>> polar_lift(-4)
|
| 1034 |
+
4*exp_polar(I*pi)
|
| 1035 |
+
>>> polar_lift(-I)
|
| 1036 |
+
exp_polar(-I*pi/2)
|
| 1037 |
+
>>> polar_lift(I + 2)
|
| 1038 |
+
polar_lift(2 + I)
|
| 1039 |
+
|
| 1040 |
+
>>> polar_lift(4*x)
|
| 1041 |
+
4*polar_lift(x)
|
| 1042 |
+
>>> polar_lift(4*p)
|
| 1043 |
+
4*p
|
| 1044 |
+
|
| 1045 |
+
Parameters
|
| 1046 |
+
==========
|
| 1047 |
+
|
| 1048 |
+
arg : Expr
|
| 1049 |
+
Real or complex expression.
|
| 1050 |
+
|
| 1051 |
+
See Also
|
| 1052 |
+
========
|
| 1053 |
+
|
| 1054 |
+
sympy.functions.elementary.exponential.exp_polar
|
| 1055 |
+
periodic_argument
|
| 1056 |
+
"""
|
| 1057 |
+
|
| 1058 |
+
is_polar = True
|
| 1059 |
+
is_comparable = False # Cannot be evalf'd.
|
| 1060 |
+
|
| 1061 |
+
@classmethod
|
| 1062 |
+
def eval(cls, arg):
|
| 1063 |
+
from sympy.functions.elementary.complexes import arg as argument
|
| 1064 |
+
if arg.is_number:
|
| 1065 |
+
ar = argument(arg)
|
| 1066 |
+
# In general we want to affirm that something is known,
|
| 1067 |
+
# e.g. `not ar.has(argument) and not ar.has(atan)`
|
| 1068 |
+
# but for now we will just be more restrictive and
|
| 1069 |
+
# see that it has evaluated to one of the known values.
|
| 1070 |
+
if ar in (0, pi/2, -pi/2, pi):
|
| 1071 |
+
from sympy.functions.elementary.exponential import exp_polar
|
| 1072 |
+
return exp_polar(I*ar)*abs(arg)
|
| 1073 |
+
|
| 1074 |
+
if arg.is_Mul:
|
| 1075 |
+
args = arg.args
|
| 1076 |
+
else:
|
| 1077 |
+
args = [arg]
|
| 1078 |
+
included = []
|
| 1079 |
+
excluded = []
|
| 1080 |
+
positive = []
|
| 1081 |
+
for arg in args:
|
| 1082 |
+
if arg.is_polar:
|
| 1083 |
+
included += [arg]
|
| 1084 |
+
elif arg.is_positive:
|
| 1085 |
+
positive += [arg]
|
| 1086 |
+
else:
|
| 1087 |
+
excluded += [arg]
|
| 1088 |
+
if len(excluded) < len(args):
|
| 1089 |
+
if excluded:
|
| 1090 |
+
return Mul(*(included + positive))*polar_lift(Mul(*excluded))
|
| 1091 |
+
elif included:
|
| 1092 |
+
return Mul(*(included + positive))
|
| 1093 |
+
else:
|
| 1094 |
+
from sympy.functions.elementary.exponential import exp_polar
|
| 1095 |
+
return Mul(*positive)*exp_polar(0)
|
| 1096 |
+
|
| 1097 |
+
def _eval_evalf(self, prec):
|
| 1098 |
+
""" Careful! any evalf of polar numbers is flaky """
|
| 1099 |
+
return self.args[0]._eval_evalf(prec)
|
| 1100 |
+
|
| 1101 |
+
def _eval_Abs(self):
|
| 1102 |
+
return Abs(self.args[0], evaluate=True)
|
| 1103 |
+
|
| 1104 |
+
|
| 1105 |
+
class periodic_argument(DefinedFunction):
|
| 1106 |
+
r"""
|
| 1107 |
+
Represent the argument on a quotient of the Riemann surface of the
|
| 1108 |
+
logarithm. That is, given a period $P$, always return a value in
|
| 1109 |
+
$(-P/2, P/2]$, by using $\exp(PI) = 1$.
|
| 1110 |
+
|
| 1111 |
+
Examples
|
| 1112 |
+
========
|
| 1113 |
+
|
| 1114 |
+
>>> from sympy import exp_polar, periodic_argument
|
| 1115 |
+
>>> from sympy import I, pi
|
| 1116 |
+
>>> periodic_argument(exp_polar(10*I*pi), 2*pi)
|
| 1117 |
+
0
|
| 1118 |
+
>>> periodic_argument(exp_polar(5*I*pi), 4*pi)
|
| 1119 |
+
pi
|
| 1120 |
+
>>> from sympy import exp_polar, periodic_argument
|
| 1121 |
+
>>> from sympy import I, pi
|
| 1122 |
+
>>> periodic_argument(exp_polar(5*I*pi), 2*pi)
|
| 1123 |
+
pi
|
| 1124 |
+
>>> periodic_argument(exp_polar(5*I*pi), 3*pi)
|
| 1125 |
+
-pi
|
| 1126 |
+
>>> periodic_argument(exp_polar(5*I*pi), pi)
|
| 1127 |
+
0
|
| 1128 |
+
|
| 1129 |
+
Parameters
|
| 1130 |
+
==========
|
| 1131 |
+
|
| 1132 |
+
ar : Expr
|
| 1133 |
+
A polar number.
|
| 1134 |
+
|
| 1135 |
+
period : Expr
|
| 1136 |
+
The period $P$.
|
| 1137 |
+
|
| 1138 |
+
See Also
|
| 1139 |
+
========
|
| 1140 |
+
|
| 1141 |
+
sympy.functions.elementary.exponential.exp_polar
|
| 1142 |
+
polar_lift : Lift argument to the Riemann surface of the logarithm
|
| 1143 |
+
principal_branch
|
| 1144 |
+
"""
|
| 1145 |
+
|
| 1146 |
+
@classmethod
|
| 1147 |
+
def _getunbranched(cls, ar):
|
| 1148 |
+
from sympy.functions.elementary.exponential import exp_polar, log
|
| 1149 |
+
if ar.is_Mul:
|
| 1150 |
+
args = ar.args
|
| 1151 |
+
else:
|
| 1152 |
+
args = [ar]
|
| 1153 |
+
unbranched = 0
|
| 1154 |
+
for a in args:
|
| 1155 |
+
if not a.is_polar:
|
| 1156 |
+
unbranched += arg(a)
|
| 1157 |
+
elif isinstance(a, exp_polar):
|
| 1158 |
+
unbranched += a.exp.as_real_imag()[1]
|
| 1159 |
+
elif a.is_Pow:
|
| 1160 |
+
re, im = a.exp.as_real_imag()
|
| 1161 |
+
unbranched += re*unbranched_argument(
|
| 1162 |
+
a.base) + im*log(abs(a.base))
|
| 1163 |
+
elif isinstance(a, polar_lift):
|
| 1164 |
+
unbranched += arg(a.args[0])
|
| 1165 |
+
else:
|
| 1166 |
+
return None
|
| 1167 |
+
return unbranched
|
| 1168 |
+
|
| 1169 |
+
@classmethod
|
| 1170 |
+
def eval(cls, ar, period):
|
| 1171 |
+
# Our strategy is to evaluate the argument on the Riemann surface of the
|
| 1172 |
+
# logarithm, and then reduce.
|
| 1173 |
+
# NOTE evidently this means it is a rather bad idea to use this with
|
| 1174 |
+
# period != 2*pi and non-polar numbers.
|
| 1175 |
+
if not period.is_extended_positive:
|
| 1176 |
+
return None
|
| 1177 |
+
if period == oo and isinstance(ar, principal_branch):
|
| 1178 |
+
return periodic_argument(*ar.args)
|
| 1179 |
+
if isinstance(ar, polar_lift) and period >= 2*pi:
|
| 1180 |
+
return periodic_argument(ar.args[0], period)
|
| 1181 |
+
if ar.is_Mul:
|
| 1182 |
+
newargs = [x for x in ar.args if not x.is_positive]
|
| 1183 |
+
if len(newargs) != len(ar.args):
|
| 1184 |
+
return periodic_argument(Mul(*newargs), period)
|
| 1185 |
+
unbranched = cls._getunbranched(ar)
|
| 1186 |
+
if unbranched is None:
|
| 1187 |
+
return None
|
| 1188 |
+
from sympy.functions.elementary.trigonometric import atan, atan2
|
| 1189 |
+
if unbranched.has(periodic_argument, atan2, atan):
|
| 1190 |
+
return None
|
| 1191 |
+
if period == oo:
|
| 1192 |
+
return unbranched
|
| 1193 |
+
if period != oo:
|
| 1194 |
+
from sympy.functions.elementary.integers import ceiling
|
| 1195 |
+
n = ceiling(unbranched/period - S.Half)*period
|
| 1196 |
+
if not n.has(ceiling):
|
| 1197 |
+
return unbranched - n
|
| 1198 |
+
|
| 1199 |
+
def _eval_evalf(self, prec):
|
| 1200 |
+
z, period = self.args
|
| 1201 |
+
if period == oo:
|
| 1202 |
+
unbranched = periodic_argument._getunbranched(z)
|
| 1203 |
+
if unbranched is None:
|
| 1204 |
+
return self
|
| 1205 |
+
return unbranched._eval_evalf(prec)
|
| 1206 |
+
ub = periodic_argument(z, oo)._eval_evalf(prec)
|
| 1207 |
+
from sympy.functions.elementary.integers import ceiling
|
| 1208 |
+
return (ub - ceiling(ub/period - S.Half)*period)._eval_evalf(prec)
|
| 1209 |
+
|
| 1210 |
+
|
| 1211 |
+
def unbranched_argument(arg):
|
| 1212 |
+
'''
|
| 1213 |
+
Returns periodic argument of arg with period as infinity.
|
| 1214 |
+
|
| 1215 |
+
Examples
|
| 1216 |
+
========
|
| 1217 |
+
|
| 1218 |
+
>>> from sympy import exp_polar, unbranched_argument
|
| 1219 |
+
>>> from sympy import I, pi
|
| 1220 |
+
>>> unbranched_argument(exp_polar(15*I*pi))
|
| 1221 |
+
15*pi
|
| 1222 |
+
>>> unbranched_argument(exp_polar(7*I*pi))
|
| 1223 |
+
7*pi
|
| 1224 |
+
|
| 1225 |
+
See also
|
| 1226 |
+
========
|
| 1227 |
+
|
| 1228 |
+
periodic_argument
|
| 1229 |
+
'''
|
| 1230 |
+
return periodic_argument(arg, oo)
|
| 1231 |
+
|
| 1232 |
+
|
| 1233 |
+
class principal_branch(DefinedFunction):
|
| 1234 |
+
"""
|
| 1235 |
+
Represent a polar number reduced to its principal branch on a quotient
|
| 1236 |
+
of the Riemann surface of the logarithm.
|
| 1237 |
+
|
| 1238 |
+
Explanation
|
| 1239 |
+
===========
|
| 1240 |
+
|
| 1241 |
+
This is a function of two arguments. The first argument is a polar
|
| 1242 |
+
number `z`, and the second one a positive real number or infinity, `p`.
|
| 1243 |
+
The result is ``z mod exp_polar(I*p)``.
|
| 1244 |
+
|
| 1245 |
+
Examples
|
| 1246 |
+
========
|
| 1247 |
+
|
| 1248 |
+
>>> from sympy import exp_polar, principal_branch, oo, I, pi
|
| 1249 |
+
>>> from sympy.abc import z
|
| 1250 |
+
>>> principal_branch(z, oo)
|
| 1251 |
+
z
|
| 1252 |
+
>>> principal_branch(exp_polar(2*pi*I)*3, 2*pi)
|
| 1253 |
+
3*exp_polar(0)
|
| 1254 |
+
>>> principal_branch(exp_polar(2*pi*I)*3*z, 2*pi)
|
| 1255 |
+
3*principal_branch(z, 2*pi)
|
| 1256 |
+
|
| 1257 |
+
Parameters
|
| 1258 |
+
==========
|
| 1259 |
+
|
| 1260 |
+
x : Expr
|
| 1261 |
+
A polar number.
|
| 1262 |
+
|
| 1263 |
+
period : Expr
|
| 1264 |
+
Positive real number or infinity.
|
| 1265 |
+
|
| 1266 |
+
See Also
|
| 1267 |
+
========
|
| 1268 |
+
|
| 1269 |
+
sympy.functions.elementary.exponential.exp_polar
|
| 1270 |
+
polar_lift : Lift argument to the Riemann surface of the logarithm
|
| 1271 |
+
periodic_argument
|
| 1272 |
+
"""
|
| 1273 |
+
|
| 1274 |
+
is_polar = True
|
| 1275 |
+
is_comparable = False # cannot always be evalf'd
|
| 1276 |
+
|
| 1277 |
+
@classmethod
|
| 1278 |
+
def eval(self, x, period):
|
| 1279 |
+
from sympy.functions.elementary.exponential import exp_polar
|
| 1280 |
+
if isinstance(x, polar_lift):
|
| 1281 |
+
return principal_branch(x.args[0], period)
|
| 1282 |
+
if period == oo:
|
| 1283 |
+
return x
|
| 1284 |
+
ub = periodic_argument(x, oo)
|
| 1285 |
+
barg = periodic_argument(x, period)
|
| 1286 |
+
if ub != barg and not ub.has(periodic_argument) \
|
| 1287 |
+
and not barg.has(periodic_argument):
|
| 1288 |
+
pl = polar_lift(x)
|
| 1289 |
+
|
| 1290 |
+
def mr(expr):
|
| 1291 |
+
if not isinstance(expr, Symbol):
|
| 1292 |
+
return polar_lift(expr)
|
| 1293 |
+
return expr
|
| 1294 |
+
pl = pl.replace(polar_lift, mr)
|
| 1295 |
+
# Recompute unbranched argument
|
| 1296 |
+
ub = periodic_argument(pl, oo)
|
| 1297 |
+
if not pl.has(polar_lift):
|
| 1298 |
+
if ub != barg:
|
| 1299 |
+
res = exp_polar(I*(barg - ub))*pl
|
| 1300 |
+
else:
|
| 1301 |
+
res = pl
|
| 1302 |
+
if not res.is_polar and not res.has(exp_polar):
|
| 1303 |
+
res *= exp_polar(0)
|
| 1304 |
+
return res
|
| 1305 |
+
|
| 1306 |
+
if not x.free_symbols:
|
| 1307 |
+
c, m = x, ()
|
| 1308 |
+
else:
|
| 1309 |
+
c, m = x.as_coeff_mul(*x.free_symbols)
|
| 1310 |
+
others = []
|
| 1311 |
+
for y in m:
|
| 1312 |
+
if y.is_positive:
|
| 1313 |
+
c *= y
|
| 1314 |
+
else:
|
| 1315 |
+
others += [y]
|
| 1316 |
+
m = tuple(others)
|
| 1317 |
+
arg = periodic_argument(c, period)
|
| 1318 |
+
if arg.has(periodic_argument):
|
| 1319 |
+
return None
|
| 1320 |
+
if arg.is_number and (unbranched_argument(c) != arg or
|
| 1321 |
+
(arg == 0 and m != () and c != 1)):
|
| 1322 |
+
if arg == 0:
|
| 1323 |
+
return abs(c)*principal_branch(Mul(*m), period)
|
| 1324 |
+
return principal_branch(exp_polar(I*arg)*Mul(*m), period)*abs(c)
|
| 1325 |
+
if arg.is_number and ((abs(arg) < period/2) == True or arg == period/2) \
|
| 1326 |
+
and m == ():
|
| 1327 |
+
return exp_polar(arg*I)*abs(c)
|
| 1328 |
+
|
| 1329 |
+
def _eval_evalf(self, prec):
|
| 1330 |
+
z, period = self.args
|
| 1331 |
+
p = periodic_argument(z, period)._eval_evalf(prec)
|
| 1332 |
+
if abs(p) > pi or p == -pi:
|
| 1333 |
+
return self # Cannot evalf for this argument.
|
| 1334 |
+
from sympy.functions.elementary.exponential import exp
|
| 1335 |
+
return (abs(z)*exp(I*p))._eval_evalf(prec)
|
| 1336 |
+
|
| 1337 |
+
|
| 1338 |
+
def _polarify(eq, lift, pause=False):
|
| 1339 |
+
from sympy.integrals.integrals import Integral
|
| 1340 |
+
if eq.is_polar:
|
| 1341 |
+
return eq
|
| 1342 |
+
if eq.is_number and not pause:
|
| 1343 |
+
return polar_lift(eq)
|
| 1344 |
+
if isinstance(eq, Symbol) and not pause and lift:
|
| 1345 |
+
return polar_lift(eq)
|
| 1346 |
+
elif eq.is_Atom:
|
| 1347 |
+
return eq
|
| 1348 |
+
elif eq.is_Add:
|
| 1349 |
+
r = eq.func(*[_polarify(arg, lift, pause=True) for arg in eq.args])
|
| 1350 |
+
if lift:
|
| 1351 |
+
return polar_lift(r)
|
| 1352 |
+
return r
|
| 1353 |
+
elif eq.is_Pow and eq.base == S.Exp1:
|
| 1354 |
+
return eq.func(S.Exp1, _polarify(eq.exp, lift, pause=False))
|
| 1355 |
+
elif eq.is_Function:
|
| 1356 |
+
return eq.func(*[_polarify(arg, lift, pause=False) for arg in eq.args])
|
| 1357 |
+
elif isinstance(eq, Integral):
|
| 1358 |
+
# Don't lift the integration variable
|
| 1359 |
+
func = _polarify(eq.function, lift, pause=pause)
|
| 1360 |
+
limits = []
|
| 1361 |
+
for limit in eq.args[1:]:
|
| 1362 |
+
var = _polarify(limit[0], lift=False, pause=pause)
|
| 1363 |
+
rest = _polarify(limit[1:], lift=lift, pause=pause)
|
| 1364 |
+
limits.append((var,) + rest)
|
| 1365 |
+
return Integral(*((func,) + tuple(limits)))
|
| 1366 |
+
else:
|
| 1367 |
+
return eq.func(*[_polarify(arg, lift, pause=pause)
|
| 1368 |
+
if isinstance(arg, Expr) else arg for arg in eq.args])
|
| 1369 |
+
|
| 1370 |
+
|
| 1371 |
+
def polarify(eq, subs=True, lift=False):
|
| 1372 |
+
"""
|
| 1373 |
+
Turn all numbers in eq into their polar equivalents (under the standard
|
| 1374 |
+
choice of argument).
|
| 1375 |
+
|
| 1376 |
+
Note that no attempt is made to guess a formal convention of adding
|
| 1377 |
+
polar numbers, expressions like $1 + x$ will generally not be altered.
|
| 1378 |
+
|
| 1379 |
+
Note also that this function does not promote ``exp(x)`` to ``exp_polar(x)``.
|
| 1380 |
+
|
| 1381 |
+
If ``subs`` is ``True``, all symbols which are not already polar will be
|
| 1382 |
+
substituted for polar dummies; in this case the function behaves much
|
| 1383 |
+
like :func:`~.posify`.
|
| 1384 |
+
|
| 1385 |
+
If ``lift`` is ``True``, both addition statements and non-polar symbols are
|
| 1386 |
+
changed to their ``polar_lift()``ed versions.
|
| 1387 |
+
Note that ``lift=True`` implies ``subs=False``.
|
| 1388 |
+
|
| 1389 |
+
Examples
|
| 1390 |
+
========
|
| 1391 |
+
|
| 1392 |
+
>>> from sympy import polarify, sin, I
|
| 1393 |
+
>>> from sympy.abc import x, y
|
| 1394 |
+
>>> expr = (-x)**y
|
| 1395 |
+
>>> expr.expand()
|
| 1396 |
+
(-x)**y
|
| 1397 |
+
>>> polarify(expr)
|
| 1398 |
+
((_x*exp_polar(I*pi))**_y, {_x: x, _y: y})
|
| 1399 |
+
>>> polarify(expr)[0].expand()
|
| 1400 |
+
_x**_y*exp_polar(_y*I*pi)
|
| 1401 |
+
>>> polarify(x, lift=True)
|
| 1402 |
+
polar_lift(x)
|
| 1403 |
+
>>> polarify(x*(1+y), lift=True)
|
| 1404 |
+
polar_lift(x)*polar_lift(y + 1)
|
| 1405 |
+
|
| 1406 |
+
Adds are treated carefully:
|
| 1407 |
+
|
| 1408 |
+
>>> polarify(1 + sin((1 + I)*x))
|
| 1409 |
+
(sin(_x*polar_lift(1 + I)) + 1, {_x: x})
|
| 1410 |
+
"""
|
| 1411 |
+
if lift:
|
| 1412 |
+
subs = False
|
| 1413 |
+
eq = _polarify(sympify(eq), lift)
|
| 1414 |
+
if not subs:
|
| 1415 |
+
return eq
|
| 1416 |
+
reps = {s: Dummy(s.name, polar=True) for s in eq.free_symbols}
|
| 1417 |
+
eq = eq.subs(reps)
|
| 1418 |
+
return eq, {r: s for s, r in reps.items()}
|
| 1419 |
+
|
| 1420 |
+
|
| 1421 |
+
def _unpolarify(eq, exponents_only, pause=False):
|
| 1422 |
+
if not isinstance(eq, Basic) or eq.is_Atom:
|
| 1423 |
+
return eq
|
| 1424 |
+
|
| 1425 |
+
if not pause:
|
| 1426 |
+
from sympy.functions.elementary.exponential import exp, exp_polar
|
| 1427 |
+
if isinstance(eq, exp_polar):
|
| 1428 |
+
return exp(_unpolarify(eq.exp, exponents_only))
|
| 1429 |
+
if isinstance(eq, principal_branch) and eq.args[1] == 2*pi:
|
| 1430 |
+
return _unpolarify(eq.args[0], exponents_only)
|
| 1431 |
+
if (
|
| 1432 |
+
eq.is_Add or eq.is_Mul or eq.is_Boolean or
|
| 1433 |
+
eq.is_Relational and (
|
| 1434 |
+
eq.rel_op in ('==', '!=') and 0 in eq.args or
|
| 1435 |
+
eq.rel_op not in ('==', '!='))
|
| 1436 |
+
):
|
| 1437 |
+
return eq.func(*[_unpolarify(x, exponents_only) for x in eq.args])
|
| 1438 |
+
if isinstance(eq, polar_lift):
|
| 1439 |
+
return _unpolarify(eq.args[0], exponents_only)
|
| 1440 |
+
|
| 1441 |
+
if eq.is_Pow:
|
| 1442 |
+
expo = _unpolarify(eq.exp, exponents_only)
|
| 1443 |
+
base = _unpolarify(eq.base, exponents_only,
|
| 1444 |
+
not (expo.is_integer and not pause))
|
| 1445 |
+
return base**expo
|
| 1446 |
+
|
| 1447 |
+
if eq.is_Function and getattr(eq.func, 'unbranched', False):
|
| 1448 |
+
return eq.func(*[_unpolarify(x, exponents_only, exponents_only)
|
| 1449 |
+
for x in eq.args])
|
| 1450 |
+
|
| 1451 |
+
return eq.func(*[_unpolarify(x, exponents_only, True) for x in eq.args])
|
| 1452 |
+
|
| 1453 |
+
|
| 1454 |
+
def unpolarify(eq, subs=None, exponents_only=False):
|
| 1455 |
+
"""
|
| 1456 |
+
If `p` denotes the projection from the Riemann surface of the logarithm to
|
| 1457 |
+
the complex line, return a simplified version `eq'` of `eq` such that
|
| 1458 |
+
`p(eq') = p(eq)`.
|
| 1459 |
+
Also apply the substitution subs in the end. (This is a convenience, since
|
| 1460 |
+
``unpolarify``, in a certain sense, undoes :func:`polarify`.)
|
| 1461 |
+
|
| 1462 |
+
Examples
|
| 1463 |
+
========
|
| 1464 |
+
|
| 1465 |
+
>>> from sympy import unpolarify, polar_lift, sin, I
|
| 1466 |
+
>>> unpolarify(polar_lift(I + 2))
|
| 1467 |
+
2 + I
|
| 1468 |
+
>>> unpolarify(sin(polar_lift(I + 7)))
|
| 1469 |
+
sin(7 + I)
|
| 1470 |
+
"""
|
| 1471 |
+
if isinstance(eq, bool):
|
| 1472 |
+
return eq
|
| 1473 |
+
|
| 1474 |
+
eq = sympify(eq)
|
| 1475 |
+
if subs is not None:
|
| 1476 |
+
return unpolarify(eq.subs(subs))
|
| 1477 |
+
changed = True
|
| 1478 |
+
pause = False
|
| 1479 |
+
if exponents_only:
|
| 1480 |
+
pause = True
|
| 1481 |
+
while changed:
|
| 1482 |
+
changed = False
|
| 1483 |
+
res = _unpolarify(eq, exponents_only, pause)
|
| 1484 |
+
if res != eq:
|
| 1485 |
+
changed = True
|
| 1486 |
+
eq = res
|
| 1487 |
+
if isinstance(res, bool):
|
| 1488 |
+
return res
|
| 1489 |
+
# Finally, replacing Exp(0) by 1 is always correct.
|
| 1490 |
+
# So is polar_lift(0) -> 0.
|
| 1491 |
+
from sympy.functions.elementary.exponential import exp_polar
|
| 1492 |
+
return res.subs({exp_polar(0): 1, polar_lift(0): 0})
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/exponential.py
ADDED
|
@@ -0,0 +1,1286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
from itertools import product
|
| 3 |
+
|
| 4 |
+
from sympy.core.add import Add
|
| 5 |
+
from sympy.core.cache import cacheit
|
| 6 |
+
from sympy.core.expr import Expr
|
| 7 |
+
from sympy.core.function import (DefinedFunction, ArgumentIndexError, expand_log,
|
| 8 |
+
expand_mul, FunctionClass, PoleError, expand_multinomial, expand_complex)
|
| 9 |
+
from sympy.core.logic import fuzzy_and, fuzzy_not, fuzzy_or
|
| 10 |
+
from sympy.core.mul import Mul
|
| 11 |
+
from sympy.core.numbers import Integer, Rational, pi, I
|
| 12 |
+
from sympy.core.parameters import global_parameters
|
| 13 |
+
from sympy.core.power import Pow
|
| 14 |
+
from sympy.core.singleton import S
|
| 15 |
+
from sympy.core.symbol import Wild, Dummy
|
| 16 |
+
from sympy.core.sympify import sympify
|
| 17 |
+
from sympy.functions.combinatorial.factorials import factorial
|
| 18 |
+
from sympy.functions.elementary.complexes import arg, unpolarify, im, re, Abs
|
| 19 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 20 |
+
from sympy.ntheory import multiplicity, perfect_power
|
| 21 |
+
from sympy.ntheory.factor_ import factorint
|
| 22 |
+
|
| 23 |
+
# NOTE IMPORTANT
|
| 24 |
+
# The series expansion code in this file is an important part of the gruntz
|
| 25 |
+
# algorithm for determining limits. _eval_nseries has to return a generalized
|
| 26 |
+
# power series with coefficients in C(log(x), log).
|
| 27 |
+
# In more detail, the result of _eval_nseries(self, x, n) must be
|
| 28 |
+
# c_0*x**e_0 + ... (finitely many terms)
|
| 29 |
+
# where e_i are numbers (not necessarily integers) and c_i involve only
|
| 30 |
+
# numbers, the function log, and log(x). [This also means it must not contain
|
| 31 |
+
# log(x(1+p)), this *has* to be expanded to log(x)+log(1+p) if x.is_positive and
|
| 32 |
+
# p.is_positive.]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class ExpBase(DefinedFunction):
|
| 36 |
+
|
| 37 |
+
unbranched = True
|
| 38 |
+
_singularities = (S.ComplexInfinity,)
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def kind(self):
|
| 42 |
+
return self.exp.kind
|
| 43 |
+
|
| 44 |
+
def inverse(self, argindex=1):
|
| 45 |
+
"""
|
| 46 |
+
Returns the inverse function of ``exp(x)``.
|
| 47 |
+
"""
|
| 48 |
+
return log
|
| 49 |
+
|
| 50 |
+
def as_numer_denom(self):
|
| 51 |
+
"""
|
| 52 |
+
Returns this with a positive exponent as a 2-tuple (a fraction).
|
| 53 |
+
|
| 54 |
+
Examples
|
| 55 |
+
========
|
| 56 |
+
|
| 57 |
+
>>> from sympy import exp
|
| 58 |
+
>>> from sympy.abc import x
|
| 59 |
+
>>> exp(-x).as_numer_denom()
|
| 60 |
+
(1, exp(x))
|
| 61 |
+
>>> exp(x).as_numer_denom()
|
| 62 |
+
(exp(x), 1)
|
| 63 |
+
"""
|
| 64 |
+
# this should be the same as Pow.as_numer_denom wrt
|
| 65 |
+
# exponent handling
|
| 66 |
+
if not self.is_commutative:
|
| 67 |
+
return self, S.One
|
| 68 |
+
exp = self.exp
|
| 69 |
+
neg_exp = exp.is_negative
|
| 70 |
+
if not neg_exp and not (-exp).is_negative:
|
| 71 |
+
neg_exp = exp.could_extract_minus_sign()
|
| 72 |
+
if neg_exp:
|
| 73 |
+
return S.One, self.func(-exp)
|
| 74 |
+
return self, S.One
|
| 75 |
+
|
| 76 |
+
@property
|
| 77 |
+
def exp(self):
|
| 78 |
+
"""
|
| 79 |
+
Returns the exponent of the function.
|
| 80 |
+
"""
|
| 81 |
+
return self.args[0]
|
| 82 |
+
|
| 83 |
+
def as_base_exp(self):
|
| 84 |
+
"""
|
| 85 |
+
Returns the 2-tuple (base, exponent).
|
| 86 |
+
"""
|
| 87 |
+
return self.func(1), Mul(*self.args)
|
| 88 |
+
|
| 89 |
+
def _eval_adjoint(self):
|
| 90 |
+
return self.func(self.exp.adjoint())
|
| 91 |
+
|
| 92 |
+
def _eval_conjugate(self):
|
| 93 |
+
return self.func(self.exp.conjugate())
|
| 94 |
+
|
| 95 |
+
def _eval_transpose(self):
|
| 96 |
+
return self.func(self.exp.transpose())
|
| 97 |
+
|
| 98 |
+
def _eval_is_finite(self):
|
| 99 |
+
arg = self.exp
|
| 100 |
+
if arg.is_infinite:
|
| 101 |
+
if arg.is_extended_negative:
|
| 102 |
+
return True
|
| 103 |
+
if arg.is_extended_positive:
|
| 104 |
+
return False
|
| 105 |
+
if arg.is_finite:
|
| 106 |
+
return True
|
| 107 |
+
|
| 108 |
+
def _eval_is_rational(self):
|
| 109 |
+
s = self.func(*self.args)
|
| 110 |
+
if s.func == self.func:
|
| 111 |
+
z = s.exp.is_zero
|
| 112 |
+
if z:
|
| 113 |
+
return True
|
| 114 |
+
elif s.exp.is_rational and fuzzy_not(z):
|
| 115 |
+
return False
|
| 116 |
+
else:
|
| 117 |
+
return s.is_rational
|
| 118 |
+
|
| 119 |
+
def _eval_is_zero(self):
|
| 120 |
+
return self.exp is S.NegativeInfinity
|
| 121 |
+
|
| 122 |
+
def _eval_power(self, other):
|
| 123 |
+
"""exp(arg)**e -> exp(arg*e) if assumptions allow it.
|
| 124 |
+
"""
|
| 125 |
+
b, e = self.as_base_exp()
|
| 126 |
+
return Pow._eval_power(Pow(b, e, evaluate=False), other)
|
| 127 |
+
|
| 128 |
+
def _eval_expand_power_exp(self, **hints):
|
| 129 |
+
from sympy.concrete.products import Product
|
| 130 |
+
from sympy.concrete.summations import Sum
|
| 131 |
+
arg = self.args[0]
|
| 132 |
+
if arg.is_Add and arg.is_commutative:
|
| 133 |
+
return Mul.fromiter(self.func(x) for x in arg.args)
|
| 134 |
+
elif isinstance(arg, Sum) and arg.is_commutative:
|
| 135 |
+
return Product(self.func(arg.function), *arg.limits)
|
| 136 |
+
return self.func(arg)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
class exp_polar(ExpBase):
|
| 140 |
+
r"""
|
| 141 |
+
Represent a *polar number* (see g-function Sphinx documentation).
|
| 142 |
+
|
| 143 |
+
Explanation
|
| 144 |
+
===========
|
| 145 |
+
|
| 146 |
+
``exp_polar`` represents the function
|
| 147 |
+
`Exp: \mathbb{C} \rightarrow \mathcal{S}`, sending the complex number
|
| 148 |
+
`z = a + bi` to the polar number `r = exp(a), \theta = b`. It is one of
|
| 149 |
+
the main functions to construct polar numbers.
|
| 150 |
+
|
| 151 |
+
Examples
|
| 152 |
+
========
|
| 153 |
+
|
| 154 |
+
>>> from sympy import exp_polar, pi, I, exp
|
| 155 |
+
|
| 156 |
+
The main difference is that polar numbers do not "wrap around" at `2 \pi`:
|
| 157 |
+
|
| 158 |
+
>>> exp(2*pi*I)
|
| 159 |
+
1
|
| 160 |
+
>>> exp_polar(2*pi*I)
|
| 161 |
+
exp_polar(2*I*pi)
|
| 162 |
+
|
| 163 |
+
apart from that they behave mostly like classical complex numbers:
|
| 164 |
+
|
| 165 |
+
>>> exp_polar(2)*exp_polar(3)
|
| 166 |
+
exp_polar(5)
|
| 167 |
+
|
| 168 |
+
See Also
|
| 169 |
+
========
|
| 170 |
+
|
| 171 |
+
sympy.simplify.powsimp.powsimp
|
| 172 |
+
polar_lift
|
| 173 |
+
periodic_argument
|
| 174 |
+
principal_branch
|
| 175 |
+
"""
|
| 176 |
+
|
| 177 |
+
is_polar = True
|
| 178 |
+
is_comparable = False # cannot be evalf'd
|
| 179 |
+
|
| 180 |
+
def _eval_Abs(self): # Abs is never a polar number
|
| 181 |
+
return exp(re(self.args[0]))
|
| 182 |
+
|
| 183 |
+
def _eval_evalf(self, prec):
|
| 184 |
+
""" Careful! any evalf of polar numbers is flaky """
|
| 185 |
+
i = im(self.args[0])
|
| 186 |
+
try:
|
| 187 |
+
bad = (i <= -pi or i > pi)
|
| 188 |
+
except TypeError:
|
| 189 |
+
bad = True
|
| 190 |
+
if bad:
|
| 191 |
+
return self # cannot evalf for this argument
|
| 192 |
+
res = exp(self.args[0])._eval_evalf(prec)
|
| 193 |
+
if i > 0 and im(res) < 0:
|
| 194 |
+
# i ~ pi, but exp(I*i) evaluated to argument slightly bigger than pi
|
| 195 |
+
return re(res)
|
| 196 |
+
return res
|
| 197 |
+
|
| 198 |
+
def _eval_power(self, other):
|
| 199 |
+
return self.func(self.args[0]*other)
|
| 200 |
+
|
| 201 |
+
def _eval_is_extended_real(self):
|
| 202 |
+
if self.args[0].is_extended_real:
|
| 203 |
+
return True
|
| 204 |
+
|
| 205 |
+
def as_base_exp(self):
|
| 206 |
+
# XXX exp_polar(0) is special!
|
| 207 |
+
if self.args[0] == 0:
|
| 208 |
+
return self, S.One
|
| 209 |
+
return ExpBase.as_base_exp(self)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
class ExpMeta(FunctionClass):
|
| 213 |
+
def __instancecheck__(cls, instance):
|
| 214 |
+
if exp in instance.__class__.__mro__:
|
| 215 |
+
return True
|
| 216 |
+
return isinstance(instance, Pow) and instance.base is S.Exp1
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
class exp(ExpBase, metaclass=ExpMeta):
|
| 220 |
+
"""
|
| 221 |
+
The exponential function, :math:`e^x`.
|
| 222 |
+
|
| 223 |
+
Examples
|
| 224 |
+
========
|
| 225 |
+
|
| 226 |
+
>>> from sympy import exp, I, pi
|
| 227 |
+
>>> from sympy.abc import x
|
| 228 |
+
>>> exp(x)
|
| 229 |
+
exp(x)
|
| 230 |
+
>>> exp(x).diff(x)
|
| 231 |
+
exp(x)
|
| 232 |
+
>>> exp(I*pi)
|
| 233 |
+
-1
|
| 234 |
+
|
| 235 |
+
Parameters
|
| 236 |
+
==========
|
| 237 |
+
|
| 238 |
+
arg : Expr
|
| 239 |
+
|
| 240 |
+
See Also
|
| 241 |
+
========
|
| 242 |
+
|
| 243 |
+
log
|
| 244 |
+
"""
|
| 245 |
+
|
| 246 |
+
def fdiff(self, argindex=1):
|
| 247 |
+
"""
|
| 248 |
+
Returns the first derivative of this function.
|
| 249 |
+
"""
|
| 250 |
+
if argindex == 1:
|
| 251 |
+
return self
|
| 252 |
+
else:
|
| 253 |
+
raise ArgumentIndexError(self, argindex)
|
| 254 |
+
|
| 255 |
+
def _eval_refine(self, assumptions):
|
| 256 |
+
from sympy.assumptions import ask, Q
|
| 257 |
+
arg = self.args[0]
|
| 258 |
+
if arg.is_Mul:
|
| 259 |
+
Ioo = I*S.Infinity
|
| 260 |
+
if arg in [Ioo, -Ioo]:
|
| 261 |
+
return S.NaN
|
| 262 |
+
|
| 263 |
+
coeff = arg.as_coefficient(pi*I)
|
| 264 |
+
if coeff:
|
| 265 |
+
if ask(Q.integer(2*coeff)):
|
| 266 |
+
if ask(Q.even(coeff)):
|
| 267 |
+
return S.One
|
| 268 |
+
elif ask(Q.odd(coeff)):
|
| 269 |
+
return S.NegativeOne
|
| 270 |
+
elif ask(Q.even(coeff + S.Half)):
|
| 271 |
+
return -I
|
| 272 |
+
elif ask(Q.odd(coeff + S.Half)):
|
| 273 |
+
return I
|
| 274 |
+
|
| 275 |
+
@classmethod
|
| 276 |
+
def eval(cls, arg):
|
| 277 |
+
from sympy.calculus import AccumBounds
|
| 278 |
+
from sympy.matrices.matrixbase import MatrixBase
|
| 279 |
+
from sympy.sets.setexpr import SetExpr
|
| 280 |
+
from sympy.simplify.simplify import logcombine
|
| 281 |
+
if isinstance(arg, MatrixBase):
|
| 282 |
+
return arg.exp()
|
| 283 |
+
elif global_parameters.exp_is_pow:
|
| 284 |
+
return Pow(S.Exp1, arg)
|
| 285 |
+
elif arg.is_Number:
|
| 286 |
+
if arg is S.NaN:
|
| 287 |
+
return S.NaN
|
| 288 |
+
elif arg.is_zero:
|
| 289 |
+
return S.One
|
| 290 |
+
elif arg is S.One:
|
| 291 |
+
return S.Exp1
|
| 292 |
+
elif arg is S.Infinity:
|
| 293 |
+
return S.Infinity
|
| 294 |
+
elif arg is S.NegativeInfinity:
|
| 295 |
+
return S.Zero
|
| 296 |
+
elif arg is S.ComplexInfinity:
|
| 297 |
+
return S.NaN
|
| 298 |
+
elif isinstance(arg, log):
|
| 299 |
+
return arg.args[0]
|
| 300 |
+
elif isinstance(arg, AccumBounds):
|
| 301 |
+
return AccumBounds(exp(arg.min), exp(arg.max))
|
| 302 |
+
elif isinstance(arg, SetExpr):
|
| 303 |
+
return arg._eval_func(cls)
|
| 304 |
+
elif arg.is_Mul:
|
| 305 |
+
coeff = arg.as_coefficient(pi*I)
|
| 306 |
+
if coeff:
|
| 307 |
+
if (2*coeff).is_integer:
|
| 308 |
+
if coeff.is_even:
|
| 309 |
+
return S.One
|
| 310 |
+
elif coeff.is_odd:
|
| 311 |
+
return S.NegativeOne
|
| 312 |
+
elif (coeff + S.Half).is_even:
|
| 313 |
+
return -I
|
| 314 |
+
elif (coeff + S.Half).is_odd:
|
| 315 |
+
return I
|
| 316 |
+
elif coeff.is_Rational:
|
| 317 |
+
ncoeff = coeff % 2 # restrict to [0, 2pi)
|
| 318 |
+
if ncoeff > 1: # restrict to (-pi, pi]
|
| 319 |
+
ncoeff -= 2
|
| 320 |
+
if ncoeff != coeff:
|
| 321 |
+
return cls(ncoeff*pi*I)
|
| 322 |
+
|
| 323 |
+
# Warning: code in risch.py will be very sensitive to changes
|
| 324 |
+
# in this (see DifferentialExtension).
|
| 325 |
+
|
| 326 |
+
# look for a single log factor
|
| 327 |
+
|
| 328 |
+
coeff, terms = arg.as_coeff_Mul()
|
| 329 |
+
|
| 330 |
+
# but it can't be multiplied by oo
|
| 331 |
+
if coeff in [S.NegativeInfinity, S.Infinity]:
|
| 332 |
+
if terms.is_number:
|
| 333 |
+
if coeff is S.NegativeInfinity:
|
| 334 |
+
terms = -terms
|
| 335 |
+
if re(terms).is_zero and terms is not S.Zero:
|
| 336 |
+
return S.NaN
|
| 337 |
+
if re(terms).is_positive and im(terms) is not S.Zero:
|
| 338 |
+
return S.ComplexInfinity
|
| 339 |
+
if re(terms).is_negative:
|
| 340 |
+
return S.Zero
|
| 341 |
+
return None
|
| 342 |
+
|
| 343 |
+
coeffs, log_term = [coeff], None
|
| 344 |
+
for term in Mul.make_args(terms):
|
| 345 |
+
term_ = logcombine(term)
|
| 346 |
+
if isinstance(term_, log):
|
| 347 |
+
if log_term is None:
|
| 348 |
+
log_term = term_.args[0]
|
| 349 |
+
else:
|
| 350 |
+
return None
|
| 351 |
+
elif term.is_comparable:
|
| 352 |
+
coeffs.append(term)
|
| 353 |
+
else:
|
| 354 |
+
return None
|
| 355 |
+
|
| 356 |
+
return log_term**Mul(*coeffs) if log_term else None
|
| 357 |
+
|
| 358 |
+
elif arg.is_Add:
|
| 359 |
+
out = []
|
| 360 |
+
add = []
|
| 361 |
+
argchanged = False
|
| 362 |
+
for a in arg.args:
|
| 363 |
+
if a is S.One:
|
| 364 |
+
add.append(a)
|
| 365 |
+
continue
|
| 366 |
+
newa = cls(a)
|
| 367 |
+
if isinstance(newa, cls):
|
| 368 |
+
if newa.args[0] != a:
|
| 369 |
+
add.append(newa.args[0])
|
| 370 |
+
argchanged = True
|
| 371 |
+
else:
|
| 372 |
+
add.append(a)
|
| 373 |
+
else:
|
| 374 |
+
out.append(newa)
|
| 375 |
+
if out or argchanged:
|
| 376 |
+
return Mul(*out)*cls(Add(*add), evaluate=False)
|
| 377 |
+
|
| 378 |
+
if arg.is_zero:
|
| 379 |
+
return S.One
|
| 380 |
+
|
| 381 |
+
@property
|
| 382 |
+
def base(self):
|
| 383 |
+
"""
|
| 384 |
+
Returns the base of the exponential function.
|
| 385 |
+
"""
|
| 386 |
+
return S.Exp1
|
| 387 |
+
|
| 388 |
+
@staticmethod
|
| 389 |
+
@cacheit
|
| 390 |
+
def taylor_term(n, x, *previous_terms):
|
| 391 |
+
"""
|
| 392 |
+
Calculates the next term in the Taylor series expansion.
|
| 393 |
+
"""
|
| 394 |
+
if n < 0:
|
| 395 |
+
return S.Zero
|
| 396 |
+
if n == 0:
|
| 397 |
+
return S.One
|
| 398 |
+
x = sympify(x)
|
| 399 |
+
if previous_terms:
|
| 400 |
+
p = previous_terms[-1]
|
| 401 |
+
if p is not None:
|
| 402 |
+
return p * x / n
|
| 403 |
+
return x**n/factorial(n)
|
| 404 |
+
|
| 405 |
+
def as_real_imag(self, deep=True, **hints):
|
| 406 |
+
"""
|
| 407 |
+
Returns this function as a 2-tuple representing a complex number.
|
| 408 |
+
|
| 409 |
+
Examples
|
| 410 |
+
========
|
| 411 |
+
|
| 412 |
+
>>> from sympy import exp, I
|
| 413 |
+
>>> from sympy.abc import x
|
| 414 |
+
>>> exp(x).as_real_imag()
|
| 415 |
+
(exp(re(x))*cos(im(x)), exp(re(x))*sin(im(x)))
|
| 416 |
+
>>> exp(1).as_real_imag()
|
| 417 |
+
(E, 0)
|
| 418 |
+
>>> exp(I).as_real_imag()
|
| 419 |
+
(cos(1), sin(1))
|
| 420 |
+
>>> exp(1+I).as_real_imag()
|
| 421 |
+
(E*cos(1), E*sin(1))
|
| 422 |
+
|
| 423 |
+
See Also
|
| 424 |
+
========
|
| 425 |
+
|
| 426 |
+
sympy.functions.elementary.complexes.re
|
| 427 |
+
sympy.functions.elementary.complexes.im
|
| 428 |
+
"""
|
| 429 |
+
from sympy.functions.elementary.trigonometric import cos, sin
|
| 430 |
+
re, im = self.args[0].as_real_imag()
|
| 431 |
+
if deep:
|
| 432 |
+
re = re.expand(deep, **hints)
|
| 433 |
+
im = im.expand(deep, **hints)
|
| 434 |
+
cos, sin = cos(im), sin(im)
|
| 435 |
+
return (exp(re)*cos, exp(re)*sin)
|
| 436 |
+
|
| 437 |
+
def _eval_subs(self, old, new):
|
| 438 |
+
# keep processing of power-like args centralized in Pow
|
| 439 |
+
if old.is_Pow: # handle (exp(3*log(x))).subs(x**2, z) -> z**(3/2)
|
| 440 |
+
old = exp(old.exp*log(old.base))
|
| 441 |
+
elif old is S.Exp1 and new.is_Function:
|
| 442 |
+
old = exp
|
| 443 |
+
if isinstance(old, exp) or old is S.Exp1:
|
| 444 |
+
f = lambda a: Pow(*a.as_base_exp(), evaluate=False) if (
|
| 445 |
+
a.is_Pow or isinstance(a, exp)) else a
|
| 446 |
+
return Pow._eval_subs(f(self), f(old), new)
|
| 447 |
+
|
| 448 |
+
if old is exp and not new.is_Function:
|
| 449 |
+
return new**self.exp._subs(old, new)
|
| 450 |
+
return super()._eval_subs(old, new)
|
| 451 |
+
|
| 452 |
+
def _eval_is_extended_real(self):
|
| 453 |
+
if self.args[0].is_extended_real:
|
| 454 |
+
return True
|
| 455 |
+
elif self.args[0].is_imaginary:
|
| 456 |
+
arg2 = -S(2) * I * self.args[0] / pi
|
| 457 |
+
return arg2.is_even
|
| 458 |
+
|
| 459 |
+
def _eval_is_complex(self):
|
| 460 |
+
def complex_extended_negative(arg):
|
| 461 |
+
yield arg.is_complex
|
| 462 |
+
yield arg.is_extended_negative
|
| 463 |
+
return fuzzy_or(complex_extended_negative(self.args[0]))
|
| 464 |
+
|
| 465 |
+
def _eval_is_algebraic(self):
|
| 466 |
+
if (self.exp / pi / I).is_rational:
|
| 467 |
+
return True
|
| 468 |
+
if fuzzy_not(self.exp.is_zero):
|
| 469 |
+
if self.exp.is_algebraic:
|
| 470 |
+
return False
|
| 471 |
+
elif (self.exp / pi).is_rational:
|
| 472 |
+
return False
|
| 473 |
+
|
| 474 |
+
def _eval_is_extended_positive(self):
|
| 475 |
+
if self.exp.is_extended_real:
|
| 476 |
+
return self.args[0] is not S.NegativeInfinity
|
| 477 |
+
elif self.exp.is_imaginary:
|
| 478 |
+
arg2 = -I * self.args[0] / pi
|
| 479 |
+
return arg2.is_even
|
| 480 |
+
|
| 481 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 482 |
+
# NOTE Please see the comment at the beginning of this file, labelled
|
| 483 |
+
# IMPORTANT.
|
| 484 |
+
from sympy.functions.elementary.complexes import sign
|
| 485 |
+
from sympy.functions.elementary.integers import ceiling
|
| 486 |
+
from sympy.series.limits import limit
|
| 487 |
+
from sympy.series.order import Order
|
| 488 |
+
from sympy.simplify.powsimp import powsimp
|
| 489 |
+
arg = self.exp
|
| 490 |
+
arg_series = arg._eval_nseries(x, n=n, logx=logx)
|
| 491 |
+
if arg_series.is_Order:
|
| 492 |
+
return 1 + arg_series
|
| 493 |
+
arg0 = limit(arg_series.removeO(), x, 0)
|
| 494 |
+
if arg0 is S.NegativeInfinity:
|
| 495 |
+
return Order(x**n, x)
|
| 496 |
+
if arg0 is S.Infinity:
|
| 497 |
+
return self
|
| 498 |
+
if arg0.is_infinite:
|
| 499 |
+
raise PoleError("Cannot expand %s around 0" % (self))
|
| 500 |
+
# checking for indecisiveness/ sign terms in arg0
|
| 501 |
+
if any(isinstance(arg, sign) for arg in arg0.args):
|
| 502 |
+
return self
|
| 503 |
+
t = Dummy("t")
|
| 504 |
+
nterms = n
|
| 505 |
+
try:
|
| 506 |
+
cf = Order(arg.as_leading_term(x, logx=logx), x).getn()
|
| 507 |
+
except (NotImplementedError, PoleError):
|
| 508 |
+
cf = 0
|
| 509 |
+
if cf and cf > 0:
|
| 510 |
+
nterms = ceiling(n/cf)
|
| 511 |
+
exp_series = exp(t)._taylor(t, nterms)
|
| 512 |
+
r = exp(arg0)*exp_series.subs(t, arg_series - arg0)
|
| 513 |
+
rep = {logx: log(x)} if logx is not None else {}
|
| 514 |
+
if r.subs(rep) == self:
|
| 515 |
+
return r
|
| 516 |
+
if cf and cf > 1:
|
| 517 |
+
r += Order((arg_series - arg0)**n, x)/x**((cf-1)*n)
|
| 518 |
+
else:
|
| 519 |
+
r += Order((arg_series - arg0)**n, x)
|
| 520 |
+
r = r.expand()
|
| 521 |
+
r = powsimp(r, deep=True, combine='exp')
|
| 522 |
+
# powsimp may introduce unexpanded (-1)**Rational; see PR #17201
|
| 523 |
+
simplerat = lambda x: x.is_Rational and x.q in [3, 4, 6]
|
| 524 |
+
w = Wild('w', properties=[simplerat])
|
| 525 |
+
r = r.replace(S.NegativeOne**w, expand_complex(S.NegativeOne**w))
|
| 526 |
+
return r
|
| 527 |
+
|
| 528 |
+
def _taylor(self, x, n):
|
| 529 |
+
l = []
|
| 530 |
+
g = None
|
| 531 |
+
for i in range(n):
|
| 532 |
+
g = self.taylor_term(i, self.args[0], g)
|
| 533 |
+
g = g.nseries(x, n=n)
|
| 534 |
+
l.append(g.removeO())
|
| 535 |
+
return Add(*l)
|
| 536 |
+
|
| 537 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 538 |
+
from sympy.calculus.util import AccumBounds
|
| 539 |
+
arg = self.args[0].cancel().as_leading_term(x, logx=logx)
|
| 540 |
+
arg0 = arg.subs(x, 0)
|
| 541 |
+
if arg is S.NaN:
|
| 542 |
+
return S.NaN
|
| 543 |
+
if isinstance(arg0, AccumBounds):
|
| 544 |
+
# This check addresses a corner case involving AccumBounds.
|
| 545 |
+
# if isinstance(arg, AccumBounds) is True, then arg0 can either be 0,
|
| 546 |
+
# AccumBounds(-oo, 0) or AccumBounds(-oo, oo).
|
| 547 |
+
# Check out function: test_issue_18473() in test_exponential.py and
|
| 548 |
+
# test_limits.py for more information.
|
| 549 |
+
if re(cdir) < S.Zero:
|
| 550 |
+
return exp(-arg0)
|
| 551 |
+
return exp(arg0)
|
| 552 |
+
if arg0 is S.NaN:
|
| 553 |
+
arg0 = arg.limit(x, 0)
|
| 554 |
+
if arg0.is_infinite is False:
|
| 555 |
+
return exp(arg0)
|
| 556 |
+
raise PoleError("Cannot expand %s around 0" % (self))
|
| 557 |
+
|
| 558 |
+
def _eval_rewrite_as_sin(self, arg, **kwargs):
|
| 559 |
+
from sympy.functions.elementary.trigonometric import sin
|
| 560 |
+
return sin(I*arg + pi/2) - I*sin(I*arg)
|
| 561 |
+
|
| 562 |
+
def _eval_rewrite_as_cos(self, arg, **kwargs):
|
| 563 |
+
from sympy.functions.elementary.trigonometric import cos
|
| 564 |
+
return cos(I*arg) + I*cos(I*arg + pi/2)
|
| 565 |
+
|
| 566 |
+
def _eval_rewrite_as_tanh(self, arg, **kwargs):
|
| 567 |
+
from sympy.functions.elementary.hyperbolic import tanh
|
| 568 |
+
return (1 + tanh(arg/2))/(1 - tanh(arg/2))
|
| 569 |
+
|
| 570 |
+
def _eval_rewrite_as_sqrt(self, arg, **kwargs):
|
| 571 |
+
from sympy.functions.elementary.trigonometric import sin, cos
|
| 572 |
+
if arg.is_Mul:
|
| 573 |
+
coeff = arg.coeff(pi*I)
|
| 574 |
+
if coeff and coeff.is_number:
|
| 575 |
+
cosine, sine = cos(pi*coeff), sin(pi*coeff)
|
| 576 |
+
if not isinstance(cosine, cos) and not isinstance (sine, sin):
|
| 577 |
+
return cosine + I*sine
|
| 578 |
+
|
| 579 |
+
def _eval_rewrite_as_Pow(self, arg, **kwargs):
|
| 580 |
+
if arg.is_Mul:
|
| 581 |
+
logs = [a for a in arg.args if isinstance(a, log) and len(a.args) == 1]
|
| 582 |
+
if logs:
|
| 583 |
+
return Pow(logs[0].args[0], arg.coeff(logs[0]))
|
| 584 |
+
|
| 585 |
+
|
| 586 |
+
def match_real_imag(expr):
|
| 587 |
+
r"""
|
| 588 |
+
Try to match expr with $a + Ib$ for real $a$ and $b$.
|
| 589 |
+
|
| 590 |
+
``match_real_imag`` returns a tuple containing the real and imaginary
|
| 591 |
+
parts of expr or ``(None, None)`` if direct matching is not possible. Contrary
|
| 592 |
+
to :func:`~.re`, :func:`~.im``, and ``as_real_imag()``, this helper will not force things
|
| 593 |
+
by returning expressions themselves containing ``re()`` or ``im()`` and it
|
| 594 |
+
does not expand its argument either.
|
| 595 |
+
|
| 596 |
+
"""
|
| 597 |
+
r_, i_ = expr.as_independent(I, as_Add=True)
|
| 598 |
+
if i_ == 0 and r_.is_real:
|
| 599 |
+
return (r_, i_)
|
| 600 |
+
i_ = i_.as_coefficient(I)
|
| 601 |
+
if i_ and i_.is_real and r_.is_real:
|
| 602 |
+
return (r_, i_)
|
| 603 |
+
else:
|
| 604 |
+
return (None, None) # simpler to check for than None
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
class log(DefinedFunction):
|
| 608 |
+
r"""
|
| 609 |
+
The natural logarithm function `\ln(x)` or `\log(x)`.
|
| 610 |
+
|
| 611 |
+
Explanation
|
| 612 |
+
===========
|
| 613 |
+
|
| 614 |
+
Logarithms are taken with the natural base, `e`. To get
|
| 615 |
+
a logarithm of a different base ``b``, use ``log(x, b)``,
|
| 616 |
+
which is essentially short-hand for ``log(x)/log(b)``.
|
| 617 |
+
|
| 618 |
+
``log`` represents the principal branch of the natural
|
| 619 |
+
logarithm. As such it has a branch cut along the negative
|
| 620 |
+
real axis and returns values having a complex argument in
|
| 621 |
+
`(-\pi, \pi]`.
|
| 622 |
+
|
| 623 |
+
Examples
|
| 624 |
+
========
|
| 625 |
+
|
| 626 |
+
>>> from sympy import log, sqrt, S, I
|
| 627 |
+
>>> log(8, 2)
|
| 628 |
+
3
|
| 629 |
+
>>> log(S(8)/3, 2)
|
| 630 |
+
-log(3)/log(2) + 3
|
| 631 |
+
>>> log(-1 + I*sqrt(3))
|
| 632 |
+
log(2) + 2*I*pi/3
|
| 633 |
+
|
| 634 |
+
See Also
|
| 635 |
+
========
|
| 636 |
+
|
| 637 |
+
exp
|
| 638 |
+
|
| 639 |
+
"""
|
| 640 |
+
|
| 641 |
+
args: tuple[Expr]
|
| 642 |
+
|
| 643 |
+
_singularities = (S.Zero, S.ComplexInfinity)
|
| 644 |
+
|
| 645 |
+
def fdiff(self, argindex=1):
|
| 646 |
+
"""
|
| 647 |
+
Returns the first derivative of the function.
|
| 648 |
+
"""
|
| 649 |
+
if argindex == 1:
|
| 650 |
+
return 1/self.args[0]
|
| 651 |
+
else:
|
| 652 |
+
raise ArgumentIndexError(self, argindex)
|
| 653 |
+
|
| 654 |
+
def inverse(self, argindex=1):
|
| 655 |
+
r"""
|
| 656 |
+
Returns `e^x`, the inverse function of `\log(x)`.
|
| 657 |
+
"""
|
| 658 |
+
return exp
|
| 659 |
+
|
| 660 |
+
@classmethod
|
| 661 |
+
def eval(cls, arg, base=None):
|
| 662 |
+
from sympy.calculus import AccumBounds
|
| 663 |
+
from sympy.sets.setexpr import SetExpr
|
| 664 |
+
|
| 665 |
+
arg = sympify(arg)
|
| 666 |
+
|
| 667 |
+
if base is not None:
|
| 668 |
+
base = sympify(base)
|
| 669 |
+
if base == 1:
|
| 670 |
+
if arg == 1:
|
| 671 |
+
return S.NaN
|
| 672 |
+
else:
|
| 673 |
+
return S.ComplexInfinity
|
| 674 |
+
try:
|
| 675 |
+
# handle extraction of powers of the base now
|
| 676 |
+
# or else expand_log in Mul would have to handle this
|
| 677 |
+
n = multiplicity(base, arg)
|
| 678 |
+
if n:
|
| 679 |
+
return n + log(arg / base**n) / log(base)
|
| 680 |
+
else:
|
| 681 |
+
return log(arg)/log(base)
|
| 682 |
+
except ValueError:
|
| 683 |
+
pass
|
| 684 |
+
if base is not S.Exp1:
|
| 685 |
+
return cls(arg)/cls(base)
|
| 686 |
+
else:
|
| 687 |
+
return cls(arg)
|
| 688 |
+
|
| 689 |
+
if arg.is_Number:
|
| 690 |
+
if arg.is_zero:
|
| 691 |
+
return S.ComplexInfinity
|
| 692 |
+
elif arg is S.One:
|
| 693 |
+
return S.Zero
|
| 694 |
+
elif arg is S.Infinity:
|
| 695 |
+
return S.Infinity
|
| 696 |
+
elif arg is S.NegativeInfinity:
|
| 697 |
+
return S.Infinity
|
| 698 |
+
elif arg is S.NaN:
|
| 699 |
+
return S.NaN
|
| 700 |
+
elif arg.is_Rational and arg.p == 1:
|
| 701 |
+
return -cls(arg.q)
|
| 702 |
+
|
| 703 |
+
if arg.is_Pow and arg.base is S.Exp1 and arg.exp.is_extended_real:
|
| 704 |
+
return arg.exp
|
| 705 |
+
if isinstance(arg, exp) and arg.exp.is_extended_real:
|
| 706 |
+
return arg.exp
|
| 707 |
+
elif isinstance(arg, exp) and arg.exp.is_number:
|
| 708 |
+
r_, i_ = match_real_imag(arg.exp)
|
| 709 |
+
if i_ and i_.is_comparable:
|
| 710 |
+
i_ %= 2*pi
|
| 711 |
+
if i_ > pi:
|
| 712 |
+
i_ -= 2*pi
|
| 713 |
+
return r_ + expand_mul(i_ * I, deep=False)
|
| 714 |
+
elif isinstance(arg, exp_polar):
|
| 715 |
+
return unpolarify(arg.exp)
|
| 716 |
+
elif isinstance(arg, AccumBounds):
|
| 717 |
+
if arg.min.is_positive:
|
| 718 |
+
return AccumBounds(log(arg.min), log(arg.max))
|
| 719 |
+
elif arg.min.is_zero:
|
| 720 |
+
return AccumBounds(S.NegativeInfinity, log(arg.max))
|
| 721 |
+
else:
|
| 722 |
+
return S.NaN
|
| 723 |
+
elif isinstance(arg, SetExpr):
|
| 724 |
+
return arg._eval_func(cls)
|
| 725 |
+
|
| 726 |
+
if arg.is_number:
|
| 727 |
+
if arg.is_negative:
|
| 728 |
+
return pi * I + cls(-arg)
|
| 729 |
+
elif arg is S.ComplexInfinity:
|
| 730 |
+
return S.ComplexInfinity
|
| 731 |
+
elif arg is S.Exp1:
|
| 732 |
+
return S.One
|
| 733 |
+
|
| 734 |
+
if arg.is_zero:
|
| 735 |
+
return S.ComplexInfinity
|
| 736 |
+
|
| 737 |
+
# don't autoexpand Pow or Mul (see the issue 3351):
|
| 738 |
+
if not arg.is_Add:
|
| 739 |
+
coeff = arg.as_coefficient(I)
|
| 740 |
+
|
| 741 |
+
if coeff is not None:
|
| 742 |
+
if coeff is S.Infinity:
|
| 743 |
+
return S.Infinity
|
| 744 |
+
elif coeff is S.NegativeInfinity:
|
| 745 |
+
return S.Infinity
|
| 746 |
+
elif coeff.is_Rational:
|
| 747 |
+
if coeff.is_nonnegative:
|
| 748 |
+
return pi * I * S.Half + cls(coeff)
|
| 749 |
+
else:
|
| 750 |
+
return -pi * I * S.Half + cls(-coeff)
|
| 751 |
+
|
| 752 |
+
if arg.is_number and arg.is_algebraic:
|
| 753 |
+
# Match arg = coeff*(r_ + i_*I) with coeff>0, r_ and i_ real.
|
| 754 |
+
coeff, arg_ = arg.as_independent(I, as_Add=False)
|
| 755 |
+
if coeff.is_negative:
|
| 756 |
+
coeff *= -1
|
| 757 |
+
arg_ *= -1
|
| 758 |
+
arg_ = expand_mul(arg_, deep=False)
|
| 759 |
+
r_, i_ = arg_.as_independent(I, as_Add=True)
|
| 760 |
+
i_ = i_.as_coefficient(I)
|
| 761 |
+
if coeff.is_real and i_ and i_.is_real and r_.is_real:
|
| 762 |
+
if r_.is_zero:
|
| 763 |
+
if i_.is_positive:
|
| 764 |
+
return pi * I * S.Half + cls(coeff * i_)
|
| 765 |
+
elif i_.is_negative:
|
| 766 |
+
return -pi * I * S.Half + cls(coeff * -i_)
|
| 767 |
+
else:
|
| 768 |
+
from sympy.simplify import ratsimp
|
| 769 |
+
# Check for arguments involving rational multiples of pi
|
| 770 |
+
t = (i_/r_).cancel()
|
| 771 |
+
t1 = (-t).cancel()
|
| 772 |
+
atan_table = _log_atan_table()
|
| 773 |
+
if t in atan_table:
|
| 774 |
+
modulus = ratsimp(coeff * Abs(arg_))
|
| 775 |
+
if r_.is_positive:
|
| 776 |
+
return cls(modulus) + I * atan_table[t]
|
| 777 |
+
else:
|
| 778 |
+
return cls(modulus) + I * (atan_table[t] - pi)
|
| 779 |
+
elif t1 in atan_table:
|
| 780 |
+
modulus = ratsimp(coeff * Abs(arg_))
|
| 781 |
+
if r_.is_positive:
|
| 782 |
+
return cls(modulus) + I * (-atan_table[t1])
|
| 783 |
+
else:
|
| 784 |
+
return cls(modulus) + I * (pi - atan_table[t1])
|
| 785 |
+
|
| 786 |
+
@staticmethod
|
| 787 |
+
@cacheit
|
| 788 |
+
def taylor_term(n, x, *previous_terms): # of log(1+x)
|
| 789 |
+
r"""
|
| 790 |
+
Returns the next term in the Taylor series expansion of `\log(1+x)`.
|
| 791 |
+
"""
|
| 792 |
+
from sympy.simplify.powsimp import powsimp
|
| 793 |
+
if n < 0:
|
| 794 |
+
return S.Zero
|
| 795 |
+
x = sympify(x)
|
| 796 |
+
if n == 0:
|
| 797 |
+
return x
|
| 798 |
+
if previous_terms:
|
| 799 |
+
p = previous_terms[-1]
|
| 800 |
+
if p is not None:
|
| 801 |
+
return powsimp((-n) * p * x / (n + 1), deep=True, combine='exp')
|
| 802 |
+
return (1 - 2*(n % 2)) * x**(n + 1)/(n + 1)
|
| 803 |
+
|
| 804 |
+
def _eval_expand_log(self, deep=True, **hints):
|
| 805 |
+
from sympy.concrete import Sum, Product
|
| 806 |
+
force = hints.get('force', False)
|
| 807 |
+
factor = hints.get('factor', False)
|
| 808 |
+
if (len(self.args) == 2):
|
| 809 |
+
return expand_log(self.func(*self.args), deep=deep, force=force)
|
| 810 |
+
arg = self.args[0]
|
| 811 |
+
if arg.is_Integer:
|
| 812 |
+
# remove perfect powers
|
| 813 |
+
p = perfect_power(arg)
|
| 814 |
+
logarg = None
|
| 815 |
+
coeff = 1
|
| 816 |
+
if p is not False:
|
| 817 |
+
arg, coeff = p
|
| 818 |
+
logarg = self.func(arg)
|
| 819 |
+
# expand as product of its prime factors if factor=True
|
| 820 |
+
if factor:
|
| 821 |
+
p = factorint(arg)
|
| 822 |
+
if arg not in p.keys():
|
| 823 |
+
logarg = sum(n*log(val) for val, n in p.items())
|
| 824 |
+
if logarg is not None:
|
| 825 |
+
return coeff*logarg
|
| 826 |
+
elif arg.is_Rational:
|
| 827 |
+
return log(arg.p) - log(arg.q)
|
| 828 |
+
elif arg.is_Mul:
|
| 829 |
+
expr = []
|
| 830 |
+
nonpos = []
|
| 831 |
+
for x in arg.args:
|
| 832 |
+
if force or x.is_positive or x.is_polar:
|
| 833 |
+
a = self.func(x)
|
| 834 |
+
if isinstance(a, log):
|
| 835 |
+
expr.append(self.func(x)._eval_expand_log(**hints))
|
| 836 |
+
else:
|
| 837 |
+
expr.append(a)
|
| 838 |
+
elif x.is_negative:
|
| 839 |
+
a = self.func(-x)
|
| 840 |
+
expr.append(a)
|
| 841 |
+
nonpos.append(S.NegativeOne)
|
| 842 |
+
else:
|
| 843 |
+
nonpos.append(x)
|
| 844 |
+
return Add(*expr) + log(Mul(*nonpos))
|
| 845 |
+
elif arg.is_Pow or isinstance(arg, exp):
|
| 846 |
+
if force or (arg.exp.is_extended_real and (arg.base.is_positive or ((arg.exp+1)
|
| 847 |
+
.is_positive and (arg.exp-1).is_nonpositive))) or arg.base.is_polar:
|
| 848 |
+
b = arg.base
|
| 849 |
+
e = arg.exp
|
| 850 |
+
a = self.func(b)
|
| 851 |
+
if isinstance(a, log):
|
| 852 |
+
return unpolarify(e) * a._eval_expand_log(**hints)
|
| 853 |
+
else:
|
| 854 |
+
return unpolarify(e) * a
|
| 855 |
+
elif isinstance(arg, Product):
|
| 856 |
+
if force or arg.function.is_positive:
|
| 857 |
+
return Sum(log(arg.function), *arg.limits)
|
| 858 |
+
|
| 859 |
+
return self.func(arg)
|
| 860 |
+
|
| 861 |
+
def _eval_simplify(self, **kwargs):
|
| 862 |
+
from sympy.simplify.simplify import expand_log, simplify, inversecombine
|
| 863 |
+
if len(self.args) == 2: # it's unevaluated
|
| 864 |
+
return simplify(self.func(*self.args), **kwargs)
|
| 865 |
+
|
| 866 |
+
expr = self.func(simplify(self.args[0], **kwargs))
|
| 867 |
+
if kwargs['inverse']:
|
| 868 |
+
expr = inversecombine(expr)
|
| 869 |
+
expr = expand_log(expr, deep=True)
|
| 870 |
+
return min([expr, self], key=kwargs['measure'])
|
| 871 |
+
|
| 872 |
+
def as_real_imag(self, deep=True, **hints):
|
| 873 |
+
"""
|
| 874 |
+
Returns this function as a complex coordinate.
|
| 875 |
+
|
| 876 |
+
Examples
|
| 877 |
+
========
|
| 878 |
+
|
| 879 |
+
>>> from sympy import I, log
|
| 880 |
+
>>> from sympy.abc import x
|
| 881 |
+
>>> log(x).as_real_imag()
|
| 882 |
+
(log(Abs(x)), arg(x))
|
| 883 |
+
>>> log(I).as_real_imag()
|
| 884 |
+
(0, pi/2)
|
| 885 |
+
>>> log(1 + I).as_real_imag()
|
| 886 |
+
(log(sqrt(2)), pi/4)
|
| 887 |
+
>>> log(I*x).as_real_imag()
|
| 888 |
+
(log(Abs(x)), arg(I*x))
|
| 889 |
+
|
| 890 |
+
"""
|
| 891 |
+
sarg = self.args[0]
|
| 892 |
+
if deep:
|
| 893 |
+
sarg = self.args[0].expand(deep, **hints)
|
| 894 |
+
sarg_abs = Abs(sarg)
|
| 895 |
+
if sarg_abs == sarg:
|
| 896 |
+
return self, S.Zero
|
| 897 |
+
sarg_arg = arg(sarg)
|
| 898 |
+
if hints.get('log', False): # Expand the log
|
| 899 |
+
hints['complex'] = False
|
| 900 |
+
return (log(sarg_abs).expand(deep, **hints), sarg_arg)
|
| 901 |
+
else:
|
| 902 |
+
return log(sarg_abs), sarg_arg
|
| 903 |
+
|
| 904 |
+
def _eval_is_rational(self):
|
| 905 |
+
s = self.func(*self.args)
|
| 906 |
+
if s.func == self.func:
|
| 907 |
+
if (self.args[0] - 1).is_zero:
|
| 908 |
+
return True
|
| 909 |
+
if s.args[0].is_rational and fuzzy_not((self.args[0] - 1).is_zero):
|
| 910 |
+
return False
|
| 911 |
+
else:
|
| 912 |
+
return s.is_rational
|
| 913 |
+
|
| 914 |
+
def _eval_is_algebraic(self):
|
| 915 |
+
s = self.func(*self.args)
|
| 916 |
+
if s.func == self.func:
|
| 917 |
+
if (self.args[0] - 1).is_zero:
|
| 918 |
+
return True
|
| 919 |
+
elif fuzzy_not((self.args[0] - 1).is_zero):
|
| 920 |
+
if self.args[0].is_algebraic:
|
| 921 |
+
return False
|
| 922 |
+
else:
|
| 923 |
+
return s.is_algebraic
|
| 924 |
+
|
| 925 |
+
def _eval_is_extended_real(self):
|
| 926 |
+
return self.args[0].is_extended_positive
|
| 927 |
+
|
| 928 |
+
def _eval_is_complex(self):
|
| 929 |
+
z = self.args[0]
|
| 930 |
+
return fuzzy_and([z.is_complex, fuzzy_not(z.is_zero)])
|
| 931 |
+
|
| 932 |
+
def _eval_is_finite(self):
|
| 933 |
+
arg = self.args[0]
|
| 934 |
+
if arg.is_zero:
|
| 935 |
+
return False
|
| 936 |
+
return arg.is_finite
|
| 937 |
+
|
| 938 |
+
def _eval_is_extended_positive(self):
|
| 939 |
+
return (self.args[0] - 1).is_extended_positive
|
| 940 |
+
|
| 941 |
+
def _eval_is_zero(self):
|
| 942 |
+
return (self.args[0] - 1).is_zero
|
| 943 |
+
|
| 944 |
+
def _eval_is_extended_nonnegative(self):
|
| 945 |
+
return (self.args[0] - 1).is_extended_nonnegative
|
| 946 |
+
|
| 947 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 948 |
+
# NOTE Please see the comment at the beginning of this file, labelled
|
| 949 |
+
# IMPORTANT.
|
| 950 |
+
from sympy.series.order import Order
|
| 951 |
+
from sympy.simplify.simplify import logcombine
|
| 952 |
+
from sympy.core.symbol import Dummy
|
| 953 |
+
|
| 954 |
+
if self.args[0] == x:
|
| 955 |
+
return log(x) if logx is None else logx
|
| 956 |
+
arg = self.args[0]
|
| 957 |
+
t = Dummy('t', positive=True)
|
| 958 |
+
if cdir == 0:
|
| 959 |
+
cdir = 1
|
| 960 |
+
z = arg.subs(x, cdir*t)
|
| 961 |
+
|
| 962 |
+
k, l = Wild("k"), Wild("l")
|
| 963 |
+
r = z.match(k*t**l)
|
| 964 |
+
if r is not None:
|
| 965 |
+
k, l = r[k], r[l]
|
| 966 |
+
if l != 0 and not l.has(t) and not k.has(t):
|
| 967 |
+
r = l*log(x) if logx is None else l*logx
|
| 968 |
+
r += log(k) - l*log(cdir) # XXX true regardless of assumptions?
|
| 969 |
+
return r
|
| 970 |
+
|
| 971 |
+
def coeff_exp(term, x):
|
| 972 |
+
coeff, exp = S.One, S.Zero
|
| 973 |
+
for factor in Mul.make_args(term):
|
| 974 |
+
if factor.has(x):
|
| 975 |
+
base, exp = factor.as_base_exp()
|
| 976 |
+
if base != x:
|
| 977 |
+
try:
|
| 978 |
+
return term.leadterm(x)
|
| 979 |
+
except ValueError:
|
| 980 |
+
return term, S.Zero
|
| 981 |
+
else:
|
| 982 |
+
coeff *= factor
|
| 983 |
+
return coeff, exp
|
| 984 |
+
|
| 985 |
+
# TODO new and probably slow
|
| 986 |
+
try:
|
| 987 |
+
a, b = z.leadterm(t, logx=logx, cdir=1)
|
| 988 |
+
except (ValueError, NotImplementedError, PoleError):
|
| 989 |
+
s = z._eval_nseries(t, n=n, logx=logx, cdir=1)
|
| 990 |
+
while s.is_Order:
|
| 991 |
+
n += 1
|
| 992 |
+
s = z._eval_nseries(t, n=n, logx=logx, cdir=1)
|
| 993 |
+
try:
|
| 994 |
+
a, b = s.removeO().leadterm(t, cdir=1)
|
| 995 |
+
except ValueError:
|
| 996 |
+
a, b = s.removeO().as_leading_term(t, cdir=1), S.Zero
|
| 997 |
+
|
| 998 |
+
p = (z/(a*t**b) - 1).cancel()._eval_nseries(t, n=n, logx=logx, cdir=1)
|
| 999 |
+
if p.has(exp):
|
| 1000 |
+
p = logcombine(p)
|
| 1001 |
+
if isinstance(p, Order):
|
| 1002 |
+
n = p.getn()
|
| 1003 |
+
_, d = coeff_exp(p, t)
|
| 1004 |
+
logx = log(x) if logx is None else logx
|
| 1005 |
+
|
| 1006 |
+
if not d.is_positive:
|
| 1007 |
+
res = log(a) - b*log(cdir) + b*logx
|
| 1008 |
+
_res = res
|
| 1009 |
+
logflags = {"deep": True, "log": True, "mul": False, "power_exp": False,
|
| 1010 |
+
"power_base": False, "multinomial": False, "basic": False, "force": True,
|
| 1011 |
+
"factor": False}
|
| 1012 |
+
expr = self.expand(**logflags)
|
| 1013 |
+
if (not a.could_extract_minus_sign() and
|
| 1014 |
+
logx.could_extract_minus_sign()):
|
| 1015 |
+
_res = _res.subs(-logx, -log(x)).expand(**logflags)
|
| 1016 |
+
else:
|
| 1017 |
+
_res = _res.subs(logx, log(x)).expand(**logflags)
|
| 1018 |
+
if _res == expr:
|
| 1019 |
+
return res
|
| 1020 |
+
return res + Order(x**n, x)
|
| 1021 |
+
|
| 1022 |
+
def mul(d1, d2):
|
| 1023 |
+
res = {}
|
| 1024 |
+
for e1, e2 in product(d1, d2):
|
| 1025 |
+
ex = e1 + e2
|
| 1026 |
+
if ex < n:
|
| 1027 |
+
res[ex] = res.get(ex, S.Zero) + d1[e1]*d2[e2]
|
| 1028 |
+
return res
|
| 1029 |
+
|
| 1030 |
+
pterms = {}
|
| 1031 |
+
|
| 1032 |
+
for term in Add.make_args(p.removeO()):
|
| 1033 |
+
co1, e1 = coeff_exp(term, t)
|
| 1034 |
+
pterms[e1] = pterms.get(e1, S.Zero) + co1
|
| 1035 |
+
|
| 1036 |
+
k = S.One
|
| 1037 |
+
terms = {}
|
| 1038 |
+
pk = pterms
|
| 1039 |
+
|
| 1040 |
+
while k*d < n:
|
| 1041 |
+
coeff = -S.NegativeOne**k/k
|
| 1042 |
+
for ex in pk:
|
| 1043 |
+
terms[ex] = terms.get(ex, S.Zero) + coeff*pk[ex]
|
| 1044 |
+
pk = mul(pk, pterms)
|
| 1045 |
+
k += S.One
|
| 1046 |
+
|
| 1047 |
+
res = log(a) - b*log(cdir) + b*logx
|
| 1048 |
+
for ex in terms:
|
| 1049 |
+
res += terms[ex].cancel()*t**(ex)
|
| 1050 |
+
|
| 1051 |
+
if a.is_negative and im(z) != 0:
|
| 1052 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 1053 |
+
for i, term in enumerate(z.lseries(t)):
|
| 1054 |
+
if not term.is_real or i == 5:
|
| 1055 |
+
break
|
| 1056 |
+
if i < 5:
|
| 1057 |
+
coeff, _ = term.as_coeff_exponent(t)
|
| 1058 |
+
res += -2*I*pi*Heaviside(-im(coeff), 0)
|
| 1059 |
+
|
| 1060 |
+
res = res.subs(t, x/cdir)
|
| 1061 |
+
return res + Order(x**n, x)
|
| 1062 |
+
|
| 1063 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1064 |
+
# NOTE
|
| 1065 |
+
# Refer https://github.com/sympy/sympy/pull/23592 for more information
|
| 1066 |
+
# on each of the following steps involved in this method.
|
| 1067 |
+
arg0 = self.args[0].together()
|
| 1068 |
+
|
| 1069 |
+
# STEP 1
|
| 1070 |
+
t = Dummy('t', positive=True)
|
| 1071 |
+
if cdir == 0:
|
| 1072 |
+
cdir = 1
|
| 1073 |
+
z = arg0.subs(x, cdir*t)
|
| 1074 |
+
|
| 1075 |
+
# STEP 2
|
| 1076 |
+
try:
|
| 1077 |
+
c, e = z.leadterm(t, logx=logx, cdir=1)
|
| 1078 |
+
except ValueError:
|
| 1079 |
+
arg = arg0.as_leading_term(x, logx=logx, cdir=cdir)
|
| 1080 |
+
return log(arg)
|
| 1081 |
+
if c.has(t):
|
| 1082 |
+
c = c.subs(t, x/cdir)
|
| 1083 |
+
if e != 0:
|
| 1084 |
+
raise PoleError("Cannot expand %s around 0" % (self))
|
| 1085 |
+
return log(c)
|
| 1086 |
+
|
| 1087 |
+
# STEP 3
|
| 1088 |
+
if c == S.One and e == S.Zero:
|
| 1089 |
+
return (arg0 - S.One).as_leading_term(x, logx=logx)
|
| 1090 |
+
|
| 1091 |
+
# STEP 4
|
| 1092 |
+
res = log(c) - e*log(cdir)
|
| 1093 |
+
logx = log(x) if logx is None else logx
|
| 1094 |
+
res += e*logx
|
| 1095 |
+
|
| 1096 |
+
# STEP 5
|
| 1097 |
+
if c.is_negative and im(z) != 0:
|
| 1098 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 1099 |
+
for i, term in enumerate(z.lseries(t)):
|
| 1100 |
+
if not term.is_real or i == 5:
|
| 1101 |
+
break
|
| 1102 |
+
if i < 5:
|
| 1103 |
+
coeff, _ = term.as_coeff_exponent(t)
|
| 1104 |
+
res += -2*I*pi*Heaviside(-im(coeff), 0)
|
| 1105 |
+
return res
|
| 1106 |
+
|
| 1107 |
+
|
| 1108 |
+
class LambertW(DefinedFunction):
|
| 1109 |
+
r"""
|
| 1110 |
+
The Lambert W function $W(z)$ is defined as the inverse
|
| 1111 |
+
function of $w \exp(w)$ [1]_.
|
| 1112 |
+
|
| 1113 |
+
Explanation
|
| 1114 |
+
===========
|
| 1115 |
+
|
| 1116 |
+
In other words, the value of $W(z)$ is such that $z = W(z) \exp(W(z))$
|
| 1117 |
+
for any complex number $z$. The Lambert W function is a multivalued
|
| 1118 |
+
function with infinitely many branches $W_k(z)$, indexed by
|
| 1119 |
+
$k \in \mathbb{Z}$. Each branch gives a different solution $w$
|
| 1120 |
+
of the equation $z = w \exp(w)$.
|
| 1121 |
+
|
| 1122 |
+
The Lambert W function has two partially real branches: the
|
| 1123 |
+
principal branch ($k = 0$) is real for real $z > -1/e$, and the
|
| 1124 |
+
$k = -1$ branch is real for $-1/e < z < 0$. All branches except
|
| 1125 |
+
$k = 0$ have a logarithmic singularity at $z = 0$.
|
| 1126 |
+
|
| 1127 |
+
Examples
|
| 1128 |
+
========
|
| 1129 |
+
|
| 1130 |
+
>>> from sympy import LambertW
|
| 1131 |
+
>>> LambertW(1.2)
|
| 1132 |
+
0.635564016364870
|
| 1133 |
+
>>> LambertW(1.2, -1).n()
|
| 1134 |
+
-1.34747534407696 - 4.41624341514535*I
|
| 1135 |
+
>>> LambertW(-1).is_real
|
| 1136 |
+
False
|
| 1137 |
+
|
| 1138 |
+
References
|
| 1139 |
+
==========
|
| 1140 |
+
|
| 1141 |
+
.. [1] https://en.wikipedia.org/wiki/Lambert_W_function
|
| 1142 |
+
"""
|
| 1143 |
+
_singularities = (-Pow(S.Exp1, -1, evaluate=False), S.ComplexInfinity)
|
| 1144 |
+
|
| 1145 |
+
@classmethod
|
| 1146 |
+
def eval(cls, x, k=None):
|
| 1147 |
+
if k == S.Zero:
|
| 1148 |
+
return cls(x)
|
| 1149 |
+
elif k is None:
|
| 1150 |
+
k = S.Zero
|
| 1151 |
+
|
| 1152 |
+
if k.is_zero:
|
| 1153 |
+
if x.is_zero:
|
| 1154 |
+
return S.Zero
|
| 1155 |
+
if x is S.Exp1:
|
| 1156 |
+
return S.One
|
| 1157 |
+
if x == -1/S.Exp1:
|
| 1158 |
+
return S.NegativeOne
|
| 1159 |
+
if x == -log(2)/2:
|
| 1160 |
+
return -log(2)
|
| 1161 |
+
if x == 2*log(2):
|
| 1162 |
+
return log(2)
|
| 1163 |
+
if x == -pi/2:
|
| 1164 |
+
return I*pi/2
|
| 1165 |
+
if x == exp(1 + S.Exp1):
|
| 1166 |
+
return S.Exp1
|
| 1167 |
+
if x is S.Infinity:
|
| 1168 |
+
return S.Infinity
|
| 1169 |
+
|
| 1170 |
+
if fuzzy_not(k.is_zero):
|
| 1171 |
+
if x.is_zero:
|
| 1172 |
+
return S.NegativeInfinity
|
| 1173 |
+
if k is S.NegativeOne:
|
| 1174 |
+
if x == -pi/2:
|
| 1175 |
+
return -I*pi/2
|
| 1176 |
+
elif x == -1/S.Exp1:
|
| 1177 |
+
return S.NegativeOne
|
| 1178 |
+
elif x == -2*exp(-2):
|
| 1179 |
+
return -Integer(2)
|
| 1180 |
+
|
| 1181 |
+
def fdiff(self, argindex=1):
|
| 1182 |
+
"""
|
| 1183 |
+
Return the first derivative of this function.
|
| 1184 |
+
"""
|
| 1185 |
+
x = self.args[0]
|
| 1186 |
+
|
| 1187 |
+
if len(self.args) == 1:
|
| 1188 |
+
if argindex == 1:
|
| 1189 |
+
return LambertW(x)/(x*(1 + LambertW(x)))
|
| 1190 |
+
else:
|
| 1191 |
+
k = self.args[1]
|
| 1192 |
+
if argindex == 1:
|
| 1193 |
+
return LambertW(x, k)/(x*(1 + LambertW(x, k)))
|
| 1194 |
+
|
| 1195 |
+
raise ArgumentIndexError(self, argindex)
|
| 1196 |
+
|
| 1197 |
+
def _eval_is_extended_real(self):
|
| 1198 |
+
x = self.args[0]
|
| 1199 |
+
if len(self.args) == 1:
|
| 1200 |
+
k = S.Zero
|
| 1201 |
+
else:
|
| 1202 |
+
k = self.args[1]
|
| 1203 |
+
if k.is_zero:
|
| 1204 |
+
if (x + 1/S.Exp1).is_positive:
|
| 1205 |
+
return True
|
| 1206 |
+
elif (x + 1/S.Exp1).is_nonpositive:
|
| 1207 |
+
return False
|
| 1208 |
+
elif (k + 1).is_zero:
|
| 1209 |
+
if x.is_negative and (x + 1/S.Exp1).is_positive:
|
| 1210 |
+
return True
|
| 1211 |
+
elif x.is_nonpositive or (x + 1/S.Exp1).is_nonnegative:
|
| 1212 |
+
return False
|
| 1213 |
+
elif fuzzy_not(k.is_zero) and fuzzy_not((k + 1).is_zero):
|
| 1214 |
+
if x.is_extended_real:
|
| 1215 |
+
return False
|
| 1216 |
+
|
| 1217 |
+
def _eval_is_finite(self):
|
| 1218 |
+
return self.args[0].is_finite
|
| 1219 |
+
|
| 1220 |
+
def _eval_is_algebraic(self):
|
| 1221 |
+
s = self.func(*self.args)
|
| 1222 |
+
if s.func == self.func:
|
| 1223 |
+
if fuzzy_not(self.args[0].is_zero) and self.args[0].is_algebraic:
|
| 1224 |
+
return False
|
| 1225 |
+
else:
|
| 1226 |
+
return s.is_algebraic
|
| 1227 |
+
|
| 1228 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1229 |
+
if len(self.args) == 1:
|
| 1230 |
+
arg = self.args[0]
|
| 1231 |
+
arg0 = arg.subs(x, 0).cancel()
|
| 1232 |
+
if not arg0.is_zero:
|
| 1233 |
+
return self.func(arg0)
|
| 1234 |
+
return arg.as_leading_term(x)
|
| 1235 |
+
|
| 1236 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 1237 |
+
if len(self.args) == 1:
|
| 1238 |
+
from sympy.functions.elementary.integers import ceiling
|
| 1239 |
+
from sympy.series.order import Order
|
| 1240 |
+
arg = self.args[0].nseries(x, n=n, logx=logx)
|
| 1241 |
+
lt = arg.as_leading_term(x, logx=logx)
|
| 1242 |
+
lte = 1
|
| 1243 |
+
if lt.is_Pow:
|
| 1244 |
+
lte = lt.exp
|
| 1245 |
+
if ceiling(n/lte) >= 1:
|
| 1246 |
+
s = Add(*[(-S.One)**(k - 1)*Integer(k)**(k - 2)/
|
| 1247 |
+
factorial(k - 1)*arg**k for k in range(1, ceiling(n/lte))])
|
| 1248 |
+
s = expand_multinomial(s)
|
| 1249 |
+
else:
|
| 1250 |
+
s = S.Zero
|
| 1251 |
+
|
| 1252 |
+
return s + Order(x**n, x)
|
| 1253 |
+
return super()._eval_nseries(x, n, logx)
|
| 1254 |
+
|
| 1255 |
+
def _eval_is_zero(self):
|
| 1256 |
+
x = self.args[0]
|
| 1257 |
+
if len(self.args) == 1:
|
| 1258 |
+
return x.is_zero
|
| 1259 |
+
else:
|
| 1260 |
+
return fuzzy_and([x.is_zero, self.args[1].is_zero])
|
| 1261 |
+
|
| 1262 |
+
|
| 1263 |
+
@cacheit
|
| 1264 |
+
def _log_atan_table():
|
| 1265 |
+
return {
|
| 1266 |
+
# first quadrant only
|
| 1267 |
+
sqrt(3): pi / 3,
|
| 1268 |
+
1: pi / 4,
|
| 1269 |
+
sqrt(5 - 2 * sqrt(5)): pi / 5,
|
| 1270 |
+
sqrt(2) * sqrt(5 - sqrt(5)) / (1 + sqrt(5)): pi / 5,
|
| 1271 |
+
sqrt(5 + 2 * sqrt(5)): pi * Rational(2, 5),
|
| 1272 |
+
sqrt(2) * sqrt(sqrt(5) + 5) / (-1 + sqrt(5)): pi * Rational(2, 5),
|
| 1273 |
+
sqrt(3) / 3: pi / 6,
|
| 1274 |
+
sqrt(2) - 1: pi / 8,
|
| 1275 |
+
sqrt(2 - sqrt(2)) / sqrt(sqrt(2) + 2): pi / 8,
|
| 1276 |
+
sqrt(2) + 1: pi * Rational(3, 8),
|
| 1277 |
+
sqrt(sqrt(2) + 2) / sqrt(2 - sqrt(2)): pi * Rational(3, 8),
|
| 1278 |
+
sqrt(1 - 2 * sqrt(5) / 5): pi / 10,
|
| 1279 |
+
(-sqrt(2) + sqrt(10)) / (2 * sqrt(sqrt(5) + 5)): pi / 10,
|
| 1280 |
+
sqrt(1 + 2 * sqrt(5) / 5): pi * Rational(3, 10),
|
| 1281 |
+
(sqrt(2) + sqrt(10)) / (2 * sqrt(5 - sqrt(5))): pi * Rational(3, 10),
|
| 1282 |
+
2 - sqrt(3): pi / 12,
|
| 1283 |
+
(-1 + sqrt(3)) / (1 + sqrt(3)): pi / 12,
|
| 1284 |
+
2 + sqrt(3): pi * Rational(5, 12),
|
| 1285 |
+
(1 + sqrt(3)) / (-1 + sqrt(3)): pi * Rational(5, 12)
|
| 1286 |
+
}
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/hyperbolic.py
ADDED
|
@@ -0,0 +1,2285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S, sympify, cacheit
|
| 2 |
+
from sympy.core.add import Add
|
| 3 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 4 |
+
from sympy.core.logic import fuzzy_or, fuzzy_and, fuzzy_not, FuzzyBool
|
| 5 |
+
from sympy.core.numbers import I, pi, Rational
|
| 6 |
+
from sympy.core.symbol import Dummy
|
| 7 |
+
from sympy.functions.combinatorial.factorials import (binomial, factorial,
|
| 8 |
+
RisingFactorial)
|
| 9 |
+
from sympy.functions.combinatorial.numbers import bernoulli, euler, nC
|
| 10 |
+
from sympy.functions.elementary.complexes import Abs, im, re
|
| 11 |
+
from sympy.functions.elementary.exponential import exp, log, match_real_imag
|
| 12 |
+
from sympy.functions.elementary.integers import floor
|
| 13 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 14 |
+
from sympy.functions.elementary.trigonometric import (
|
| 15 |
+
acos, acot, asin, atan, cos, cot, csc, sec, sin, tan,
|
| 16 |
+
_imaginary_unit_as_coefficient)
|
| 17 |
+
from sympy.polys.specialpolys import symmetric_poly
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def _rewrite_hyperbolics_as_exp(expr):
|
| 21 |
+
return expr.xreplace({h: h.rewrite(exp)
|
| 22 |
+
for h in expr.atoms(HyperbolicFunction)})
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@cacheit
|
| 26 |
+
def _acosh_table():
|
| 27 |
+
return {
|
| 28 |
+
I: log(I*(1 + sqrt(2))),
|
| 29 |
+
-I: log(-I*(1 + sqrt(2))),
|
| 30 |
+
S.Half: pi/3,
|
| 31 |
+
Rational(-1, 2): pi*Rational(2, 3),
|
| 32 |
+
sqrt(2)/2: pi/4,
|
| 33 |
+
-sqrt(2)/2: pi*Rational(3, 4),
|
| 34 |
+
1/sqrt(2): pi/4,
|
| 35 |
+
-1/sqrt(2): pi*Rational(3, 4),
|
| 36 |
+
sqrt(3)/2: pi/6,
|
| 37 |
+
-sqrt(3)/2: pi*Rational(5, 6),
|
| 38 |
+
(sqrt(3) - 1)/sqrt(2**3): pi*Rational(5, 12),
|
| 39 |
+
-(sqrt(3) - 1)/sqrt(2**3): pi*Rational(7, 12),
|
| 40 |
+
sqrt(2 + sqrt(2))/2: pi/8,
|
| 41 |
+
-sqrt(2 + sqrt(2))/2: pi*Rational(7, 8),
|
| 42 |
+
sqrt(2 - sqrt(2))/2: pi*Rational(3, 8),
|
| 43 |
+
-sqrt(2 - sqrt(2))/2: pi*Rational(5, 8),
|
| 44 |
+
(1 + sqrt(3))/(2*sqrt(2)): pi/12,
|
| 45 |
+
-(1 + sqrt(3))/(2*sqrt(2)): pi*Rational(11, 12),
|
| 46 |
+
(sqrt(5) + 1)/4: pi/5,
|
| 47 |
+
-(sqrt(5) + 1)/4: pi*Rational(4, 5)
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@cacheit
|
| 52 |
+
def _acsch_table():
|
| 53 |
+
return {
|
| 54 |
+
I: -pi / 2,
|
| 55 |
+
I*(sqrt(2) + sqrt(6)): -pi / 12,
|
| 56 |
+
I*(1 + sqrt(5)): -pi / 10,
|
| 57 |
+
I*2 / sqrt(2 - sqrt(2)): -pi / 8,
|
| 58 |
+
I*2: -pi / 6,
|
| 59 |
+
I*sqrt(2 + 2/sqrt(5)): -pi / 5,
|
| 60 |
+
I*sqrt(2): -pi / 4,
|
| 61 |
+
I*(sqrt(5)-1): -3*pi / 10,
|
| 62 |
+
I*2 / sqrt(3): -pi / 3,
|
| 63 |
+
I*2 / sqrt(2 + sqrt(2)): -3*pi / 8,
|
| 64 |
+
I*sqrt(2 - 2/sqrt(5)): -2*pi / 5,
|
| 65 |
+
I*(sqrt(6) - sqrt(2)): -5*pi / 12,
|
| 66 |
+
S(2): -I*log((1+sqrt(5))/2),
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
@cacheit
|
| 71 |
+
def _asech_table():
|
| 72 |
+
return {
|
| 73 |
+
I: - (pi*I / 2) + log(1 + sqrt(2)),
|
| 74 |
+
-I: (pi*I / 2) + log(1 + sqrt(2)),
|
| 75 |
+
(sqrt(6) - sqrt(2)): pi / 12,
|
| 76 |
+
(sqrt(2) - sqrt(6)): 11*pi / 12,
|
| 77 |
+
sqrt(2 - 2/sqrt(5)): pi / 10,
|
| 78 |
+
-sqrt(2 - 2/sqrt(5)): 9*pi / 10,
|
| 79 |
+
2 / sqrt(2 + sqrt(2)): pi / 8,
|
| 80 |
+
-2 / sqrt(2 + sqrt(2)): 7*pi / 8,
|
| 81 |
+
2 / sqrt(3): pi / 6,
|
| 82 |
+
-2 / sqrt(3): 5*pi / 6,
|
| 83 |
+
(sqrt(5) - 1): pi / 5,
|
| 84 |
+
(1 - sqrt(5)): 4*pi / 5,
|
| 85 |
+
sqrt(2): pi / 4,
|
| 86 |
+
-sqrt(2): 3*pi / 4,
|
| 87 |
+
sqrt(2 + 2/sqrt(5)): 3*pi / 10,
|
| 88 |
+
-sqrt(2 + 2/sqrt(5)): 7*pi / 10,
|
| 89 |
+
S(2): pi / 3,
|
| 90 |
+
-S(2): 2*pi / 3,
|
| 91 |
+
sqrt(2*(2 + sqrt(2))): 3*pi / 8,
|
| 92 |
+
-sqrt(2*(2 + sqrt(2))): 5*pi / 8,
|
| 93 |
+
(1 + sqrt(5)): 2*pi / 5,
|
| 94 |
+
(-1 - sqrt(5)): 3*pi / 5,
|
| 95 |
+
(sqrt(6) + sqrt(2)): 5*pi / 12,
|
| 96 |
+
(-sqrt(6) - sqrt(2)): 7*pi / 12,
|
| 97 |
+
I*S.Infinity: -pi*I / 2,
|
| 98 |
+
I*S.NegativeInfinity: pi*I / 2,
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
###############################################################################
|
| 102 |
+
########################### HYPERBOLIC FUNCTIONS ##############################
|
| 103 |
+
###############################################################################
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class HyperbolicFunction(DefinedFunction):
|
| 107 |
+
"""
|
| 108 |
+
Base class for hyperbolic functions.
|
| 109 |
+
|
| 110 |
+
See Also
|
| 111 |
+
========
|
| 112 |
+
|
| 113 |
+
sinh, cosh, tanh, coth
|
| 114 |
+
"""
|
| 115 |
+
|
| 116 |
+
unbranched = True
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def _peeloff_ipi(arg):
|
| 120 |
+
r"""
|
| 121 |
+
Split ARG into two parts, a "rest" and a multiple of $I\pi$.
|
| 122 |
+
This assumes ARG to be an ``Add``.
|
| 123 |
+
The multiple of $I\pi$ returned in the second position is always a ``Rational``.
|
| 124 |
+
|
| 125 |
+
Examples
|
| 126 |
+
========
|
| 127 |
+
|
| 128 |
+
>>> from sympy.functions.elementary.hyperbolic import _peeloff_ipi as peel
|
| 129 |
+
>>> from sympy import pi, I
|
| 130 |
+
>>> from sympy.abc import x, y
|
| 131 |
+
>>> peel(x + I*pi/2)
|
| 132 |
+
(x, 1/2)
|
| 133 |
+
>>> peel(x + I*2*pi/3 + I*pi*y)
|
| 134 |
+
(x + I*pi*y + I*pi/6, 1/2)
|
| 135 |
+
"""
|
| 136 |
+
ipi = pi*I
|
| 137 |
+
for a in Add.make_args(arg):
|
| 138 |
+
if a == ipi:
|
| 139 |
+
K = S.One
|
| 140 |
+
break
|
| 141 |
+
elif a.is_Mul:
|
| 142 |
+
K, p = a.as_two_terms()
|
| 143 |
+
if p == ipi and K.is_Rational:
|
| 144 |
+
break
|
| 145 |
+
else:
|
| 146 |
+
return arg, S.Zero
|
| 147 |
+
|
| 148 |
+
m1 = (K % S.Half)
|
| 149 |
+
m2 = K - m1
|
| 150 |
+
return arg - m2*ipi, m2
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
class sinh(HyperbolicFunction):
|
| 154 |
+
r"""
|
| 155 |
+
``sinh(x)`` is the hyperbolic sine of ``x``.
|
| 156 |
+
|
| 157 |
+
The hyperbolic sine function is $\frac{e^x - e^{-x}}{2}$.
|
| 158 |
+
|
| 159 |
+
Examples
|
| 160 |
+
========
|
| 161 |
+
|
| 162 |
+
>>> from sympy import sinh
|
| 163 |
+
>>> from sympy.abc import x
|
| 164 |
+
>>> sinh(x)
|
| 165 |
+
sinh(x)
|
| 166 |
+
|
| 167 |
+
See Also
|
| 168 |
+
========
|
| 169 |
+
|
| 170 |
+
cosh, tanh, asinh
|
| 171 |
+
"""
|
| 172 |
+
|
| 173 |
+
def fdiff(self, argindex=1):
|
| 174 |
+
"""
|
| 175 |
+
Returns the first derivative of this function.
|
| 176 |
+
"""
|
| 177 |
+
if argindex == 1:
|
| 178 |
+
return cosh(self.args[0])
|
| 179 |
+
else:
|
| 180 |
+
raise ArgumentIndexError(self, argindex)
|
| 181 |
+
|
| 182 |
+
def inverse(self, argindex=1):
|
| 183 |
+
"""
|
| 184 |
+
Returns the inverse of this function.
|
| 185 |
+
"""
|
| 186 |
+
return asinh
|
| 187 |
+
|
| 188 |
+
@classmethod
|
| 189 |
+
def eval(cls, arg):
|
| 190 |
+
if arg.is_Number:
|
| 191 |
+
if arg is S.NaN:
|
| 192 |
+
return S.NaN
|
| 193 |
+
elif arg is S.Infinity:
|
| 194 |
+
return S.Infinity
|
| 195 |
+
elif arg is S.NegativeInfinity:
|
| 196 |
+
return S.NegativeInfinity
|
| 197 |
+
elif arg.is_zero:
|
| 198 |
+
return S.Zero
|
| 199 |
+
elif arg.is_negative:
|
| 200 |
+
return -cls(-arg)
|
| 201 |
+
else:
|
| 202 |
+
if arg is S.ComplexInfinity:
|
| 203 |
+
return S.NaN
|
| 204 |
+
|
| 205 |
+
i_coeff = _imaginary_unit_as_coefficient(arg)
|
| 206 |
+
|
| 207 |
+
if i_coeff is not None:
|
| 208 |
+
return I * sin(i_coeff)
|
| 209 |
+
else:
|
| 210 |
+
if arg.could_extract_minus_sign():
|
| 211 |
+
return -cls(-arg)
|
| 212 |
+
|
| 213 |
+
if arg.is_Add:
|
| 214 |
+
x, m = _peeloff_ipi(arg)
|
| 215 |
+
if m:
|
| 216 |
+
m = m*pi*I
|
| 217 |
+
return sinh(m)*cosh(x) + cosh(m)*sinh(x)
|
| 218 |
+
|
| 219 |
+
if arg.is_zero:
|
| 220 |
+
return S.Zero
|
| 221 |
+
|
| 222 |
+
if arg.func == asinh:
|
| 223 |
+
return arg.args[0]
|
| 224 |
+
|
| 225 |
+
if arg.func == acosh:
|
| 226 |
+
x = arg.args[0]
|
| 227 |
+
return sqrt(x - 1) * sqrt(x + 1)
|
| 228 |
+
|
| 229 |
+
if arg.func == atanh:
|
| 230 |
+
x = arg.args[0]
|
| 231 |
+
return x/sqrt(1 - x**2)
|
| 232 |
+
|
| 233 |
+
if arg.func == acoth:
|
| 234 |
+
x = arg.args[0]
|
| 235 |
+
return 1/(sqrt(x - 1) * sqrt(x + 1))
|
| 236 |
+
|
| 237 |
+
@staticmethod
|
| 238 |
+
@cacheit
|
| 239 |
+
def taylor_term(n, x, *previous_terms):
|
| 240 |
+
"""
|
| 241 |
+
Returns the next term in the Taylor series expansion.
|
| 242 |
+
"""
|
| 243 |
+
if n < 0 or n % 2 == 0:
|
| 244 |
+
return S.Zero
|
| 245 |
+
else:
|
| 246 |
+
x = sympify(x)
|
| 247 |
+
|
| 248 |
+
if len(previous_terms) > 2:
|
| 249 |
+
p = previous_terms[-2]
|
| 250 |
+
return p * x**2 / (n*(n - 1))
|
| 251 |
+
else:
|
| 252 |
+
return x**(n) / factorial(n)
|
| 253 |
+
|
| 254 |
+
def _eval_conjugate(self):
|
| 255 |
+
return self.func(self.args[0].conjugate())
|
| 256 |
+
|
| 257 |
+
def as_real_imag(self, deep=True, **hints):
|
| 258 |
+
"""
|
| 259 |
+
Returns this function as a complex coordinate.
|
| 260 |
+
"""
|
| 261 |
+
if self.args[0].is_extended_real:
|
| 262 |
+
if deep:
|
| 263 |
+
hints['complex'] = False
|
| 264 |
+
return (self.expand(deep, **hints), S.Zero)
|
| 265 |
+
else:
|
| 266 |
+
return (self, S.Zero)
|
| 267 |
+
if deep:
|
| 268 |
+
re, im = self.args[0].expand(deep, **hints).as_real_imag()
|
| 269 |
+
else:
|
| 270 |
+
re, im = self.args[0].as_real_imag()
|
| 271 |
+
return (sinh(re)*cos(im), cosh(re)*sin(im))
|
| 272 |
+
|
| 273 |
+
def _eval_expand_complex(self, deep=True, **hints):
|
| 274 |
+
re_part, im_part = self.as_real_imag(deep=deep, **hints)
|
| 275 |
+
return re_part + im_part*I
|
| 276 |
+
|
| 277 |
+
def _eval_expand_trig(self, deep=True, **hints):
|
| 278 |
+
if deep:
|
| 279 |
+
arg = self.args[0].expand(deep, **hints)
|
| 280 |
+
else:
|
| 281 |
+
arg = self.args[0]
|
| 282 |
+
x = None
|
| 283 |
+
if arg.is_Add: # TODO, implement more if deep stuff here
|
| 284 |
+
x, y = arg.as_two_terms()
|
| 285 |
+
else:
|
| 286 |
+
coeff, terms = arg.as_coeff_Mul(rational=True)
|
| 287 |
+
if coeff is not S.One and coeff.is_Integer and terms is not S.One:
|
| 288 |
+
x = terms
|
| 289 |
+
y = (coeff - 1)*x
|
| 290 |
+
if x is not None:
|
| 291 |
+
return (sinh(x)*cosh(y) + sinh(y)*cosh(x)).expand(trig=True)
|
| 292 |
+
return sinh(arg)
|
| 293 |
+
|
| 294 |
+
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
|
| 295 |
+
return (exp(arg) - exp(-arg)) / 2
|
| 296 |
+
|
| 297 |
+
def _eval_rewrite_as_exp(self, arg, **kwargs):
|
| 298 |
+
return (exp(arg) - exp(-arg)) / 2
|
| 299 |
+
|
| 300 |
+
def _eval_rewrite_as_sin(self, arg, **kwargs):
|
| 301 |
+
return -I * sin(I * arg)
|
| 302 |
+
|
| 303 |
+
def _eval_rewrite_as_csc(self, arg, **kwargs):
|
| 304 |
+
return -I / csc(I * arg)
|
| 305 |
+
|
| 306 |
+
def _eval_rewrite_as_cosh(self, arg, **kwargs):
|
| 307 |
+
return -I*cosh(arg + pi*I/2)
|
| 308 |
+
|
| 309 |
+
def _eval_rewrite_as_tanh(self, arg, **kwargs):
|
| 310 |
+
tanh_half = tanh(S.Half*arg)
|
| 311 |
+
return 2*tanh_half/(1 - tanh_half**2)
|
| 312 |
+
|
| 313 |
+
def _eval_rewrite_as_coth(self, arg, **kwargs):
|
| 314 |
+
coth_half = coth(S.Half*arg)
|
| 315 |
+
return 2*coth_half/(coth_half**2 - 1)
|
| 316 |
+
|
| 317 |
+
def _eval_rewrite_as_csch(self, arg, **kwargs):
|
| 318 |
+
return 1 / csch(arg)
|
| 319 |
+
|
| 320 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 321 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 322 |
+
arg0 = arg.subs(x, 0)
|
| 323 |
+
|
| 324 |
+
if arg0 is S.NaN:
|
| 325 |
+
arg0 = arg.limit(x, 0, dir='-' if cdir.is_negative else '+')
|
| 326 |
+
if arg0.is_zero:
|
| 327 |
+
return arg
|
| 328 |
+
elif arg0.is_finite:
|
| 329 |
+
return self.func(arg0)
|
| 330 |
+
else:
|
| 331 |
+
return self
|
| 332 |
+
|
| 333 |
+
def _eval_is_real(self):
|
| 334 |
+
arg = self.args[0]
|
| 335 |
+
if arg.is_real:
|
| 336 |
+
return True
|
| 337 |
+
|
| 338 |
+
# if `im` is of the form n*pi
|
| 339 |
+
# else, check if it is a number
|
| 340 |
+
re, im = arg.as_real_imag()
|
| 341 |
+
return (im%pi).is_zero
|
| 342 |
+
|
| 343 |
+
def _eval_is_extended_real(self):
|
| 344 |
+
if self.args[0].is_extended_real:
|
| 345 |
+
return True
|
| 346 |
+
|
| 347 |
+
def _eval_is_positive(self):
|
| 348 |
+
if self.args[0].is_extended_real:
|
| 349 |
+
return self.args[0].is_positive
|
| 350 |
+
|
| 351 |
+
def _eval_is_negative(self):
|
| 352 |
+
if self.args[0].is_extended_real:
|
| 353 |
+
return self.args[0].is_negative
|
| 354 |
+
|
| 355 |
+
def _eval_is_finite(self):
|
| 356 |
+
arg = self.args[0]
|
| 357 |
+
return arg.is_finite
|
| 358 |
+
|
| 359 |
+
def _eval_is_zero(self):
|
| 360 |
+
rest, ipi_mult = _peeloff_ipi(self.args[0])
|
| 361 |
+
if rest.is_zero:
|
| 362 |
+
return ipi_mult.is_integer
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
class cosh(HyperbolicFunction):
|
| 366 |
+
r"""
|
| 367 |
+
``cosh(x)`` is the hyperbolic cosine of ``x``.
|
| 368 |
+
|
| 369 |
+
The hyperbolic cosine function is $\frac{e^x + e^{-x}}{2}$.
|
| 370 |
+
|
| 371 |
+
Examples
|
| 372 |
+
========
|
| 373 |
+
|
| 374 |
+
>>> from sympy import cosh
|
| 375 |
+
>>> from sympy.abc import x
|
| 376 |
+
>>> cosh(x)
|
| 377 |
+
cosh(x)
|
| 378 |
+
|
| 379 |
+
See Also
|
| 380 |
+
========
|
| 381 |
+
|
| 382 |
+
sinh, tanh, acosh
|
| 383 |
+
"""
|
| 384 |
+
|
| 385 |
+
def fdiff(self, argindex=1):
|
| 386 |
+
if argindex == 1:
|
| 387 |
+
return sinh(self.args[0])
|
| 388 |
+
else:
|
| 389 |
+
raise ArgumentIndexError(self, argindex)
|
| 390 |
+
|
| 391 |
+
@classmethod
|
| 392 |
+
def eval(cls, arg):
|
| 393 |
+
from sympy.functions.elementary.trigonometric import cos
|
| 394 |
+
if arg.is_Number:
|
| 395 |
+
if arg is S.NaN:
|
| 396 |
+
return S.NaN
|
| 397 |
+
elif arg is S.Infinity:
|
| 398 |
+
return S.Infinity
|
| 399 |
+
elif arg is S.NegativeInfinity:
|
| 400 |
+
return S.Infinity
|
| 401 |
+
elif arg.is_zero:
|
| 402 |
+
return S.One
|
| 403 |
+
elif arg.is_negative:
|
| 404 |
+
return cls(-arg)
|
| 405 |
+
else:
|
| 406 |
+
if arg is S.ComplexInfinity:
|
| 407 |
+
return S.NaN
|
| 408 |
+
|
| 409 |
+
i_coeff = _imaginary_unit_as_coefficient(arg)
|
| 410 |
+
|
| 411 |
+
if i_coeff is not None:
|
| 412 |
+
return cos(i_coeff)
|
| 413 |
+
else:
|
| 414 |
+
if arg.could_extract_minus_sign():
|
| 415 |
+
return cls(-arg)
|
| 416 |
+
|
| 417 |
+
if arg.is_Add:
|
| 418 |
+
x, m = _peeloff_ipi(arg)
|
| 419 |
+
if m:
|
| 420 |
+
m = m*pi*I
|
| 421 |
+
return cosh(m)*cosh(x) + sinh(m)*sinh(x)
|
| 422 |
+
|
| 423 |
+
if arg.is_zero:
|
| 424 |
+
return S.One
|
| 425 |
+
|
| 426 |
+
if arg.func == asinh:
|
| 427 |
+
return sqrt(1 + arg.args[0]**2)
|
| 428 |
+
|
| 429 |
+
if arg.func == acosh:
|
| 430 |
+
return arg.args[0]
|
| 431 |
+
|
| 432 |
+
if arg.func == atanh:
|
| 433 |
+
return 1/sqrt(1 - arg.args[0]**2)
|
| 434 |
+
|
| 435 |
+
if arg.func == acoth:
|
| 436 |
+
x = arg.args[0]
|
| 437 |
+
return x/(sqrt(x - 1) * sqrt(x + 1))
|
| 438 |
+
|
| 439 |
+
@staticmethod
|
| 440 |
+
@cacheit
|
| 441 |
+
def taylor_term(n, x, *previous_terms):
|
| 442 |
+
if n < 0 or n % 2 == 1:
|
| 443 |
+
return S.Zero
|
| 444 |
+
else:
|
| 445 |
+
x = sympify(x)
|
| 446 |
+
|
| 447 |
+
if len(previous_terms) > 2:
|
| 448 |
+
p = previous_terms[-2]
|
| 449 |
+
return p * x**2 / (n*(n - 1))
|
| 450 |
+
else:
|
| 451 |
+
return x**(n)/factorial(n)
|
| 452 |
+
|
| 453 |
+
def _eval_conjugate(self):
|
| 454 |
+
return self.func(self.args[0].conjugate())
|
| 455 |
+
|
| 456 |
+
def as_real_imag(self, deep=True, **hints):
|
| 457 |
+
if self.args[0].is_extended_real:
|
| 458 |
+
if deep:
|
| 459 |
+
hints['complex'] = False
|
| 460 |
+
return (self.expand(deep, **hints), S.Zero)
|
| 461 |
+
else:
|
| 462 |
+
return (self, S.Zero)
|
| 463 |
+
if deep:
|
| 464 |
+
re, im = self.args[0].expand(deep, **hints).as_real_imag()
|
| 465 |
+
else:
|
| 466 |
+
re, im = self.args[0].as_real_imag()
|
| 467 |
+
|
| 468 |
+
return (cosh(re)*cos(im), sinh(re)*sin(im))
|
| 469 |
+
|
| 470 |
+
def _eval_expand_complex(self, deep=True, **hints):
|
| 471 |
+
re_part, im_part = self.as_real_imag(deep=deep, **hints)
|
| 472 |
+
return re_part + im_part*I
|
| 473 |
+
|
| 474 |
+
def _eval_expand_trig(self, deep=True, **hints):
|
| 475 |
+
if deep:
|
| 476 |
+
arg = self.args[0].expand(deep, **hints)
|
| 477 |
+
else:
|
| 478 |
+
arg = self.args[0]
|
| 479 |
+
x = None
|
| 480 |
+
if arg.is_Add: # TODO, implement more if deep stuff here
|
| 481 |
+
x, y = arg.as_two_terms()
|
| 482 |
+
else:
|
| 483 |
+
coeff, terms = arg.as_coeff_Mul(rational=True)
|
| 484 |
+
if coeff is not S.One and coeff.is_Integer and terms is not S.One:
|
| 485 |
+
x = terms
|
| 486 |
+
y = (coeff - 1)*x
|
| 487 |
+
if x is not None:
|
| 488 |
+
return (cosh(x)*cosh(y) + sinh(x)*sinh(y)).expand(trig=True)
|
| 489 |
+
return cosh(arg)
|
| 490 |
+
|
| 491 |
+
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
|
| 492 |
+
return (exp(arg) + exp(-arg)) / 2
|
| 493 |
+
|
| 494 |
+
def _eval_rewrite_as_exp(self, arg, **kwargs):
|
| 495 |
+
return (exp(arg) + exp(-arg)) / 2
|
| 496 |
+
|
| 497 |
+
def _eval_rewrite_as_cos(self, arg, **kwargs):
|
| 498 |
+
return cos(I * arg, evaluate=False)
|
| 499 |
+
|
| 500 |
+
def _eval_rewrite_as_sec(self, arg, **kwargs):
|
| 501 |
+
return 1 / sec(I * arg, evaluate=False)
|
| 502 |
+
|
| 503 |
+
def _eval_rewrite_as_sinh(self, arg, **kwargs):
|
| 504 |
+
return -I*sinh(arg + pi*I/2, evaluate=False)
|
| 505 |
+
|
| 506 |
+
def _eval_rewrite_as_tanh(self, arg, **kwargs):
|
| 507 |
+
tanh_half = tanh(S.Half*arg)**2
|
| 508 |
+
return (1 + tanh_half)/(1 - tanh_half)
|
| 509 |
+
|
| 510 |
+
def _eval_rewrite_as_coth(self, arg, **kwargs):
|
| 511 |
+
coth_half = coth(S.Half*arg)**2
|
| 512 |
+
return (coth_half + 1)/(coth_half - 1)
|
| 513 |
+
|
| 514 |
+
def _eval_rewrite_as_sech(self, arg, **kwargs):
|
| 515 |
+
return 1 / sech(arg)
|
| 516 |
+
|
| 517 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 518 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 519 |
+
arg0 = arg.subs(x, 0)
|
| 520 |
+
|
| 521 |
+
if arg0 is S.NaN:
|
| 522 |
+
arg0 = arg.limit(x, 0, dir='-' if cdir.is_negative else '+')
|
| 523 |
+
if arg0.is_zero:
|
| 524 |
+
return S.One
|
| 525 |
+
elif arg0.is_finite:
|
| 526 |
+
return self.func(arg0)
|
| 527 |
+
else:
|
| 528 |
+
return self
|
| 529 |
+
|
| 530 |
+
def _eval_is_real(self):
|
| 531 |
+
arg = self.args[0]
|
| 532 |
+
|
| 533 |
+
# `cosh(x)` is real for real OR purely imaginary `x`
|
| 534 |
+
if arg.is_real or arg.is_imaginary:
|
| 535 |
+
return True
|
| 536 |
+
|
| 537 |
+
# cosh(a+ib) = cos(b)*cosh(a) + i*sin(b)*sinh(a)
|
| 538 |
+
# the imaginary part can be an expression like n*pi
|
| 539 |
+
# if not, check if the imaginary part is a number
|
| 540 |
+
re, im = arg.as_real_imag()
|
| 541 |
+
return (im%pi).is_zero
|
| 542 |
+
|
| 543 |
+
def _eval_is_positive(self):
|
| 544 |
+
# cosh(x+I*y) = cos(y)*cosh(x) + I*sin(y)*sinh(x)
|
| 545 |
+
# cosh(z) is positive iff it is real and the real part is positive.
|
| 546 |
+
# So we need sin(y)*sinh(x) = 0 which gives x=0 or y=n*pi
|
| 547 |
+
# Case 1 (y=n*pi): cosh(z) = (-1)**n * cosh(x) -> positive for n even
|
| 548 |
+
# Case 2 (x=0): cosh(z) = cos(y) -> positive when cos(y) is positive
|
| 549 |
+
z = self.args[0]
|
| 550 |
+
|
| 551 |
+
x, y = z.as_real_imag()
|
| 552 |
+
ymod = y % (2*pi)
|
| 553 |
+
|
| 554 |
+
yzero = ymod.is_zero
|
| 555 |
+
# shortcut if ymod is zero
|
| 556 |
+
if yzero:
|
| 557 |
+
return True
|
| 558 |
+
|
| 559 |
+
xzero = x.is_zero
|
| 560 |
+
# shortcut x is not zero
|
| 561 |
+
if xzero is False:
|
| 562 |
+
return yzero
|
| 563 |
+
|
| 564 |
+
return fuzzy_or([
|
| 565 |
+
# Case 1:
|
| 566 |
+
yzero,
|
| 567 |
+
# Case 2:
|
| 568 |
+
fuzzy_and([
|
| 569 |
+
xzero,
|
| 570 |
+
fuzzy_or([ymod < pi/2, ymod > 3*pi/2])
|
| 571 |
+
])
|
| 572 |
+
])
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
def _eval_is_nonnegative(self):
|
| 576 |
+
z = self.args[0]
|
| 577 |
+
|
| 578 |
+
x, y = z.as_real_imag()
|
| 579 |
+
ymod = y % (2*pi)
|
| 580 |
+
|
| 581 |
+
yzero = ymod.is_zero
|
| 582 |
+
# shortcut if ymod is zero
|
| 583 |
+
if yzero:
|
| 584 |
+
return True
|
| 585 |
+
|
| 586 |
+
xzero = x.is_zero
|
| 587 |
+
# shortcut x is not zero
|
| 588 |
+
if xzero is False:
|
| 589 |
+
return yzero
|
| 590 |
+
|
| 591 |
+
return fuzzy_or([
|
| 592 |
+
# Case 1:
|
| 593 |
+
yzero,
|
| 594 |
+
# Case 2:
|
| 595 |
+
fuzzy_and([
|
| 596 |
+
xzero,
|
| 597 |
+
fuzzy_or([ymod <= pi/2, ymod >= 3*pi/2])
|
| 598 |
+
])
|
| 599 |
+
])
|
| 600 |
+
|
| 601 |
+
def _eval_is_finite(self):
|
| 602 |
+
arg = self.args[0]
|
| 603 |
+
return arg.is_finite
|
| 604 |
+
|
| 605 |
+
def _eval_is_zero(self):
|
| 606 |
+
rest, ipi_mult = _peeloff_ipi(self.args[0])
|
| 607 |
+
if ipi_mult and rest.is_zero:
|
| 608 |
+
return (ipi_mult - S.Half).is_integer
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
class tanh(HyperbolicFunction):
|
| 612 |
+
r"""
|
| 613 |
+
``tanh(x)`` is the hyperbolic tangent of ``x``.
|
| 614 |
+
|
| 615 |
+
The hyperbolic tangent function is $\frac{\sinh(x)}{\cosh(x)}$.
|
| 616 |
+
|
| 617 |
+
Examples
|
| 618 |
+
========
|
| 619 |
+
|
| 620 |
+
>>> from sympy import tanh
|
| 621 |
+
>>> from sympy.abc import x
|
| 622 |
+
>>> tanh(x)
|
| 623 |
+
tanh(x)
|
| 624 |
+
|
| 625 |
+
See Also
|
| 626 |
+
========
|
| 627 |
+
|
| 628 |
+
sinh, cosh, atanh
|
| 629 |
+
"""
|
| 630 |
+
|
| 631 |
+
def fdiff(self, argindex=1):
|
| 632 |
+
if argindex == 1:
|
| 633 |
+
return S.One - tanh(self.args[0])**2
|
| 634 |
+
else:
|
| 635 |
+
raise ArgumentIndexError(self, argindex)
|
| 636 |
+
|
| 637 |
+
def inverse(self, argindex=1):
|
| 638 |
+
"""
|
| 639 |
+
Returns the inverse of this function.
|
| 640 |
+
"""
|
| 641 |
+
return atanh
|
| 642 |
+
|
| 643 |
+
@classmethod
|
| 644 |
+
def eval(cls, arg):
|
| 645 |
+
if arg.is_Number:
|
| 646 |
+
if arg is S.NaN:
|
| 647 |
+
return S.NaN
|
| 648 |
+
elif arg is S.Infinity:
|
| 649 |
+
return S.One
|
| 650 |
+
elif arg is S.NegativeInfinity:
|
| 651 |
+
return S.NegativeOne
|
| 652 |
+
elif arg.is_zero:
|
| 653 |
+
return S.Zero
|
| 654 |
+
elif arg.is_negative:
|
| 655 |
+
return -cls(-arg)
|
| 656 |
+
else:
|
| 657 |
+
if arg is S.ComplexInfinity:
|
| 658 |
+
return S.NaN
|
| 659 |
+
|
| 660 |
+
i_coeff = _imaginary_unit_as_coefficient(arg)
|
| 661 |
+
|
| 662 |
+
if i_coeff is not None:
|
| 663 |
+
if i_coeff.could_extract_minus_sign():
|
| 664 |
+
return -I * tan(-i_coeff)
|
| 665 |
+
return I * tan(i_coeff)
|
| 666 |
+
else:
|
| 667 |
+
if arg.could_extract_minus_sign():
|
| 668 |
+
return -cls(-arg)
|
| 669 |
+
|
| 670 |
+
if arg.is_Add:
|
| 671 |
+
x, m = _peeloff_ipi(arg)
|
| 672 |
+
if m:
|
| 673 |
+
tanhm = tanh(m*pi*I)
|
| 674 |
+
if tanhm is S.ComplexInfinity:
|
| 675 |
+
return coth(x)
|
| 676 |
+
else: # tanhm == 0
|
| 677 |
+
return tanh(x)
|
| 678 |
+
|
| 679 |
+
if arg.is_zero:
|
| 680 |
+
return S.Zero
|
| 681 |
+
|
| 682 |
+
if arg.func == asinh:
|
| 683 |
+
x = arg.args[0]
|
| 684 |
+
return x/sqrt(1 + x**2)
|
| 685 |
+
|
| 686 |
+
if arg.func == acosh:
|
| 687 |
+
x = arg.args[0]
|
| 688 |
+
return sqrt(x - 1) * sqrt(x + 1) / x
|
| 689 |
+
|
| 690 |
+
if arg.func == atanh:
|
| 691 |
+
return arg.args[0]
|
| 692 |
+
|
| 693 |
+
if arg.func == acoth:
|
| 694 |
+
return 1/arg.args[0]
|
| 695 |
+
|
| 696 |
+
@staticmethod
|
| 697 |
+
@cacheit
|
| 698 |
+
def taylor_term(n, x, *previous_terms):
|
| 699 |
+
if n < 0 or n % 2 == 0:
|
| 700 |
+
return S.Zero
|
| 701 |
+
else:
|
| 702 |
+
x = sympify(x)
|
| 703 |
+
|
| 704 |
+
a = 2**(n + 1)
|
| 705 |
+
|
| 706 |
+
B = bernoulli(n + 1)
|
| 707 |
+
F = factorial(n + 1)
|
| 708 |
+
|
| 709 |
+
return a*(a - 1) * B/F * x**n
|
| 710 |
+
|
| 711 |
+
def _eval_conjugate(self):
|
| 712 |
+
return self.func(self.args[0].conjugate())
|
| 713 |
+
|
| 714 |
+
def as_real_imag(self, deep=True, **hints):
|
| 715 |
+
if self.args[0].is_extended_real:
|
| 716 |
+
if deep:
|
| 717 |
+
hints['complex'] = False
|
| 718 |
+
return (self.expand(deep, **hints), S.Zero)
|
| 719 |
+
else:
|
| 720 |
+
return (self, S.Zero)
|
| 721 |
+
if deep:
|
| 722 |
+
re, im = self.args[0].expand(deep, **hints).as_real_imag()
|
| 723 |
+
else:
|
| 724 |
+
re, im = self.args[0].as_real_imag()
|
| 725 |
+
denom = sinh(re)**2 + cos(im)**2
|
| 726 |
+
return (sinh(re)*cosh(re)/denom, sin(im)*cos(im)/denom)
|
| 727 |
+
|
| 728 |
+
def _eval_expand_trig(self, **hints):
|
| 729 |
+
arg = self.args[0]
|
| 730 |
+
if arg.is_Add:
|
| 731 |
+
n = len(arg.args)
|
| 732 |
+
TX = [tanh(x, evaluate=False)._eval_expand_trig()
|
| 733 |
+
for x in arg.args]
|
| 734 |
+
p = [0, 0] # [den, num]
|
| 735 |
+
for i in range(n + 1):
|
| 736 |
+
p[i % 2] += symmetric_poly(i, TX)
|
| 737 |
+
return p[1]/p[0]
|
| 738 |
+
elif arg.is_Mul:
|
| 739 |
+
coeff, terms = arg.as_coeff_Mul()
|
| 740 |
+
if coeff.is_Integer and coeff > 1:
|
| 741 |
+
T = tanh(terms)
|
| 742 |
+
n = [nC(range(coeff), k)*T**k for k in range(1, coeff + 1, 2)]
|
| 743 |
+
d = [nC(range(coeff), k)*T**k for k in range(0, coeff + 1, 2)]
|
| 744 |
+
return Add(*n)/Add(*d)
|
| 745 |
+
return tanh(arg)
|
| 746 |
+
|
| 747 |
+
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
|
| 748 |
+
neg_exp, pos_exp = exp(-arg), exp(arg)
|
| 749 |
+
return (pos_exp - neg_exp)/(pos_exp + neg_exp)
|
| 750 |
+
|
| 751 |
+
def _eval_rewrite_as_exp(self, arg, **kwargs):
|
| 752 |
+
neg_exp, pos_exp = exp(-arg), exp(arg)
|
| 753 |
+
return (pos_exp - neg_exp)/(pos_exp + neg_exp)
|
| 754 |
+
|
| 755 |
+
def _eval_rewrite_as_tan(self, arg, **kwargs):
|
| 756 |
+
return -I * tan(I * arg, evaluate=False)
|
| 757 |
+
|
| 758 |
+
def _eval_rewrite_as_cot(self, arg, **kwargs):
|
| 759 |
+
return -I / cot(I * arg, evaluate=False)
|
| 760 |
+
|
| 761 |
+
def _eval_rewrite_as_sinh(self, arg, **kwargs):
|
| 762 |
+
return I*sinh(arg)/sinh(pi*I/2 - arg, evaluate=False)
|
| 763 |
+
|
| 764 |
+
def _eval_rewrite_as_cosh(self, arg, **kwargs):
|
| 765 |
+
return I*cosh(pi*I/2 - arg, evaluate=False)/cosh(arg)
|
| 766 |
+
|
| 767 |
+
def _eval_rewrite_as_coth(self, arg, **kwargs):
|
| 768 |
+
return 1/coth(arg)
|
| 769 |
+
|
| 770 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 771 |
+
from sympy.series.order import Order
|
| 772 |
+
arg = self.args[0].as_leading_term(x)
|
| 773 |
+
|
| 774 |
+
if x in arg.free_symbols and Order(1, x).contains(arg):
|
| 775 |
+
return arg
|
| 776 |
+
else:
|
| 777 |
+
return self.func(arg)
|
| 778 |
+
|
| 779 |
+
def _eval_is_real(self):
|
| 780 |
+
arg = self.args[0]
|
| 781 |
+
if arg.is_real:
|
| 782 |
+
return True
|
| 783 |
+
|
| 784 |
+
re, im = arg.as_real_imag()
|
| 785 |
+
|
| 786 |
+
# if denom = 0, tanh(arg) = zoo
|
| 787 |
+
if re == 0 and im % pi == pi/2:
|
| 788 |
+
return None
|
| 789 |
+
|
| 790 |
+
# check if im is of the form n*pi/2 to make sin(2*im) = 0
|
| 791 |
+
# if not, im could be a number, return False in that case
|
| 792 |
+
return (im % (pi/2)).is_zero
|
| 793 |
+
|
| 794 |
+
def _eval_is_extended_real(self):
|
| 795 |
+
if self.args[0].is_extended_real:
|
| 796 |
+
return True
|
| 797 |
+
|
| 798 |
+
def _eval_is_positive(self):
|
| 799 |
+
if self.args[0].is_extended_real:
|
| 800 |
+
return self.args[0].is_positive
|
| 801 |
+
|
| 802 |
+
def _eval_is_negative(self):
|
| 803 |
+
if self.args[0].is_extended_real:
|
| 804 |
+
return self.args[0].is_negative
|
| 805 |
+
|
| 806 |
+
def _eval_is_finite(self):
|
| 807 |
+
arg = self.args[0]
|
| 808 |
+
|
| 809 |
+
re, im = arg.as_real_imag()
|
| 810 |
+
denom = cos(im)**2 + sinh(re)**2
|
| 811 |
+
if denom == 0:
|
| 812 |
+
return False
|
| 813 |
+
elif denom.is_number:
|
| 814 |
+
return True
|
| 815 |
+
if arg.is_extended_real:
|
| 816 |
+
return True
|
| 817 |
+
|
| 818 |
+
def _eval_is_zero(self):
|
| 819 |
+
arg = self.args[0]
|
| 820 |
+
if arg.is_zero:
|
| 821 |
+
return True
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
class coth(HyperbolicFunction):
|
| 825 |
+
r"""
|
| 826 |
+
``coth(x)`` is the hyperbolic cotangent of ``x``.
|
| 827 |
+
|
| 828 |
+
The hyperbolic cotangent function is $\frac{\cosh(x)}{\sinh(x)}$.
|
| 829 |
+
|
| 830 |
+
Examples
|
| 831 |
+
========
|
| 832 |
+
|
| 833 |
+
>>> from sympy import coth
|
| 834 |
+
>>> from sympy.abc import x
|
| 835 |
+
>>> coth(x)
|
| 836 |
+
coth(x)
|
| 837 |
+
|
| 838 |
+
See Also
|
| 839 |
+
========
|
| 840 |
+
|
| 841 |
+
sinh, cosh, acoth
|
| 842 |
+
"""
|
| 843 |
+
|
| 844 |
+
def fdiff(self, argindex=1):
|
| 845 |
+
if argindex == 1:
|
| 846 |
+
return -1/sinh(self.args[0])**2
|
| 847 |
+
else:
|
| 848 |
+
raise ArgumentIndexError(self, argindex)
|
| 849 |
+
|
| 850 |
+
def inverse(self, argindex=1):
|
| 851 |
+
"""
|
| 852 |
+
Returns the inverse of this function.
|
| 853 |
+
"""
|
| 854 |
+
return acoth
|
| 855 |
+
|
| 856 |
+
@classmethod
|
| 857 |
+
def eval(cls, arg):
|
| 858 |
+
if arg.is_Number:
|
| 859 |
+
if arg is S.NaN:
|
| 860 |
+
return S.NaN
|
| 861 |
+
elif arg is S.Infinity:
|
| 862 |
+
return S.One
|
| 863 |
+
elif arg is S.NegativeInfinity:
|
| 864 |
+
return S.NegativeOne
|
| 865 |
+
elif arg.is_zero:
|
| 866 |
+
return S.ComplexInfinity
|
| 867 |
+
elif arg.is_negative:
|
| 868 |
+
return -cls(-arg)
|
| 869 |
+
else:
|
| 870 |
+
if arg is S.ComplexInfinity:
|
| 871 |
+
return S.NaN
|
| 872 |
+
|
| 873 |
+
i_coeff = _imaginary_unit_as_coefficient(arg)
|
| 874 |
+
|
| 875 |
+
if i_coeff is not None:
|
| 876 |
+
if i_coeff.could_extract_minus_sign():
|
| 877 |
+
return I * cot(-i_coeff)
|
| 878 |
+
return -I * cot(i_coeff)
|
| 879 |
+
else:
|
| 880 |
+
if arg.could_extract_minus_sign():
|
| 881 |
+
return -cls(-arg)
|
| 882 |
+
|
| 883 |
+
if arg.is_Add:
|
| 884 |
+
x, m = _peeloff_ipi(arg)
|
| 885 |
+
if m:
|
| 886 |
+
cothm = coth(m*pi*I)
|
| 887 |
+
if cothm is S.ComplexInfinity:
|
| 888 |
+
return coth(x)
|
| 889 |
+
else: # cothm == 0
|
| 890 |
+
return tanh(x)
|
| 891 |
+
|
| 892 |
+
if arg.is_zero:
|
| 893 |
+
return S.ComplexInfinity
|
| 894 |
+
|
| 895 |
+
if arg.func == asinh:
|
| 896 |
+
x = arg.args[0]
|
| 897 |
+
return sqrt(1 + x**2)/x
|
| 898 |
+
|
| 899 |
+
if arg.func == acosh:
|
| 900 |
+
x = arg.args[0]
|
| 901 |
+
return x/(sqrt(x - 1) * sqrt(x + 1))
|
| 902 |
+
|
| 903 |
+
if arg.func == atanh:
|
| 904 |
+
return 1/arg.args[0]
|
| 905 |
+
|
| 906 |
+
if arg.func == acoth:
|
| 907 |
+
return arg.args[0]
|
| 908 |
+
|
| 909 |
+
@staticmethod
|
| 910 |
+
@cacheit
|
| 911 |
+
def taylor_term(n, x, *previous_terms):
|
| 912 |
+
if n == 0:
|
| 913 |
+
return 1 / sympify(x)
|
| 914 |
+
elif n < 0 or n % 2 == 0:
|
| 915 |
+
return S.Zero
|
| 916 |
+
else:
|
| 917 |
+
x = sympify(x)
|
| 918 |
+
|
| 919 |
+
B = bernoulli(n + 1)
|
| 920 |
+
F = factorial(n + 1)
|
| 921 |
+
|
| 922 |
+
return 2**(n + 1) * B/F * x**n
|
| 923 |
+
|
| 924 |
+
def _eval_conjugate(self):
|
| 925 |
+
return self.func(self.args[0].conjugate())
|
| 926 |
+
|
| 927 |
+
def as_real_imag(self, deep=True, **hints):
|
| 928 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 929 |
+
if self.args[0].is_extended_real:
|
| 930 |
+
if deep:
|
| 931 |
+
hints['complex'] = False
|
| 932 |
+
return (self.expand(deep, **hints), S.Zero)
|
| 933 |
+
else:
|
| 934 |
+
return (self, S.Zero)
|
| 935 |
+
if deep:
|
| 936 |
+
re, im = self.args[0].expand(deep, **hints).as_real_imag()
|
| 937 |
+
else:
|
| 938 |
+
re, im = self.args[0].as_real_imag()
|
| 939 |
+
denom = sinh(re)**2 + sin(im)**2
|
| 940 |
+
return (sinh(re)*cosh(re)/denom, -sin(im)*cos(im)/denom)
|
| 941 |
+
|
| 942 |
+
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
|
| 943 |
+
neg_exp, pos_exp = exp(-arg), exp(arg)
|
| 944 |
+
return (pos_exp + neg_exp)/(pos_exp - neg_exp)
|
| 945 |
+
|
| 946 |
+
def _eval_rewrite_as_exp(self, arg, **kwargs):
|
| 947 |
+
neg_exp, pos_exp = exp(-arg), exp(arg)
|
| 948 |
+
return (pos_exp + neg_exp)/(pos_exp - neg_exp)
|
| 949 |
+
|
| 950 |
+
def _eval_rewrite_as_sinh(self, arg, **kwargs):
|
| 951 |
+
return -I*sinh(pi*I/2 - arg, evaluate=False)/sinh(arg)
|
| 952 |
+
|
| 953 |
+
def _eval_rewrite_as_cosh(self, arg, **kwargs):
|
| 954 |
+
return -I*cosh(arg)/cosh(pi*I/2 - arg, evaluate=False)
|
| 955 |
+
|
| 956 |
+
def _eval_rewrite_as_tanh(self, arg, **kwargs):
|
| 957 |
+
return 1/tanh(arg)
|
| 958 |
+
|
| 959 |
+
def _eval_is_positive(self):
|
| 960 |
+
if self.args[0].is_extended_real:
|
| 961 |
+
return self.args[0].is_positive
|
| 962 |
+
|
| 963 |
+
def _eval_is_negative(self):
|
| 964 |
+
if self.args[0].is_extended_real:
|
| 965 |
+
return self.args[0].is_negative
|
| 966 |
+
|
| 967 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 968 |
+
from sympy.series.order import Order
|
| 969 |
+
arg = self.args[0].as_leading_term(x)
|
| 970 |
+
|
| 971 |
+
if x in arg.free_symbols and Order(1, x).contains(arg):
|
| 972 |
+
return 1/arg
|
| 973 |
+
else:
|
| 974 |
+
return self.func(arg)
|
| 975 |
+
|
| 976 |
+
def _eval_expand_trig(self, **hints):
|
| 977 |
+
arg = self.args[0]
|
| 978 |
+
if arg.is_Add:
|
| 979 |
+
CX = [coth(x, evaluate=False)._eval_expand_trig() for x in arg.args]
|
| 980 |
+
p = [[], []]
|
| 981 |
+
n = len(arg.args)
|
| 982 |
+
for i in range(n, -1, -1):
|
| 983 |
+
p[(n - i) % 2].append(symmetric_poly(i, CX))
|
| 984 |
+
return Add(*p[0])/Add(*p[1])
|
| 985 |
+
elif arg.is_Mul:
|
| 986 |
+
coeff, x = arg.as_coeff_Mul(rational=True)
|
| 987 |
+
if coeff.is_Integer and coeff > 1:
|
| 988 |
+
c = coth(x, evaluate=False)
|
| 989 |
+
p = [[], []]
|
| 990 |
+
for i in range(coeff, -1, -1):
|
| 991 |
+
p[(coeff - i) % 2].append(binomial(coeff, i)*c**i)
|
| 992 |
+
return Add(*p[0])/Add(*p[1])
|
| 993 |
+
return coth(arg)
|
| 994 |
+
|
| 995 |
+
|
| 996 |
+
class ReciprocalHyperbolicFunction(HyperbolicFunction):
|
| 997 |
+
"""Base class for reciprocal functions of hyperbolic functions. """
|
| 998 |
+
|
| 999 |
+
#To be defined in class
|
| 1000 |
+
_reciprocal_of = None
|
| 1001 |
+
_is_even: FuzzyBool = None
|
| 1002 |
+
_is_odd: FuzzyBool = None
|
| 1003 |
+
|
| 1004 |
+
@classmethod
|
| 1005 |
+
def eval(cls, arg):
|
| 1006 |
+
if arg.could_extract_minus_sign():
|
| 1007 |
+
if cls._is_even:
|
| 1008 |
+
return cls(-arg)
|
| 1009 |
+
if cls._is_odd:
|
| 1010 |
+
return -cls(-arg)
|
| 1011 |
+
|
| 1012 |
+
t = cls._reciprocal_of.eval(arg)
|
| 1013 |
+
if hasattr(arg, 'inverse') and arg.inverse() == cls:
|
| 1014 |
+
return arg.args[0]
|
| 1015 |
+
return 1/t if t is not None else t
|
| 1016 |
+
|
| 1017 |
+
def _call_reciprocal(self, method_name, *args, **kwargs):
|
| 1018 |
+
# Calls method_name on _reciprocal_of
|
| 1019 |
+
o = self._reciprocal_of(self.args[0])
|
| 1020 |
+
return getattr(o, method_name)(*args, **kwargs)
|
| 1021 |
+
|
| 1022 |
+
def _calculate_reciprocal(self, method_name, *args, **kwargs):
|
| 1023 |
+
# If calling method_name on _reciprocal_of returns a value != None
|
| 1024 |
+
# then return the reciprocal of that value
|
| 1025 |
+
t = self._call_reciprocal(method_name, *args, **kwargs)
|
| 1026 |
+
return 1/t if t is not None else t
|
| 1027 |
+
|
| 1028 |
+
def _rewrite_reciprocal(self, method_name, arg):
|
| 1029 |
+
# Special handling for rewrite functions. If reciprocal rewrite returns
|
| 1030 |
+
# unmodified expression, then return None
|
| 1031 |
+
t = self._call_reciprocal(method_name, arg)
|
| 1032 |
+
if t is not None and t != self._reciprocal_of(arg):
|
| 1033 |
+
return 1/t
|
| 1034 |
+
|
| 1035 |
+
def _eval_rewrite_as_exp(self, arg, **kwargs):
|
| 1036 |
+
return self._rewrite_reciprocal("_eval_rewrite_as_exp", arg)
|
| 1037 |
+
|
| 1038 |
+
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
|
| 1039 |
+
return self._rewrite_reciprocal("_eval_rewrite_as_tractable", arg)
|
| 1040 |
+
|
| 1041 |
+
def _eval_rewrite_as_tanh(self, arg, **kwargs):
|
| 1042 |
+
return self._rewrite_reciprocal("_eval_rewrite_as_tanh", arg)
|
| 1043 |
+
|
| 1044 |
+
def _eval_rewrite_as_coth(self, arg, **kwargs):
|
| 1045 |
+
return self._rewrite_reciprocal("_eval_rewrite_as_coth", arg)
|
| 1046 |
+
|
| 1047 |
+
def as_real_imag(self, deep = True, **hints):
|
| 1048 |
+
return (1 / self._reciprocal_of(self.args[0])).as_real_imag(deep, **hints)
|
| 1049 |
+
|
| 1050 |
+
def _eval_conjugate(self):
|
| 1051 |
+
return self.func(self.args[0].conjugate())
|
| 1052 |
+
|
| 1053 |
+
def _eval_expand_complex(self, deep=True, **hints):
|
| 1054 |
+
re_part, im_part = self.as_real_imag(deep=True, **hints)
|
| 1055 |
+
return re_part + I*im_part
|
| 1056 |
+
|
| 1057 |
+
def _eval_expand_trig(self, **hints):
|
| 1058 |
+
return self._calculate_reciprocal("_eval_expand_trig", **hints)
|
| 1059 |
+
|
| 1060 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1061 |
+
return (1/self._reciprocal_of(self.args[0]))._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1062 |
+
|
| 1063 |
+
def _eval_is_extended_real(self):
|
| 1064 |
+
return self._reciprocal_of(self.args[0]).is_extended_real
|
| 1065 |
+
|
| 1066 |
+
def _eval_is_finite(self):
|
| 1067 |
+
return (1/self._reciprocal_of(self.args[0])).is_finite
|
| 1068 |
+
|
| 1069 |
+
|
| 1070 |
+
class csch(ReciprocalHyperbolicFunction):
|
| 1071 |
+
r"""
|
| 1072 |
+
``csch(x)`` is the hyperbolic cosecant of ``x``.
|
| 1073 |
+
|
| 1074 |
+
The hyperbolic cosecant function is $\frac{2}{e^x - e^{-x}}$
|
| 1075 |
+
|
| 1076 |
+
Examples
|
| 1077 |
+
========
|
| 1078 |
+
|
| 1079 |
+
>>> from sympy import csch
|
| 1080 |
+
>>> from sympy.abc import x
|
| 1081 |
+
>>> csch(x)
|
| 1082 |
+
csch(x)
|
| 1083 |
+
|
| 1084 |
+
See Also
|
| 1085 |
+
========
|
| 1086 |
+
|
| 1087 |
+
sinh, cosh, tanh, sech, asinh, acosh
|
| 1088 |
+
"""
|
| 1089 |
+
|
| 1090 |
+
_reciprocal_of = sinh
|
| 1091 |
+
_is_odd = True
|
| 1092 |
+
|
| 1093 |
+
def fdiff(self, argindex=1):
|
| 1094 |
+
"""
|
| 1095 |
+
Returns the first derivative of this function
|
| 1096 |
+
"""
|
| 1097 |
+
if argindex == 1:
|
| 1098 |
+
return -coth(self.args[0]) * csch(self.args[0])
|
| 1099 |
+
else:
|
| 1100 |
+
raise ArgumentIndexError(self, argindex)
|
| 1101 |
+
|
| 1102 |
+
@staticmethod
|
| 1103 |
+
@cacheit
|
| 1104 |
+
def taylor_term(n, x, *previous_terms):
|
| 1105 |
+
"""
|
| 1106 |
+
Returns the next term in the Taylor series expansion
|
| 1107 |
+
"""
|
| 1108 |
+
if n == 0:
|
| 1109 |
+
return 1/sympify(x)
|
| 1110 |
+
elif n < 0 or n % 2 == 0:
|
| 1111 |
+
return S.Zero
|
| 1112 |
+
else:
|
| 1113 |
+
x = sympify(x)
|
| 1114 |
+
|
| 1115 |
+
B = bernoulli(n + 1)
|
| 1116 |
+
F = factorial(n + 1)
|
| 1117 |
+
|
| 1118 |
+
return 2 * (1 - 2**n) * B/F * x**n
|
| 1119 |
+
|
| 1120 |
+
def _eval_rewrite_as_sin(self, arg, **kwargs):
|
| 1121 |
+
return I / sin(I * arg, evaluate=False)
|
| 1122 |
+
|
| 1123 |
+
def _eval_rewrite_as_csc(self, arg, **kwargs):
|
| 1124 |
+
return I * csc(I * arg, evaluate=False)
|
| 1125 |
+
|
| 1126 |
+
def _eval_rewrite_as_cosh(self, arg, **kwargs):
|
| 1127 |
+
return I / cosh(arg + I * pi / 2, evaluate=False)
|
| 1128 |
+
|
| 1129 |
+
def _eval_rewrite_as_sinh(self, arg, **kwargs):
|
| 1130 |
+
return 1 / sinh(arg)
|
| 1131 |
+
|
| 1132 |
+
def _eval_is_positive(self):
|
| 1133 |
+
if self.args[0].is_extended_real:
|
| 1134 |
+
return self.args[0].is_positive
|
| 1135 |
+
|
| 1136 |
+
def _eval_is_negative(self):
|
| 1137 |
+
if self.args[0].is_extended_real:
|
| 1138 |
+
return self.args[0].is_negative
|
| 1139 |
+
|
| 1140 |
+
|
| 1141 |
+
class sech(ReciprocalHyperbolicFunction):
|
| 1142 |
+
r"""
|
| 1143 |
+
``sech(x)`` is the hyperbolic secant of ``x``.
|
| 1144 |
+
|
| 1145 |
+
The hyperbolic secant function is $\frac{2}{e^x + e^{-x}}$
|
| 1146 |
+
|
| 1147 |
+
Examples
|
| 1148 |
+
========
|
| 1149 |
+
|
| 1150 |
+
>>> from sympy import sech
|
| 1151 |
+
>>> from sympy.abc import x
|
| 1152 |
+
>>> sech(x)
|
| 1153 |
+
sech(x)
|
| 1154 |
+
|
| 1155 |
+
See Also
|
| 1156 |
+
========
|
| 1157 |
+
|
| 1158 |
+
sinh, cosh, tanh, coth, csch, asinh, acosh
|
| 1159 |
+
"""
|
| 1160 |
+
|
| 1161 |
+
_reciprocal_of = cosh
|
| 1162 |
+
_is_even = True
|
| 1163 |
+
|
| 1164 |
+
def fdiff(self, argindex=1):
|
| 1165 |
+
if argindex == 1:
|
| 1166 |
+
return - tanh(self.args[0])*sech(self.args[0])
|
| 1167 |
+
else:
|
| 1168 |
+
raise ArgumentIndexError(self, argindex)
|
| 1169 |
+
|
| 1170 |
+
@staticmethod
|
| 1171 |
+
@cacheit
|
| 1172 |
+
def taylor_term(n, x, *previous_terms):
|
| 1173 |
+
if n < 0 or n % 2 == 1:
|
| 1174 |
+
return S.Zero
|
| 1175 |
+
else:
|
| 1176 |
+
x = sympify(x)
|
| 1177 |
+
return euler(n) / factorial(n) * x**(n)
|
| 1178 |
+
|
| 1179 |
+
def _eval_rewrite_as_cos(self, arg, **kwargs):
|
| 1180 |
+
return 1 / cos(I * arg, evaluate=False)
|
| 1181 |
+
|
| 1182 |
+
def _eval_rewrite_as_sec(self, arg, **kwargs):
|
| 1183 |
+
return sec(I * arg, evaluate=False)
|
| 1184 |
+
|
| 1185 |
+
def _eval_rewrite_as_sinh(self, arg, **kwargs):
|
| 1186 |
+
return I / sinh(arg + I * pi /2, evaluate=False)
|
| 1187 |
+
|
| 1188 |
+
def _eval_rewrite_as_cosh(self, arg, **kwargs):
|
| 1189 |
+
return 1 / cosh(arg)
|
| 1190 |
+
|
| 1191 |
+
def _eval_is_positive(self):
|
| 1192 |
+
if self.args[0].is_extended_real:
|
| 1193 |
+
return True
|
| 1194 |
+
|
| 1195 |
+
|
| 1196 |
+
###############################################################################
|
| 1197 |
+
############################# HYPERBOLIC INVERSES #############################
|
| 1198 |
+
###############################################################################
|
| 1199 |
+
|
| 1200 |
+
class InverseHyperbolicFunction(DefinedFunction):
|
| 1201 |
+
"""Base class for inverse hyperbolic functions."""
|
| 1202 |
+
|
| 1203 |
+
pass
|
| 1204 |
+
|
| 1205 |
+
|
| 1206 |
+
class asinh(InverseHyperbolicFunction):
|
| 1207 |
+
"""
|
| 1208 |
+
``asinh(x)`` is the inverse hyperbolic sine of ``x``.
|
| 1209 |
+
|
| 1210 |
+
The inverse hyperbolic sine function.
|
| 1211 |
+
|
| 1212 |
+
Examples
|
| 1213 |
+
========
|
| 1214 |
+
|
| 1215 |
+
>>> from sympy import asinh
|
| 1216 |
+
>>> from sympy.abc import x
|
| 1217 |
+
>>> asinh(x).diff(x)
|
| 1218 |
+
1/sqrt(x**2 + 1)
|
| 1219 |
+
>>> asinh(1)
|
| 1220 |
+
log(1 + sqrt(2))
|
| 1221 |
+
|
| 1222 |
+
See Also
|
| 1223 |
+
========
|
| 1224 |
+
|
| 1225 |
+
acosh, atanh, sinh
|
| 1226 |
+
"""
|
| 1227 |
+
|
| 1228 |
+
def fdiff(self, argindex=1):
|
| 1229 |
+
if argindex == 1:
|
| 1230 |
+
return 1/sqrt(self.args[0]**2 + 1)
|
| 1231 |
+
else:
|
| 1232 |
+
raise ArgumentIndexError(self, argindex)
|
| 1233 |
+
|
| 1234 |
+
@classmethod
|
| 1235 |
+
def eval(cls, arg):
|
| 1236 |
+
if arg.is_Number:
|
| 1237 |
+
if arg is S.NaN:
|
| 1238 |
+
return S.NaN
|
| 1239 |
+
elif arg is S.Infinity:
|
| 1240 |
+
return S.Infinity
|
| 1241 |
+
elif arg is S.NegativeInfinity:
|
| 1242 |
+
return S.NegativeInfinity
|
| 1243 |
+
elif arg.is_zero:
|
| 1244 |
+
return S.Zero
|
| 1245 |
+
elif arg is S.One:
|
| 1246 |
+
return log(sqrt(2) + 1)
|
| 1247 |
+
elif arg is S.NegativeOne:
|
| 1248 |
+
return log(sqrt(2) - 1)
|
| 1249 |
+
elif arg.is_negative:
|
| 1250 |
+
return -cls(-arg)
|
| 1251 |
+
else:
|
| 1252 |
+
if arg is S.ComplexInfinity:
|
| 1253 |
+
return S.ComplexInfinity
|
| 1254 |
+
|
| 1255 |
+
if arg.is_zero:
|
| 1256 |
+
return S.Zero
|
| 1257 |
+
|
| 1258 |
+
i_coeff = _imaginary_unit_as_coefficient(arg)
|
| 1259 |
+
|
| 1260 |
+
if i_coeff is not None:
|
| 1261 |
+
return I * asin(i_coeff)
|
| 1262 |
+
else:
|
| 1263 |
+
if arg.could_extract_minus_sign():
|
| 1264 |
+
return -cls(-arg)
|
| 1265 |
+
|
| 1266 |
+
if isinstance(arg, sinh) and arg.args[0].is_number:
|
| 1267 |
+
z = arg.args[0]
|
| 1268 |
+
if z.is_real:
|
| 1269 |
+
return z
|
| 1270 |
+
r, i = match_real_imag(z)
|
| 1271 |
+
if r is not None and i is not None:
|
| 1272 |
+
f = floor((i + pi/2)/pi)
|
| 1273 |
+
m = z - I*pi*f
|
| 1274 |
+
even = f.is_even
|
| 1275 |
+
if even is True:
|
| 1276 |
+
return m
|
| 1277 |
+
elif even is False:
|
| 1278 |
+
return -m
|
| 1279 |
+
|
| 1280 |
+
@staticmethod
|
| 1281 |
+
@cacheit
|
| 1282 |
+
def taylor_term(n, x, *previous_terms):
|
| 1283 |
+
if n < 0 or n % 2 == 0:
|
| 1284 |
+
return S.Zero
|
| 1285 |
+
else:
|
| 1286 |
+
x = sympify(x)
|
| 1287 |
+
if len(previous_terms) >= 2 and n > 2:
|
| 1288 |
+
p = previous_terms[-2]
|
| 1289 |
+
return -p * (n - 2)**2/(n*(n - 1)) * x**2
|
| 1290 |
+
else:
|
| 1291 |
+
k = (n - 1) // 2
|
| 1292 |
+
R = RisingFactorial(S.Half, k)
|
| 1293 |
+
F = factorial(k)
|
| 1294 |
+
return S.NegativeOne**k * R / F * x**n / n
|
| 1295 |
+
|
| 1296 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1297 |
+
arg = self.args[0]
|
| 1298 |
+
x0 = arg.subs(x, 0).cancel()
|
| 1299 |
+
if x0.is_zero:
|
| 1300 |
+
return arg.as_leading_term(x)
|
| 1301 |
+
|
| 1302 |
+
if x0 is S.NaN:
|
| 1303 |
+
expr = self.func(arg.as_leading_term(x))
|
| 1304 |
+
if expr.is_finite:
|
| 1305 |
+
return expr
|
| 1306 |
+
else:
|
| 1307 |
+
return self
|
| 1308 |
+
|
| 1309 |
+
# Handling branch points
|
| 1310 |
+
if x0 in (-I, I, S.ComplexInfinity):
|
| 1311 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1312 |
+
# Handling points lying on branch cuts (-I*oo, -I) U (I, I*oo)
|
| 1313 |
+
if (1 + x0**2).is_negative:
|
| 1314 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1315 |
+
if re(ndir).is_positive:
|
| 1316 |
+
if im(x0).is_negative:
|
| 1317 |
+
return -self.func(x0) - I*pi
|
| 1318 |
+
elif re(ndir).is_negative:
|
| 1319 |
+
if im(x0).is_positive:
|
| 1320 |
+
return -self.func(x0) + I*pi
|
| 1321 |
+
else:
|
| 1322 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1323 |
+
return self.func(x0)
|
| 1324 |
+
|
| 1325 |
+
def _eval_nseries(self, x, n, logx, cdir=0): # asinh
|
| 1326 |
+
arg = self.args[0]
|
| 1327 |
+
arg0 = arg.subs(x, 0)
|
| 1328 |
+
|
| 1329 |
+
# Handling branch points
|
| 1330 |
+
if arg0 in (I, -I):
|
| 1331 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1332 |
+
|
| 1333 |
+
res = super()._eval_nseries(x, n=n, logx=logx)
|
| 1334 |
+
if arg0 is S.ComplexInfinity:
|
| 1335 |
+
return res
|
| 1336 |
+
|
| 1337 |
+
# Handling points lying on branch cuts (-I*oo, -I) U (I, I*oo)
|
| 1338 |
+
if (1 + arg0**2).is_negative:
|
| 1339 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1340 |
+
if re(ndir).is_positive:
|
| 1341 |
+
if im(arg0).is_negative:
|
| 1342 |
+
return -res - I*pi
|
| 1343 |
+
elif re(ndir).is_negative:
|
| 1344 |
+
if im(arg0).is_positive:
|
| 1345 |
+
return -res + I*pi
|
| 1346 |
+
else:
|
| 1347 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1348 |
+
return res
|
| 1349 |
+
|
| 1350 |
+
def _eval_rewrite_as_log(self, x, **kwargs):
|
| 1351 |
+
return log(x + sqrt(x**2 + 1))
|
| 1352 |
+
|
| 1353 |
+
_eval_rewrite_as_tractable = _eval_rewrite_as_log
|
| 1354 |
+
|
| 1355 |
+
def _eval_rewrite_as_atanh(self, x, **kwargs):
|
| 1356 |
+
return atanh(x/sqrt(1 + x**2))
|
| 1357 |
+
|
| 1358 |
+
def _eval_rewrite_as_acosh(self, x, **kwargs):
|
| 1359 |
+
ix = I*x
|
| 1360 |
+
return I*(sqrt(1 - ix)/sqrt(ix - 1) * acosh(ix) - pi/2)
|
| 1361 |
+
|
| 1362 |
+
def _eval_rewrite_as_asin(self, x, **kwargs):
|
| 1363 |
+
return -I * asin(I * x, evaluate=False)
|
| 1364 |
+
|
| 1365 |
+
def _eval_rewrite_as_acos(self, x, **kwargs):
|
| 1366 |
+
return I * acos(I * x, evaluate=False) - I*pi/2
|
| 1367 |
+
|
| 1368 |
+
def inverse(self, argindex=1):
|
| 1369 |
+
"""
|
| 1370 |
+
Returns the inverse of this function.
|
| 1371 |
+
"""
|
| 1372 |
+
return sinh
|
| 1373 |
+
|
| 1374 |
+
def _eval_is_zero(self):
|
| 1375 |
+
return self.args[0].is_zero
|
| 1376 |
+
|
| 1377 |
+
def _eval_is_extended_real(self):
|
| 1378 |
+
return self.args[0].is_extended_real
|
| 1379 |
+
|
| 1380 |
+
def _eval_is_finite(self):
|
| 1381 |
+
return self.args[0].is_finite
|
| 1382 |
+
|
| 1383 |
+
|
| 1384 |
+
class acosh(InverseHyperbolicFunction):
|
| 1385 |
+
"""
|
| 1386 |
+
``acosh(x)`` is the inverse hyperbolic cosine of ``x``.
|
| 1387 |
+
|
| 1388 |
+
The inverse hyperbolic cosine function.
|
| 1389 |
+
|
| 1390 |
+
Examples
|
| 1391 |
+
========
|
| 1392 |
+
|
| 1393 |
+
>>> from sympy import acosh
|
| 1394 |
+
>>> from sympy.abc import x
|
| 1395 |
+
>>> acosh(x).diff(x)
|
| 1396 |
+
1/(sqrt(x - 1)*sqrt(x + 1))
|
| 1397 |
+
>>> acosh(1)
|
| 1398 |
+
0
|
| 1399 |
+
|
| 1400 |
+
See Also
|
| 1401 |
+
========
|
| 1402 |
+
|
| 1403 |
+
asinh, atanh, cosh
|
| 1404 |
+
"""
|
| 1405 |
+
|
| 1406 |
+
def fdiff(self, argindex=1):
|
| 1407 |
+
if argindex == 1:
|
| 1408 |
+
arg = self.args[0]
|
| 1409 |
+
return 1/(sqrt(arg - 1)*sqrt(arg + 1))
|
| 1410 |
+
else:
|
| 1411 |
+
raise ArgumentIndexError(self, argindex)
|
| 1412 |
+
|
| 1413 |
+
@classmethod
|
| 1414 |
+
def eval(cls, arg):
|
| 1415 |
+
if arg.is_Number:
|
| 1416 |
+
if arg is S.NaN:
|
| 1417 |
+
return S.NaN
|
| 1418 |
+
elif arg is S.Infinity:
|
| 1419 |
+
return S.Infinity
|
| 1420 |
+
elif arg is S.NegativeInfinity:
|
| 1421 |
+
return S.Infinity
|
| 1422 |
+
elif arg.is_zero:
|
| 1423 |
+
return pi*I / 2
|
| 1424 |
+
elif arg is S.One:
|
| 1425 |
+
return S.Zero
|
| 1426 |
+
elif arg is S.NegativeOne:
|
| 1427 |
+
return pi*I
|
| 1428 |
+
|
| 1429 |
+
if arg.is_number:
|
| 1430 |
+
cst_table = _acosh_table()
|
| 1431 |
+
|
| 1432 |
+
if arg in cst_table:
|
| 1433 |
+
if arg.is_extended_real:
|
| 1434 |
+
return cst_table[arg]*I
|
| 1435 |
+
return cst_table[arg]
|
| 1436 |
+
|
| 1437 |
+
if arg is S.ComplexInfinity:
|
| 1438 |
+
return S.ComplexInfinity
|
| 1439 |
+
if arg == I*S.Infinity:
|
| 1440 |
+
return S.Infinity + I*pi/2
|
| 1441 |
+
if arg == -I*S.Infinity:
|
| 1442 |
+
return S.Infinity - I*pi/2
|
| 1443 |
+
|
| 1444 |
+
if arg.is_zero:
|
| 1445 |
+
return pi*I*S.Half
|
| 1446 |
+
|
| 1447 |
+
if isinstance(arg, cosh) and arg.args[0].is_number:
|
| 1448 |
+
z = arg.args[0]
|
| 1449 |
+
if z.is_real:
|
| 1450 |
+
return Abs(z)
|
| 1451 |
+
r, i = match_real_imag(z)
|
| 1452 |
+
if r is not None and i is not None:
|
| 1453 |
+
f = floor(i/pi)
|
| 1454 |
+
m = z - I*pi*f
|
| 1455 |
+
even = f.is_even
|
| 1456 |
+
if even is True:
|
| 1457 |
+
if r.is_nonnegative:
|
| 1458 |
+
return m
|
| 1459 |
+
elif r.is_negative:
|
| 1460 |
+
return -m
|
| 1461 |
+
elif even is False:
|
| 1462 |
+
m -= I*pi
|
| 1463 |
+
if r.is_nonpositive:
|
| 1464 |
+
return -m
|
| 1465 |
+
elif r.is_positive:
|
| 1466 |
+
return m
|
| 1467 |
+
|
| 1468 |
+
@staticmethod
|
| 1469 |
+
@cacheit
|
| 1470 |
+
def taylor_term(n, x, *previous_terms):
|
| 1471 |
+
if n == 0:
|
| 1472 |
+
return I*pi/2
|
| 1473 |
+
elif n < 0 or n % 2 == 0:
|
| 1474 |
+
return S.Zero
|
| 1475 |
+
else:
|
| 1476 |
+
x = sympify(x)
|
| 1477 |
+
if len(previous_terms) >= 2 and n > 2:
|
| 1478 |
+
p = previous_terms[-2]
|
| 1479 |
+
return p * (n - 2)**2/(n*(n - 1)) * x**2
|
| 1480 |
+
else:
|
| 1481 |
+
k = (n - 1) // 2
|
| 1482 |
+
R = RisingFactorial(S.Half, k)
|
| 1483 |
+
F = factorial(k)
|
| 1484 |
+
return -R / F * I * x**n / n
|
| 1485 |
+
|
| 1486 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1487 |
+
arg = self.args[0]
|
| 1488 |
+
x0 = arg.subs(x, 0).cancel()
|
| 1489 |
+
# Handling branch points
|
| 1490 |
+
if x0 in (-S.One, S.Zero, S.One, S.ComplexInfinity):
|
| 1491 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1492 |
+
|
| 1493 |
+
if x0 is S.NaN:
|
| 1494 |
+
expr = self.func(arg.as_leading_term(x))
|
| 1495 |
+
if expr.is_finite:
|
| 1496 |
+
return expr
|
| 1497 |
+
else:
|
| 1498 |
+
return self
|
| 1499 |
+
|
| 1500 |
+
# Handling points lying on branch cuts (-oo, 1)
|
| 1501 |
+
if (x0 - 1).is_negative:
|
| 1502 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1503 |
+
if im(ndir).is_negative:
|
| 1504 |
+
if (x0 + 1).is_negative:
|
| 1505 |
+
return self.func(x0) - 2*I*pi
|
| 1506 |
+
return -self.func(x0)
|
| 1507 |
+
elif not im(ndir).is_positive:
|
| 1508 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1509 |
+
return self.func(x0)
|
| 1510 |
+
|
| 1511 |
+
def _eval_nseries(self, x, n, logx, cdir=0): # acosh
|
| 1512 |
+
arg = self.args[0]
|
| 1513 |
+
arg0 = arg.subs(x, 0)
|
| 1514 |
+
|
| 1515 |
+
# Handling branch points
|
| 1516 |
+
if arg0 in (S.One, S.NegativeOne):
|
| 1517 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1518 |
+
|
| 1519 |
+
res = super()._eval_nseries(x, n=n, logx=logx)
|
| 1520 |
+
if arg0 is S.ComplexInfinity:
|
| 1521 |
+
return res
|
| 1522 |
+
|
| 1523 |
+
# Handling points lying on branch cuts (-oo, 1)
|
| 1524 |
+
if (arg0 - 1).is_negative:
|
| 1525 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1526 |
+
if im(ndir).is_negative:
|
| 1527 |
+
if (arg0 + 1).is_negative:
|
| 1528 |
+
return res - 2*I*pi
|
| 1529 |
+
return -res
|
| 1530 |
+
elif not im(ndir).is_positive:
|
| 1531 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1532 |
+
return res
|
| 1533 |
+
|
| 1534 |
+
def _eval_rewrite_as_log(self, x, **kwargs):
|
| 1535 |
+
return log(x + sqrt(x + 1) * sqrt(x - 1))
|
| 1536 |
+
|
| 1537 |
+
_eval_rewrite_as_tractable = _eval_rewrite_as_log
|
| 1538 |
+
|
| 1539 |
+
def _eval_rewrite_as_acos(self, x, **kwargs):
|
| 1540 |
+
return sqrt(x - 1)/sqrt(1 - x) * acos(x)
|
| 1541 |
+
|
| 1542 |
+
def _eval_rewrite_as_asin(self, x, **kwargs):
|
| 1543 |
+
return sqrt(x - 1)/sqrt(1 - x) * (pi/2 - asin(x))
|
| 1544 |
+
|
| 1545 |
+
def _eval_rewrite_as_asinh(self, x, **kwargs):
|
| 1546 |
+
return sqrt(x - 1)/sqrt(1 - x) * (pi/2 + I*asinh(I*x, evaluate=False))
|
| 1547 |
+
|
| 1548 |
+
def _eval_rewrite_as_atanh(self, x, **kwargs):
|
| 1549 |
+
sxm1 = sqrt(x - 1)
|
| 1550 |
+
s1mx = sqrt(1 - x)
|
| 1551 |
+
sx2m1 = sqrt(x**2 - 1)
|
| 1552 |
+
return (pi/2*sxm1/s1mx*(1 - x * sqrt(1/x**2)) +
|
| 1553 |
+
sxm1*sqrt(x + 1)/sx2m1 * atanh(sx2m1/x))
|
| 1554 |
+
|
| 1555 |
+
def inverse(self, argindex=1):
|
| 1556 |
+
"""
|
| 1557 |
+
Returns the inverse of this function.
|
| 1558 |
+
"""
|
| 1559 |
+
return cosh
|
| 1560 |
+
|
| 1561 |
+
def _eval_is_zero(self):
|
| 1562 |
+
if (self.args[0] - 1).is_zero:
|
| 1563 |
+
return True
|
| 1564 |
+
|
| 1565 |
+
def _eval_is_extended_real(self):
|
| 1566 |
+
return fuzzy_and([self.args[0].is_extended_real, (self.args[0] - 1).is_extended_nonnegative])
|
| 1567 |
+
|
| 1568 |
+
def _eval_is_finite(self):
|
| 1569 |
+
return self.args[0].is_finite
|
| 1570 |
+
|
| 1571 |
+
|
| 1572 |
+
class atanh(InverseHyperbolicFunction):
|
| 1573 |
+
"""
|
| 1574 |
+
``atanh(x)`` is the inverse hyperbolic tangent of ``x``.
|
| 1575 |
+
|
| 1576 |
+
The inverse hyperbolic tangent function.
|
| 1577 |
+
|
| 1578 |
+
Examples
|
| 1579 |
+
========
|
| 1580 |
+
|
| 1581 |
+
>>> from sympy import atanh
|
| 1582 |
+
>>> from sympy.abc import x
|
| 1583 |
+
>>> atanh(x).diff(x)
|
| 1584 |
+
1/(1 - x**2)
|
| 1585 |
+
|
| 1586 |
+
See Also
|
| 1587 |
+
========
|
| 1588 |
+
|
| 1589 |
+
asinh, acosh, tanh
|
| 1590 |
+
"""
|
| 1591 |
+
|
| 1592 |
+
def fdiff(self, argindex=1):
|
| 1593 |
+
if argindex == 1:
|
| 1594 |
+
return 1/(1 - self.args[0]**2)
|
| 1595 |
+
else:
|
| 1596 |
+
raise ArgumentIndexError(self, argindex)
|
| 1597 |
+
|
| 1598 |
+
@classmethod
|
| 1599 |
+
def eval(cls, arg):
|
| 1600 |
+
if arg.is_Number:
|
| 1601 |
+
if arg is S.NaN:
|
| 1602 |
+
return S.NaN
|
| 1603 |
+
elif arg.is_zero:
|
| 1604 |
+
return S.Zero
|
| 1605 |
+
elif arg is S.One:
|
| 1606 |
+
return S.Infinity
|
| 1607 |
+
elif arg is S.NegativeOne:
|
| 1608 |
+
return S.NegativeInfinity
|
| 1609 |
+
elif arg is S.Infinity:
|
| 1610 |
+
return -I * atan(arg)
|
| 1611 |
+
elif arg is S.NegativeInfinity:
|
| 1612 |
+
return I * atan(-arg)
|
| 1613 |
+
elif arg.is_negative:
|
| 1614 |
+
return -cls(-arg)
|
| 1615 |
+
else:
|
| 1616 |
+
if arg is S.ComplexInfinity:
|
| 1617 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 1618 |
+
return I*AccumBounds(-pi/2, pi/2)
|
| 1619 |
+
|
| 1620 |
+
i_coeff = _imaginary_unit_as_coefficient(arg)
|
| 1621 |
+
|
| 1622 |
+
if i_coeff is not None:
|
| 1623 |
+
return I * atan(i_coeff)
|
| 1624 |
+
else:
|
| 1625 |
+
if arg.could_extract_minus_sign():
|
| 1626 |
+
return -cls(-arg)
|
| 1627 |
+
|
| 1628 |
+
if arg.is_zero:
|
| 1629 |
+
return S.Zero
|
| 1630 |
+
|
| 1631 |
+
if isinstance(arg, tanh) and arg.args[0].is_number:
|
| 1632 |
+
z = arg.args[0]
|
| 1633 |
+
if z.is_real:
|
| 1634 |
+
return z
|
| 1635 |
+
r, i = match_real_imag(z)
|
| 1636 |
+
if r is not None and i is not None:
|
| 1637 |
+
f = floor(2*i/pi)
|
| 1638 |
+
even = f.is_even
|
| 1639 |
+
m = z - I*f*pi/2
|
| 1640 |
+
if even is True:
|
| 1641 |
+
return m
|
| 1642 |
+
elif even is False:
|
| 1643 |
+
return m - I*pi/2
|
| 1644 |
+
|
| 1645 |
+
@staticmethod
|
| 1646 |
+
@cacheit
|
| 1647 |
+
def taylor_term(n, x, *previous_terms):
|
| 1648 |
+
if n < 0 or n % 2 == 0:
|
| 1649 |
+
return S.Zero
|
| 1650 |
+
else:
|
| 1651 |
+
x = sympify(x)
|
| 1652 |
+
return x**n / n
|
| 1653 |
+
|
| 1654 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1655 |
+
arg = self.args[0]
|
| 1656 |
+
x0 = arg.subs(x, 0).cancel()
|
| 1657 |
+
if x0.is_zero:
|
| 1658 |
+
return arg.as_leading_term(x)
|
| 1659 |
+
if x0 is S.NaN:
|
| 1660 |
+
expr = self.func(arg.as_leading_term(x))
|
| 1661 |
+
if expr.is_finite:
|
| 1662 |
+
return expr
|
| 1663 |
+
else:
|
| 1664 |
+
return self
|
| 1665 |
+
|
| 1666 |
+
# Handling branch points
|
| 1667 |
+
if x0 in (-S.One, S.One, S.ComplexInfinity):
|
| 1668 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1669 |
+
# Handling points lying on branch cuts (-oo, -1] U [1, oo)
|
| 1670 |
+
if (1 - x0**2).is_negative:
|
| 1671 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1672 |
+
if im(ndir).is_negative:
|
| 1673 |
+
if x0.is_negative:
|
| 1674 |
+
return self.func(x0) - I*pi
|
| 1675 |
+
elif im(ndir).is_positive:
|
| 1676 |
+
if x0.is_positive:
|
| 1677 |
+
return self.func(x0) + I*pi
|
| 1678 |
+
else:
|
| 1679 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1680 |
+
return self.func(x0)
|
| 1681 |
+
|
| 1682 |
+
def _eval_nseries(self, x, n, logx, cdir=0): # atanh
|
| 1683 |
+
arg = self.args[0]
|
| 1684 |
+
arg0 = arg.subs(x, 0)
|
| 1685 |
+
|
| 1686 |
+
# Handling branch points
|
| 1687 |
+
if arg0 in (S.One, S.NegativeOne):
|
| 1688 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1689 |
+
|
| 1690 |
+
res = super()._eval_nseries(x, n=n, logx=logx)
|
| 1691 |
+
if arg0 is S.ComplexInfinity:
|
| 1692 |
+
return res
|
| 1693 |
+
|
| 1694 |
+
# Handling points lying on branch cuts (-oo, -1] U [1, oo)
|
| 1695 |
+
if (1 - arg0**2).is_negative:
|
| 1696 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1697 |
+
if im(ndir).is_negative:
|
| 1698 |
+
if arg0.is_negative:
|
| 1699 |
+
return res - I*pi
|
| 1700 |
+
elif im(ndir).is_positive:
|
| 1701 |
+
if arg0.is_positive:
|
| 1702 |
+
return res + I*pi
|
| 1703 |
+
else:
|
| 1704 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1705 |
+
return res
|
| 1706 |
+
|
| 1707 |
+
def _eval_rewrite_as_log(self, x, **kwargs):
|
| 1708 |
+
return (log(1 + x) - log(1 - x)) / 2
|
| 1709 |
+
|
| 1710 |
+
_eval_rewrite_as_tractable = _eval_rewrite_as_log
|
| 1711 |
+
|
| 1712 |
+
def _eval_rewrite_as_asinh(self, x, **kwargs):
|
| 1713 |
+
f = sqrt(1/(x**2 - 1))
|
| 1714 |
+
return (pi*x/(2*sqrt(-x**2)) -
|
| 1715 |
+
sqrt(-x)*sqrt(1 - x**2)/sqrt(x)*f*asinh(f))
|
| 1716 |
+
|
| 1717 |
+
def _eval_is_zero(self):
|
| 1718 |
+
if self.args[0].is_zero:
|
| 1719 |
+
return True
|
| 1720 |
+
|
| 1721 |
+
def _eval_is_extended_real(self):
|
| 1722 |
+
return fuzzy_and([self.args[0].is_extended_real, (1 - self.args[0]).is_nonnegative, (self.args[0] + 1).is_nonnegative])
|
| 1723 |
+
|
| 1724 |
+
def _eval_is_finite(self):
|
| 1725 |
+
return fuzzy_not(fuzzy_or([(self.args[0] - 1).is_zero, (self.args[0] + 1).is_zero]))
|
| 1726 |
+
|
| 1727 |
+
def _eval_is_imaginary(self):
|
| 1728 |
+
return self.args[0].is_imaginary
|
| 1729 |
+
|
| 1730 |
+
def inverse(self, argindex=1):
|
| 1731 |
+
"""
|
| 1732 |
+
Returns the inverse of this function.
|
| 1733 |
+
"""
|
| 1734 |
+
return tanh
|
| 1735 |
+
|
| 1736 |
+
|
| 1737 |
+
class acoth(InverseHyperbolicFunction):
|
| 1738 |
+
"""
|
| 1739 |
+
``acoth(x)`` is the inverse hyperbolic cotangent of ``x``.
|
| 1740 |
+
|
| 1741 |
+
The inverse hyperbolic cotangent function.
|
| 1742 |
+
|
| 1743 |
+
Examples
|
| 1744 |
+
========
|
| 1745 |
+
|
| 1746 |
+
>>> from sympy import acoth
|
| 1747 |
+
>>> from sympy.abc import x
|
| 1748 |
+
>>> acoth(x).diff(x)
|
| 1749 |
+
1/(1 - x**2)
|
| 1750 |
+
|
| 1751 |
+
See Also
|
| 1752 |
+
========
|
| 1753 |
+
|
| 1754 |
+
asinh, acosh, coth
|
| 1755 |
+
"""
|
| 1756 |
+
|
| 1757 |
+
def fdiff(self, argindex=1):
|
| 1758 |
+
if argindex == 1:
|
| 1759 |
+
return 1/(1 - self.args[0]**2)
|
| 1760 |
+
else:
|
| 1761 |
+
raise ArgumentIndexError(self, argindex)
|
| 1762 |
+
|
| 1763 |
+
@classmethod
|
| 1764 |
+
def eval(cls, arg):
|
| 1765 |
+
if arg.is_Number:
|
| 1766 |
+
if arg is S.NaN:
|
| 1767 |
+
return S.NaN
|
| 1768 |
+
elif arg is S.Infinity:
|
| 1769 |
+
return S.Zero
|
| 1770 |
+
elif arg is S.NegativeInfinity:
|
| 1771 |
+
return S.Zero
|
| 1772 |
+
elif arg.is_zero:
|
| 1773 |
+
return pi*I / 2
|
| 1774 |
+
elif arg is S.One:
|
| 1775 |
+
return S.Infinity
|
| 1776 |
+
elif arg is S.NegativeOne:
|
| 1777 |
+
return S.NegativeInfinity
|
| 1778 |
+
elif arg.is_negative:
|
| 1779 |
+
return -cls(-arg)
|
| 1780 |
+
else:
|
| 1781 |
+
if arg is S.ComplexInfinity:
|
| 1782 |
+
return S.Zero
|
| 1783 |
+
|
| 1784 |
+
i_coeff = _imaginary_unit_as_coefficient(arg)
|
| 1785 |
+
|
| 1786 |
+
if i_coeff is not None:
|
| 1787 |
+
return -I * acot(i_coeff)
|
| 1788 |
+
else:
|
| 1789 |
+
if arg.could_extract_minus_sign():
|
| 1790 |
+
return -cls(-arg)
|
| 1791 |
+
|
| 1792 |
+
if arg.is_zero:
|
| 1793 |
+
return pi*I*S.Half
|
| 1794 |
+
|
| 1795 |
+
@staticmethod
|
| 1796 |
+
@cacheit
|
| 1797 |
+
def taylor_term(n, x, *previous_terms):
|
| 1798 |
+
if n == 0:
|
| 1799 |
+
return -I*pi/2
|
| 1800 |
+
elif n < 0 or n % 2 == 0:
|
| 1801 |
+
return S.Zero
|
| 1802 |
+
else:
|
| 1803 |
+
x = sympify(x)
|
| 1804 |
+
return x**n / n
|
| 1805 |
+
|
| 1806 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1807 |
+
arg = self.args[0]
|
| 1808 |
+
x0 = arg.subs(x, 0).cancel()
|
| 1809 |
+
if x0 is S.ComplexInfinity:
|
| 1810 |
+
return (1/arg).as_leading_term(x)
|
| 1811 |
+
if x0 is S.NaN:
|
| 1812 |
+
expr = self.func(arg.as_leading_term(x))
|
| 1813 |
+
if expr.is_finite:
|
| 1814 |
+
return expr
|
| 1815 |
+
else:
|
| 1816 |
+
return self
|
| 1817 |
+
|
| 1818 |
+
# Handling branch points
|
| 1819 |
+
if x0 in (-S.One, S.One, S.Zero):
|
| 1820 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1821 |
+
# Handling points lying on branch cuts [-1, 1]
|
| 1822 |
+
if x0.is_real and (1 - x0**2).is_positive:
|
| 1823 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1824 |
+
if im(ndir).is_negative:
|
| 1825 |
+
if x0.is_positive:
|
| 1826 |
+
return self.func(x0) + I*pi
|
| 1827 |
+
elif im(ndir).is_positive:
|
| 1828 |
+
if x0.is_negative:
|
| 1829 |
+
return self.func(x0) - I*pi
|
| 1830 |
+
else:
|
| 1831 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1832 |
+
return self.func(x0)
|
| 1833 |
+
|
| 1834 |
+
def _eval_nseries(self, x, n, logx, cdir=0): # acoth
|
| 1835 |
+
arg = self.args[0]
|
| 1836 |
+
arg0 = arg.subs(x, 0)
|
| 1837 |
+
|
| 1838 |
+
# Handling branch points
|
| 1839 |
+
if arg0 in (S.One, S.NegativeOne):
|
| 1840 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1841 |
+
|
| 1842 |
+
res = super()._eval_nseries(x, n=n, logx=logx)
|
| 1843 |
+
if arg0 is S.ComplexInfinity:
|
| 1844 |
+
return res
|
| 1845 |
+
|
| 1846 |
+
# Handling points lying on branch cuts [-1, 1]
|
| 1847 |
+
if arg0.is_real and (1 - arg0**2).is_positive:
|
| 1848 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1849 |
+
if im(ndir).is_negative:
|
| 1850 |
+
if arg0.is_positive:
|
| 1851 |
+
return res + I*pi
|
| 1852 |
+
elif im(ndir).is_positive:
|
| 1853 |
+
if arg0.is_negative:
|
| 1854 |
+
return res - I*pi
|
| 1855 |
+
else:
|
| 1856 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 1857 |
+
return res
|
| 1858 |
+
|
| 1859 |
+
def _eval_rewrite_as_log(self, x, **kwargs):
|
| 1860 |
+
return (log(1 + 1/x) - log(1 - 1/x)) / 2
|
| 1861 |
+
|
| 1862 |
+
_eval_rewrite_as_tractable = _eval_rewrite_as_log
|
| 1863 |
+
|
| 1864 |
+
def _eval_rewrite_as_atanh(self, x, **kwargs):
|
| 1865 |
+
return atanh(1/x)
|
| 1866 |
+
|
| 1867 |
+
def _eval_rewrite_as_asinh(self, x, **kwargs):
|
| 1868 |
+
return (pi*I/2*(sqrt((x - 1)/x)*sqrt(x/(x - 1)) - sqrt(1 + 1/x)*sqrt(x/(x + 1))) +
|
| 1869 |
+
x*sqrt(1/x**2)*asinh(sqrt(1/(x**2 - 1))))
|
| 1870 |
+
|
| 1871 |
+
def inverse(self, argindex=1):
|
| 1872 |
+
"""
|
| 1873 |
+
Returns the inverse of this function.
|
| 1874 |
+
"""
|
| 1875 |
+
return coth
|
| 1876 |
+
|
| 1877 |
+
def _eval_is_extended_real(self):
|
| 1878 |
+
return fuzzy_and([self.args[0].is_extended_real, fuzzy_or([(self.args[0] - 1).is_extended_nonnegative, (self.args[0] + 1).is_extended_nonpositive])])
|
| 1879 |
+
|
| 1880 |
+
def _eval_is_finite(self):
|
| 1881 |
+
return fuzzy_not(fuzzy_or([(self.args[0] - 1).is_zero, (self.args[0] + 1).is_zero]))
|
| 1882 |
+
|
| 1883 |
+
|
| 1884 |
+
class asech(InverseHyperbolicFunction):
|
| 1885 |
+
"""
|
| 1886 |
+
``asech(x)`` is the inverse hyperbolic secant of ``x``.
|
| 1887 |
+
|
| 1888 |
+
The inverse hyperbolic secant function.
|
| 1889 |
+
|
| 1890 |
+
Examples
|
| 1891 |
+
========
|
| 1892 |
+
|
| 1893 |
+
>>> from sympy import asech, sqrt, S
|
| 1894 |
+
>>> from sympy.abc import x
|
| 1895 |
+
>>> asech(x).diff(x)
|
| 1896 |
+
-1/(x*sqrt(1 - x**2))
|
| 1897 |
+
>>> asech(1).diff(x)
|
| 1898 |
+
0
|
| 1899 |
+
>>> asech(1)
|
| 1900 |
+
0
|
| 1901 |
+
>>> asech(S(2))
|
| 1902 |
+
I*pi/3
|
| 1903 |
+
>>> asech(-sqrt(2))
|
| 1904 |
+
3*I*pi/4
|
| 1905 |
+
>>> asech((sqrt(6) - sqrt(2)))
|
| 1906 |
+
I*pi/12
|
| 1907 |
+
|
| 1908 |
+
See Also
|
| 1909 |
+
========
|
| 1910 |
+
|
| 1911 |
+
asinh, atanh, cosh, acoth
|
| 1912 |
+
|
| 1913 |
+
References
|
| 1914 |
+
==========
|
| 1915 |
+
|
| 1916 |
+
.. [1] https://en.wikipedia.org/wiki/Hyperbolic_function
|
| 1917 |
+
.. [2] https://dlmf.nist.gov/4.37
|
| 1918 |
+
.. [3] https://functions.wolfram.com/ElementaryFunctions/ArcSech/
|
| 1919 |
+
|
| 1920 |
+
"""
|
| 1921 |
+
|
| 1922 |
+
def fdiff(self, argindex=1):
|
| 1923 |
+
if argindex == 1:
|
| 1924 |
+
z = self.args[0]
|
| 1925 |
+
return -1/(z*sqrt(1 - z**2))
|
| 1926 |
+
else:
|
| 1927 |
+
raise ArgumentIndexError(self, argindex)
|
| 1928 |
+
|
| 1929 |
+
@classmethod
|
| 1930 |
+
def eval(cls, arg):
|
| 1931 |
+
if arg.is_Number:
|
| 1932 |
+
if arg is S.NaN:
|
| 1933 |
+
return S.NaN
|
| 1934 |
+
elif arg is S.Infinity:
|
| 1935 |
+
return pi*I / 2
|
| 1936 |
+
elif arg is S.NegativeInfinity:
|
| 1937 |
+
return pi*I / 2
|
| 1938 |
+
elif arg.is_zero:
|
| 1939 |
+
return S.Infinity
|
| 1940 |
+
elif arg is S.One:
|
| 1941 |
+
return S.Zero
|
| 1942 |
+
elif arg is S.NegativeOne:
|
| 1943 |
+
return pi*I
|
| 1944 |
+
|
| 1945 |
+
if arg.is_number:
|
| 1946 |
+
cst_table = _asech_table()
|
| 1947 |
+
|
| 1948 |
+
if arg in cst_table:
|
| 1949 |
+
if arg.is_extended_real:
|
| 1950 |
+
return cst_table[arg]*I
|
| 1951 |
+
return cst_table[arg]
|
| 1952 |
+
|
| 1953 |
+
if arg is S.ComplexInfinity:
|
| 1954 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 1955 |
+
return I*AccumBounds(-pi/2, pi/2)
|
| 1956 |
+
|
| 1957 |
+
if arg.is_zero:
|
| 1958 |
+
return S.Infinity
|
| 1959 |
+
|
| 1960 |
+
@staticmethod
|
| 1961 |
+
@cacheit
|
| 1962 |
+
def taylor_term(n, x, *previous_terms):
|
| 1963 |
+
if n == 0:
|
| 1964 |
+
return log(2 / x)
|
| 1965 |
+
elif n < 0 or n % 2 == 1:
|
| 1966 |
+
return S.Zero
|
| 1967 |
+
else:
|
| 1968 |
+
x = sympify(x)
|
| 1969 |
+
if len(previous_terms) > 2 and n > 2:
|
| 1970 |
+
p = previous_terms[-2]
|
| 1971 |
+
return p * ((n - 1)*(n-2)) * x**2/(4 * (n//2)**2)
|
| 1972 |
+
else:
|
| 1973 |
+
k = n // 2
|
| 1974 |
+
R = RisingFactorial(S.Half, k) * n
|
| 1975 |
+
F = factorial(k) * n // 2 * n // 2
|
| 1976 |
+
return -1 * R / F * x**n / 4
|
| 1977 |
+
|
| 1978 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1979 |
+
arg = self.args[0]
|
| 1980 |
+
x0 = arg.subs(x, 0).cancel()
|
| 1981 |
+
# Handling branch points
|
| 1982 |
+
if x0 in (-S.One, S.Zero, S.One, S.ComplexInfinity):
|
| 1983 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1984 |
+
|
| 1985 |
+
if x0 is S.NaN:
|
| 1986 |
+
expr = self.func(arg.as_leading_term(x))
|
| 1987 |
+
if expr.is_finite:
|
| 1988 |
+
return expr
|
| 1989 |
+
else:
|
| 1990 |
+
return self
|
| 1991 |
+
|
| 1992 |
+
# Handling points lying on branch cuts (-oo, 0] U (1, oo)
|
| 1993 |
+
if x0.is_negative or (1 - x0).is_negative:
|
| 1994 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 1995 |
+
if im(ndir).is_positive:
|
| 1996 |
+
if x0.is_positive or (x0 + 1).is_negative:
|
| 1997 |
+
return -self.func(x0)
|
| 1998 |
+
return self.func(x0) - 2*I*pi
|
| 1999 |
+
elif not im(ndir).is_negative:
|
| 2000 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 2001 |
+
return self.func(x0)
|
| 2002 |
+
|
| 2003 |
+
def _eval_nseries(self, x, n, logx, cdir=0): # asech
|
| 2004 |
+
from sympy.series.order import O
|
| 2005 |
+
arg = self.args[0]
|
| 2006 |
+
arg0 = arg.subs(x, 0)
|
| 2007 |
+
|
| 2008 |
+
# Handling branch points
|
| 2009 |
+
if arg0 is S.One:
|
| 2010 |
+
t = Dummy('t', positive=True)
|
| 2011 |
+
ser = asech(S.One - t**2).rewrite(log).nseries(t, 0, 2*n)
|
| 2012 |
+
arg1 = S.One - self.args[0]
|
| 2013 |
+
f = arg1.as_leading_term(x)
|
| 2014 |
+
g = (arg1 - f)/ f
|
| 2015 |
+
if not g.is_meromorphic(x, 0): # cannot be expanded
|
| 2016 |
+
return O(1) if n == 0 else O(sqrt(x))
|
| 2017 |
+
res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
|
| 2018 |
+
res = (res1.removeO()*sqrt(f)).expand()
|
| 2019 |
+
return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
|
| 2020 |
+
|
| 2021 |
+
if arg0 is S.NegativeOne:
|
| 2022 |
+
t = Dummy('t', positive=True)
|
| 2023 |
+
ser = asech(S.NegativeOne + t**2).rewrite(log).nseries(t, 0, 2*n)
|
| 2024 |
+
arg1 = S.One + self.args[0]
|
| 2025 |
+
f = arg1.as_leading_term(x)
|
| 2026 |
+
g = (arg1 - f)/ f
|
| 2027 |
+
if not g.is_meromorphic(x, 0): # cannot be expanded
|
| 2028 |
+
return O(1) if n == 0 else I*pi + O(sqrt(x))
|
| 2029 |
+
res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
|
| 2030 |
+
res = (res1.removeO()*sqrt(f)).expand()
|
| 2031 |
+
return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
|
| 2032 |
+
|
| 2033 |
+
res = super()._eval_nseries(x, n=n, logx=logx)
|
| 2034 |
+
if arg0 is S.ComplexInfinity:
|
| 2035 |
+
return res
|
| 2036 |
+
|
| 2037 |
+
# Handling points lying on branch cuts (-oo, 0] U (1, oo)
|
| 2038 |
+
if arg0.is_negative or (1 - arg0).is_negative:
|
| 2039 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 2040 |
+
if im(ndir).is_positive:
|
| 2041 |
+
if arg0.is_positive or (arg0 + 1).is_negative:
|
| 2042 |
+
return -res
|
| 2043 |
+
return res - 2*I*pi
|
| 2044 |
+
elif not im(ndir).is_negative:
|
| 2045 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 2046 |
+
return res
|
| 2047 |
+
|
| 2048 |
+
def inverse(self, argindex=1):
|
| 2049 |
+
"""
|
| 2050 |
+
Returns the inverse of this function.
|
| 2051 |
+
"""
|
| 2052 |
+
return sech
|
| 2053 |
+
|
| 2054 |
+
def _eval_rewrite_as_log(self, arg, **kwargs):
|
| 2055 |
+
return log(1/arg + sqrt(1/arg - 1) * sqrt(1/arg + 1))
|
| 2056 |
+
|
| 2057 |
+
_eval_rewrite_as_tractable = _eval_rewrite_as_log
|
| 2058 |
+
|
| 2059 |
+
def _eval_rewrite_as_acosh(self, arg, **kwargs):
|
| 2060 |
+
return acosh(1/arg)
|
| 2061 |
+
|
| 2062 |
+
def _eval_rewrite_as_asinh(self, arg, **kwargs):
|
| 2063 |
+
return sqrt(1/arg - 1)/sqrt(1 - 1/arg)*(I*asinh(I/arg, evaluate=False)
|
| 2064 |
+
+ pi*S.Half)
|
| 2065 |
+
|
| 2066 |
+
def _eval_rewrite_as_atanh(self, x, **kwargs):
|
| 2067 |
+
return (I*pi*(1 - sqrt(x)*sqrt(1/x) - I/2*sqrt(-x)/sqrt(x) - I/2*sqrt(x**2)/sqrt(-x**2))
|
| 2068 |
+
+ sqrt(1/(x + 1))*sqrt(x + 1)*atanh(sqrt(1 - x**2)))
|
| 2069 |
+
|
| 2070 |
+
def _eval_rewrite_as_acsch(self, x, **kwargs):
|
| 2071 |
+
return sqrt(1/x - 1)/sqrt(1 - 1/x)*(pi/2 - I*acsch(I*x, evaluate=False))
|
| 2072 |
+
|
| 2073 |
+
def _eval_is_extended_real(self):
|
| 2074 |
+
return fuzzy_and([self.args[0].is_extended_real, self.args[0].is_nonnegative, (1 - self.args[0]).is_nonnegative])
|
| 2075 |
+
|
| 2076 |
+
def _eval_is_finite(self):
|
| 2077 |
+
return fuzzy_not(self.args[0].is_zero)
|
| 2078 |
+
|
| 2079 |
+
|
| 2080 |
+
class acsch(InverseHyperbolicFunction):
|
| 2081 |
+
"""
|
| 2082 |
+
``acsch(x)`` is the inverse hyperbolic cosecant of ``x``.
|
| 2083 |
+
|
| 2084 |
+
The inverse hyperbolic cosecant function.
|
| 2085 |
+
|
| 2086 |
+
Examples
|
| 2087 |
+
========
|
| 2088 |
+
|
| 2089 |
+
>>> from sympy import acsch, sqrt, I
|
| 2090 |
+
>>> from sympy.abc import x
|
| 2091 |
+
>>> acsch(x).diff(x)
|
| 2092 |
+
-1/(x**2*sqrt(1 + x**(-2)))
|
| 2093 |
+
>>> acsch(1).diff(x)
|
| 2094 |
+
0
|
| 2095 |
+
>>> acsch(1)
|
| 2096 |
+
log(1 + sqrt(2))
|
| 2097 |
+
>>> acsch(I)
|
| 2098 |
+
-I*pi/2
|
| 2099 |
+
>>> acsch(-2*I)
|
| 2100 |
+
I*pi/6
|
| 2101 |
+
>>> acsch(I*(sqrt(6) - sqrt(2)))
|
| 2102 |
+
-5*I*pi/12
|
| 2103 |
+
|
| 2104 |
+
See Also
|
| 2105 |
+
========
|
| 2106 |
+
|
| 2107 |
+
asinh
|
| 2108 |
+
|
| 2109 |
+
References
|
| 2110 |
+
==========
|
| 2111 |
+
|
| 2112 |
+
.. [1] https://en.wikipedia.org/wiki/Hyperbolic_function
|
| 2113 |
+
.. [2] https://dlmf.nist.gov/4.37
|
| 2114 |
+
.. [3] https://functions.wolfram.com/ElementaryFunctions/ArcCsch/
|
| 2115 |
+
|
| 2116 |
+
"""
|
| 2117 |
+
|
| 2118 |
+
def fdiff(self, argindex=1):
|
| 2119 |
+
if argindex == 1:
|
| 2120 |
+
z = self.args[0]
|
| 2121 |
+
return -1/(z**2*sqrt(1 + 1/z**2))
|
| 2122 |
+
else:
|
| 2123 |
+
raise ArgumentIndexError(self, argindex)
|
| 2124 |
+
|
| 2125 |
+
@classmethod
|
| 2126 |
+
def eval(cls, arg):
|
| 2127 |
+
if arg.is_Number:
|
| 2128 |
+
if arg is S.NaN:
|
| 2129 |
+
return S.NaN
|
| 2130 |
+
elif arg is S.Infinity:
|
| 2131 |
+
return S.Zero
|
| 2132 |
+
elif arg is S.NegativeInfinity:
|
| 2133 |
+
return S.Zero
|
| 2134 |
+
elif arg.is_zero:
|
| 2135 |
+
return S.ComplexInfinity
|
| 2136 |
+
elif arg is S.One:
|
| 2137 |
+
return log(1 + sqrt(2))
|
| 2138 |
+
elif arg is S.NegativeOne:
|
| 2139 |
+
return - log(1 + sqrt(2))
|
| 2140 |
+
|
| 2141 |
+
if arg.is_number:
|
| 2142 |
+
cst_table = _acsch_table()
|
| 2143 |
+
|
| 2144 |
+
if arg in cst_table:
|
| 2145 |
+
return cst_table[arg]*I
|
| 2146 |
+
|
| 2147 |
+
if arg is S.ComplexInfinity:
|
| 2148 |
+
return S.Zero
|
| 2149 |
+
|
| 2150 |
+
if arg.is_infinite:
|
| 2151 |
+
return S.Zero
|
| 2152 |
+
|
| 2153 |
+
if arg.is_zero:
|
| 2154 |
+
return S.ComplexInfinity
|
| 2155 |
+
|
| 2156 |
+
if arg.could_extract_minus_sign():
|
| 2157 |
+
return -cls(-arg)
|
| 2158 |
+
|
| 2159 |
+
@staticmethod
|
| 2160 |
+
@cacheit
|
| 2161 |
+
def taylor_term(n, x, *previous_terms):
|
| 2162 |
+
if n == 0:
|
| 2163 |
+
return log(2 / x)
|
| 2164 |
+
elif n < 0 or n % 2 == 1:
|
| 2165 |
+
return S.Zero
|
| 2166 |
+
else:
|
| 2167 |
+
x = sympify(x)
|
| 2168 |
+
if len(previous_terms) > 2 and n > 2:
|
| 2169 |
+
p = previous_terms[-2]
|
| 2170 |
+
return -p * ((n - 1)*(n-2)) * x**2/(4 * (n//2)**2)
|
| 2171 |
+
else:
|
| 2172 |
+
k = n // 2
|
| 2173 |
+
R = RisingFactorial(S.Half, k) * n
|
| 2174 |
+
F = factorial(k) * n // 2 * n // 2
|
| 2175 |
+
return S.NegativeOne**(k +1) * R / F * x**n / 4
|
| 2176 |
+
|
| 2177 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 2178 |
+
arg = self.args[0]
|
| 2179 |
+
x0 = arg.subs(x, 0).cancel()
|
| 2180 |
+
# Handling branch points
|
| 2181 |
+
if x0 in (-I, I, S.Zero):
|
| 2182 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 2183 |
+
|
| 2184 |
+
if x0 is S.NaN:
|
| 2185 |
+
expr = self.func(arg.as_leading_term(x))
|
| 2186 |
+
if expr.is_finite:
|
| 2187 |
+
return expr
|
| 2188 |
+
else:
|
| 2189 |
+
return self
|
| 2190 |
+
|
| 2191 |
+
if x0 is S.ComplexInfinity:
|
| 2192 |
+
return (1/arg).as_leading_term(x)
|
| 2193 |
+
# Handling points lying on branch cuts (-I, I)
|
| 2194 |
+
if x0.is_imaginary and (1 + x0**2).is_positive:
|
| 2195 |
+
ndir = arg.dir(x, cdir if cdir else 1)
|
| 2196 |
+
if re(ndir).is_positive:
|
| 2197 |
+
if im(x0).is_positive:
|
| 2198 |
+
return -self.func(x0) - I*pi
|
| 2199 |
+
elif re(ndir).is_negative:
|
| 2200 |
+
if im(x0).is_negative:
|
| 2201 |
+
return -self.func(x0) + I*pi
|
| 2202 |
+
else:
|
| 2203 |
+
return self.rewrite(log)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 2204 |
+
return self.func(x0)
|
| 2205 |
+
|
| 2206 |
+
def _eval_nseries(self, x, n, logx, cdir=0): # acsch
|
| 2207 |
+
from sympy.series.order import O
|
| 2208 |
+
arg = self.args[0]
|
| 2209 |
+
arg0 = arg.subs(x, 0)
|
| 2210 |
+
|
| 2211 |
+
# Handling branch points
|
| 2212 |
+
if arg0 is I:
|
| 2213 |
+
t = Dummy('t', positive=True)
|
| 2214 |
+
ser = acsch(I + t**2).rewrite(log).nseries(t, 0, 2*n)
|
| 2215 |
+
arg1 = -I + self.args[0]
|
| 2216 |
+
f = arg1.as_leading_term(x)
|
| 2217 |
+
g = (arg1 - f)/ f
|
| 2218 |
+
if not g.is_meromorphic(x, 0): # cannot be expanded
|
| 2219 |
+
return O(1) if n == 0 else -I*pi/2 + O(sqrt(x))
|
| 2220 |
+
res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
|
| 2221 |
+
res = (res1.removeO()*sqrt(f)).expand()
|
| 2222 |
+
res = ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
|
| 2223 |
+
return res
|
| 2224 |
+
|
| 2225 |
+
if arg0 == S.NegativeOne*I:
|
| 2226 |
+
t = Dummy('t', positive=True)
|
| 2227 |
+
ser = acsch(-I + t**2).rewrite(log).nseries(t, 0, 2*n)
|
| 2228 |
+
arg1 = I + self.args[0]
|
| 2229 |
+
f = arg1.as_leading_term(x)
|
| 2230 |
+
g = (arg1 - f)/ f
|
| 2231 |
+
if not g.is_meromorphic(x, 0): # cannot be expanded
|
| 2232 |
+
return O(1) if n == 0 else I*pi/2 + O(sqrt(x))
|
| 2233 |
+
res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
|
| 2234 |
+
res = (res1.removeO()*sqrt(f)).expand()
|
| 2235 |
+
return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
|
| 2236 |
+
|
| 2237 |
+
res = super()._eval_nseries(x, n=n, logx=logx)
|
| 2238 |
+
if arg0 is S.ComplexInfinity:
|
| 2239 |
+
return res
|
| 2240 |
+
|
| 2241 |
+
# Handling points lying on branch cuts (-I, I)
|
| 2242 |
+
if arg0.is_imaginary and (1 + arg0**2).is_positive:
|
| 2243 |
+
ndir = self.args[0].dir(x, cdir if cdir else 1)
|
| 2244 |
+
if re(ndir).is_positive:
|
| 2245 |
+
if im(arg0).is_positive:
|
| 2246 |
+
return -res - I*pi
|
| 2247 |
+
elif re(ndir).is_negative:
|
| 2248 |
+
if im(arg0).is_negative:
|
| 2249 |
+
return -res + I*pi
|
| 2250 |
+
else:
|
| 2251 |
+
return self.rewrite(log)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 2252 |
+
return res
|
| 2253 |
+
|
| 2254 |
+
def inverse(self, argindex=1):
|
| 2255 |
+
"""
|
| 2256 |
+
Returns the inverse of this function.
|
| 2257 |
+
"""
|
| 2258 |
+
return csch
|
| 2259 |
+
|
| 2260 |
+
def _eval_rewrite_as_log(self, arg, **kwargs):
|
| 2261 |
+
return log(1/arg + sqrt(1/arg**2 + 1))
|
| 2262 |
+
|
| 2263 |
+
_eval_rewrite_as_tractable = _eval_rewrite_as_log
|
| 2264 |
+
|
| 2265 |
+
def _eval_rewrite_as_asinh(self, arg, **kwargs):
|
| 2266 |
+
return asinh(1/arg)
|
| 2267 |
+
|
| 2268 |
+
def _eval_rewrite_as_acosh(self, arg, **kwargs):
|
| 2269 |
+
return I*(sqrt(1 - I/arg)/sqrt(I/arg - 1)*
|
| 2270 |
+
acosh(I/arg, evaluate=False) - pi*S.Half)
|
| 2271 |
+
|
| 2272 |
+
def _eval_rewrite_as_atanh(self, arg, **kwargs):
|
| 2273 |
+
arg2 = arg**2
|
| 2274 |
+
arg2p1 = arg2 + 1
|
| 2275 |
+
return sqrt(-arg2)/arg*(pi*S.Half -
|
| 2276 |
+
sqrt(-arg2p1**2)/arg2p1*atanh(sqrt(arg2p1)))
|
| 2277 |
+
|
| 2278 |
+
def _eval_is_zero(self):
|
| 2279 |
+
return self.args[0].is_infinite
|
| 2280 |
+
|
| 2281 |
+
def _eval_is_extended_real(self):
|
| 2282 |
+
return self.args[0].is_extended_real
|
| 2283 |
+
|
| 2284 |
+
def _eval_is_finite(self):
|
| 2285 |
+
return fuzzy_not(self.args[0].is_zero)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/integers.py
ADDED
|
@@ -0,0 +1,710 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from sympy.core.basic import Basic
|
| 4 |
+
from sympy.core.expr import Expr
|
| 5 |
+
|
| 6 |
+
from sympy.core import Add, S
|
| 7 |
+
from sympy.core.evalf import get_integer_part, PrecisionExhausted
|
| 8 |
+
from sympy.core.function import DefinedFunction
|
| 9 |
+
from sympy.core.logic import fuzzy_or, fuzzy_and
|
| 10 |
+
from sympy.core.numbers import Integer, int_valued
|
| 11 |
+
from sympy.core.relational import Gt, Lt, Ge, Le, Relational, is_eq, is_le, is_lt
|
| 12 |
+
from sympy.core.sympify import _sympify
|
| 13 |
+
from sympy.functions.elementary.complexes import im, re
|
| 14 |
+
from sympy.multipledispatch import dispatch
|
| 15 |
+
|
| 16 |
+
###############################################################################
|
| 17 |
+
######################### FLOOR and CEILING FUNCTIONS #########################
|
| 18 |
+
###############################################################################
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class RoundFunction(DefinedFunction):
|
| 22 |
+
"""Abstract base class for rounding functions."""
|
| 23 |
+
|
| 24 |
+
args: tuple[Expr]
|
| 25 |
+
|
| 26 |
+
@classmethod
|
| 27 |
+
def eval(cls, arg):
|
| 28 |
+
if (v := cls._eval_number(arg)) is not None:
|
| 29 |
+
return v
|
| 30 |
+
if (v := cls._eval_const_number(arg)) is not None:
|
| 31 |
+
return v
|
| 32 |
+
|
| 33 |
+
if arg.is_integer or arg.is_finite is False:
|
| 34 |
+
return arg
|
| 35 |
+
if arg.is_imaginary or (S.ImaginaryUnit*arg).is_real:
|
| 36 |
+
i = im(arg)
|
| 37 |
+
if not i.has(S.ImaginaryUnit):
|
| 38 |
+
return cls(i)*S.ImaginaryUnit
|
| 39 |
+
return cls(arg, evaluate=False)
|
| 40 |
+
|
| 41 |
+
# Integral, numerical, symbolic part
|
| 42 |
+
ipart = npart = spart = S.Zero
|
| 43 |
+
|
| 44 |
+
# Extract integral (or complex integral) terms
|
| 45 |
+
intof = lambda x: int(x) if int_valued(x) else (
|
| 46 |
+
x if x.is_integer else None)
|
| 47 |
+
for t in Add.make_args(arg):
|
| 48 |
+
if t.is_imaginary and (i := intof(im(t))) is not None:
|
| 49 |
+
ipart += i*S.ImaginaryUnit
|
| 50 |
+
elif (i := intof(t)) is not None:
|
| 51 |
+
ipart += i
|
| 52 |
+
elif t.is_number:
|
| 53 |
+
npart += t
|
| 54 |
+
else:
|
| 55 |
+
spart += t
|
| 56 |
+
|
| 57 |
+
if not (npart or spart):
|
| 58 |
+
return ipart
|
| 59 |
+
|
| 60 |
+
# Evaluate npart numerically if independent of spart
|
| 61 |
+
if npart and (
|
| 62 |
+
not spart or
|
| 63 |
+
npart.is_real and (spart.is_imaginary or (S.ImaginaryUnit*spart).is_real) or
|
| 64 |
+
npart.is_imaginary and spart.is_real):
|
| 65 |
+
try:
|
| 66 |
+
r, i = get_integer_part(
|
| 67 |
+
npart, cls._dir, {}, return_ints=True)
|
| 68 |
+
ipart += Integer(r) + Integer(i)*S.ImaginaryUnit
|
| 69 |
+
npart = S.Zero
|
| 70 |
+
except (PrecisionExhausted, NotImplementedError):
|
| 71 |
+
pass
|
| 72 |
+
|
| 73 |
+
spart += npart
|
| 74 |
+
if not spart:
|
| 75 |
+
return ipart
|
| 76 |
+
elif spart.is_imaginary or (S.ImaginaryUnit*spart).is_real:
|
| 77 |
+
return ipart + cls(im(spart), evaluate=False)*S.ImaginaryUnit
|
| 78 |
+
elif isinstance(spart, (floor, ceiling)):
|
| 79 |
+
return ipart + spart
|
| 80 |
+
else:
|
| 81 |
+
return ipart + cls(spart, evaluate=False)
|
| 82 |
+
|
| 83 |
+
@classmethod
|
| 84 |
+
def _eval_number(cls, arg):
|
| 85 |
+
raise NotImplementedError()
|
| 86 |
+
|
| 87 |
+
def _eval_is_finite(self):
|
| 88 |
+
return self.args[0].is_finite
|
| 89 |
+
|
| 90 |
+
def _eval_is_real(self):
|
| 91 |
+
return self.args[0].is_real
|
| 92 |
+
|
| 93 |
+
def _eval_is_integer(self):
|
| 94 |
+
return self.args[0].is_real
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class floor(RoundFunction):
|
| 98 |
+
"""
|
| 99 |
+
Floor is a univariate function which returns the largest integer
|
| 100 |
+
value not greater than its argument. This implementation
|
| 101 |
+
generalizes floor to complex numbers by taking the floor of the
|
| 102 |
+
real and imaginary parts separately.
|
| 103 |
+
|
| 104 |
+
Examples
|
| 105 |
+
========
|
| 106 |
+
|
| 107 |
+
>>> from sympy import floor, E, I, S, Float, Rational
|
| 108 |
+
>>> floor(17)
|
| 109 |
+
17
|
| 110 |
+
>>> floor(Rational(23, 10))
|
| 111 |
+
2
|
| 112 |
+
>>> floor(2*E)
|
| 113 |
+
5
|
| 114 |
+
>>> floor(-Float(0.567))
|
| 115 |
+
-1
|
| 116 |
+
>>> floor(-I/2)
|
| 117 |
+
-I
|
| 118 |
+
>>> floor(S(5)/2 + 5*I/2)
|
| 119 |
+
2 + 2*I
|
| 120 |
+
|
| 121 |
+
See Also
|
| 122 |
+
========
|
| 123 |
+
|
| 124 |
+
sympy.functions.elementary.integers.ceiling
|
| 125 |
+
|
| 126 |
+
References
|
| 127 |
+
==========
|
| 128 |
+
|
| 129 |
+
.. [1] "Concrete mathematics" by Graham, pp. 87
|
| 130 |
+
.. [2] https://mathworld.wolfram.com/FloorFunction.html
|
| 131 |
+
|
| 132 |
+
"""
|
| 133 |
+
_dir = -1
|
| 134 |
+
|
| 135 |
+
@classmethod
|
| 136 |
+
def _eval_number(cls, arg):
|
| 137 |
+
if arg.is_Number:
|
| 138 |
+
return arg.floor()
|
| 139 |
+
if any(isinstance(i, j)
|
| 140 |
+
for i in (arg, -arg) for j in (floor, ceiling)):
|
| 141 |
+
return arg
|
| 142 |
+
if arg.is_NumberSymbol:
|
| 143 |
+
return arg.approximation_interval(Integer)[0]
|
| 144 |
+
|
| 145 |
+
@classmethod
|
| 146 |
+
def _eval_const_number(cls, arg):
|
| 147 |
+
if arg.is_real:
|
| 148 |
+
if arg.is_zero:
|
| 149 |
+
return S.Zero
|
| 150 |
+
if arg.is_positive:
|
| 151 |
+
num, den = arg.as_numer_denom()
|
| 152 |
+
s = den.is_negative
|
| 153 |
+
if s is None:
|
| 154 |
+
return None
|
| 155 |
+
if s:
|
| 156 |
+
num, den = -num, -den
|
| 157 |
+
# 0 <= num/den < 1 -> 0
|
| 158 |
+
if is_lt(num, den):
|
| 159 |
+
return S.Zero
|
| 160 |
+
# 1 <= num/den < 2 -> 1
|
| 161 |
+
if fuzzy_and([is_le(den, num), is_lt(num, 2*den)]):
|
| 162 |
+
return S.One
|
| 163 |
+
if arg.is_negative:
|
| 164 |
+
num, den = arg.as_numer_denom()
|
| 165 |
+
s = den.is_negative
|
| 166 |
+
if s is None:
|
| 167 |
+
return None
|
| 168 |
+
if s:
|
| 169 |
+
num, den = -num, -den
|
| 170 |
+
# -1 <= num/den < 0 -> -1
|
| 171 |
+
if is_le(-den, num):
|
| 172 |
+
return S.NegativeOne
|
| 173 |
+
# -2 <= num/den < -1 -> -2
|
| 174 |
+
if fuzzy_and([is_le(-2*den, num), is_lt(num, -den)]):
|
| 175 |
+
return Integer(-2)
|
| 176 |
+
|
| 177 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 178 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 179 |
+
arg = self.args[0]
|
| 180 |
+
arg0 = arg.subs(x, 0)
|
| 181 |
+
r = self.subs(x, 0)
|
| 182 |
+
if arg0 is S.NaN or isinstance(arg0, AccumBounds):
|
| 183 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 184 |
+
r = floor(arg0)
|
| 185 |
+
if arg0.is_finite:
|
| 186 |
+
if arg0 == r:
|
| 187 |
+
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
| 188 |
+
if ndir.is_negative:
|
| 189 |
+
return r - 1
|
| 190 |
+
elif ndir.is_positive:
|
| 191 |
+
return r
|
| 192 |
+
else:
|
| 193 |
+
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
| 194 |
+
else:
|
| 195 |
+
return r
|
| 196 |
+
return arg.as_leading_term(x, logx=logx, cdir=cdir)
|
| 197 |
+
|
| 198 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 199 |
+
arg = self.args[0]
|
| 200 |
+
arg0 = arg.subs(x, 0)
|
| 201 |
+
r = self.subs(x, 0)
|
| 202 |
+
if arg0 is S.NaN:
|
| 203 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 204 |
+
r = floor(arg0)
|
| 205 |
+
if arg0.is_infinite:
|
| 206 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 207 |
+
from sympy.series.order import Order
|
| 208 |
+
s = arg._eval_nseries(x, n, logx, cdir)
|
| 209 |
+
o = Order(1, (x, 0)) if n <= 0 else AccumBounds(-1, 0)
|
| 210 |
+
return s + o
|
| 211 |
+
if arg0 == r:
|
| 212 |
+
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
| 213 |
+
if ndir.is_negative:
|
| 214 |
+
return r - 1
|
| 215 |
+
elif ndir.is_positive:
|
| 216 |
+
return r
|
| 217 |
+
else:
|
| 218 |
+
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
| 219 |
+
else:
|
| 220 |
+
return r
|
| 221 |
+
|
| 222 |
+
def _eval_is_negative(self):
|
| 223 |
+
return self.args[0].is_negative
|
| 224 |
+
|
| 225 |
+
def _eval_is_nonnegative(self):
|
| 226 |
+
return self.args[0].is_nonnegative
|
| 227 |
+
|
| 228 |
+
def _eval_rewrite_as_ceiling(self, arg, **kwargs):
|
| 229 |
+
return -ceiling(-arg)
|
| 230 |
+
|
| 231 |
+
def _eval_rewrite_as_frac(self, arg, **kwargs):
|
| 232 |
+
return arg - frac(arg)
|
| 233 |
+
|
| 234 |
+
def __le__(self, other):
|
| 235 |
+
other = S(other)
|
| 236 |
+
if self.args[0].is_real:
|
| 237 |
+
if other.is_integer:
|
| 238 |
+
return self.args[0] < other + 1
|
| 239 |
+
if other.is_number and other.is_real:
|
| 240 |
+
return self.args[0] < ceiling(other)
|
| 241 |
+
if self.args[0] == other and other.is_real:
|
| 242 |
+
return S.true
|
| 243 |
+
if other is S.Infinity and self.is_finite:
|
| 244 |
+
return S.true
|
| 245 |
+
|
| 246 |
+
return Le(self, other, evaluate=False)
|
| 247 |
+
|
| 248 |
+
def __ge__(self, other):
|
| 249 |
+
other = S(other)
|
| 250 |
+
if self.args[0].is_real:
|
| 251 |
+
if other.is_integer:
|
| 252 |
+
return self.args[0] >= other
|
| 253 |
+
if other.is_number and other.is_real:
|
| 254 |
+
return self.args[0] >= ceiling(other)
|
| 255 |
+
if self.args[0] == other and other.is_real and other.is_noninteger:
|
| 256 |
+
return S.false
|
| 257 |
+
if other is S.NegativeInfinity and self.is_finite:
|
| 258 |
+
return S.true
|
| 259 |
+
|
| 260 |
+
return Ge(self, other, evaluate=False)
|
| 261 |
+
|
| 262 |
+
def __gt__(self, other):
|
| 263 |
+
other = S(other)
|
| 264 |
+
if self.args[0].is_real:
|
| 265 |
+
if other.is_integer:
|
| 266 |
+
return self.args[0] >= other + 1
|
| 267 |
+
if other.is_number and other.is_real:
|
| 268 |
+
return self.args[0] >= ceiling(other)
|
| 269 |
+
if self.args[0] == other and other.is_real:
|
| 270 |
+
return S.false
|
| 271 |
+
if other is S.NegativeInfinity and self.is_finite:
|
| 272 |
+
return S.true
|
| 273 |
+
|
| 274 |
+
return Gt(self, other, evaluate=False)
|
| 275 |
+
|
| 276 |
+
def __lt__(self, other):
|
| 277 |
+
other = S(other)
|
| 278 |
+
if self.args[0].is_real:
|
| 279 |
+
if other.is_integer:
|
| 280 |
+
return self.args[0] < other
|
| 281 |
+
if other.is_number and other.is_real:
|
| 282 |
+
return self.args[0] < ceiling(other)
|
| 283 |
+
if self.args[0] == other and other.is_real and other.is_noninteger:
|
| 284 |
+
return S.true
|
| 285 |
+
if other is S.Infinity and self.is_finite:
|
| 286 |
+
return S.true
|
| 287 |
+
|
| 288 |
+
return Lt(self, other, evaluate=False)
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
@dispatch(floor, Expr)
|
| 292 |
+
def _eval_is_eq(lhs, rhs): # noqa:F811
|
| 293 |
+
return is_eq(lhs.rewrite(ceiling), rhs) or \
|
| 294 |
+
is_eq(lhs.rewrite(frac),rhs)
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
class ceiling(RoundFunction):
|
| 298 |
+
"""
|
| 299 |
+
Ceiling is a univariate function which returns the smallest integer
|
| 300 |
+
value not less than its argument. This implementation
|
| 301 |
+
generalizes ceiling to complex numbers by taking the ceiling of the
|
| 302 |
+
real and imaginary parts separately.
|
| 303 |
+
|
| 304 |
+
Examples
|
| 305 |
+
========
|
| 306 |
+
|
| 307 |
+
>>> from sympy import ceiling, E, I, S, Float, Rational
|
| 308 |
+
>>> ceiling(17)
|
| 309 |
+
17
|
| 310 |
+
>>> ceiling(Rational(23, 10))
|
| 311 |
+
3
|
| 312 |
+
>>> ceiling(2*E)
|
| 313 |
+
6
|
| 314 |
+
>>> ceiling(-Float(0.567))
|
| 315 |
+
0
|
| 316 |
+
>>> ceiling(I/2)
|
| 317 |
+
I
|
| 318 |
+
>>> ceiling(S(5)/2 + 5*I/2)
|
| 319 |
+
3 + 3*I
|
| 320 |
+
|
| 321 |
+
See Also
|
| 322 |
+
========
|
| 323 |
+
|
| 324 |
+
sympy.functions.elementary.integers.floor
|
| 325 |
+
|
| 326 |
+
References
|
| 327 |
+
==========
|
| 328 |
+
|
| 329 |
+
.. [1] "Concrete mathematics" by Graham, pp. 87
|
| 330 |
+
.. [2] https://mathworld.wolfram.com/CeilingFunction.html
|
| 331 |
+
|
| 332 |
+
"""
|
| 333 |
+
_dir = 1
|
| 334 |
+
|
| 335 |
+
@classmethod
|
| 336 |
+
def _eval_number(cls, arg):
|
| 337 |
+
if arg.is_Number:
|
| 338 |
+
return arg.ceiling()
|
| 339 |
+
if any(isinstance(i, j)
|
| 340 |
+
for i in (arg, -arg) for j in (floor, ceiling)):
|
| 341 |
+
return arg
|
| 342 |
+
if arg.is_NumberSymbol:
|
| 343 |
+
return arg.approximation_interval(Integer)[1]
|
| 344 |
+
|
| 345 |
+
@classmethod
|
| 346 |
+
def _eval_const_number(cls, arg):
|
| 347 |
+
if arg.is_real:
|
| 348 |
+
if arg.is_zero:
|
| 349 |
+
return S.Zero
|
| 350 |
+
if arg.is_positive:
|
| 351 |
+
num, den = arg.as_numer_denom()
|
| 352 |
+
s = den.is_negative
|
| 353 |
+
if s is None:
|
| 354 |
+
return None
|
| 355 |
+
if s:
|
| 356 |
+
num, den = -num, -den
|
| 357 |
+
# 0 < num/den <= 1 -> 1
|
| 358 |
+
if is_le(num, den):
|
| 359 |
+
return S.One
|
| 360 |
+
# 1 < num/den <= 2 -> 2
|
| 361 |
+
if fuzzy_and([is_lt(den, num), is_le(num, 2*den)]):
|
| 362 |
+
return Integer(2)
|
| 363 |
+
if arg.is_negative:
|
| 364 |
+
num, den = arg.as_numer_denom()
|
| 365 |
+
s = den.is_negative
|
| 366 |
+
if s is None:
|
| 367 |
+
return None
|
| 368 |
+
if s:
|
| 369 |
+
num, den = -num, -den
|
| 370 |
+
# -1 < num/den <= 0 -> 0
|
| 371 |
+
if is_lt(-den, num):
|
| 372 |
+
return S.Zero
|
| 373 |
+
# -2 < num/den <= -1 -> -1
|
| 374 |
+
if fuzzy_and([is_lt(-2*den, num), is_le(num, -den)]):
|
| 375 |
+
return S.NegativeOne
|
| 376 |
+
|
| 377 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 378 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 379 |
+
arg = self.args[0]
|
| 380 |
+
arg0 = arg.subs(x, 0)
|
| 381 |
+
r = self.subs(x, 0)
|
| 382 |
+
if arg0 is S.NaN or isinstance(arg0, AccumBounds):
|
| 383 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 384 |
+
r = ceiling(arg0)
|
| 385 |
+
if arg0.is_finite:
|
| 386 |
+
if arg0 == r:
|
| 387 |
+
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
| 388 |
+
if ndir.is_negative:
|
| 389 |
+
return r
|
| 390 |
+
elif ndir.is_positive:
|
| 391 |
+
return r + 1
|
| 392 |
+
else:
|
| 393 |
+
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
| 394 |
+
else:
|
| 395 |
+
return r
|
| 396 |
+
return arg.as_leading_term(x, logx=logx, cdir=cdir)
|
| 397 |
+
|
| 398 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 399 |
+
arg = self.args[0]
|
| 400 |
+
arg0 = arg.subs(x, 0)
|
| 401 |
+
r = self.subs(x, 0)
|
| 402 |
+
if arg0 is S.NaN:
|
| 403 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 404 |
+
r = ceiling(arg0)
|
| 405 |
+
if arg0.is_infinite:
|
| 406 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 407 |
+
from sympy.series.order import Order
|
| 408 |
+
s = arg._eval_nseries(x, n, logx, cdir)
|
| 409 |
+
o = Order(1, (x, 0)) if n <= 0 else AccumBounds(0, 1)
|
| 410 |
+
return s + o
|
| 411 |
+
if arg0 == r:
|
| 412 |
+
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
| 413 |
+
if ndir.is_negative:
|
| 414 |
+
return r
|
| 415 |
+
elif ndir.is_positive:
|
| 416 |
+
return r + 1
|
| 417 |
+
else:
|
| 418 |
+
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
| 419 |
+
else:
|
| 420 |
+
return r
|
| 421 |
+
|
| 422 |
+
def _eval_rewrite_as_floor(self, arg, **kwargs):
|
| 423 |
+
return -floor(-arg)
|
| 424 |
+
|
| 425 |
+
def _eval_rewrite_as_frac(self, arg, **kwargs):
|
| 426 |
+
return arg + frac(-arg)
|
| 427 |
+
|
| 428 |
+
def _eval_is_positive(self):
|
| 429 |
+
return self.args[0].is_positive
|
| 430 |
+
|
| 431 |
+
def _eval_is_nonpositive(self):
|
| 432 |
+
return self.args[0].is_nonpositive
|
| 433 |
+
|
| 434 |
+
def __lt__(self, other):
|
| 435 |
+
other = S(other)
|
| 436 |
+
if self.args[0].is_real:
|
| 437 |
+
if other.is_integer:
|
| 438 |
+
return self.args[0] <= other - 1
|
| 439 |
+
if other.is_number and other.is_real:
|
| 440 |
+
return self.args[0] <= floor(other)
|
| 441 |
+
if self.args[0] == other and other.is_real:
|
| 442 |
+
return S.false
|
| 443 |
+
if other is S.Infinity and self.is_finite:
|
| 444 |
+
return S.true
|
| 445 |
+
|
| 446 |
+
return Lt(self, other, evaluate=False)
|
| 447 |
+
|
| 448 |
+
def __gt__(self, other):
|
| 449 |
+
other = S(other)
|
| 450 |
+
if self.args[0].is_real:
|
| 451 |
+
if other.is_integer:
|
| 452 |
+
return self.args[0] > other
|
| 453 |
+
if other.is_number and other.is_real:
|
| 454 |
+
return self.args[0] > floor(other)
|
| 455 |
+
if self.args[0] == other and other.is_real and other.is_noninteger:
|
| 456 |
+
return S.true
|
| 457 |
+
if other is S.NegativeInfinity and self.is_finite:
|
| 458 |
+
return S.true
|
| 459 |
+
|
| 460 |
+
return Gt(self, other, evaluate=False)
|
| 461 |
+
|
| 462 |
+
def __ge__(self, other):
|
| 463 |
+
other = S(other)
|
| 464 |
+
if self.args[0].is_real:
|
| 465 |
+
if other.is_integer:
|
| 466 |
+
return self.args[0] > other - 1
|
| 467 |
+
if other.is_number and other.is_real:
|
| 468 |
+
return self.args[0] > floor(other)
|
| 469 |
+
if self.args[0] == other and other.is_real:
|
| 470 |
+
return S.true
|
| 471 |
+
if other is S.NegativeInfinity and self.is_finite:
|
| 472 |
+
return S.true
|
| 473 |
+
|
| 474 |
+
return Ge(self, other, evaluate=False)
|
| 475 |
+
|
| 476 |
+
def __le__(self, other):
|
| 477 |
+
other = S(other)
|
| 478 |
+
if self.args[0].is_real:
|
| 479 |
+
if other.is_integer:
|
| 480 |
+
return self.args[0] <= other
|
| 481 |
+
if other.is_number and other.is_real:
|
| 482 |
+
return self.args[0] <= floor(other)
|
| 483 |
+
if self.args[0] == other and other.is_real and other.is_noninteger:
|
| 484 |
+
return S.false
|
| 485 |
+
if other is S.Infinity and self.is_finite:
|
| 486 |
+
return S.true
|
| 487 |
+
|
| 488 |
+
return Le(self, other, evaluate=False)
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
@dispatch(ceiling, Basic) # type:ignore
|
| 492 |
+
def _eval_is_eq(lhs, rhs): # noqa:F811
|
| 493 |
+
return is_eq(lhs.rewrite(floor), rhs) or is_eq(lhs.rewrite(frac),rhs)
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
class frac(DefinedFunction):
|
| 497 |
+
r"""Represents the fractional part of x
|
| 498 |
+
|
| 499 |
+
For real numbers it is defined [1]_ as
|
| 500 |
+
|
| 501 |
+
.. math::
|
| 502 |
+
x - \left\lfloor{x}\right\rfloor
|
| 503 |
+
|
| 504 |
+
Examples
|
| 505 |
+
========
|
| 506 |
+
|
| 507 |
+
>>> from sympy import Symbol, frac, Rational, floor, I
|
| 508 |
+
>>> frac(Rational(4, 3))
|
| 509 |
+
1/3
|
| 510 |
+
>>> frac(-Rational(4, 3))
|
| 511 |
+
2/3
|
| 512 |
+
|
| 513 |
+
returns zero for integer arguments
|
| 514 |
+
|
| 515 |
+
>>> n = Symbol('n', integer=True)
|
| 516 |
+
>>> frac(n)
|
| 517 |
+
0
|
| 518 |
+
|
| 519 |
+
rewrite as floor
|
| 520 |
+
|
| 521 |
+
>>> x = Symbol('x')
|
| 522 |
+
>>> frac(x).rewrite(floor)
|
| 523 |
+
x - floor(x)
|
| 524 |
+
|
| 525 |
+
for complex arguments
|
| 526 |
+
|
| 527 |
+
>>> r = Symbol('r', real=True)
|
| 528 |
+
>>> t = Symbol('t', real=True)
|
| 529 |
+
>>> frac(t + I*r)
|
| 530 |
+
I*frac(r) + frac(t)
|
| 531 |
+
|
| 532 |
+
See Also
|
| 533 |
+
========
|
| 534 |
+
|
| 535 |
+
sympy.functions.elementary.integers.floor
|
| 536 |
+
sympy.functions.elementary.integers.ceiling
|
| 537 |
+
|
| 538 |
+
References
|
| 539 |
+
===========
|
| 540 |
+
|
| 541 |
+
.. [1] https://en.wikipedia.org/wiki/Fractional_part
|
| 542 |
+
.. [2] https://mathworld.wolfram.com/FractionalPart.html
|
| 543 |
+
|
| 544 |
+
"""
|
| 545 |
+
@classmethod
|
| 546 |
+
def eval(cls, arg):
|
| 547 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 548 |
+
|
| 549 |
+
def _eval(arg):
|
| 550 |
+
if arg in (S.Infinity, S.NegativeInfinity):
|
| 551 |
+
return AccumBounds(0, 1)
|
| 552 |
+
if arg.is_integer:
|
| 553 |
+
return S.Zero
|
| 554 |
+
if arg.is_number:
|
| 555 |
+
if arg is S.NaN:
|
| 556 |
+
return S.NaN
|
| 557 |
+
elif arg is S.ComplexInfinity:
|
| 558 |
+
return S.NaN
|
| 559 |
+
else:
|
| 560 |
+
return arg - floor(arg)
|
| 561 |
+
return cls(arg, evaluate=False)
|
| 562 |
+
|
| 563 |
+
real, imag = S.Zero, S.Zero
|
| 564 |
+
for t in Add.make_args(arg):
|
| 565 |
+
# Two checks are needed for complex arguments
|
| 566 |
+
# see issue-7649 for details
|
| 567 |
+
if t.is_imaginary or (S.ImaginaryUnit*t).is_real:
|
| 568 |
+
i = im(t)
|
| 569 |
+
if not i.has(S.ImaginaryUnit):
|
| 570 |
+
imag += i
|
| 571 |
+
else:
|
| 572 |
+
real += t
|
| 573 |
+
else:
|
| 574 |
+
real += t
|
| 575 |
+
|
| 576 |
+
real = _eval(real)
|
| 577 |
+
imag = _eval(imag)
|
| 578 |
+
return real + S.ImaginaryUnit*imag
|
| 579 |
+
|
| 580 |
+
def _eval_rewrite_as_floor(self, arg, **kwargs):
|
| 581 |
+
return arg - floor(arg)
|
| 582 |
+
|
| 583 |
+
def _eval_rewrite_as_ceiling(self, arg, **kwargs):
|
| 584 |
+
return arg + ceiling(-arg)
|
| 585 |
+
|
| 586 |
+
def _eval_is_finite(self):
|
| 587 |
+
return True
|
| 588 |
+
|
| 589 |
+
def _eval_is_real(self):
|
| 590 |
+
return self.args[0].is_extended_real
|
| 591 |
+
|
| 592 |
+
def _eval_is_imaginary(self):
|
| 593 |
+
return self.args[0].is_imaginary
|
| 594 |
+
|
| 595 |
+
def _eval_is_integer(self):
|
| 596 |
+
return self.args[0].is_integer
|
| 597 |
+
|
| 598 |
+
def _eval_is_zero(self):
|
| 599 |
+
return fuzzy_or([self.args[0].is_zero, self.args[0].is_integer])
|
| 600 |
+
|
| 601 |
+
def _eval_is_negative(self):
|
| 602 |
+
return False
|
| 603 |
+
|
| 604 |
+
def __ge__(self, other):
|
| 605 |
+
if self.is_extended_real:
|
| 606 |
+
other = _sympify(other)
|
| 607 |
+
# Check if other <= 0
|
| 608 |
+
if other.is_extended_nonpositive:
|
| 609 |
+
return S.true
|
| 610 |
+
# Check if other >= 1
|
| 611 |
+
res = self._value_one_or_more(other)
|
| 612 |
+
if res is not None:
|
| 613 |
+
return not(res)
|
| 614 |
+
return Ge(self, other, evaluate=False)
|
| 615 |
+
|
| 616 |
+
def __gt__(self, other):
|
| 617 |
+
if self.is_extended_real:
|
| 618 |
+
other = _sympify(other)
|
| 619 |
+
# Check if other < 0
|
| 620 |
+
res = self._value_one_or_more(other)
|
| 621 |
+
if res is not None:
|
| 622 |
+
return not(res)
|
| 623 |
+
# Check if other >= 1
|
| 624 |
+
if other.is_extended_negative:
|
| 625 |
+
return S.true
|
| 626 |
+
return Gt(self, other, evaluate=False)
|
| 627 |
+
|
| 628 |
+
def __le__(self, other):
|
| 629 |
+
if self.is_extended_real:
|
| 630 |
+
other = _sympify(other)
|
| 631 |
+
# Check if other < 0
|
| 632 |
+
if other.is_extended_negative:
|
| 633 |
+
return S.false
|
| 634 |
+
# Check if other >= 1
|
| 635 |
+
res = self._value_one_or_more(other)
|
| 636 |
+
if res is not None:
|
| 637 |
+
return res
|
| 638 |
+
return Le(self, other, evaluate=False)
|
| 639 |
+
|
| 640 |
+
def __lt__(self, other):
|
| 641 |
+
if self.is_extended_real:
|
| 642 |
+
other = _sympify(other)
|
| 643 |
+
# Check if other <= 0
|
| 644 |
+
if other.is_extended_nonpositive:
|
| 645 |
+
return S.false
|
| 646 |
+
# Check if other >= 1
|
| 647 |
+
res = self._value_one_or_more(other)
|
| 648 |
+
if res is not None:
|
| 649 |
+
return res
|
| 650 |
+
return Lt(self, other, evaluate=False)
|
| 651 |
+
|
| 652 |
+
def _value_one_or_more(self, other):
|
| 653 |
+
if other.is_extended_real:
|
| 654 |
+
if other.is_number:
|
| 655 |
+
res = other >= 1
|
| 656 |
+
if res and not isinstance(res, Relational):
|
| 657 |
+
return S.true
|
| 658 |
+
if other.is_integer and other.is_positive:
|
| 659 |
+
return S.true
|
| 660 |
+
|
| 661 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 662 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 663 |
+
arg = self.args[0]
|
| 664 |
+
arg0 = arg.subs(x, 0)
|
| 665 |
+
r = self.subs(x, 0)
|
| 666 |
+
|
| 667 |
+
if arg0.is_finite:
|
| 668 |
+
if r.is_zero:
|
| 669 |
+
ndir = arg.dir(x, cdir=cdir)
|
| 670 |
+
if ndir.is_negative:
|
| 671 |
+
return S.One
|
| 672 |
+
return (arg - arg0).as_leading_term(x, logx=logx, cdir=cdir)
|
| 673 |
+
else:
|
| 674 |
+
return r
|
| 675 |
+
elif arg0 in (S.ComplexInfinity, S.Infinity, S.NegativeInfinity):
|
| 676 |
+
return AccumBounds(0, 1)
|
| 677 |
+
return arg.as_leading_term(x, logx=logx, cdir=cdir)
|
| 678 |
+
|
| 679 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 680 |
+
from sympy.series.order import Order
|
| 681 |
+
arg = self.args[0]
|
| 682 |
+
arg0 = arg.subs(x, 0)
|
| 683 |
+
r = self.subs(x, 0)
|
| 684 |
+
|
| 685 |
+
if arg0.is_infinite:
|
| 686 |
+
from sympy.calculus.accumulationbounds import AccumBounds
|
| 687 |
+
o = Order(1, (x, 0)) if n <= 0 else AccumBounds(0, 1) + Order(x**n, (x, 0))
|
| 688 |
+
return o
|
| 689 |
+
else:
|
| 690 |
+
res = (arg - arg0)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 691 |
+
if r.is_zero:
|
| 692 |
+
ndir = arg.dir(x, cdir=cdir)
|
| 693 |
+
res += S.One if ndir.is_negative else S.Zero
|
| 694 |
+
else:
|
| 695 |
+
res += r
|
| 696 |
+
return res
|
| 697 |
+
|
| 698 |
+
|
| 699 |
+
@dispatch(frac, Basic) # type:ignore
|
| 700 |
+
def _eval_is_eq(lhs, rhs): # noqa:F811
|
| 701 |
+
if (lhs.rewrite(floor) == rhs) or \
|
| 702 |
+
(lhs.rewrite(ceiling) == rhs):
|
| 703 |
+
return True
|
| 704 |
+
# Check if other < 0
|
| 705 |
+
if rhs.is_extended_negative:
|
| 706 |
+
return False
|
| 707 |
+
# Check if other >= 1
|
| 708 |
+
res = lhs._value_one_or_more(rhs)
|
| 709 |
+
if res is not None:
|
| 710 |
+
return False
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/miscellaneous.py
ADDED
|
@@ -0,0 +1,915 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S, sympify, NumberKind
|
| 2 |
+
from sympy.utilities.iterables import sift
|
| 3 |
+
from sympy.core.add import Add
|
| 4 |
+
from sympy.core.containers import Tuple
|
| 5 |
+
from sympy.core.operations import LatticeOp, ShortCircuit
|
| 6 |
+
from sympy.core.function import (Application, Lambda,
|
| 7 |
+
ArgumentIndexError, DefinedFunction)
|
| 8 |
+
from sympy.core.expr import Expr
|
| 9 |
+
from sympy.core.exprtools import factor_terms
|
| 10 |
+
from sympy.core.mod import Mod
|
| 11 |
+
from sympy.core.mul import Mul
|
| 12 |
+
from sympy.core.numbers import Rational
|
| 13 |
+
from sympy.core.power import Pow
|
| 14 |
+
from sympy.core.relational import Eq, Relational
|
| 15 |
+
from sympy.core.singleton import Singleton
|
| 16 |
+
from sympy.core.sorting import ordered
|
| 17 |
+
from sympy.core.symbol import Dummy
|
| 18 |
+
from sympy.core.rules import Transform
|
| 19 |
+
from sympy.core.logic import fuzzy_and, fuzzy_or, _torf
|
| 20 |
+
from sympy.core.traversal import walk
|
| 21 |
+
from sympy.core.numbers import Integer
|
| 22 |
+
from sympy.logic.boolalg import And, Or
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _minmax_as_Piecewise(op, *args):
|
| 26 |
+
# helper for Min/Max rewrite as Piecewise
|
| 27 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 28 |
+
ec = []
|
| 29 |
+
for i, a in enumerate(args):
|
| 30 |
+
c = [Relational(a, args[j], op) for j in range(i + 1, len(args))]
|
| 31 |
+
ec.append((a, And(*c)))
|
| 32 |
+
return Piecewise(*ec)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class IdentityFunction(Lambda, metaclass=Singleton):
|
| 36 |
+
"""
|
| 37 |
+
The identity function
|
| 38 |
+
|
| 39 |
+
Examples
|
| 40 |
+
========
|
| 41 |
+
|
| 42 |
+
>>> from sympy import Id, Symbol
|
| 43 |
+
>>> x = Symbol('x')
|
| 44 |
+
>>> Id(x)
|
| 45 |
+
x
|
| 46 |
+
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
_symbol = Dummy('x')
|
| 50 |
+
|
| 51 |
+
@property
|
| 52 |
+
def signature(self):
|
| 53 |
+
return Tuple(self._symbol)
|
| 54 |
+
|
| 55 |
+
@property
|
| 56 |
+
def expr(self):
|
| 57 |
+
return self._symbol
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
Id = S.IdentityFunction
|
| 61 |
+
|
| 62 |
+
###############################################################################
|
| 63 |
+
############################# ROOT and SQUARE ROOT FUNCTION ###################
|
| 64 |
+
###############################################################################
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def sqrt(arg, evaluate=None):
|
| 68 |
+
"""Returns the principal square root.
|
| 69 |
+
|
| 70 |
+
Parameters
|
| 71 |
+
==========
|
| 72 |
+
|
| 73 |
+
evaluate : bool, optional
|
| 74 |
+
The parameter determines if the expression should be evaluated.
|
| 75 |
+
If ``None``, its value is taken from
|
| 76 |
+
``global_parameters.evaluate``.
|
| 77 |
+
|
| 78 |
+
Examples
|
| 79 |
+
========
|
| 80 |
+
|
| 81 |
+
>>> from sympy import sqrt, Symbol, S
|
| 82 |
+
>>> x = Symbol('x')
|
| 83 |
+
|
| 84 |
+
>>> sqrt(x)
|
| 85 |
+
sqrt(x)
|
| 86 |
+
|
| 87 |
+
>>> sqrt(x)**2
|
| 88 |
+
x
|
| 89 |
+
|
| 90 |
+
Note that sqrt(x**2) does not simplify to x.
|
| 91 |
+
|
| 92 |
+
>>> sqrt(x**2)
|
| 93 |
+
sqrt(x**2)
|
| 94 |
+
|
| 95 |
+
This is because the two are not equal to each other in general.
|
| 96 |
+
For example, consider x == -1:
|
| 97 |
+
|
| 98 |
+
>>> from sympy import Eq
|
| 99 |
+
>>> Eq(sqrt(x**2), x).subs(x, -1)
|
| 100 |
+
False
|
| 101 |
+
|
| 102 |
+
This is because sqrt computes the principal square root, so the square may
|
| 103 |
+
put the argument in a different branch. This identity does hold if x is
|
| 104 |
+
positive:
|
| 105 |
+
|
| 106 |
+
>>> y = Symbol('y', positive=True)
|
| 107 |
+
>>> sqrt(y**2)
|
| 108 |
+
y
|
| 109 |
+
|
| 110 |
+
You can force this simplification by using the powdenest() function with
|
| 111 |
+
the force option set to True:
|
| 112 |
+
|
| 113 |
+
>>> from sympy import powdenest
|
| 114 |
+
>>> sqrt(x**2)
|
| 115 |
+
sqrt(x**2)
|
| 116 |
+
>>> powdenest(sqrt(x**2), force=True)
|
| 117 |
+
x
|
| 118 |
+
|
| 119 |
+
To get both branches of the square root you can use the rootof function:
|
| 120 |
+
|
| 121 |
+
>>> from sympy import rootof
|
| 122 |
+
|
| 123 |
+
>>> [rootof(x**2-3,i) for i in (0,1)]
|
| 124 |
+
[-sqrt(3), sqrt(3)]
|
| 125 |
+
|
| 126 |
+
Although ``sqrt`` is printed, there is no ``sqrt`` function so looking for
|
| 127 |
+
``sqrt`` in an expression will fail:
|
| 128 |
+
|
| 129 |
+
>>> from sympy.utilities.misc import func_name
|
| 130 |
+
>>> func_name(sqrt(x))
|
| 131 |
+
'Pow'
|
| 132 |
+
>>> sqrt(x).has(sqrt)
|
| 133 |
+
False
|
| 134 |
+
|
| 135 |
+
To find ``sqrt`` look for ``Pow`` with an exponent of ``1/2``:
|
| 136 |
+
|
| 137 |
+
>>> (x + 1/sqrt(x)).find(lambda i: i.is_Pow and abs(i.exp) is S.Half)
|
| 138 |
+
{1/sqrt(x)}
|
| 139 |
+
|
| 140 |
+
See Also
|
| 141 |
+
========
|
| 142 |
+
|
| 143 |
+
sympy.polys.rootoftools.rootof, root, real_root
|
| 144 |
+
|
| 145 |
+
References
|
| 146 |
+
==========
|
| 147 |
+
|
| 148 |
+
.. [1] https://en.wikipedia.org/wiki/Square_root
|
| 149 |
+
.. [2] https://en.wikipedia.org/wiki/Principal_value
|
| 150 |
+
"""
|
| 151 |
+
# arg = sympify(arg) is handled by Pow
|
| 152 |
+
return Pow(arg, S.Half, evaluate=evaluate)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def cbrt(arg, evaluate=None):
|
| 156 |
+
"""Returns the principal cube root.
|
| 157 |
+
|
| 158 |
+
Parameters
|
| 159 |
+
==========
|
| 160 |
+
|
| 161 |
+
evaluate : bool, optional
|
| 162 |
+
The parameter determines if the expression should be evaluated.
|
| 163 |
+
If ``None``, its value is taken from
|
| 164 |
+
``global_parameters.evaluate``.
|
| 165 |
+
|
| 166 |
+
Examples
|
| 167 |
+
========
|
| 168 |
+
|
| 169 |
+
>>> from sympy import cbrt, Symbol
|
| 170 |
+
>>> x = Symbol('x')
|
| 171 |
+
|
| 172 |
+
>>> cbrt(x)
|
| 173 |
+
x**(1/3)
|
| 174 |
+
|
| 175 |
+
>>> cbrt(x)**3
|
| 176 |
+
x
|
| 177 |
+
|
| 178 |
+
Note that cbrt(x**3) does not simplify to x.
|
| 179 |
+
|
| 180 |
+
>>> cbrt(x**3)
|
| 181 |
+
(x**3)**(1/3)
|
| 182 |
+
|
| 183 |
+
This is because the two are not equal to each other in general.
|
| 184 |
+
For example, consider `x == -1`:
|
| 185 |
+
|
| 186 |
+
>>> from sympy import Eq
|
| 187 |
+
>>> Eq(cbrt(x**3), x).subs(x, -1)
|
| 188 |
+
False
|
| 189 |
+
|
| 190 |
+
This is because cbrt computes the principal cube root, this
|
| 191 |
+
identity does hold if `x` is positive:
|
| 192 |
+
|
| 193 |
+
>>> y = Symbol('y', positive=True)
|
| 194 |
+
>>> cbrt(y**3)
|
| 195 |
+
y
|
| 196 |
+
|
| 197 |
+
See Also
|
| 198 |
+
========
|
| 199 |
+
|
| 200 |
+
sympy.polys.rootoftools.rootof, root, real_root
|
| 201 |
+
|
| 202 |
+
References
|
| 203 |
+
==========
|
| 204 |
+
|
| 205 |
+
.. [1] https://en.wikipedia.org/wiki/Cube_root
|
| 206 |
+
.. [2] https://en.wikipedia.org/wiki/Principal_value
|
| 207 |
+
|
| 208 |
+
"""
|
| 209 |
+
return Pow(arg, Rational(1, 3), evaluate=evaluate)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def root(arg, n, k=0, evaluate=None):
|
| 213 |
+
r"""Returns the *k*-th *n*-th root of ``arg``.
|
| 214 |
+
|
| 215 |
+
Parameters
|
| 216 |
+
==========
|
| 217 |
+
|
| 218 |
+
k : int, optional
|
| 219 |
+
Should be an integer in $\{0, 1, ..., n-1\}$.
|
| 220 |
+
Defaults to the principal root if $0$.
|
| 221 |
+
|
| 222 |
+
evaluate : bool, optional
|
| 223 |
+
The parameter determines if the expression should be evaluated.
|
| 224 |
+
If ``None``, its value is taken from
|
| 225 |
+
``global_parameters.evaluate``.
|
| 226 |
+
|
| 227 |
+
Examples
|
| 228 |
+
========
|
| 229 |
+
|
| 230 |
+
>>> from sympy import root, Rational
|
| 231 |
+
>>> from sympy.abc import x, n
|
| 232 |
+
|
| 233 |
+
>>> root(x, 2)
|
| 234 |
+
sqrt(x)
|
| 235 |
+
|
| 236 |
+
>>> root(x, 3)
|
| 237 |
+
x**(1/3)
|
| 238 |
+
|
| 239 |
+
>>> root(x, n)
|
| 240 |
+
x**(1/n)
|
| 241 |
+
|
| 242 |
+
>>> root(x, -Rational(2, 3))
|
| 243 |
+
x**(-3/2)
|
| 244 |
+
|
| 245 |
+
To get the k-th n-th root, specify k:
|
| 246 |
+
|
| 247 |
+
>>> root(-2, 3, 2)
|
| 248 |
+
-(-1)**(2/3)*2**(1/3)
|
| 249 |
+
|
| 250 |
+
To get all n n-th roots you can use the rootof function.
|
| 251 |
+
The following examples show the roots of unity for n
|
| 252 |
+
equal 2, 3 and 4:
|
| 253 |
+
|
| 254 |
+
>>> from sympy import rootof
|
| 255 |
+
|
| 256 |
+
>>> [rootof(x**2 - 1, i) for i in range(2)]
|
| 257 |
+
[-1, 1]
|
| 258 |
+
|
| 259 |
+
>>> [rootof(x**3 - 1,i) for i in range(3)]
|
| 260 |
+
[1, -1/2 - sqrt(3)*I/2, -1/2 + sqrt(3)*I/2]
|
| 261 |
+
|
| 262 |
+
>>> [rootof(x**4 - 1,i) for i in range(4)]
|
| 263 |
+
[-1, 1, -I, I]
|
| 264 |
+
|
| 265 |
+
SymPy, like other symbolic algebra systems, returns the
|
| 266 |
+
complex root of negative numbers. This is the principal
|
| 267 |
+
root and differs from the text-book result that one might
|
| 268 |
+
be expecting. For example, the cube root of -8 does not
|
| 269 |
+
come back as -2:
|
| 270 |
+
|
| 271 |
+
>>> root(-8, 3)
|
| 272 |
+
2*(-1)**(1/3)
|
| 273 |
+
|
| 274 |
+
The real_root function can be used to either make the principal
|
| 275 |
+
result real (or simply to return the real root directly):
|
| 276 |
+
|
| 277 |
+
>>> from sympy import real_root
|
| 278 |
+
>>> real_root(_)
|
| 279 |
+
-2
|
| 280 |
+
>>> real_root(-32, 5)
|
| 281 |
+
-2
|
| 282 |
+
|
| 283 |
+
Alternatively, the n//2-th n-th root of a negative number can be
|
| 284 |
+
computed with root:
|
| 285 |
+
|
| 286 |
+
>>> root(-32, 5, 5//2)
|
| 287 |
+
-2
|
| 288 |
+
|
| 289 |
+
See Also
|
| 290 |
+
========
|
| 291 |
+
|
| 292 |
+
sympy.polys.rootoftools.rootof
|
| 293 |
+
sympy.core.intfunc.integer_nthroot
|
| 294 |
+
sqrt, real_root
|
| 295 |
+
|
| 296 |
+
References
|
| 297 |
+
==========
|
| 298 |
+
|
| 299 |
+
.. [1] https://en.wikipedia.org/wiki/Square_root
|
| 300 |
+
.. [2] https://en.wikipedia.org/wiki/Real_root
|
| 301 |
+
.. [3] https://en.wikipedia.org/wiki/Root_of_unity
|
| 302 |
+
.. [4] https://en.wikipedia.org/wiki/Principal_value
|
| 303 |
+
.. [5] https://mathworld.wolfram.com/CubeRoot.html
|
| 304 |
+
|
| 305 |
+
"""
|
| 306 |
+
n = sympify(n)
|
| 307 |
+
if k:
|
| 308 |
+
return Mul(Pow(arg, S.One/n, evaluate=evaluate), S.NegativeOne**(2*k/n), evaluate=evaluate)
|
| 309 |
+
return Pow(arg, 1/n, evaluate=evaluate)
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
def real_root(arg, n=None, evaluate=None):
|
| 313 |
+
r"""Return the real *n*'th-root of *arg* if possible.
|
| 314 |
+
|
| 315 |
+
Parameters
|
| 316 |
+
==========
|
| 317 |
+
|
| 318 |
+
n : int or None, optional
|
| 319 |
+
If *n* is ``None``, then all instances of
|
| 320 |
+
$(-n)^{1/\text{odd}}$ will be changed to $-n^{1/\text{odd}}$.
|
| 321 |
+
This will only create a real root of a principal root.
|
| 322 |
+
The presence of other factors may cause the result to not be
|
| 323 |
+
real.
|
| 324 |
+
|
| 325 |
+
evaluate : bool, optional
|
| 326 |
+
The parameter determines if the expression should be evaluated.
|
| 327 |
+
If ``None``, its value is taken from
|
| 328 |
+
``global_parameters.evaluate``.
|
| 329 |
+
|
| 330 |
+
Examples
|
| 331 |
+
========
|
| 332 |
+
|
| 333 |
+
>>> from sympy import root, real_root
|
| 334 |
+
|
| 335 |
+
>>> real_root(-8, 3)
|
| 336 |
+
-2
|
| 337 |
+
>>> root(-8, 3)
|
| 338 |
+
2*(-1)**(1/3)
|
| 339 |
+
>>> real_root(_)
|
| 340 |
+
-2
|
| 341 |
+
|
| 342 |
+
If one creates a non-principal root and applies real_root, the
|
| 343 |
+
result will not be real (so use with caution):
|
| 344 |
+
|
| 345 |
+
>>> root(-8, 3, 2)
|
| 346 |
+
-2*(-1)**(2/3)
|
| 347 |
+
>>> real_root(_)
|
| 348 |
+
-2*(-1)**(2/3)
|
| 349 |
+
|
| 350 |
+
See Also
|
| 351 |
+
========
|
| 352 |
+
|
| 353 |
+
sympy.polys.rootoftools.rootof
|
| 354 |
+
sympy.core.intfunc.integer_nthroot
|
| 355 |
+
root, sqrt
|
| 356 |
+
"""
|
| 357 |
+
from sympy.functions.elementary.complexes import Abs, im, sign
|
| 358 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 359 |
+
if n is not None:
|
| 360 |
+
return Piecewise(
|
| 361 |
+
(root(arg, n, evaluate=evaluate), Or(Eq(n, S.One), Eq(n, S.NegativeOne))),
|
| 362 |
+
(Mul(sign(arg), root(Abs(arg), n, evaluate=evaluate), evaluate=evaluate),
|
| 363 |
+
And(Eq(im(arg), S.Zero), Eq(Mod(n, 2), S.One))),
|
| 364 |
+
(root(arg, n, evaluate=evaluate), True))
|
| 365 |
+
rv = sympify(arg)
|
| 366 |
+
n1pow = Transform(lambda x: -(-x.base)**x.exp,
|
| 367 |
+
lambda x:
|
| 368 |
+
x.is_Pow and
|
| 369 |
+
x.base.is_negative and
|
| 370 |
+
x.exp.is_Rational and
|
| 371 |
+
x.exp.p == 1 and x.exp.q % 2)
|
| 372 |
+
return rv.xreplace(n1pow)
|
| 373 |
+
|
| 374 |
+
###############################################################################
|
| 375 |
+
############################# MINIMUM and MAXIMUM #############################
|
| 376 |
+
###############################################################################
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
class MinMaxBase(Expr, LatticeOp):
|
| 380 |
+
def __new__(cls, *args, **assumptions):
|
| 381 |
+
from sympy.core.parameters import global_parameters
|
| 382 |
+
evaluate = assumptions.pop('evaluate', global_parameters.evaluate)
|
| 383 |
+
args = (sympify(arg) for arg in args)
|
| 384 |
+
|
| 385 |
+
# first standard filter, for cls.zero and cls.identity
|
| 386 |
+
# also reshape Max(a, Max(b, c)) to Max(a, b, c)
|
| 387 |
+
|
| 388 |
+
if evaluate:
|
| 389 |
+
try:
|
| 390 |
+
args = frozenset(cls._new_args_filter(args))
|
| 391 |
+
except ShortCircuit:
|
| 392 |
+
return cls.zero
|
| 393 |
+
# remove redundant args that are easily identified
|
| 394 |
+
args = cls._collapse_arguments(args, **assumptions)
|
| 395 |
+
# find local zeros
|
| 396 |
+
args = cls._find_localzeros(args, **assumptions)
|
| 397 |
+
args = frozenset(args)
|
| 398 |
+
|
| 399 |
+
if not args:
|
| 400 |
+
return cls.identity
|
| 401 |
+
|
| 402 |
+
if len(args) == 1:
|
| 403 |
+
return list(args).pop()
|
| 404 |
+
|
| 405 |
+
# base creation
|
| 406 |
+
obj = Expr.__new__(cls, *ordered(args), **assumptions)
|
| 407 |
+
obj._argset = args
|
| 408 |
+
return obj
|
| 409 |
+
|
| 410 |
+
@classmethod
|
| 411 |
+
def _collapse_arguments(cls, args, **assumptions):
|
| 412 |
+
"""Remove redundant args.
|
| 413 |
+
|
| 414 |
+
Examples
|
| 415 |
+
========
|
| 416 |
+
|
| 417 |
+
>>> from sympy import Min, Max
|
| 418 |
+
>>> from sympy.abc import a, b, c, d, e
|
| 419 |
+
|
| 420 |
+
Any arg in parent that appears in any
|
| 421 |
+
parent-like function in any of the flat args
|
| 422 |
+
of parent can be removed from that sub-arg:
|
| 423 |
+
|
| 424 |
+
>>> Min(a, Max(b, Min(a, c, d)))
|
| 425 |
+
Min(a, Max(b, Min(c, d)))
|
| 426 |
+
|
| 427 |
+
If the arg of parent appears in an opposite-than parent
|
| 428 |
+
function in any of the flat args of parent that function
|
| 429 |
+
can be replaced with the arg:
|
| 430 |
+
|
| 431 |
+
>>> Min(a, Max(b, Min(c, d, Max(a, e))))
|
| 432 |
+
Min(a, Max(b, Min(a, c, d)))
|
| 433 |
+
"""
|
| 434 |
+
if not args:
|
| 435 |
+
return args
|
| 436 |
+
args = list(ordered(args))
|
| 437 |
+
if cls == Min:
|
| 438 |
+
other = Max
|
| 439 |
+
else:
|
| 440 |
+
other = Min
|
| 441 |
+
|
| 442 |
+
# find global comparable max of Max and min of Min if a new
|
| 443 |
+
# value is being introduced in these args at position 0 of
|
| 444 |
+
# the ordered args
|
| 445 |
+
if args[0].is_number:
|
| 446 |
+
sifted = mins, maxs = [], []
|
| 447 |
+
for i in args:
|
| 448 |
+
for v in walk(i, Min, Max):
|
| 449 |
+
if v.args[0].is_comparable:
|
| 450 |
+
sifted[isinstance(v, Max)].append(v)
|
| 451 |
+
small = Min.identity
|
| 452 |
+
for i in mins:
|
| 453 |
+
v = i.args[0]
|
| 454 |
+
if v.is_number and (v < small) == True:
|
| 455 |
+
small = v
|
| 456 |
+
big = Max.identity
|
| 457 |
+
for i in maxs:
|
| 458 |
+
v = i.args[0]
|
| 459 |
+
if v.is_number and (v > big) == True:
|
| 460 |
+
big = v
|
| 461 |
+
# at the point when this function is called from __new__,
|
| 462 |
+
# there may be more than one numeric arg present since
|
| 463 |
+
# local zeros have not been handled yet, so look through
|
| 464 |
+
# more than the first arg
|
| 465 |
+
if cls == Min:
|
| 466 |
+
for arg in args:
|
| 467 |
+
if not arg.is_number:
|
| 468 |
+
break
|
| 469 |
+
if (arg < small) == True:
|
| 470 |
+
small = arg
|
| 471 |
+
elif cls == Max:
|
| 472 |
+
for arg in args:
|
| 473 |
+
if not arg.is_number:
|
| 474 |
+
break
|
| 475 |
+
if (arg > big) == True:
|
| 476 |
+
big = arg
|
| 477 |
+
T = None
|
| 478 |
+
if cls == Min:
|
| 479 |
+
if small != Min.identity:
|
| 480 |
+
other = Max
|
| 481 |
+
T = small
|
| 482 |
+
elif big != Max.identity:
|
| 483 |
+
other = Min
|
| 484 |
+
T = big
|
| 485 |
+
if T is not None:
|
| 486 |
+
# remove numerical redundancy
|
| 487 |
+
for i in range(len(args)):
|
| 488 |
+
a = args[i]
|
| 489 |
+
if isinstance(a, other):
|
| 490 |
+
a0 = a.args[0]
|
| 491 |
+
if ((a0 > T) if other == Max else (a0 < T)) == True:
|
| 492 |
+
args[i] = cls.identity
|
| 493 |
+
|
| 494 |
+
# remove redundant symbolic args
|
| 495 |
+
def do(ai, a):
|
| 496 |
+
if not isinstance(ai, (Min, Max)):
|
| 497 |
+
return ai
|
| 498 |
+
cond = a in ai.args
|
| 499 |
+
if not cond:
|
| 500 |
+
return ai.func(*[do(i, a) for i in ai.args],
|
| 501 |
+
evaluate=False)
|
| 502 |
+
if isinstance(ai, cls):
|
| 503 |
+
return ai.func(*[do(i, a) for i in ai.args if i != a],
|
| 504 |
+
evaluate=False)
|
| 505 |
+
return a
|
| 506 |
+
for i, a in enumerate(args):
|
| 507 |
+
args[i + 1:] = [do(ai, a) for ai in args[i + 1:]]
|
| 508 |
+
|
| 509 |
+
# factor out common elements as for
|
| 510 |
+
# Min(Max(x, y), Max(x, z)) -> Max(x, Min(y, z))
|
| 511 |
+
# and vice versa when swapping Min/Max -- do this only for the
|
| 512 |
+
# easy case where all functions contain something in common;
|
| 513 |
+
# trying to find some optimal subset of args to modify takes
|
| 514 |
+
# too long
|
| 515 |
+
|
| 516 |
+
def factor_minmax(args):
|
| 517 |
+
is_other = lambda arg: isinstance(arg, other)
|
| 518 |
+
other_args, remaining_args = sift(args, is_other, binary=True)
|
| 519 |
+
if not other_args:
|
| 520 |
+
return args
|
| 521 |
+
|
| 522 |
+
# Min(Max(x, y, z), Max(x, y, u, v)) -> {x,y}, ({z}, {u,v})
|
| 523 |
+
arg_sets = [set(arg.args) for arg in other_args]
|
| 524 |
+
common = set.intersection(*arg_sets)
|
| 525 |
+
if not common:
|
| 526 |
+
return args
|
| 527 |
+
|
| 528 |
+
new_other_args = list(common)
|
| 529 |
+
arg_sets_diff = [arg_set - common for arg_set in arg_sets]
|
| 530 |
+
|
| 531 |
+
# If any set is empty after removing common then all can be
|
| 532 |
+
# discarded e.g. Min(Max(a, b, c), Max(a, b)) -> Max(a, b)
|
| 533 |
+
if all(arg_sets_diff):
|
| 534 |
+
other_args_diff = [other(*s, evaluate=False) for s in arg_sets_diff]
|
| 535 |
+
new_other_args.append(cls(*other_args_diff, evaluate=False))
|
| 536 |
+
|
| 537 |
+
other_args_factored = other(*new_other_args, evaluate=False)
|
| 538 |
+
return remaining_args + [other_args_factored]
|
| 539 |
+
|
| 540 |
+
if len(args) > 1:
|
| 541 |
+
args = factor_minmax(args)
|
| 542 |
+
|
| 543 |
+
return args
|
| 544 |
+
|
| 545 |
+
@classmethod
|
| 546 |
+
def _new_args_filter(cls, arg_sequence):
|
| 547 |
+
"""
|
| 548 |
+
Generator filtering args.
|
| 549 |
+
|
| 550 |
+
first standard filter, for cls.zero and cls.identity.
|
| 551 |
+
Also reshape ``Max(a, Max(b, c))`` to ``Max(a, b, c)``,
|
| 552 |
+
and check arguments for comparability
|
| 553 |
+
"""
|
| 554 |
+
for arg in arg_sequence:
|
| 555 |
+
# pre-filter, checking comparability of arguments
|
| 556 |
+
if not isinstance(arg, Expr) or arg.is_extended_real is False or (
|
| 557 |
+
arg.is_number and
|
| 558 |
+
not arg.is_comparable):
|
| 559 |
+
raise ValueError("The argument '%s' is not comparable." % arg)
|
| 560 |
+
|
| 561 |
+
if arg == cls.zero:
|
| 562 |
+
raise ShortCircuit(arg)
|
| 563 |
+
elif arg == cls.identity:
|
| 564 |
+
continue
|
| 565 |
+
elif arg.func == cls:
|
| 566 |
+
yield from arg.args
|
| 567 |
+
else:
|
| 568 |
+
yield arg
|
| 569 |
+
|
| 570 |
+
@classmethod
|
| 571 |
+
def _find_localzeros(cls, values, **options):
|
| 572 |
+
"""
|
| 573 |
+
Sequentially allocate values to localzeros.
|
| 574 |
+
|
| 575 |
+
When a value is identified as being more extreme than another member it
|
| 576 |
+
replaces that member; if this is never true, then the value is simply
|
| 577 |
+
appended to the localzeros.
|
| 578 |
+
"""
|
| 579 |
+
localzeros = set()
|
| 580 |
+
for v in values:
|
| 581 |
+
is_newzero = True
|
| 582 |
+
localzeros_ = list(localzeros)
|
| 583 |
+
for z in localzeros_:
|
| 584 |
+
if id(v) == id(z):
|
| 585 |
+
is_newzero = False
|
| 586 |
+
else:
|
| 587 |
+
con = cls._is_connected(v, z)
|
| 588 |
+
if con:
|
| 589 |
+
is_newzero = False
|
| 590 |
+
if con is True or con == cls:
|
| 591 |
+
localzeros.remove(z)
|
| 592 |
+
localzeros.update([v])
|
| 593 |
+
if is_newzero:
|
| 594 |
+
localzeros.update([v])
|
| 595 |
+
return localzeros
|
| 596 |
+
|
| 597 |
+
@classmethod
|
| 598 |
+
def _is_connected(cls, x, y):
|
| 599 |
+
"""
|
| 600 |
+
Check if x and y are connected somehow.
|
| 601 |
+
"""
|
| 602 |
+
for i in range(2):
|
| 603 |
+
if x == y:
|
| 604 |
+
return True
|
| 605 |
+
t, f = Max, Min
|
| 606 |
+
for op in "><":
|
| 607 |
+
for j in range(2):
|
| 608 |
+
try:
|
| 609 |
+
if op == ">":
|
| 610 |
+
v = x >= y
|
| 611 |
+
else:
|
| 612 |
+
v = x <= y
|
| 613 |
+
except TypeError:
|
| 614 |
+
return False # non-real arg
|
| 615 |
+
if not v.is_Relational:
|
| 616 |
+
return t if v else f
|
| 617 |
+
t, f = f, t
|
| 618 |
+
x, y = y, x
|
| 619 |
+
x, y = y, x # run next pass with reversed order relative to start
|
| 620 |
+
# simplification can be expensive, so be conservative
|
| 621 |
+
# in what is attempted
|
| 622 |
+
x = factor_terms(x - y)
|
| 623 |
+
y = S.Zero
|
| 624 |
+
|
| 625 |
+
return False
|
| 626 |
+
|
| 627 |
+
def _eval_derivative(self, s):
|
| 628 |
+
# f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s)
|
| 629 |
+
i = 0
|
| 630 |
+
l = []
|
| 631 |
+
for a in self.args:
|
| 632 |
+
i += 1
|
| 633 |
+
da = a.diff(s)
|
| 634 |
+
if da.is_zero:
|
| 635 |
+
continue
|
| 636 |
+
try:
|
| 637 |
+
df = self.fdiff(i)
|
| 638 |
+
except ArgumentIndexError:
|
| 639 |
+
df = super().fdiff(i)
|
| 640 |
+
l.append(df * da)
|
| 641 |
+
return Add(*l)
|
| 642 |
+
|
| 643 |
+
def _eval_rewrite_as_Abs(self, *args, **kwargs):
|
| 644 |
+
from sympy.functions.elementary.complexes import Abs
|
| 645 |
+
s = (args[0] + self.func(*args[1:]))/2
|
| 646 |
+
d = abs(args[0] - self.func(*args[1:]))/2
|
| 647 |
+
return (s + d if isinstance(self, Max) else s - d).rewrite(Abs)
|
| 648 |
+
|
| 649 |
+
def evalf(self, n=15, **options):
|
| 650 |
+
return self.func(*[a.evalf(n, **options) for a in self.args])
|
| 651 |
+
|
| 652 |
+
def n(self, *args, **kwargs):
|
| 653 |
+
return self.evalf(*args, **kwargs)
|
| 654 |
+
|
| 655 |
+
_eval_is_algebraic = lambda s: _torf(i.is_algebraic for i in s.args)
|
| 656 |
+
_eval_is_antihermitian = lambda s: _torf(i.is_antihermitian for i in s.args)
|
| 657 |
+
_eval_is_commutative = lambda s: _torf(i.is_commutative for i in s.args)
|
| 658 |
+
_eval_is_complex = lambda s: _torf(i.is_complex for i in s.args)
|
| 659 |
+
_eval_is_composite = lambda s: _torf(i.is_composite for i in s.args)
|
| 660 |
+
_eval_is_even = lambda s: _torf(i.is_even for i in s.args)
|
| 661 |
+
_eval_is_finite = lambda s: _torf(i.is_finite for i in s.args)
|
| 662 |
+
_eval_is_hermitian = lambda s: _torf(i.is_hermitian for i in s.args)
|
| 663 |
+
_eval_is_imaginary = lambda s: _torf(i.is_imaginary for i in s.args)
|
| 664 |
+
_eval_is_infinite = lambda s: _torf(i.is_infinite for i in s.args)
|
| 665 |
+
_eval_is_integer = lambda s: _torf(i.is_integer for i in s.args)
|
| 666 |
+
_eval_is_irrational = lambda s: _torf(i.is_irrational for i in s.args)
|
| 667 |
+
_eval_is_negative = lambda s: _torf(i.is_negative for i in s.args)
|
| 668 |
+
_eval_is_noninteger = lambda s: _torf(i.is_noninteger for i in s.args)
|
| 669 |
+
_eval_is_nonnegative = lambda s: _torf(i.is_nonnegative for i in s.args)
|
| 670 |
+
_eval_is_nonpositive = lambda s: _torf(i.is_nonpositive for i in s.args)
|
| 671 |
+
_eval_is_nonzero = lambda s: _torf(i.is_nonzero for i in s.args)
|
| 672 |
+
_eval_is_odd = lambda s: _torf(i.is_odd for i in s.args)
|
| 673 |
+
_eval_is_polar = lambda s: _torf(i.is_polar for i in s.args)
|
| 674 |
+
_eval_is_positive = lambda s: _torf(i.is_positive for i in s.args)
|
| 675 |
+
_eval_is_prime = lambda s: _torf(i.is_prime for i in s.args)
|
| 676 |
+
_eval_is_rational = lambda s: _torf(i.is_rational for i in s.args)
|
| 677 |
+
_eval_is_real = lambda s: _torf(i.is_real for i in s.args)
|
| 678 |
+
_eval_is_extended_real = lambda s: _torf(i.is_extended_real for i in s.args)
|
| 679 |
+
_eval_is_transcendental = lambda s: _torf(i.is_transcendental for i in s.args)
|
| 680 |
+
_eval_is_zero = lambda s: _torf(i.is_zero for i in s.args)
|
| 681 |
+
|
| 682 |
+
|
| 683 |
+
class Max(MinMaxBase, Application):
|
| 684 |
+
r"""
|
| 685 |
+
Return, if possible, the maximum value of the list.
|
| 686 |
+
|
| 687 |
+
When number of arguments is equal one, then
|
| 688 |
+
return this argument.
|
| 689 |
+
|
| 690 |
+
When number of arguments is equal two, then
|
| 691 |
+
return, if possible, the value from (a, b) that is $\ge$ the other.
|
| 692 |
+
|
| 693 |
+
In common case, when the length of list greater than 2, the task
|
| 694 |
+
is more complicated. Return only the arguments, which are greater
|
| 695 |
+
than others, if it is possible to determine directional relation.
|
| 696 |
+
|
| 697 |
+
If is not possible to determine such a relation, return a partially
|
| 698 |
+
evaluated result.
|
| 699 |
+
|
| 700 |
+
Assumptions are used to make the decision too.
|
| 701 |
+
|
| 702 |
+
Also, only comparable arguments are permitted.
|
| 703 |
+
|
| 704 |
+
It is named ``Max`` and not ``max`` to avoid conflicts
|
| 705 |
+
with the built-in function ``max``.
|
| 706 |
+
|
| 707 |
+
|
| 708 |
+
Examples
|
| 709 |
+
========
|
| 710 |
+
|
| 711 |
+
>>> from sympy import Max, Symbol, oo
|
| 712 |
+
>>> from sympy.abc import x, y, z
|
| 713 |
+
>>> p = Symbol('p', positive=True)
|
| 714 |
+
>>> n = Symbol('n', negative=True)
|
| 715 |
+
|
| 716 |
+
>>> Max(x, -2)
|
| 717 |
+
Max(-2, x)
|
| 718 |
+
>>> Max(x, -2).subs(x, 3)
|
| 719 |
+
3
|
| 720 |
+
>>> Max(p, -2)
|
| 721 |
+
p
|
| 722 |
+
>>> Max(x, y)
|
| 723 |
+
Max(x, y)
|
| 724 |
+
>>> Max(x, y) == Max(y, x)
|
| 725 |
+
True
|
| 726 |
+
>>> Max(x, Max(y, z))
|
| 727 |
+
Max(x, y, z)
|
| 728 |
+
>>> Max(n, 8, p, 7, -oo)
|
| 729 |
+
Max(8, p)
|
| 730 |
+
>>> Max (1, x, oo)
|
| 731 |
+
oo
|
| 732 |
+
|
| 733 |
+
* Algorithm
|
| 734 |
+
|
| 735 |
+
The task can be considered as searching of supremums in the
|
| 736 |
+
directed complete partial orders [1]_.
|
| 737 |
+
|
| 738 |
+
The source values are sequentially allocated by the isolated subsets
|
| 739 |
+
in which supremums are searched and result as Max arguments.
|
| 740 |
+
|
| 741 |
+
If the resulted supremum is single, then it is returned.
|
| 742 |
+
|
| 743 |
+
The isolated subsets are the sets of values which are only the comparable
|
| 744 |
+
with each other in the current set. E.g. natural numbers are comparable with
|
| 745 |
+
each other, but not comparable with the `x` symbol. Another example: the
|
| 746 |
+
symbol `x` with negative assumption is comparable with a natural number.
|
| 747 |
+
|
| 748 |
+
Also there are "least" elements, which are comparable with all others,
|
| 749 |
+
and have a zero property (maximum or minimum for all elements).
|
| 750 |
+
For example, in case of $\infty$, the allocation operation is terminated
|
| 751 |
+
and only this value is returned.
|
| 752 |
+
|
| 753 |
+
Assumption:
|
| 754 |
+
- if $A > B > C$ then $A > C$
|
| 755 |
+
- if $A = B$ then $B$ can be removed
|
| 756 |
+
|
| 757 |
+
References
|
| 758 |
+
==========
|
| 759 |
+
|
| 760 |
+
.. [1] https://en.wikipedia.org/wiki/Directed_complete_partial_order
|
| 761 |
+
.. [2] https://en.wikipedia.org/wiki/Lattice_%28order%29
|
| 762 |
+
|
| 763 |
+
See Also
|
| 764 |
+
========
|
| 765 |
+
|
| 766 |
+
Min : find minimum values
|
| 767 |
+
"""
|
| 768 |
+
zero = S.Infinity
|
| 769 |
+
identity = S.NegativeInfinity
|
| 770 |
+
|
| 771 |
+
def fdiff( self, argindex ):
|
| 772 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 773 |
+
n = len(self.args)
|
| 774 |
+
if 0 < argindex and argindex <= n:
|
| 775 |
+
argindex -= 1
|
| 776 |
+
if n == 2:
|
| 777 |
+
return Heaviside(self.args[argindex] - self.args[1 - argindex])
|
| 778 |
+
newargs = tuple([self.args[i] for i in range(n) if i != argindex])
|
| 779 |
+
return Heaviside(self.args[argindex] - Max(*newargs))
|
| 780 |
+
else:
|
| 781 |
+
raise ArgumentIndexError(self, argindex)
|
| 782 |
+
|
| 783 |
+
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
| 784 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 785 |
+
return Add(*[j*Mul(*[Heaviside(j - i) for i in args if i!=j]) \
|
| 786 |
+
for j in args])
|
| 787 |
+
|
| 788 |
+
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
| 789 |
+
return _minmax_as_Piecewise('>=', *args)
|
| 790 |
+
|
| 791 |
+
def _eval_is_positive(self):
|
| 792 |
+
return fuzzy_or(a.is_positive for a in self.args)
|
| 793 |
+
|
| 794 |
+
def _eval_is_nonnegative(self):
|
| 795 |
+
return fuzzy_or(a.is_nonnegative for a in self.args)
|
| 796 |
+
|
| 797 |
+
def _eval_is_negative(self):
|
| 798 |
+
return fuzzy_and(a.is_negative for a in self.args)
|
| 799 |
+
|
| 800 |
+
|
| 801 |
+
class Min(MinMaxBase, Application):
|
| 802 |
+
"""
|
| 803 |
+
Return, if possible, the minimum value of the list.
|
| 804 |
+
It is named ``Min`` and not ``min`` to avoid conflicts
|
| 805 |
+
with the built-in function ``min``.
|
| 806 |
+
|
| 807 |
+
Examples
|
| 808 |
+
========
|
| 809 |
+
|
| 810 |
+
>>> from sympy import Min, Symbol, oo
|
| 811 |
+
>>> from sympy.abc import x, y
|
| 812 |
+
>>> p = Symbol('p', positive=True)
|
| 813 |
+
>>> n = Symbol('n', negative=True)
|
| 814 |
+
|
| 815 |
+
>>> Min(x, -2)
|
| 816 |
+
Min(-2, x)
|
| 817 |
+
>>> Min(x, -2).subs(x, 3)
|
| 818 |
+
-2
|
| 819 |
+
>>> Min(p, -3)
|
| 820 |
+
-3
|
| 821 |
+
>>> Min(x, y)
|
| 822 |
+
Min(x, y)
|
| 823 |
+
>>> Min(n, 8, p, -7, p, oo)
|
| 824 |
+
Min(-7, n)
|
| 825 |
+
|
| 826 |
+
See Also
|
| 827 |
+
========
|
| 828 |
+
|
| 829 |
+
Max : find maximum values
|
| 830 |
+
"""
|
| 831 |
+
zero = S.NegativeInfinity
|
| 832 |
+
identity = S.Infinity
|
| 833 |
+
|
| 834 |
+
def fdiff( self, argindex ):
|
| 835 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 836 |
+
n = len(self.args)
|
| 837 |
+
if 0 < argindex and argindex <= n:
|
| 838 |
+
argindex -= 1
|
| 839 |
+
if n == 2:
|
| 840 |
+
return Heaviside( self.args[1-argindex] - self.args[argindex] )
|
| 841 |
+
newargs = tuple([ self.args[i] for i in range(n) if i != argindex])
|
| 842 |
+
return Heaviside( Min(*newargs) - self.args[argindex] )
|
| 843 |
+
else:
|
| 844 |
+
raise ArgumentIndexError(self, argindex)
|
| 845 |
+
|
| 846 |
+
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
| 847 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 848 |
+
return Add(*[j*Mul(*[Heaviside(i-j) for i in args if i!=j]) \
|
| 849 |
+
for j in args])
|
| 850 |
+
|
| 851 |
+
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
| 852 |
+
return _minmax_as_Piecewise('<=', *args)
|
| 853 |
+
|
| 854 |
+
def _eval_is_positive(self):
|
| 855 |
+
return fuzzy_and(a.is_positive for a in self.args)
|
| 856 |
+
|
| 857 |
+
def _eval_is_nonnegative(self):
|
| 858 |
+
return fuzzy_and(a.is_nonnegative for a in self.args)
|
| 859 |
+
|
| 860 |
+
def _eval_is_negative(self):
|
| 861 |
+
return fuzzy_or(a.is_negative for a in self.args)
|
| 862 |
+
|
| 863 |
+
|
| 864 |
+
class Rem(DefinedFunction):
|
| 865 |
+
"""Returns the remainder when ``p`` is divided by ``q`` where ``p`` is finite
|
| 866 |
+
and ``q`` is not equal to zero. The result, ``p - int(p/q)*q``, has the same sign
|
| 867 |
+
as the divisor.
|
| 868 |
+
|
| 869 |
+
Parameters
|
| 870 |
+
==========
|
| 871 |
+
|
| 872 |
+
p : Expr
|
| 873 |
+
Dividend.
|
| 874 |
+
|
| 875 |
+
q : Expr
|
| 876 |
+
Divisor.
|
| 877 |
+
|
| 878 |
+
Notes
|
| 879 |
+
=====
|
| 880 |
+
|
| 881 |
+
``Rem`` corresponds to the ``%`` operator in C.
|
| 882 |
+
|
| 883 |
+
Examples
|
| 884 |
+
========
|
| 885 |
+
|
| 886 |
+
>>> from sympy.abc import x, y
|
| 887 |
+
>>> from sympy import Rem
|
| 888 |
+
>>> Rem(x**3, y)
|
| 889 |
+
Rem(x**3, y)
|
| 890 |
+
>>> Rem(x**3, y).subs({x: -5, y: 3})
|
| 891 |
+
-2
|
| 892 |
+
|
| 893 |
+
See Also
|
| 894 |
+
========
|
| 895 |
+
|
| 896 |
+
Mod
|
| 897 |
+
"""
|
| 898 |
+
kind = NumberKind
|
| 899 |
+
|
| 900 |
+
@classmethod
|
| 901 |
+
def eval(cls, p, q):
|
| 902 |
+
"""Return the function remainder if both p, q are numbers and q is not
|
| 903 |
+
zero.
|
| 904 |
+
"""
|
| 905 |
+
|
| 906 |
+
if q.is_zero:
|
| 907 |
+
raise ZeroDivisionError("Division by zero")
|
| 908 |
+
if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False:
|
| 909 |
+
return S.NaN
|
| 910 |
+
if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1):
|
| 911 |
+
return S.Zero
|
| 912 |
+
|
| 913 |
+
if q.is_Number:
|
| 914 |
+
if p.is_Number:
|
| 915 |
+
return p - Integer(p/q)*q
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/piecewise.py
ADDED
|
@@ -0,0 +1,1517 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S, diff, Tuple, Dummy, Mul
|
| 2 |
+
from sympy.core.basic import Basic, as_Basic
|
| 3 |
+
from sympy.core.function import DefinedFunction
|
| 4 |
+
from sympy.core.numbers import Rational, NumberSymbol, _illegal
|
| 5 |
+
from sympy.core.parameters import global_parameters
|
| 6 |
+
from sympy.core.relational import (Lt, Gt, Eq, Ne, Relational,
|
| 7 |
+
_canonical, _canonical_coeff)
|
| 8 |
+
from sympy.core.sorting import ordered
|
| 9 |
+
from sympy.functions.elementary.miscellaneous import Max, Min
|
| 10 |
+
from sympy.logic.boolalg import (And, Boolean, distribute_and_over_or, Not,
|
| 11 |
+
true, false, Or, ITE, simplify_logic, to_cnf, distribute_or_over_and)
|
| 12 |
+
from sympy.utilities.iterables import uniq, sift, common_prefix
|
| 13 |
+
from sympy.utilities.misc import filldedent, func_name
|
| 14 |
+
|
| 15 |
+
from itertools import product
|
| 16 |
+
|
| 17 |
+
Undefined = S.NaN # Piecewise()
|
| 18 |
+
|
| 19 |
+
class ExprCondPair(Tuple):
|
| 20 |
+
"""Represents an expression, condition pair."""
|
| 21 |
+
|
| 22 |
+
def __new__(cls, expr, cond):
|
| 23 |
+
expr = as_Basic(expr)
|
| 24 |
+
if cond == True:
|
| 25 |
+
return Tuple.__new__(cls, expr, true)
|
| 26 |
+
elif cond == False:
|
| 27 |
+
return Tuple.__new__(cls, expr, false)
|
| 28 |
+
elif isinstance(cond, Basic) and cond.has(Piecewise):
|
| 29 |
+
cond = piecewise_fold(cond)
|
| 30 |
+
if isinstance(cond, Piecewise):
|
| 31 |
+
cond = cond.rewrite(ITE)
|
| 32 |
+
|
| 33 |
+
if not isinstance(cond, Boolean):
|
| 34 |
+
raise TypeError(filldedent('''
|
| 35 |
+
Second argument must be a Boolean,
|
| 36 |
+
not `%s`''' % func_name(cond)))
|
| 37 |
+
return Tuple.__new__(cls, expr, cond)
|
| 38 |
+
|
| 39 |
+
@property
|
| 40 |
+
def expr(self):
|
| 41 |
+
"""
|
| 42 |
+
Returns the expression of this pair.
|
| 43 |
+
"""
|
| 44 |
+
return self.args[0]
|
| 45 |
+
|
| 46 |
+
@property
|
| 47 |
+
def cond(self):
|
| 48 |
+
"""
|
| 49 |
+
Returns the condition of this pair.
|
| 50 |
+
"""
|
| 51 |
+
return self.args[1]
|
| 52 |
+
|
| 53 |
+
@property
|
| 54 |
+
def is_commutative(self):
|
| 55 |
+
return self.expr.is_commutative
|
| 56 |
+
|
| 57 |
+
def __iter__(self):
|
| 58 |
+
yield self.expr
|
| 59 |
+
yield self.cond
|
| 60 |
+
|
| 61 |
+
def _eval_simplify(self, **kwargs):
|
| 62 |
+
return self.func(*[a.simplify(**kwargs) for a in self.args])
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class Piecewise(DefinedFunction):
|
| 66 |
+
"""
|
| 67 |
+
Represents a piecewise function.
|
| 68 |
+
|
| 69 |
+
Usage:
|
| 70 |
+
|
| 71 |
+
Piecewise( (expr,cond), (expr,cond), ... )
|
| 72 |
+
- Each argument is a 2-tuple defining an expression and condition
|
| 73 |
+
- The conds are evaluated in turn returning the first that is True.
|
| 74 |
+
If any of the evaluated conds are not explicitly False,
|
| 75 |
+
e.g. ``x < 1``, the function is returned in symbolic form.
|
| 76 |
+
- If the function is evaluated at a place where all conditions are False,
|
| 77 |
+
nan will be returned.
|
| 78 |
+
- Pairs where the cond is explicitly False, will be removed and no pair
|
| 79 |
+
appearing after a True condition will ever be retained. If a single
|
| 80 |
+
pair with a True condition remains, it will be returned, even when
|
| 81 |
+
evaluation is False.
|
| 82 |
+
|
| 83 |
+
Examples
|
| 84 |
+
========
|
| 85 |
+
|
| 86 |
+
>>> from sympy import Piecewise, log, piecewise_fold
|
| 87 |
+
>>> from sympy.abc import x, y
|
| 88 |
+
>>> f = x**2
|
| 89 |
+
>>> g = log(x)
|
| 90 |
+
>>> p = Piecewise((0, x < -1), (f, x <= 1), (g, True))
|
| 91 |
+
>>> p.subs(x,1)
|
| 92 |
+
1
|
| 93 |
+
>>> p.subs(x,5)
|
| 94 |
+
log(5)
|
| 95 |
+
|
| 96 |
+
Booleans can contain Piecewise elements:
|
| 97 |
+
|
| 98 |
+
>>> cond = (x < y).subs(x, Piecewise((2, x < 0), (3, True))); cond
|
| 99 |
+
Piecewise((2, x < 0), (3, True)) < y
|
| 100 |
+
|
| 101 |
+
The folded version of this results in a Piecewise whose
|
| 102 |
+
expressions are Booleans:
|
| 103 |
+
|
| 104 |
+
>>> folded_cond = piecewise_fold(cond); folded_cond
|
| 105 |
+
Piecewise((2 < y, x < 0), (3 < y, True))
|
| 106 |
+
|
| 107 |
+
When a Boolean containing Piecewise (like cond) or a Piecewise
|
| 108 |
+
with Boolean expressions (like folded_cond) is used as a condition,
|
| 109 |
+
it is converted to an equivalent :class:`~.ITE` object:
|
| 110 |
+
|
| 111 |
+
>>> Piecewise((1, folded_cond))
|
| 112 |
+
Piecewise((1, ITE(x < 0, y > 2, y > 3)))
|
| 113 |
+
|
| 114 |
+
When a condition is an ``ITE``, it will be converted to a simplified
|
| 115 |
+
Boolean expression:
|
| 116 |
+
|
| 117 |
+
>>> piecewise_fold(_)
|
| 118 |
+
Piecewise((1, ((x >= 0) | (y > 2)) & ((y > 3) | (x < 0))))
|
| 119 |
+
|
| 120 |
+
See Also
|
| 121 |
+
========
|
| 122 |
+
|
| 123 |
+
piecewise_fold
|
| 124 |
+
piecewise_exclusive
|
| 125 |
+
ITE
|
| 126 |
+
"""
|
| 127 |
+
|
| 128 |
+
nargs = None
|
| 129 |
+
is_Piecewise = True
|
| 130 |
+
|
| 131 |
+
def __new__(cls, *args, **options):
|
| 132 |
+
if len(args) == 0:
|
| 133 |
+
raise TypeError("At least one (expr, cond) pair expected.")
|
| 134 |
+
# (Try to) sympify args first
|
| 135 |
+
newargs = []
|
| 136 |
+
for ec in args:
|
| 137 |
+
# ec could be a ExprCondPair or a tuple
|
| 138 |
+
pair = ExprCondPair(*getattr(ec, 'args', ec))
|
| 139 |
+
cond = pair.cond
|
| 140 |
+
if cond is false:
|
| 141 |
+
continue
|
| 142 |
+
newargs.append(pair)
|
| 143 |
+
if cond is true:
|
| 144 |
+
break
|
| 145 |
+
|
| 146 |
+
eval = options.pop('evaluate', global_parameters.evaluate)
|
| 147 |
+
if eval:
|
| 148 |
+
r = cls.eval(*newargs)
|
| 149 |
+
if r is not None:
|
| 150 |
+
return r
|
| 151 |
+
elif len(newargs) == 1 and newargs[0].cond == True:
|
| 152 |
+
return newargs[0].expr
|
| 153 |
+
|
| 154 |
+
return Basic.__new__(cls, *newargs, **options)
|
| 155 |
+
|
| 156 |
+
@classmethod
|
| 157 |
+
def eval(cls, *_args):
|
| 158 |
+
"""Either return a modified version of the args or, if no
|
| 159 |
+
modifications were made, return None.
|
| 160 |
+
|
| 161 |
+
Modifications that are made here:
|
| 162 |
+
|
| 163 |
+
1. relationals are made canonical
|
| 164 |
+
2. any False conditions are dropped
|
| 165 |
+
3. any repeat of a previous condition is ignored
|
| 166 |
+
4. any args past one with a true condition are dropped
|
| 167 |
+
|
| 168 |
+
If there are no args left, nan will be returned.
|
| 169 |
+
If there is a single arg with a True condition, its
|
| 170 |
+
corresponding expression will be returned.
|
| 171 |
+
|
| 172 |
+
EXAMPLES
|
| 173 |
+
========
|
| 174 |
+
|
| 175 |
+
>>> from sympy import Piecewise
|
| 176 |
+
>>> from sympy.abc import x
|
| 177 |
+
>>> cond = -x < -1
|
| 178 |
+
>>> args = [(1, cond), (4, cond), (3, False), (2, True), (5, x < 1)]
|
| 179 |
+
>>> Piecewise(*args, evaluate=False)
|
| 180 |
+
Piecewise((1, -x < -1), (4, -x < -1), (2, True))
|
| 181 |
+
>>> Piecewise(*args)
|
| 182 |
+
Piecewise((1, x > 1), (2, True))
|
| 183 |
+
"""
|
| 184 |
+
if not _args:
|
| 185 |
+
return Undefined
|
| 186 |
+
|
| 187 |
+
if len(_args) == 1 and _args[0][-1] == True:
|
| 188 |
+
return _args[0][0]
|
| 189 |
+
|
| 190 |
+
newargs = _piecewise_collapse_arguments(_args)
|
| 191 |
+
|
| 192 |
+
# some conditions may have been redundant
|
| 193 |
+
missing = len(newargs) != len(_args)
|
| 194 |
+
# some conditions may have changed
|
| 195 |
+
same = all(a == b for a, b in zip(newargs, _args))
|
| 196 |
+
# if either change happened we return the expr with the
|
| 197 |
+
# updated args
|
| 198 |
+
if not newargs:
|
| 199 |
+
raise ValueError(filldedent('''
|
| 200 |
+
There are no conditions (or none that
|
| 201 |
+
are not trivially false) to define an
|
| 202 |
+
expression.'''))
|
| 203 |
+
if missing or not same:
|
| 204 |
+
return cls(*newargs)
|
| 205 |
+
|
| 206 |
+
def doit(self, **hints):
|
| 207 |
+
"""
|
| 208 |
+
Evaluate this piecewise function.
|
| 209 |
+
"""
|
| 210 |
+
newargs = []
|
| 211 |
+
for e, c in self.args:
|
| 212 |
+
if hints.get('deep', True):
|
| 213 |
+
if isinstance(e, Basic):
|
| 214 |
+
newe = e.doit(**hints)
|
| 215 |
+
if newe != self:
|
| 216 |
+
e = newe
|
| 217 |
+
if isinstance(c, Basic):
|
| 218 |
+
c = c.doit(**hints)
|
| 219 |
+
newargs.append((e, c))
|
| 220 |
+
return self.func(*newargs)
|
| 221 |
+
|
| 222 |
+
def _eval_simplify(self, **kwargs):
|
| 223 |
+
return piecewise_simplify(self, **kwargs)
|
| 224 |
+
|
| 225 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 226 |
+
for e, c in self.args:
|
| 227 |
+
if c == True or c.subs(x, 0) == True:
|
| 228 |
+
return e.as_leading_term(x)
|
| 229 |
+
|
| 230 |
+
def _eval_adjoint(self):
|
| 231 |
+
return self.func(*[(e.adjoint(), c) for e, c in self.args])
|
| 232 |
+
|
| 233 |
+
def _eval_conjugate(self):
|
| 234 |
+
return self.func(*[(e.conjugate(), c) for e, c in self.args])
|
| 235 |
+
|
| 236 |
+
def _eval_derivative(self, x):
|
| 237 |
+
return self.func(*[(diff(e, x), c) for e, c in self.args])
|
| 238 |
+
|
| 239 |
+
def _eval_evalf(self, prec):
|
| 240 |
+
return self.func(*[(e._evalf(prec), c) for e, c in self.args])
|
| 241 |
+
|
| 242 |
+
def _eval_is_meromorphic(self, x, a):
|
| 243 |
+
# Conditions often implicitly assume that the argument is real.
|
| 244 |
+
# Hence, there needs to be some check for as_set.
|
| 245 |
+
if not a.is_real:
|
| 246 |
+
return None
|
| 247 |
+
|
| 248 |
+
# Then, scan ExprCondPairs in the given order to find a piece that would contain a,
|
| 249 |
+
# possibly as a boundary point.
|
| 250 |
+
for e, c in self.args:
|
| 251 |
+
cond = c.subs(x, a)
|
| 252 |
+
|
| 253 |
+
if cond.is_Relational:
|
| 254 |
+
return None
|
| 255 |
+
if a in c.as_set().boundary:
|
| 256 |
+
return None
|
| 257 |
+
# Apply expression if a is an interior point of the domain of e.
|
| 258 |
+
if cond:
|
| 259 |
+
return e._eval_is_meromorphic(x, a)
|
| 260 |
+
|
| 261 |
+
def piecewise_integrate(self, x, **kwargs):
|
| 262 |
+
"""Return the Piecewise with each expression being
|
| 263 |
+
replaced with its antiderivative. To obtain a continuous
|
| 264 |
+
antiderivative, use the :func:`~.integrate` function or method.
|
| 265 |
+
|
| 266 |
+
Examples
|
| 267 |
+
========
|
| 268 |
+
|
| 269 |
+
>>> from sympy import Piecewise
|
| 270 |
+
>>> from sympy.abc import x
|
| 271 |
+
>>> p = Piecewise((0, x < 0), (1, x < 1), (2, True))
|
| 272 |
+
>>> p.piecewise_integrate(x)
|
| 273 |
+
Piecewise((0, x < 0), (x, x < 1), (2*x, True))
|
| 274 |
+
|
| 275 |
+
Note that this does not give a continuous function, e.g.
|
| 276 |
+
at x = 1 the 3rd condition applies and the antiderivative
|
| 277 |
+
there is 2*x so the value of the antiderivative is 2:
|
| 278 |
+
|
| 279 |
+
>>> anti = _
|
| 280 |
+
>>> anti.subs(x, 1)
|
| 281 |
+
2
|
| 282 |
+
|
| 283 |
+
The continuous derivative accounts for the integral *up to*
|
| 284 |
+
the point of interest, however:
|
| 285 |
+
|
| 286 |
+
>>> p.integrate(x)
|
| 287 |
+
Piecewise((0, x < 0), (x, x < 1), (2*x - 1, True))
|
| 288 |
+
>>> _.subs(x, 1)
|
| 289 |
+
1
|
| 290 |
+
|
| 291 |
+
See Also
|
| 292 |
+
========
|
| 293 |
+
Piecewise._eval_integral
|
| 294 |
+
"""
|
| 295 |
+
from sympy.integrals import integrate
|
| 296 |
+
return self.func(*[(integrate(e, x, **kwargs), c) for e, c in self.args])
|
| 297 |
+
|
| 298 |
+
def _handle_irel(self, x, handler):
|
| 299 |
+
"""Return either None (if the conditions of self depend only on x) else
|
| 300 |
+
a Piecewise expression whose expressions (handled by the handler that
|
| 301 |
+
was passed) are paired with the governing x-independent relationals,
|
| 302 |
+
e.g. Piecewise((A, a(x) & b(y)), (B, c(x) | c(y)) ->
|
| 303 |
+
Piecewise(
|
| 304 |
+
(handler(Piecewise((A, a(x) & True), (B, c(x) | True)), b(y) & c(y)),
|
| 305 |
+
(handler(Piecewise((A, a(x) & True), (B, c(x) | False)), b(y)),
|
| 306 |
+
(handler(Piecewise((A, a(x) & False), (B, c(x) | True)), c(y)),
|
| 307 |
+
(handler(Piecewise((A, a(x) & False), (B, c(x) | False)), True))
|
| 308 |
+
"""
|
| 309 |
+
# identify governing relationals
|
| 310 |
+
rel = self.atoms(Relational)
|
| 311 |
+
irel = list(ordered([r for r in rel if x not in r.free_symbols
|
| 312 |
+
and r not in (S.true, S.false)]))
|
| 313 |
+
if irel:
|
| 314 |
+
args = {}
|
| 315 |
+
exprinorder = []
|
| 316 |
+
for truth in product((1, 0), repeat=len(irel)):
|
| 317 |
+
reps = dict(zip(irel, truth))
|
| 318 |
+
# only store the true conditions since the false are implied
|
| 319 |
+
# when they appear lower in the Piecewise args
|
| 320 |
+
if 1 not in truth:
|
| 321 |
+
cond = None # flag this one so it doesn't get combined
|
| 322 |
+
else:
|
| 323 |
+
andargs = Tuple(*[i for i in reps if reps[i]])
|
| 324 |
+
free = list(andargs.free_symbols)
|
| 325 |
+
if len(free) == 1:
|
| 326 |
+
from sympy.solvers.inequalities import (
|
| 327 |
+
reduce_inequalities, _solve_inequality)
|
| 328 |
+
try:
|
| 329 |
+
t = reduce_inequalities(andargs, free[0])
|
| 330 |
+
# ValueError when there are potentially
|
| 331 |
+
# nonvanishing imaginary parts
|
| 332 |
+
except (ValueError, NotImplementedError):
|
| 333 |
+
# at least isolate free symbol on left
|
| 334 |
+
t = And(*[_solve_inequality(
|
| 335 |
+
a, free[0], linear=True)
|
| 336 |
+
for a in andargs])
|
| 337 |
+
else:
|
| 338 |
+
t = And(*andargs)
|
| 339 |
+
if t is S.false:
|
| 340 |
+
continue # an impossible combination
|
| 341 |
+
cond = t
|
| 342 |
+
expr = handler(self.xreplace(reps))
|
| 343 |
+
if isinstance(expr, self.func) and len(expr.args) == 1:
|
| 344 |
+
expr, econd = expr.args[0]
|
| 345 |
+
cond = And(econd, True if cond is None else cond)
|
| 346 |
+
# the ec pairs are being collected since all possibilities
|
| 347 |
+
# are being enumerated, but don't put the last one in since
|
| 348 |
+
# its expr might match a previous expression and it
|
| 349 |
+
# must appear last in the args
|
| 350 |
+
if cond is not None:
|
| 351 |
+
args.setdefault(expr, []).append(cond)
|
| 352 |
+
# but since we only store the true conditions we must maintain
|
| 353 |
+
# the order so that the expression with the most true values
|
| 354 |
+
# comes first
|
| 355 |
+
exprinorder.append(expr)
|
| 356 |
+
# convert collected conditions as args of Or
|
| 357 |
+
for k in args:
|
| 358 |
+
args[k] = Or(*args[k])
|
| 359 |
+
# take them in the order obtained
|
| 360 |
+
args = [(e, args[e]) for e in uniq(exprinorder)]
|
| 361 |
+
# add in the last arg
|
| 362 |
+
args.append((expr, True))
|
| 363 |
+
return Piecewise(*args)
|
| 364 |
+
|
| 365 |
+
def _eval_integral(self, x, _first=True, **kwargs):
|
| 366 |
+
"""Return the indefinite integral of the
|
| 367 |
+
Piecewise such that subsequent substitution of x with a
|
| 368 |
+
value will give the value of the integral (not including
|
| 369 |
+
the constant of integration) up to that point. To only
|
| 370 |
+
integrate the individual parts of Piecewise, use the
|
| 371 |
+
``piecewise_integrate`` method.
|
| 372 |
+
|
| 373 |
+
Examples
|
| 374 |
+
========
|
| 375 |
+
|
| 376 |
+
>>> from sympy import Piecewise
|
| 377 |
+
>>> from sympy.abc import x
|
| 378 |
+
>>> p = Piecewise((0, x < 0), (1, x < 1), (2, True))
|
| 379 |
+
>>> p.integrate(x)
|
| 380 |
+
Piecewise((0, x < 0), (x, x < 1), (2*x - 1, True))
|
| 381 |
+
>>> p.piecewise_integrate(x)
|
| 382 |
+
Piecewise((0, x < 0), (x, x < 1), (2*x, True))
|
| 383 |
+
|
| 384 |
+
See Also
|
| 385 |
+
========
|
| 386 |
+
Piecewise.piecewise_integrate
|
| 387 |
+
"""
|
| 388 |
+
from sympy.integrals.integrals import integrate
|
| 389 |
+
|
| 390 |
+
if _first:
|
| 391 |
+
def handler(ipw):
|
| 392 |
+
if isinstance(ipw, self.func):
|
| 393 |
+
return ipw._eval_integral(x, _first=False, **kwargs)
|
| 394 |
+
else:
|
| 395 |
+
return ipw.integrate(x, **kwargs)
|
| 396 |
+
irv = self._handle_irel(x, handler)
|
| 397 |
+
if irv is not None:
|
| 398 |
+
return irv
|
| 399 |
+
|
| 400 |
+
# handle a Piecewise from -oo to oo with and no x-independent relationals
|
| 401 |
+
# -----------------------------------------------------------------------
|
| 402 |
+
ok, abei = self._intervals(x)
|
| 403 |
+
if not ok:
|
| 404 |
+
from sympy.integrals.integrals import Integral
|
| 405 |
+
return Integral(self, x) # unevaluated
|
| 406 |
+
|
| 407 |
+
pieces = [(a, b) for a, b, _, _ in abei]
|
| 408 |
+
oo = S.Infinity
|
| 409 |
+
done = [(-oo, oo, -1)]
|
| 410 |
+
for k, p in enumerate(pieces):
|
| 411 |
+
if p == (-oo, oo):
|
| 412 |
+
# all undone intervals will get this key
|
| 413 |
+
for j, (a, b, i) in enumerate(done):
|
| 414 |
+
if i == -1:
|
| 415 |
+
done[j] = a, b, k
|
| 416 |
+
break # nothing else to consider
|
| 417 |
+
N = len(done) - 1
|
| 418 |
+
for j, (a, b, i) in enumerate(reversed(done)):
|
| 419 |
+
if i == -1:
|
| 420 |
+
j = N - j
|
| 421 |
+
done[j: j + 1] = _clip(p, (a, b), k)
|
| 422 |
+
done = [(a, b, i) for a, b, i in done if a != b]
|
| 423 |
+
|
| 424 |
+
# append an arg if there is a hole so a reference to
|
| 425 |
+
# argument -1 will give Undefined
|
| 426 |
+
if any(i == -1 for (a, b, i) in done):
|
| 427 |
+
abei.append((-oo, oo, Undefined, -1))
|
| 428 |
+
|
| 429 |
+
# return the sum of the intervals
|
| 430 |
+
args = []
|
| 431 |
+
sum = None
|
| 432 |
+
for a, b, i in done:
|
| 433 |
+
anti = integrate(abei[i][-2], x, **kwargs)
|
| 434 |
+
if sum is None:
|
| 435 |
+
sum = anti
|
| 436 |
+
else:
|
| 437 |
+
sum = sum.subs(x, a)
|
| 438 |
+
e = anti._eval_interval(x, a, x)
|
| 439 |
+
if sum.has(*_illegal) or e.has(*_illegal):
|
| 440 |
+
sum = anti
|
| 441 |
+
else:
|
| 442 |
+
sum += e
|
| 443 |
+
# see if we know whether b is contained in original
|
| 444 |
+
# condition
|
| 445 |
+
if b is S.Infinity:
|
| 446 |
+
cond = True
|
| 447 |
+
elif self.args[abei[i][-1]].cond.subs(x, b) == False:
|
| 448 |
+
cond = (x < b)
|
| 449 |
+
else:
|
| 450 |
+
cond = (x <= b)
|
| 451 |
+
args.append((sum, cond))
|
| 452 |
+
return Piecewise(*args)
|
| 453 |
+
|
| 454 |
+
def _eval_interval(self, sym, a, b, _first=True):
|
| 455 |
+
"""Evaluates the function along the sym in a given interval [a, b]"""
|
| 456 |
+
# FIXME: Currently complex intervals are not supported. A possible
|
| 457 |
+
# replacement algorithm, discussed in issue 5227, can be found in the
|
| 458 |
+
# following papers;
|
| 459 |
+
# http://portal.acm.org/citation.cfm?id=281649
|
| 460 |
+
# http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4127&rep=rep1&type=pdf
|
| 461 |
+
|
| 462 |
+
if a is None or b is None:
|
| 463 |
+
# In this case, it is just simple substitution
|
| 464 |
+
return super()._eval_interval(sym, a, b)
|
| 465 |
+
else:
|
| 466 |
+
x, lo, hi = map(as_Basic, (sym, a, b))
|
| 467 |
+
|
| 468 |
+
if _first: # get only x-dependent relationals
|
| 469 |
+
def handler(ipw):
|
| 470 |
+
if isinstance(ipw, self.func):
|
| 471 |
+
return ipw._eval_interval(x, lo, hi, _first=None)
|
| 472 |
+
else:
|
| 473 |
+
return ipw._eval_interval(x, lo, hi)
|
| 474 |
+
irv = self._handle_irel(x, handler)
|
| 475 |
+
if irv is not None:
|
| 476 |
+
return irv
|
| 477 |
+
|
| 478 |
+
if (lo < hi) is S.false or (
|
| 479 |
+
lo is S.Infinity or hi is S.NegativeInfinity):
|
| 480 |
+
rv = self._eval_interval(x, hi, lo, _first=False)
|
| 481 |
+
if isinstance(rv, Piecewise):
|
| 482 |
+
rv = Piecewise(*[(-e, c) for e, c in rv.args])
|
| 483 |
+
else:
|
| 484 |
+
rv = -rv
|
| 485 |
+
return rv
|
| 486 |
+
|
| 487 |
+
if (lo < hi) is S.true or (
|
| 488 |
+
hi is S.Infinity or lo is S.NegativeInfinity):
|
| 489 |
+
pass
|
| 490 |
+
else:
|
| 491 |
+
_a = Dummy('lo')
|
| 492 |
+
_b = Dummy('hi')
|
| 493 |
+
a = lo if lo.is_comparable else _a
|
| 494 |
+
b = hi if hi.is_comparable else _b
|
| 495 |
+
pos = self._eval_interval(x, a, b, _first=False)
|
| 496 |
+
if a == _a and b == _b:
|
| 497 |
+
# it's purely symbolic so just swap lo and hi and
|
| 498 |
+
# change the sign to get the value for when lo > hi
|
| 499 |
+
neg, pos = (-pos.xreplace({_a: hi, _b: lo}),
|
| 500 |
+
pos.xreplace({_a: lo, _b: hi}))
|
| 501 |
+
else:
|
| 502 |
+
# at least one of the bounds was comparable, so allow
|
| 503 |
+
# _eval_interval to use that information when computing
|
| 504 |
+
# the interval with lo and hi reversed
|
| 505 |
+
neg, pos = (-self._eval_interval(x, hi, lo, _first=False),
|
| 506 |
+
pos.xreplace({_a: lo, _b: hi}))
|
| 507 |
+
|
| 508 |
+
# allow simplification based on ordering of lo and hi
|
| 509 |
+
p = Dummy('', positive=True)
|
| 510 |
+
if lo.is_Symbol:
|
| 511 |
+
pos = pos.xreplace({lo: hi - p}).xreplace({p: hi - lo})
|
| 512 |
+
neg = neg.xreplace({lo: hi + p}).xreplace({p: lo - hi})
|
| 513 |
+
elif hi.is_Symbol:
|
| 514 |
+
pos = pos.xreplace({hi: lo + p}).xreplace({p: hi - lo})
|
| 515 |
+
neg = neg.xreplace({hi: lo - p}).xreplace({p: lo - hi})
|
| 516 |
+
# evaluate limits that may have unevaluate Min/Max
|
| 517 |
+
touch = lambda _: _.replace(
|
| 518 |
+
lambda x: isinstance(x, (Min, Max)),
|
| 519 |
+
lambda x: x.func(*x.args))
|
| 520 |
+
neg = touch(neg)
|
| 521 |
+
pos = touch(pos)
|
| 522 |
+
# assemble return expression; make the first condition be Lt
|
| 523 |
+
# b/c then the first expression will look the same whether
|
| 524 |
+
# the lo or hi limit is symbolic
|
| 525 |
+
if a == _a: # the lower limit was symbolic
|
| 526 |
+
rv = Piecewise(
|
| 527 |
+
(pos,
|
| 528 |
+
lo < hi),
|
| 529 |
+
(neg,
|
| 530 |
+
True))
|
| 531 |
+
else:
|
| 532 |
+
rv = Piecewise(
|
| 533 |
+
(neg,
|
| 534 |
+
hi < lo),
|
| 535 |
+
(pos,
|
| 536 |
+
True))
|
| 537 |
+
|
| 538 |
+
if rv == Undefined:
|
| 539 |
+
raise ValueError("Can't integrate across undefined region.")
|
| 540 |
+
if any(isinstance(i, Piecewise) for i in (pos, neg)):
|
| 541 |
+
rv = piecewise_fold(rv)
|
| 542 |
+
return rv
|
| 543 |
+
|
| 544 |
+
# handle a Piecewise with lo <= hi and no x-independent relationals
|
| 545 |
+
# -----------------------------------------------------------------
|
| 546 |
+
ok, abei = self._intervals(x)
|
| 547 |
+
if not ok:
|
| 548 |
+
from sympy.integrals.integrals import Integral
|
| 549 |
+
# not being able to do the interval of f(x) can
|
| 550 |
+
# be stated as not being able to do the integral
|
| 551 |
+
# of f'(x) over the same range
|
| 552 |
+
return Integral(self.diff(x), (x, lo, hi)) # unevaluated
|
| 553 |
+
|
| 554 |
+
pieces = [(a, b) for a, b, _, _ in abei]
|
| 555 |
+
done = [(lo, hi, -1)]
|
| 556 |
+
oo = S.Infinity
|
| 557 |
+
for k, p in enumerate(pieces):
|
| 558 |
+
if p[:2] == (-oo, oo):
|
| 559 |
+
# all undone intervals will get this key
|
| 560 |
+
for j, (a, b, i) in enumerate(done):
|
| 561 |
+
if i == -1:
|
| 562 |
+
done[j] = a, b, k
|
| 563 |
+
break # nothing else to consider
|
| 564 |
+
N = len(done) - 1
|
| 565 |
+
for j, (a, b, i) in enumerate(reversed(done)):
|
| 566 |
+
if i == -1:
|
| 567 |
+
j = N - j
|
| 568 |
+
done[j: j + 1] = _clip(p, (a, b), k)
|
| 569 |
+
done = [(a, b, i) for a, b, i in done if a != b]
|
| 570 |
+
|
| 571 |
+
# return the sum of the intervals
|
| 572 |
+
sum = S.Zero
|
| 573 |
+
upto = None
|
| 574 |
+
for a, b, i in done:
|
| 575 |
+
if i == -1:
|
| 576 |
+
if upto is None:
|
| 577 |
+
return Undefined
|
| 578 |
+
# TODO simplify hi <= upto
|
| 579 |
+
return Piecewise((sum, hi <= upto), (Undefined, True))
|
| 580 |
+
sum += abei[i][-2]._eval_interval(x, a, b)
|
| 581 |
+
upto = b
|
| 582 |
+
return sum
|
| 583 |
+
|
| 584 |
+
def _intervals(self, sym, err_on_Eq=False):
|
| 585 |
+
r"""Return a bool and a message (when bool is False), else a
|
| 586 |
+
list of unique tuples, (a, b, e, i), where a and b
|
| 587 |
+
are the lower and upper bounds in which the expression e of
|
| 588 |
+
argument i in self is defined and $a < b$ (when involving
|
| 589 |
+
numbers) or $a \le b$ when involving symbols.
|
| 590 |
+
|
| 591 |
+
If there are any relationals not involving sym, or any
|
| 592 |
+
relational cannot be solved for sym, the bool will be False
|
| 593 |
+
a message be given as the second return value. The calling
|
| 594 |
+
routine should have removed such relationals before calling
|
| 595 |
+
this routine.
|
| 596 |
+
|
| 597 |
+
The evaluated conditions will be returned as ranges.
|
| 598 |
+
Discontinuous ranges will be returned separately with
|
| 599 |
+
identical expressions. The first condition that evaluates to
|
| 600 |
+
True will be returned as the last tuple with a, b = -oo, oo.
|
| 601 |
+
"""
|
| 602 |
+
from sympy.solvers.inequalities import _solve_inequality
|
| 603 |
+
|
| 604 |
+
assert isinstance(self, Piecewise)
|
| 605 |
+
|
| 606 |
+
def nonsymfail(cond):
|
| 607 |
+
return False, filldedent('''
|
| 608 |
+
A condition not involving
|
| 609 |
+
%s appeared: %s''' % (sym, cond))
|
| 610 |
+
|
| 611 |
+
def _solve_relational(r):
|
| 612 |
+
if sym not in r.free_symbols:
|
| 613 |
+
return nonsymfail(r)
|
| 614 |
+
try:
|
| 615 |
+
rv = _solve_inequality(r, sym)
|
| 616 |
+
except NotImplementedError:
|
| 617 |
+
return False, 'Unable to solve relational %s for %s.' % (r, sym)
|
| 618 |
+
if isinstance(rv, Relational):
|
| 619 |
+
free = rv.args[1].free_symbols
|
| 620 |
+
if rv.args[0] != sym or sym in free:
|
| 621 |
+
return False, 'Unable to solve relational %s for %s.' % (r, sym)
|
| 622 |
+
if rv.rel_op == '==':
|
| 623 |
+
# this equality has been affirmed to have the form
|
| 624 |
+
# Eq(sym, rhs) where rhs is sym-free; it represents
|
| 625 |
+
# a zero-width interval which will be ignored
|
| 626 |
+
# whether it is an isolated condition or contained
|
| 627 |
+
# within an And or an Or
|
| 628 |
+
rv = S.false
|
| 629 |
+
elif rv.rel_op == '!=':
|
| 630 |
+
try:
|
| 631 |
+
rv = Or(sym < rv.rhs, sym > rv.rhs)
|
| 632 |
+
except TypeError:
|
| 633 |
+
# e.g. x != I ==> all real x satisfy
|
| 634 |
+
rv = S.true
|
| 635 |
+
elif rv == (S.NegativeInfinity < sym) & (sym < S.Infinity):
|
| 636 |
+
rv = S.true
|
| 637 |
+
return True, rv
|
| 638 |
+
|
| 639 |
+
args = list(self.args)
|
| 640 |
+
# make self canonical wrt Relationals
|
| 641 |
+
keys = self.atoms(Relational)
|
| 642 |
+
reps = {}
|
| 643 |
+
for r in keys:
|
| 644 |
+
ok, s = _solve_relational(r)
|
| 645 |
+
if ok != True:
|
| 646 |
+
return False, ok
|
| 647 |
+
reps[r] = s
|
| 648 |
+
# process args individually so if any evaluate, their position
|
| 649 |
+
# in the original Piecewise will be known
|
| 650 |
+
args = [i.xreplace(reps) for i in self.args]
|
| 651 |
+
|
| 652 |
+
# precondition args
|
| 653 |
+
expr_cond = []
|
| 654 |
+
default = idefault = None
|
| 655 |
+
for i, (expr, cond) in enumerate(args):
|
| 656 |
+
if cond is S.false:
|
| 657 |
+
continue
|
| 658 |
+
if cond is S.true:
|
| 659 |
+
default = expr
|
| 660 |
+
idefault = i
|
| 661 |
+
break
|
| 662 |
+
if isinstance(cond, Eq):
|
| 663 |
+
# unanticipated condition, but it is here in case a
|
| 664 |
+
# replacement caused an Eq to appear
|
| 665 |
+
if err_on_Eq:
|
| 666 |
+
return False, 'encountered Eq condition: %s' % cond
|
| 667 |
+
continue # zero width interval
|
| 668 |
+
|
| 669 |
+
cond = to_cnf(cond)
|
| 670 |
+
if isinstance(cond, And):
|
| 671 |
+
cond = distribute_or_over_and(cond)
|
| 672 |
+
|
| 673 |
+
if isinstance(cond, Or):
|
| 674 |
+
expr_cond.extend(
|
| 675 |
+
[(i, expr, o) for o in cond.args
|
| 676 |
+
if not isinstance(o, Eq)])
|
| 677 |
+
elif cond is not S.false:
|
| 678 |
+
expr_cond.append((i, expr, cond))
|
| 679 |
+
elif cond is S.true:
|
| 680 |
+
default = expr
|
| 681 |
+
idefault = i
|
| 682 |
+
break
|
| 683 |
+
|
| 684 |
+
# determine intervals represented by conditions
|
| 685 |
+
int_expr = []
|
| 686 |
+
for iarg, expr, cond in expr_cond:
|
| 687 |
+
if isinstance(cond, And):
|
| 688 |
+
lower = S.NegativeInfinity
|
| 689 |
+
upper = S.Infinity
|
| 690 |
+
exclude = []
|
| 691 |
+
for cond2 in cond.args:
|
| 692 |
+
if not isinstance(cond2, Relational):
|
| 693 |
+
return False, 'expecting only Relationals'
|
| 694 |
+
if isinstance(cond2, Eq):
|
| 695 |
+
lower = upper # ignore
|
| 696 |
+
if err_on_Eq:
|
| 697 |
+
return False, 'encountered secondary Eq condition'
|
| 698 |
+
break
|
| 699 |
+
elif isinstance(cond2, Ne):
|
| 700 |
+
l, r = cond2.args
|
| 701 |
+
if l == sym:
|
| 702 |
+
exclude.append(r)
|
| 703 |
+
elif r == sym:
|
| 704 |
+
exclude.append(l)
|
| 705 |
+
else:
|
| 706 |
+
return nonsymfail(cond2)
|
| 707 |
+
continue
|
| 708 |
+
elif cond2.lts == sym:
|
| 709 |
+
upper = Min(cond2.gts, upper)
|
| 710 |
+
elif cond2.gts == sym:
|
| 711 |
+
lower = Max(cond2.lts, lower)
|
| 712 |
+
else:
|
| 713 |
+
return nonsymfail(cond2) # should never get here
|
| 714 |
+
if exclude:
|
| 715 |
+
exclude = list(ordered(exclude))
|
| 716 |
+
newcond = []
|
| 717 |
+
for i, e in enumerate(exclude):
|
| 718 |
+
if e < lower == True or e > upper == True:
|
| 719 |
+
continue
|
| 720 |
+
if not newcond:
|
| 721 |
+
newcond.append((None, lower)) # add a primer
|
| 722 |
+
newcond.append((newcond[-1][1], e))
|
| 723 |
+
newcond.append((newcond[-1][1], upper))
|
| 724 |
+
newcond.pop(0) # remove the primer
|
| 725 |
+
expr_cond.extend([(iarg, expr, And(i[0] < sym, sym < i[1])) for i in newcond])
|
| 726 |
+
continue
|
| 727 |
+
elif isinstance(cond, Relational) and cond.rel_op != '!=':
|
| 728 |
+
lower, upper = cond.lts, cond.gts # part 1: initialize with givens
|
| 729 |
+
if cond.lts == sym: # part 1a: expand the side ...
|
| 730 |
+
lower = S.NegativeInfinity # e.g. x <= 0 ---> -oo <= 0
|
| 731 |
+
elif cond.gts == sym: # part 1a: ... that can be expanded
|
| 732 |
+
upper = S.Infinity # e.g. x >= 0 ---> oo >= 0
|
| 733 |
+
else:
|
| 734 |
+
return nonsymfail(cond)
|
| 735 |
+
else:
|
| 736 |
+
return False, 'unrecognized condition: %s' % cond
|
| 737 |
+
|
| 738 |
+
upper = Max(lower, upper)
|
| 739 |
+
if err_on_Eq and lower == upper:
|
| 740 |
+
return False, 'encountered Eq condition'
|
| 741 |
+
if (lower >= upper) is not S.true:
|
| 742 |
+
int_expr.append((lower, upper, expr, iarg))
|
| 743 |
+
|
| 744 |
+
if default is not None:
|
| 745 |
+
int_expr.append(
|
| 746 |
+
(S.NegativeInfinity, S.Infinity, default, idefault))
|
| 747 |
+
|
| 748 |
+
return True, list(uniq(int_expr))
|
| 749 |
+
|
| 750 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 751 |
+
args = [(ec.expr._eval_nseries(x, n, logx), ec.cond) for ec in self.args]
|
| 752 |
+
return self.func(*args)
|
| 753 |
+
|
| 754 |
+
def _eval_power(self, s):
|
| 755 |
+
return self.func(*[(e**s, c) for e, c in self.args])
|
| 756 |
+
|
| 757 |
+
def _eval_subs(self, old, new):
|
| 758 |
+
# this is strictly not necessary, but we can keep track
|
| 759 |
+
# of whether True or False conditions arise and be
|
| 760 |
+
# somewhat more efficient by avoiding other substitutions
|
| 761 |
+
# and avoiding invalid conditions that appear after a
|
| 762 |
+
# True condition
|
| 763 |
+
args = list(self.args)
|
| 764 |
+
args_exist = False
|
| 765 |
+
for i, (e, c) in enumerate(args):
|
| 766 |
+
c = c._subs(old, new)
|
| 767 |
+
if c != False:
|
| 768 |
+
args_exist = True
|
| 769 |
+
e = e._subs(old, new)
|
| 770 |
+
args[i] = (e, c)
|
| 771 |
+
if c == True:
|
| 772 |
+
break
|
| 773 |
+
if not args_exist:
|
| 774 |
+
args = ((Undefined, True),)
|
| 775 |
+
return self.func(*args)
|
| 776 |
+
|
| 777 |
+
def _eval_transpose(self):
|
| 778 |
+
return self.func(*[(e.transpose(), c) for e, c in self.args])
|
| 779 |
+
|
| 780 |
+
def _eval_template_is_attr(self, is_attr):
|
| 781 |
+
b = None
|
| 782 |
+
for expr, _ in self.args:
|
| 783 |
+
a = getattr(expr, is_attr)
|
| 784 |
+
if a is None:
|
| 785 |
+
return
|
| 786 |
+
if b is None:
|
| 787 |
+
b = a
|
| 788 |
+
elif b is not a:
|
| 789 |
+
return
|
| 790 |
+
return b
|
| 791 |
+
|
| 792 |
+
_eval_is_finite = lambda self: self._eval_template_is_attr(
|
| 793 |
+
'is_finite')
|
| 794 |
+
_eval_is_complex = lambda self: self._eval_template_is_attr('is_complex')
|
| 795 |
+
_eval_is_even = lambda self: self._eval_template_is_attr('is_even')
|
| 796 |
+
_eval_is_imaginary = lambda self: self._eval_template_is_attr(
|
| 797 |
+
'is_imaginary')
|
| 798 |
+
_eval_is_integer = lambda self: self._eval_template_is_attr('is_integer')
|
| 799 |
+
_eval_is_irrational = lambda self: self._eval_template_is_attr(
|
| 800 |
+
'is_irrational')
|
| 801 |
+
_eval_is_negative = lambda self: self._eval_template_is_attr('is_negative')
|
| 802 |
+
_eval_is_nonnegative = lambda self: self._eval_template_is_attr(
|
| 803 |
+
'is_nonnegative')
|
| 804 |
+
_eval_is_nonpositive = lambda self: self._eval_template_is_attr(
|
| 805 |
+
'is_nonpositive')
|
| 806 |
+
_eval_is_nonzero = lambda self: self._eval_template_is_attr(
|
| 807 |
+
'is_nonzero')
|
| 808 |
+
_eval_is_odd = lambda self: self._eval_template_is_attr('is_odd')
|
| 809 |
+
_eval_is_polar = lambda self: self._eval_template_is_attr('is_polar')
|
| 810 |
+
_eval_is_positive = lambda self: self._eval_template_is_attr('is_positive')
|
| 811 |
+
_eval_is_extended_real = lambda self: self._eval_template_is_attr(
|
| 812 |
+
'is_extended_real')
|
| 813 |
+
_eval_is_extended_positive = lambda self: self._eval_template_is_attr(
|
| 814 |
+
'is_extended_positive')
|
| 815 |
+
_eval_is_extended_negative = lambda self: self._eval_template_is_attr(
|
| 816 |
+
'is_extended_negative')
|
| 817 |
+
_eval_is_extended_nonzero = lambda self: self._eval_template_is_attr(
|
| 818 |
+
'is_extended_nonzero')
|
| 819 |
+
_eval_is_extended_nonpositive = lambda self: self._eval_template_is_attr(
|
| 820 |
+
'is_extended_nonpositive')
|
| 821 |
+
_eval_is_extended_nonnegative = lambda self: self._eval_template_is_attr(
|
| 822 |
+
'is_extended_nonnegative')
|
| 823 |
+
_eval_is_real = lambda self: self._eval_template_is_attr('is_real')
|
| 824 |
+
_eval_is_zero = lambda self: self._eval_template_is_attr(
|
| 825 |
+
'is_zero')
|
| 826 |
+
|
| 827 |
+
@classmethod
|
| 828 |
+
def __eval_cond(cls, cond):
|
| 829 |
+
"""Return the truth value of the condition."""
|
| 830 |
+
if cond == True:
|
| 831 |
+
return True
|
| 832 |
+
if isinstance(cond, Eq):
|
| 833 |
+
try:
|
| 834 |
+
diff = cond.lhs - cond.rhs
|
| 835 |
+
if diff.is_commutative:
|
| 836 |
+
return diff.is_zero
|
| 837 |
+
except TypeError:
|
| 838 |
+
pass
|
| 839 |
+
|
| 840 |
+
def as_expr_set_pairs(self, domain=None):
|
| 841 |
+
"""Return tuples for each argument of self that give
|
| 842 |
+
the expression and the interval in which it is valid
|
| 843 |
+
which is contained within the given domain.
|
| 844 |
+
If a condition cannot be converted to a set, an error
|
| 845 |
+
will be raised. The variable of the conditions is
|
| 846 |
+
assumed to be real; sets of real values are returned.
|
| 847 |
+
|
| 848 |
+
Examples
|
| 849 |
+
========
|
| 850 |
+
|
| 851 |
+
>>> from sympy import Piecewise, Interval
|
| 852 |
+
>>> from sympy.abc import x
|
| 853 |
+
>>> p = Piecewise(
|
| 854 |
+
... (1, x < 2),
|
| 855 |
+
... (2,(x > 0) & (x < 4)),
|
| 856 |
+
... (3, True))
|
| 857 |
+
>>> p.as_expr_set_pairs()
|
| 858 |
+
[(1, Interval.open(-oo, 2)),
|
| 859 |
+
(2, Interval.Ropen(2, 4)),
|
| 860 |
+
(3, Interval(4, oo))]
|
| 861 |
+
>>> p.as_expr_set_pairs(Interval(0, 3))
|
| 862 |
+
[(1, Interval.Ropen(0, 2)),
|
| 863 |
+
(2, Interval(2, 3))]
|
| 864 |
+
"""
|
| 865 |
+
if domain is None:
|
| 866 |
+
domain = S.Reals
|
| 867 |
+
exp_sets = []
|
| 868 |
+
U = domain
|
| 869 |
+
complex = not domain.is_subset(S.Reals)
|
| 870 |
+
cond_free = set()
|
| 871 |
+
for expr, cond in self.args:
|
| 872 |
+
cond_free |= cond.free_symbols
|
| 873 |
+
if len(cond_free) > 1:
|
| 874 |
+
raise NotImplementedError(filldedent('''
|
| 875 |
+
multivariate conditions are not handled.'''))
|
| 876 |
+
if complex:
|
| 877 |
+
for i in cond.atoms(Relational):
|
| 878 |
+
if not isinstance(i, (Eq, Ne)):
|
| 879 |
+
raise ValueError(filldedent('''
|
| 880 |
+
Inequalities in the complex domain are
|
| 881 |
+
not supported. Try the real domain by
|
| 882 |
+
setting domain=S.Reals'''))
|
| 883 |
+
cond_int = U.intersect(cond.as_set())
|
| 884 |
+
U = U - cond_int
|
| 885 |
+
if cond_int != S.EmptySet:
|
| 886 |
+
exp_sets.append((expr, cond_int))
|
| 887 |
+
return exp_sets
|
| 888 |
+
|
| 889 |
+
def _eval_rewrite_as_ITE(self, *args, **kwargs):
|
| 890 |
+
byfree = {}
|
| 891 |
+
args = list(args)
|
| 892 |
+
default = any(c == True for b, c in args)
|
| 893 |
+
for i, (b, c) in enumerate(args):
|
| 894 |
+
if not isinstance(b, Boolean) and b != True:
|
| 895 |
+
raise TypeError(filldedent('''
|
| 896 |
+
Expecting Boolean or bool but got `%s`
|
| 897 |
+
''' % func_name(b)))
|
| 898 |
+
if c == True:
|
| 899 |
+
break
|
| 900 |
+
# loop over independent conditions for this b
|
| 901 |
+
for c in c.args if isinstance(c, Or) else [c]:
|
| 902 |
+
free = c.free_symbols
|
| 903 |
+
x = free.pop()
|
| 904 |
+
try:
|
| 905 |
+
byfree[x] = byfree.setdefault(
|
| 906 |
+
x, S.EmptySet).union(c.as_set())
|
| 907 |
+
except NotImplementedError:
|
| 908 |
+
if not default:
|
| 909 |
+
raise NotImplementedError(filldedent('''
|
| 910 |
+
A method to determine whether a multivariate
|
| 911 |
+
conditional is consistent with a complete coverage
|
| 912 |
+
of all variables has not been implemented so the
|
| 913 |
+
rewrite is being stopped after encountering `%s`.
|
| 914 |
+
This error would not occur if a default expression
|
| 915 |
+
like `(foo, True)` were given.
|
| 916 |
+
''' % c))
|
| 917 |
+
if byfree[x] in (S.UniversalSet, S.Reals):
|
| 918 |
+
# collapse the ith condition to True and break
|
| 919 |
+
args[i] = list(args[i])
|
| 920 |
+
c = args[i][1] = True
|
| 921 |
+
break
|
| 922 |
+
if c == True:
|
| 923 |
+
break
|
| 924 |
+
if c != True:
|
| 925 |
+
raise ValueError(filldedent('''
|
| 926 |
+
Conditions must cover all reals or a final default
|
| 927 |
+
condition `(foo, True)` must be given.
|
| 928 |
+
'''))
|
| 929 |
+
last, _ = args[i] # ignore all past ith arg
|
| 930 |
+
for a, c in reversed(args[:i]):
|
| 931 |
+
last = ITE(c, a, last)
|
| 932 |
+
return _canonical(last)
|
| 933 |
+
|
| 934 |
+
def _eval_rewrite_as_KroneckerDelta(self, *args, **kwargs):
|
| 935 |
+
from sympy.functions.special.tensor_functions import KroneckerDelta
|
| 936 |
+
|
| 937 |
+
rules = {
|
| 938 |
+
And: [False, False],
|
| 939 |
+
Or: [True, True],
|
| 940 |
+
Not: [True, False],
|
| 941 |
+
Eq: [None, None],
|
| 942 |
+
Ne: [None, None]
|
| 943 |
+
}
|
| 944 |
+
|
| 945 |
+
class UnrecognizedCondition(Exception):
|
| 946 |
+
pass
|
| 947 |
+
|
| 948 |
+
def rewrite(cond):
|
| 949 |
+
if isinstance(cond, Eq):
|
| 950 |
+
return KroneckerDelta(*cond.args)
|
| 951 |
+
if isinstance(cond, Ne):
|
| 952 |
+
return 1 - KroneckerDelta(*cond.args)
|
| 953 |
+
|
| 954 |
+
cls, args = type(cond), cond.args
|
| 955 |
+
if cls not in rules:
|
| 956 |
+
raise UnrecognizedCondition(cls)
|
| 957 |
+
|
| 958 |
+
b1, b2 = rules[cls]
|
| 959 |
+
k = Mul(*[1 - rewrite(c) for c in args]) if b1 else Mul(*[rewrite(c) for c in args])
|
| 960 |
+
|
| 961 |
+
if b2:
|
| 962 |
+
return 1 - k
|
| 963 |
+
return k
|
| 964 |
+
|
| 965 |
+
conditions = []
|
| 966 |
+
true_value = None
|
| 967 |
+
for value, cond in args:
|
| 968 |
+
if type(cond) in rules:
|
| 969 |
+
conditions.append((value, cond))
|
| 970 |
+
elif cond is S.true:
|
| 971 |
+
if true_value is None:
|
| 972 |
+
true_value = value
|
| 973 |
+
else:
|
| 974 |
+
return
|
| 975 |
+
|
| 976 |
+
if true_value is not None:
|
| 977 |
+
result = true_value
|
| 978 |
+
|
| 979 |
+
for value, cond in conditions[::-1]:
|
| 980 |
+
try:
|
| 981 |
+
k = rewrite(cond)
|
| 982 |
+
result = k * value + (1 - k) * result
|
| 983 |
+
except UnrecognizedCondition:
|
| 984 |
+
return
|
| 985 |
+
|
| 986 |
+
return result
|
| 987 |
+
|
| 988 |
+
|
| 989 |
+
def piecewise_fold(expr, evaluate=True):
|
| 990 |
+
"""
|
| 991 |
+
Takes an expression containing a piecewise function and returns the
|
| 992 |
+
expression in piecewise form. In addition, any ITE conditions are
|
| 993 |
+
rewritten in negation normal form and simplified.
|
| 994 |
+
|
| 995 |
+
The final Piecewise is evaluated (default) but if the raw form
|
| 996 |
+
is desired, send ``evaluate=False``; if trivial evaluation is
|
| 997 |
+
desired, send ``evaluate=None`` and duplicate conditions and
|
| 998 |
+
processing of True and False will be handled.
|
| 999 |
+
|
| 1000 |
+
Examples
|
| 1001 |
+
========
|
| 1002 |
+
|
| 1003 |
+
>>> from sympy import Piecewise, piecewise_fold, S
|
| 1004 |
+
>>> from sympy.abc import x
|
| 1005 |
+
>>> p = Piecewise((x, x < 1), (1, S(1) <= x))
|
| 1006 |
+
>>> piecewise_fold(x*p)
|
| 1007 |
+
Piecewise((x**2, x < 1), (x, True))
|
| 1008 |
+
|
| 1009 |
+
See Also
|
| 1010 |
+
========
|
| 1011 |
+
|
| 1012 |
+
Piecewise
|
| 1013 |
+
piecewise_exclusive
|
| 1014 |
+
"""
|
| 1015 |
+
if not isinstance(expr, Basic) or not expr.has(Piecewise):
|
| 1016 |
+
return expr
|
| 1017 |
+
|
| 1018 |
+
new_args = []
|
| 1019 |
+
if isinstance(expr, (ExprCondPair, Piecewise)):
|
| 1020 |
+
for e, c in expr.args:
|
| 1021 |
+
if not isinstance(e, Piecewise):
|
| 1022 |
+
e = piecewise_fold(e)
|
| 1023 |
+
# we don't keep Piecewise in condition because
|
| 1024 |
+
# it has to be checked to see that it's complete
|
| 1025 |
+
# and we convert it to ITE at that time
|
| 1026 |
+
assert not c.has(Piecewise) # pragma: no cover
|
| 1027 |
+
if isinstance(c, ITE):
|
| 1028 |
+
c = c.to_nnf()
|
| 1029 |
+
c = simplify_logic(c, form='cnf')
|
| 1030 |
+
if isinstance(e, Piecewise):
|
| 1031 |
+
new_args.extend([(piecewise_fold(ei), And(ci, c))
|
| 1032 |
+
for ei, ci in e.args])
|
| 1033 |
+
else:
|
| 1034 |
+
new_args.append((e, c))
|
| 1035 |
+
else:
|
| 1036 |
+
# Given
|
| 1037 |
+
# P1 = Piecewise((e11, c1), (e12, c2), A)
|
| 1038 |
+
# P2 = Piecewise((e21, c1), (e22, c2), B)
|
| 1039 |
+
# ...
|
| 1040 |
+
# the folding of f(P1, P2) is trivially
|
| 1041 |
+
# Piecewise(
|
| 1042 |
+
# (f(e11, e21), c1),
|
| 1043 |
+
# (f(e12, e22), c2),
|
| 1044 |
+
# (f(Piecewise(A), Piecewise(B)), True))
|
| 1045 |
+
# Certain objects end up rewriting themselves as thus, so
|
| 1046 |
+
# we do that grouping before the more generic folding.
|
| 1047 |
+
# The following applies this idea when f = Add or f = Mul
|
| 1048 |
+
# (and the expression is commutative).
|
| 1049 |
+
if expr.is_Add or expr.is_Mul and expr.is_commutative:
|
| 1050 |
+
p, args = sift(expr.args, lambda x: x.is_Piecewise, binary=True)
|
| 1051 |
+
pc = sift(p, lambda x: tuple([c for e,c in x.args]))
|
| 1052 |
+
for c in list(ordered(pc)):
|
| 1053 |
+
if len(pc[c]) > 1:
|
| 1054 |
+
pargs = [list(i.args) for i in pc[c]]
|
| 1055 |
+
# the first one is the same; there may be more
|
| 1056 |
+
com = common_prefix(*[
|
| 1057 |
+
[i.cond for i in j] for j in pargs])
|
| 1058 |
+
n = len(com)
|
| 1059 |
+
collected = []
|
| 1060 |
+
for i in range(n):
|
| 1061 |
+
collected.append((
|
| 1062 |
+
expr.func(*[ai[i].expr for ai in pargs]),
|
| 1063 |
+
com[i]))
|
| 1064 |
+
remains = []
|
| 1065 |
+
for a in pargs:
|
| 1066 |
+
if n == len(a): # no more args
|
| 1067 |
+
continue
|
| 1068 |
+
if a[n].cond == True: # no longer Piecewise
|
| 1069 |
+
remains.append(a[n].expr)
|
| 1070 |
+
else: # restore the remaining Piecewise
|
| 1071 |
+
remains.append(
|
| 1072 |
+
Piecewise(*a[n:], evaluate=False))
|
| 1073 |
+
if remains:
|
| 1074 |
+
collected.append((expr.func(*remains), True))
|
| 1075 |
+
args.append(Piecewise(*collected, evaluate=False))
|
| 1076 |
+
continue
|
| 1077 |
+
args.extend(pc[c])
|
| 1078 |
+
else:
|
| 1079 |
+
args = expr.args
|
| 1080 |
+
# fold
|
| 1081 |
+
folded = list(map(piecewise_fold, args))
|
| 1082 |
+
for ec in product(*[
|
| 1083 |
+
(i.args if isinstance(i, Piecewise) else
|
| 1084 |
+
[(i, true)]) for i in folded]):
|
| 1085 |
+
e, c = zip(*ec)
|
| 1086 |
+
new_args.append((expr.func(*e), And(*c)))
|
| 1087 |
+
|
| 1088 |
+
if evaluate is None:
|
| 1089 |
+
# don't return duplicate conditions, otherwise don't evaluate
|
| 1090 |
+
new_args = list(reversed([(e, c) for c, e in {
|
| 1091 |
+
c: e for e, c in reversed(new_args)}.items()]))
|
| 1092 |
+
rv = Piecewise(*new_args, evaluate=evaluate)
|
| 1093 |
+
if evaluate is None and len(rv.args) == 1 and rv.args[0].cond == True:
|
| 1094 |
+
return rv.args[0].expr
|
| 1095 |
+
if any(s.expr.has(Piecewise) for p in rv.atoms(Piecewise) for s in p.args):
|
| 1096 |
+
return piecewise_fold(rv)
|
| 1097 |
+
return rv
|
| 1098 |
+
|
| 1099 |
+
|
| 1100 |
+
def _clip(A, B, k):
|
| 1101 |
+
"""Return interval B as intervals that are covered by A (keyed
|
| 1102 |
+
to k) and all other intervals of B not covered by A keyed to -1.
|
| 1103 |
+
|
| 1104 |
+
The reference point of each interval is the rhs; if the lhs is
|
| 1105 |
+
greater than the rhs then an interval of zero width interval will
|
| 1106 |
+
result, e.g. (4, 1) is treated like (1, 1).
|
| 1107 |
+
|
| 1108 |
+
Examples
|
| 1109 |
+
========
|
| 1110 |
+
|
| 1111 |
+
>>> from sympy.functions.elementary.piecewise import _clip
|
| 1112 |
+
>>> from sympy import Tuple
|
| 1113 |
+
>>> A = Tuple(1, 3)
|
| 1114 |
+
>>> B = Tuple(2, 4)
|
| 1115 |
+
>>> _clip(A, B, 0)
|
| 1116 |
+
[(2, 3, 0), (3, 4, -1)]
|
| 1117 |
+
|
| 1118 |
+
Interpretation: interval portion (2, 3) of interval (2, 4) is
|
| 1119 |
+
covered by interval (1, 3) and is keyed to 0 as requested;
|
| 1120 |
+
interval (3, 4) was not covered by (1, 3) and is keyed to -1.
|
| 1121 |
+
"""
|
| 1122 |
+
a, b = B
|
| 1123 |
+
c, d = A
|
| 1124 |
+
c, d = Min(Max(c, a), b), Min(Max(d, a), b)
|
| 1125 |
+
a = Min(a, b)
|
| 1126 |
+
p = []
|
| 1127 |
+
if a != c:
|
| 1128 |
+
p.append((a, c, -1))
|
| 1129 |
+
else:
|
| 1130 |
+
pass
|
| 1131 |
+
if c != d:
|
| 1132 |
+
p.append((c, d, k))
|
| 1133 |
+
else:
|
| 1134 |
+
pass
|
| 1135 |
+
if b != d:
|
| 1136 |
+
if d == c and p and p[-1][-1] == -1:
|
| 1137 |
+
p[-1] = p[-1][0], b, -1
|
| 1138 |
+
else:
|
| 1139 |
+
p.append((d, b, -1))
|
| 1140 |
+
else:
|
| 1141 |
+
pass
|
| 1142 |
+
|
| 1143 |
+
return p
|
| 1144 |
+
|
| 1145 |
+
|
| 1146 |
+
def piecewise_simplify_arguments(expr, **kwargs):
|
| 1147 |
+
from sympy.simplify.simplify import simplify
|
| 1148 |
+
|
| 1149 |
+
# simplify conditions
|
| 1150 |
+
f1 = expr.args[0].cond.free_symbols
|
| 1151 |
+
args = None
|
| 1152 |
+
if len(f1) == 1 and not expr.atoms(Eq):
|
| 1153 |
+
x = f1.pop()
|
| 1154 |
+
# this won't return intervals involving Eq
|
| 1155 |
+
# and it won't handle symbols treated as
|
| 1156 |
+
# booleans
|
| 1157 |
+
ok, abe_ = expr._intervals(x, err_on_Eq=True)
|
| 1158 |
+
def include(c, x, a):
|
| 1159 |
+
"return True if c.subs(x, a) is True, else False"
|
| 1160 |
+
try:
|
| 1161 |
+
return c.subs(x, a) == True
|
| 1162 |
+
except TypeError:
|
| 1163 |
+
return False
|
| 1164 |
+
if ok:
|
| 1165 |
+
args = []
|
| 1166 |
+
covered = S.EmptySet
|
| 1167 |
+
from sympy.sets.sets import Interval
|
| 1168 |
+
for a, b, e, i in abe_:
|
| 1169 |
+
c = expr.args[i].cond
|
| 1170 |
+
incl_a = include(c, x, a)
|
| 1171 |
+
incl_b = include(c, x, b)
|
| 1172 |
+
iv = Interval(a, b, not incl_a, not incl_b)
|
| 1173 |
+
cset = iv - covered
|
| 1174 |
+
if not cset:
|
| 1175 |
+
continue
|
| 1176 |
+
try:
|
| 1177 |
+
a = cset.inf
|
| 1178 |
+
except NotImplementedError:
|
| 1179 |
+
pass # continue with the given `a`
|
| 1180 |
+
else:
|
| 1181 |
+
incl_a = include(c, x, a)
|
| 1182 |
+
if incl_a and incl_b:
|
| 1183 |
+
if a.is_infinite and b.is_infinite:
|
| 1184 |
+
c = S.true
|
| 1185 |
+
elif b.is_infinite:
|
| 1186 |
+
c = (x > a) if a in covered else (x >= a)
|
| 1187 |
+
elif a.is_infinite:
|
| 1188 |
+
c = (x <= b)
|
| 1189 |
+
elif a in covered:
|
| 1190 |
+
c = And(a < x, x <= b)
|
| 1191 |
+
else:
|
| 1192 |
+
c = And(a <= x, x <= b)
|
| 1193 |
+
elif incl_a:
|
| 1194 |
+
if a.is_infinite:
|
| 1195 |
+
c = (x < b)
|
| 1196 |
+
elif a in covered:
|
| 1197 |
+
c = And(a < x, x < b)
|
| 1198 |
+
else:
|
| 1199 |
+
c = And(a <= x, x < b)
|
| 1200 |
+
elif incl_b:
|
| 1201 |
+
if b.is_infinite:
|
| 1202 |
+
c = (x > a)
|
| 1203 |
+
else:
|
| 1204 |
+
c = And(a < x, x <= b)
|
| 1205 |
+
else:
|
| 1206 |
+
if a in covered:
|
| 1207 |
+
c = (x < b)
|
| 1208 |
+
else:
|
| 1209 |
+
c = And(a < x, x < b)
|
| 1210 |
+
covered |= iv
|
| 1211 |
+
if a is S.NegativeInfinity and incl_a:
|
| 1212 |
+
covered |= {S.NegativeInfinity}
|
| 1213 |
+
if b is S.Infinity and incl_b:
|
| 1214 |
+
covered |= {S.Infinity}
|
| 1215 |
+
args.append((e, c))
|
| 1216 |
+
if not S.Reals.is_subset(covered):
|
| 1217 |
+
args.append((Undefined, True))
|
| 1218 |
+
if args is None:
|
| 1219 |
+
args = list(expr.args)
|
| 1220 |
+
for i in range(len(args)):
|
| 1221 |
+
e, c = args[i]
|
| 1222 |
+
if isinstance(c, Basic):
|
| 1223 |
+
c = simplify(c, **kwargs)
|
| 1224 |
+
args[i] = (e, c)
|
| 1225 |
+
|
| 1226 |
+
# simplify expressions
|
| 1227 |
+
doit = kwargs.pop('doit', None)
|
| 1228 |
+
for i in range(len(args)):
|
| 1229 |
+
e, c = args[i]
|
| 1230 |
+
if isinstance(e, Basic):
|
| 1231 |
+
# Skip doit to avoid growth at every call for some integrals
|
| 1232 |
+
# and sums, see sympy/sympy#17165
|
| 1233 |
+
newe = simplify(e, doit=False, **kwargs)
|
| 1234 |
+
if newe != e:
|
| 1235 |
+
e = newe
|
| 1236 |
+
args[i] = (e, c)
|
| 1237 |
+
|
| 1238 |
+
# restore kwargs flag
|
| 1239 |
+
if doit is not None:
|
| 1240 |
+
kwargs['doit'] = doit
|
| 1241 |
+
|
| 1242 |
+
return Piecewise(*args)
|
| 1243 |
+
|
| 1244 |
+
|
| 1245 |
+
def _piecewise_collapse_arguments(_args):
|
| 1246 |
+
newargs = [] # the unevaluated conditions
|
| 1247 |
+
current_cond = set() # the conditions up to a given e, c pair
|
| 1248 |
+
for expr, cond in _args:
|
| 1249 |
+
cond = cond.replace(
|
| 1250 |
+
lambda _: _.is_Relational, _canonical_coeff)
|
| 1251 |
+
# Check here if expr is a Piecewise and collapse if one of
|
| 1252 |
+
# the conds in expr matches cond. This allows the collapsing
|
| 1253 |
+
# of Piecewise((Piecewise((x,x<0)),x<0)) to Piecewise((x,x<0)).
|
| 1254 |
+
# This is important when using piecewise_fold to simplify
|
| 1255 |
+
# multiple Piecewise instances having the same conds.
|
| 1256 |
+
# Eventually, this code should be able to collapse Piecewise's
|
| 1257 |
+
# having different intervals, but this will probably require
|
| 1258 |
+
# using the new assumptions.
|
| 1259 |
+
if isinstance(expr, Piecewise):
|
| 1260 |
+
unmatching = []
|
| 1261 |
+
for i, (e, c) in enumerate(expr.args):
|
| 1262 |
+
if c in current_cond:
|
| 1263 |
+
# this would already have triggered
|
| 1264 |
+
continue
|
| 1265 |
+
if c == cond:
|
| 1266 |
+
if c != True:
|
| 1267 |
+
# nothing past this condition will ever
|
| 1268 |
+
# trigger and only those args before this
|
| 1269 |
+
# that didn't match a previous condition
|
| 1270 |
+
# could possibly trigger
|
| 1271 |
+
if unmatching:
|
| 1272 |
+
expr = Piecewise(*(
|
| 1273 |
+
unmatching + [(e, c)]))
|
| 1274 |
+
else:
|
| 1275 |
+
expr = e
|
| 1276 |
+
break
|
| 1277 |
+
else:
|
| 1278 |
+
unmatching.append((e, c))
|
| 1279 |
+
|
| 1280 |
+
# check for condition repeats
|
| 1281 |
+
got = False
|
| 1282 |
+
# -- if an And contains a condition that was
|
| 1283 |
+
# already encountered, then the And will be
|
| 1284 |
+
# False: if the previous condition was False
|
| 1285 |
+
# then the And will be False and if the previous
|
| 1286 |
+
# condition is True then then we wouldn't get to
|
| 1287 |
+
# this point. In either case, we can skip this condition.
|
| 1288 |
+
for i in ([cond] +
|
| 1289 |
+
(list(cond.args) if isinstance(cond, And) else
|
| 1290 |
+
[])):
|
| 1291 |
+
if i in current_cond:
|
| 1292 |
+
got = True
|
| 1293 |
+
break
|
| 1294 |
+
if got:
|
| 1295 |
+
continue
|
| 1296 |
+
|
| 1297 |
+
# -- if not(c) is already in current_cond then c is
|
| 1298 |
+
# a redundant condition in an And. This does not
|
| 1299 |
+
# apply to Or, however: (e1, c), (e2, Or(~c, d))
|
| 1300 |
+
# is not (e1, c), (e2, d) because if c and d are
|
| 1301 |
+
# both False this would give no results when the
|
| 1302 |
+
# true answer should be (e2, True)
|
| 1303 |
+
if isinstance(cond, And):
|
| 1304 |
+
nonredundant = []
|
| 1305 |
+
for c in cond.args:
|
| 1306 |
+
if isinstance(c, Relational):
|
| 1307 |
+
if c.negated.canonical in current_cond:
|
| 1308 |
+
continue
|
| 1309 |
+
# if a strict inequality appears after
|
| 1310 |
+
# a non-strict one, then the condition is
|
| 1311 |
+
# redundant
|
| 1312 |
+
if isinstance(c, (Lt, Gt)) and (
|
| 1313 |
+
c.weak in current_cond):
|
| 1314 |
+
cond = False
|
| 1315 |
+
break
|
| 1316 |
+
nonredundant.append(c)
|
| 1317 |
+
else:
|
| 1318 |
+
cond = cond.func(*nonredundant)
|
| 1319 |
+
elif isinstance(cond, Relational):
|
| 1320 |
+
if cond.negated.canonical in current_cond:
|
| 1321 |
+
cond = S.true
|
| 1322 |
+
|
| 1323 |
+
current_cond.add(cond)
|
| 1324 |
+
|
| 1325 |
+
# collect successive e,c pairs when exprs or cond match
|
| 1326 |
+
if newargs:
|
| 1327 |
+
if newargs[-1].expr == expr:
|
| 1328 |
+
orcond = Or(cond, newargs[-1].cond)
|
| 1329 |
+
if isinstance(orcond, (And, Or)):
|
| 1330 |
+
orcond = distribute_and_over_or(orcond)
|
| 1331 |
+
newargs[-1] = ExprCondPair(expr, orcond)
|
| 1332 |
+
continue
|
| 1333 |
+
elif newargs[-1].cond == cond:
|
| 1334 |
+
continue
|
| 1335 |
+
newargs.append(ExprCondPair(expr, cond))
|
| 1336 |
+
return newargs
|
| 1337 |
+
|
| 1338 |
+
|
| 1339 |
+
_blessed = lambda e: getattr(e.lhs, '_diff_wrt', False) and (
|
| 1340 |
+
getattr(e.rhs, '_diff_wrt', None) or
|
| 1341 |
+
isinstance(e.rhs, (Rational, NumberSymbol)))
|
| 1342 |
+
|
| 1343 |
+
|
| 1344 |
+
def piecewise_simplify(expr, **kwargs):
|
| 1345 |
+
expr = piecewise_simplify_arguments(expr, **kwargs)
|
| 1346 |
+
if not isinstance(expr, Piecewise):
|
| 1347 |
+
return expr
|
| 1348 |
+
args = list(expr.args)
|
| 1349 |
+
|
| 1350 |
+
args = _piecewise_simplify_eq_and(args)
|
| 1351 |
+
args = _piecewise_simplify_equal_to_next_segment(args)
|
| 1352 |
+
return Piecewise(*args)
|
| 1353 |
+
|
| 1354 |
+
|
| 1355 |
+
def _piecewise_simplify_equal_to_next_segment(args):
|
| 1356 |
+
"""
|
| 1357 |
+
See if expressions valid for an Equal expression happens to evaluate
|
| 1358 |
+
to the same function as in the next piecewise segment, see:
|
| 1359 |
+
https://github.com/sympy/sympy/issues/8458
|
| 1360 |
+
"""
|
| 1361 |
+
prevexpr = None
|
| 1362 |
+
for i, (expr, cond) in reversed(list(enumerate(args))):
|
| 1363 |
+
if prevexpr is not None:
|
| 1364 |
+
if isinstance(cond, And):
|
| 1365 |
+
eqs, other = sift(cond.args,
|
| 1366 |
+
lambda i: isinstance(i, Eq), binary=True)
|
| 1367 |
+
elif isinstance(cond, Eq):
|
| 1368 |
+
eqs, other = [cond], []
|
| 1369 |
+
else:
|
| 1370 |
+
eqs = other = []
|
| 1371 |
+
_prevexpr = prevexpr
|
| 1372 |
+
_expr = expr
|
| 1373 |
+
if eqs and not other:
|
| 1374 |
+
eqs = list(ordered(eqs))
|
| 1375 |
+
for e in eqs:
|
| 1376 |
+
# allow 2 args to collapse into 1 for any e
|
| 1377 |
+
# otherwise limit simplification to only simple-arg
|
| 1378 |
+
# Eq instances
|
| 1379 |
+
if len(args) == 2 or _blessed(e):
|
| 1380 |
+
_prevexpr = _prevexpr.subs(*e.args)
|
| 1381 |
+
_expr = _expr.subs(*e.args)
|
| 1382 |
+
# Did it evaluate to the same?
|
| 1383 |
+
if _prevexpr == _expr:
|
| 1384 |
+
# Set the expression for the Not equal section to the same
|
| 1385 |
+
# as the next. These will be merged when creating the new
|
| 1386 |
+
# Piecewise
|
| 1387 |
+
args[i] = args[i].func(args[i + 1][0], cond)
|
| 1388 |
+
else:
|
| 1389 |
+
# Update the expression that we compare against
|
| 1390 |
+
prevexpr = expr
|
| 1391 |
+
else:
|
| 1392 |
+
prevexpr = expr
|
| 1393 |
+
return args
|
| 1394 |
+
|
| 1395 |
+
|
| 1396 |
+
def _piecewise_simplify_eq_and(args):
|
| 1397 |
+
"""
|
| 1398 |
+
Try to simplify conditions and the expression for
|
| 1399 |
+
equalities that are part of the condition, e.g.
|
| 1400 |
+
Piecewise((n, And(Eq(n,0), Eq(n + m, 0))), (1, True))
|
| 1401 |
+
-> Piecewise((0, And(Eq(n, 0), Eq(m, 0))), (1, True))
|
| 1402 |
+
"""
|
| 1403 |
+
for i, (expr, cond) in enumerate(args):
|
| 1404 |
+
if isinstance(cond, And):
|
| 1405 |
+
eqs, other = sift(cond.args,
|
| 1406 |
+
lambda i: isinstance(i, Eq), binary=True)
|
| 1407 |
+
elif isinstance(cond, Eq):
|
| 1408 |
+
eqs, other = [cond], []
|
| 1409 |
+
else:
|
| 1410 |
+
eqs = other = []
|
| 1411 |
+
if eqs:
|
| 1412 |
+
eqs = list(ordered(eqs))
|
| 1413 |
+
for j, e in enumerate(eqs):
|
| 1414 |
+
# these blessed lhs objects behave like Symbols
|
| 1415 |
+
# and the rhs are simple replacements for the "symbols"
|
| 1416 |
+
if _blessed(e):
|
| 1417 |
+
expr = expr.subs(*e.args)
|
| 1418 |
+
eqs[j + 1:] = [ei.subs(*e.args) for ei in eqs[j + 1:]]
|
| 1419 |
+
other = [ei.subs(*e.args) for ei in other]
|
| 1420 |
+
cond = And(*(eqs + other))
|
| 1421 |
+
args[i] = args[i].func(expr, cond)
|
| 1422 |
+
return args
|
| 1423 |
+
|
| 1424 |
+
|
| 1425 |
+
def piecewise_exclusive(expr, *, skip_nan=False, deep=True):
|
| 1426 |
+
"""
|
| 1427 |
+
Rewrite :class:`Piecewise` with mutually exclusive conditions.
|
| 1428 |
+
|
| 1429 |
+
Explanation
|
| 1430 |
+
===========
|
| 1431 |
+
|
| 1432 |
+
SymPy represents the conditions of a :class:`Piecewise` in an
|
| 1433 |
+
"if-elif"-fashion, allowing more than one condition to be simultaneously
|
| 1434 |
+
True. The interpretation is that the first condition that is True is the
|
| 1435 |
+
case that holds. While this is a useful representation computationally it
|
| 1436 |
+
is not how a piecewise formula is typically shown in a mathematical text.
|
| 1437 |
+
The :func:`piecewise_exclusive` function can be used to rewrite any
|
| 1438 |
+
:class:`Piecewise` with more typical mutually exclusive conditions.
|
| 1439 |
+
|
| 1440 |
+
Note that further manipulation of the resulting :class:`Piecewise`, e.g.
|
| 1441 |
+
simplifying it, will most likely make it non-exclusive. Hence, this is
|
| 1442 |
+
primarily a function to be used in conjunction with printing the Piecewise
|
| 1443 |
+
or if one would like to reorder the expression-condition pairs.
|
| 1444 |
+
|
| 1445 |
+
If it is not possible to determine that all possibilities are covered by
|
| 1446 |
+
the different cases of the :class:`Piecewise` then a final
|
| 1447 |
+
:class:`~sympy.core.numbers.NaN` case will be included explicitly. This
|
| 1448 |
+
can be prevented by passing ``skip_nan=True``.
|
| 1449 |
+
|
| 1450 |
+
Examples
|
| 1451 |
+
========
|
| 1452 |
+
|
| 1453 |
+
>>> from sympy import piecewise_exclusive, Symbol, Piecewise, S
|
| 1454 |
+
>>> x = Symbol('x', real=True)
|
| 1455 |
+
>>> p = Piecewise((0, x < 0), (S.Half, x <= 0), (1, True))
|
| 1456 |
+
>>> piecewise_exclusive(p)
|
| 1457 |
+
Piecewise((0, x < 0), (1/2, Eq(x, 0)), (1, x > 0))
|
| 1458 |
+
>>> piecewise_exclusive(Piecewise((2, x > 1)))
|
| 1459 |
+
Piecewise((2, x > 1), (nan, x <= 1))
|
| 1460 |
+
>>> piecewise_exclusive(Piecewise((2, x > 1)), skip_nan=True)
|
| 1461 |
+
Piecewise((2, x > 1))
|
| 1462 |
+
|
| 1463 |
+
Parameters
|
| 1464 |
+
==========
|
| 1465 |
+
|
| 1466 |
+
expr: a SymPy expression.
|
| 1467 |
+
Any :class:`Piecewise` in the expression will be rewritten.
|
| 1468 |
+
skip_nan: ``bool`` (default ``False``)
|
| 1469 |
+
If ``skip_nan`` is set to ``True`` then a final
|
| 1470 |
+
:class:`~sympy.core.numbers.NaN` case will not be included.
|
| 1471 |
+
deep: ``bool`` (default ``True``)
|
| 1472 |
+
If ``deep`` is ``True`` then :func:`piecewise_exclusive` will rewrite
|
| 1473 |
+
any :class:`Piecewise` subexpressions in ``expr`` rather than just
|
| 1474 |
+
rewriting ``expr`` itself.
|
| 1475 |
+
|
| 1476 |
+
Returns
|
| 1477 |
+
=======
|
| 1478 |
+
|
| 1479 |
+
An expression equivalent to ``expr`` but where all :class:`Piecewise` have
|
| 1480 |
+
been rewritten with mutually exclusive conditions.
|
| 1481 |
+
|
| 1482 |
+
See Also
|
| 1483 |
+
========
|
| 1484 |
+
|
| 1485 |
+
Piecewise
|
| 1486 |
+
piecewise_fold
|
| 1487 |
+
"""
|
| 1488 |
+
|
| 1489 |
+
def make_exclusive(*pwargs):
|
| 1490 |
+
|
| 1491 |
+
cumcond = false
|
| 1492 |
+
newargs = []
|
| 1493 |
+
|
| 1494 |
+
# Handle the first n-1 cases
|
| 1495 |
+
for expr_i, cond_i in pwargs[:-1]:
|
| 1496 |
+
cancond = And(cond_i, Not(cumcond)).simplify()
|
| 1497 |
+
cumcond = Or(cond_i, cumcond).simplify()
|
| 1498 |
+
newargs.append((expr_i, cancond))
|
| 1499 |
+
|
| 1500 |
+
# For the nth case defer simplification of cumcond
|
| 1501 |
+
expr_n, cond_n = pwargs[-1]
|
| 1502 |
+
cancond_n = And(cond_n, Not(cumcond)).simplify()
|
| 1503 |
+
newargs.append((expr_n, cancond_n))
|
| 1504 |
+
|
| 1505 |
+
if not skip_nan:
|
| 1506 |
+
cumcond = Or(cond_n, cumcond).simplify()
|
| 1507 |
+
if cumcond is not true:
|
| 1508 |
+
newargs.append((Undefined, Not(cumcond).simplify()))
|
| 1509 |
+
|
| 1510 |
+
return Piecewise(*newargs, evaluate=False)
|
| 1511 |
+
|
| 1512 |
+
if deep:
|
| 1513 |
+
return expr.replace(Piecewise, make_exclusive)
|
| 1514 |
+
elif isinstance(expr, Piecewise):
|
| 1515 |
+
return make_exclusive(*expr.args)
|
| 1516 |
+
else:
|
| 1517 |
+
return expr
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/tests/test_complexes.py
ADDED
|
@@ -0,0 +1,1030 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.function import (Derivative, Function, Lambda, expand, PoleError)
|
| 2 |
+
from sympy.core.numbers import (E, I, Rational, comp, nan, oo, pi, zoo)
|
| 3 |
+
from sympy.core.relational import Eq
|
| 4 |
+
from sympy.core.singleton import S
|
| 5 |
+
from sympy.core.symbol import (Symbol, symbols)
|
| 6 |
+
from sympy.functions.elementary.complexes import (Abs, adjoint, arg, conjugate, im, re, sign, transpose)
|
| 7 |
+
from sympy.functions.elementary.exponential import (exp, exp_polar, log)
|
| 8 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 9 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 10 |
+
from sympy.functions.elementary.trigonometric import (acos, atan, atan2, cos, sin)
|
| 11 |
+
from sympy.functions.elementary.hyperbolic import sinh
|
| 12 |
+
from sympy.functions.special.delta_functions import (DiracDelta, Heaviside)
|
| 13 |
+
from sympy.integrals.integrals import Integral
|
| 14 |
+
from sympy.matrices.dense import Matrix
|
| 15 |
+
from sympy.matrices.expressions.funcmatrix import FunctionMatrix
|
| 16 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 17 |
+
from sympy.matrices.immutable import (ImmutableMatrix, ImmutableSparseMatrix)
|
| 18 |
+
from sympy.matrices import SparseMatrix
|
| 19 |
+
from sympy.sets.sets import Interval
|
| 20 |
+
from sympy.core.expr import unchanged
|
| 21 |
+
from sympy.core.function import ArgumentIndexError
|
| 22 |
+
from sympy.series.order import Order
|
| 23 |
+
from sympy.testing.pytest import XFAIL, raises, _both_exp_pow
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def N_equals(a, b):
|
| 27 |
+
"""Check whether two complex numbers are numerically close"""
|
| 28 |
+
return comp(a.n(), b.n(), 1.e-6)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def test_re():
|
| 32 |
+
x, y = symbols('x,y')
|
| 33 |
+
a, b = symbols('a,b', real=True)
|
| 34 |
+
|
| 35 |
+
r = Symbol('r', real=True)
|
| 36 |
+
i = Symbol('i', imaginary=True)
|
| 37 |
+
|
| 38 |
+
assert re(nan) is nan
|
| 39 |
+
|
| 40 |
+
assert re(oo) is oo
|
| 41 |
+
assert re(-oo) is -oo
|
| 42 |
+
|
| 43 |
+
assert re(0) == 0
|
| 44 |
+
|
| 45 |
+
assert re(1) == 1
|
| 46 |
+
assert re(-1) == -1
|
| 47 |
+
|
| 48 |
+
assert re(E) == E
|
| 49 |
+
assert re(-E) == -E
|
| 50 |
+
|
| 51 |
+
assert unchanged(re, x)
|
| 52 |
+
assert re(x*I) == -im(x)
|
| 53 |
+
assert re(r*I) == 0
|
| 54 |
+
assert re(r) == r
|
| 55 |
+
assert re(i*I) == I * i
|
| 56 |
+
assert re(i) == 0
|
| 57 |
+
|
| 58 |
+
assert re(x + y) == re(x) + re(y)
|
| 59 |
+
assert re(x + r) == re(x) + r
|
| 60 |
+
|
| 61 |
+
assert re(re(x)) == re(x)
|
| 62 |
+
|
| 63 |
+
assert re(2 + I) == 2
|
| 64 |
+
assert re(x + I) == re(x)
|
| 65 |
+
|
| 66 |
+
assert re(x + y*I) == re(x) - im(y)
|
| 67 |
+
assert re(x + r*I) == re(x)
|
| 68 |
+
|
| 69 |
+
assert re(log(2*I)) == log(2)
|
| 70 |
+
|
| 71 |
+
assert re((2 + I)**2).expand(complex=True) == 3
|
| 72 |
+
|
| 73 |
+
assert re(conjugate(x)) == re(x)
|
| 74 |
+
assert conjugate(re(x)) == re(x)
|
| 75 |
+
|
| 76 |
+
assert re(x).as_real_imag() == (re(x), 0)
|
| 77 |
+
|
| 78 |
+
assert re(i*r*x).diff(r) == re(i*x)
|
| 79 |
+
assert re(i*r*x).diff(i) == I*r*im(x)
|
| 80 |
+
|
| 81 |
+
assert re(
|
| 82 |
+
sqrt(a + b*I)) == (a**2 + b**2)**Rational(1, 4)*cos(atan2(b, a)/2)
|
| 83 |
+
assert re(a * (2 + b*I)) == 2*a
|
| 84 |
+
|
| 85 |
+
assert re((1 + sqrt(a + b*I))/2) == \
|
| 86 |
+
(a**2 + b**2)**Rational(1, 4)*cos(atan2(b, a)/2)/2 + S.Half
|
| 87 |
+
|
| 88 |
+
assert re(x).rewrite(im) == x - S.ImaginaryUnit*im(x)
|
| 89 |
+
assert (x + re(y)).rewrite(re, im) == x + y - S.ImaginaryUnit*im(y)
|
| 90 |
+
|
| 91 |
+
a = Symbol('a', algebraic=True)
|
| 92 |
+
t = Symbol('t', transcendental=True)
|
| 93 |
+
x = Symbol('x')
|
| 94 |
+
assert re(a).is_algebraic
|
| 95 |
+
assert re(x).is_algebraic is None
|
| 96 |
+
assert re(t).is_algebraic is False
|
| 97 |
+
|
| 98 |
+
assert re(S.ComplexInfinity) is S.NaN
|
| 99 |
+
|
| 100 |
+
n, m, l = symbols('n m l')
|
| 101 |
+
A = MatrixSymbol('A',n,m)
|
| 102 |
+
assert re(A) == (S.Half) * (A + conjugate(A))
|
| 103 |
+
|
| 104 |
+
A = Matrix([[1 + 4*I,2],[0, -3*I]])
|
| 105 |
+
assert re(A) == Matrix([[1, 2],[0, 0]])
|
| 106 |
+
|
| 107 |
+
A = ImmutableMatrix([[1 + 3*I, 3-2*I],[0, 2*I]])
|
| 108 |
+
assert re(A) == ImmutableMatrix([[1, 3],[0, 0]])
|
| 109 |
+
|
| 110 |
+
X = SparseMatrix([[2*j + i*I for i in range(5)] for j in range(5)])
|
| 111 |
+
assert re(X) - Matrix([[0, 0, 0, 0, 0],
|
| 112 |
+
[2, 2, 2, 2, 2],
|
| 113 |
+
[4, 4, 4, 4, 4],
|
| 114 |
+
[6, 6, 6, 6, 6],
|
| 115 |
+
[8, 8, 8, 8, 8]]) == Matrix.zeros(5)
|
| 116 |
+
|
| 117 |
+
assert im(X) - Matrix([[0, 1, 2, 3, 4],
|
| 118 |
+
[0, 1, 2, 3, 4],
|
| 119 |
+
[0, 1, 2, 3, 4],
|
| 120 |
+
[0, 1, 2, 3, 4],
|
| 121 |
+
[0, 1, 2, 3, 4]]) == Matrix.zeros(5)
|
| 122 |
+
|
| 123 |
+
X = FunctionMatrix(3, 3, Lambda((n, m), n + m*I))
|
| 124 |
+
assert re(X) == Matrix([[0, 0, 0], [1, 1, 1], [2, 2, 2]])
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def test_im():
|
| 128 |
+
x, y = symbols('x,y')
|
| 129 |
+
a, b = symbols('a,b', real=True)
|
| 130 |
+
|
| 131 |
+
r = Symbol('r', real=True)
|
| 132 |
+
i = Symbol('i', imaginary=True)
|
| 133 |
+
|
| 134 |
+
assert im(nan) is nan
|
| 135 |
+
|
| 136 |
+
assert im(oo*I) is oo
|
| 137 |
+
assert im(-oo*I) is -oo
|
| 138 |
+
|
| 139 |
+
assert im(0) == 0
|
| 140 |
+
|
| 141 |
+
assert im(1) == 0
|
| 142 |
+
assert im(-1) == 0
|
| 143 |
+
|
| 144 |
+
assert im(E*I) == E
|
| 145 |
+
assert im(-E*I) == -E
|
| 146 |
+
|
| 147 |
+
assert unchanged(im, x)
|
| 148 |
+
assert im(x*I) == re(x)
|
| 149 |
+
assert im(r*I) == r
|
| 150 |
+
assert im(r) == 0
|
| 151 |
+
assert im(i*I) == 0
|
| 152 |
+
assert im(i) == -I * i
|
| 153 |
+
|
| 154 |
+
assert im(x + y) == im(x) + im(y)
|
| 155 |
+
assert im(x + r) == im(x)
|
| 156 |
+
assert im(x + r*I) == im(x) + r
|
| 157 |
+
|
| 158 |
+
assert im(im(x)*I) == im(x)
|
| 159 |
+
|
| 160 |
+
assert im(2 + I) == 1
|
| 161 |
+
assert im(x + I) == im(x) + 1
|
| 162 |
+
|
| 163 |
+
assert im(x + y*I) == im(x) + re(y)
|
| 164 |
+
assert im(x + r*I) == im(x) + r
|
| 165 |
+
|
| 166 |
+
assert im(log(2*I)) == pi/2
|
| 167 |
+
|
| 168 |
+
assert im((2 + I)**2).expand(complex=True) == 4
|
| 169 |
+
|
| 170 |
+
assert im(conjugate(x)) == -im(x)
|
| 171 |
+
assert conjugate(im(x)) == im(x)
|
| 172 |
+
|
| 173 |
+
assert im(x).as_real_imag() == (im(x), 0)
|
| 174 |
+
|
| 175 |
+
assert im(i*r*x).diff(r) == im(i*x)
|
| 176 |
+
assert im(i*r*x).diff(i) == -I * re(r*x)
|
| 177 |
+
|
| 178 |
+
assert im(
|
| 179 |
+
sqrt(a + b*I)) == (a**2 + b**2)**Rational(1, 4)*sin(atan2(b, a)/2)
|
| 180 |
+
assert im(a * (2 + b*I)) == a*b
|
| 181 |
+
|
| 182 |
+
assert im((1 + sqrt(a + b*I))/2) == \
|
| 183 |
+
(a**2 + b**2)**Rational(1, 4)*sin(atan2(b, a)/2)/2
|
| 184 |
+
|
| 185 |
+
assert im(x).rewrite(re) == -S.ImaginaryUnit * (x - re(x))
|
| 186 |
+
assert (x + im(y)).rewrite(im, re) == x - S.ImaginaryUnit * (y - re(y))
|
| 187 |
+
|
| 188 |
+
a = Symbol('a', algebraic=True)
|
| 189 |
+
t = Symbol('t', transcendental=True)
|
| 190 |
+
x = Symbol('x')
|
| 191 |
+
assert re(a).is_algebraic
|
| 192 |
+
assert re(x).is_algebraic is None
|
| 193 |
+
assert re(t).is_algebraic is False
|
| 194 |
+
|
| 195 |
+
assert im(S.ComplexInfinity) is S.NaN
|
| 196 |
+
|
| 197 |
+
n, m, l = symbols('n m l')
|
| 198 |
+
A = MatrixSymbol('A',n,m)
|
| 199 |
+
|
| 200 |
+
assert im(A) == (S.One/(2*I)) * (A - conjugate(A))
|
| 201 |
+
|
| 202 |
+
A = Matrix([[1 + 4*I, 2],[0, -3*I]])
|
| 203 |
+
assert im(A) == Matrix([[4, 0],[0, -3]])
|
| 204 |
+
|
| 205 |
+
A = ImmutableMatrix([[1 + 3*I, 3-2*I],[0, 2*I]])
|
| 206 |
+
assert im(A) == ImmutableMatrix([[3, -2],[0, 2]])
|
| 207 |
+
|
| 208 |
+
X = ImmutableSparseMatrix(
|
| 209 |
+
[[i*I + i for i in range(5)] for i in range(5)])
|
| 210 |
+
Y = SparseMatrix([list(range(5)) for i in range(5)])
|
| 211 |
+
assert im(X).as_immutable() == Y
|
| 212 |
+
|
| 213 |
+
X = FunctionMatrix(3, 3, Lambda((n, m), n + m*I))
|
| 214 |
+
assert im(X) == Matrix([[0, 1, 2], [0, 1, 2], [0, 1, 2]])
|
| 215 |
+
|
| 216 |
+
def test_sign():
|
| 217 |
+
assert sign(1.2) == 1
|
| 218 |
+
assert sign(-1.2) == -1
|
| 219 |
+
assert sign(3*I) == I
|
| 220 |
+
assert sign(-3*I) == -I
|
| 221 |
+
assert sign(0) == 0
|
| 222 |
+
assert sign(0, evaluate=False).doit() == 0
|
| 223 |
+
assert sign(oo, evaluate=False).doit() == 1
|
| 224 |
+
assert sign(nan) is nan
|
| 225 |
+
assert sign(2 + 2*I).doit() == sqrt(2)*(2 + 2*I)/4
|
| 226 |
+
assert sign(2 + 3*I).simplify() == sign(2 + 3*I)
|
| 227 |
+
assert sign(2 + 2*I).simplify() == sign(1 + I)
|
| 228 |
+
assert sign(im(sqrt(1 - sqrt(3)))) == 1
|
| 229 |
+
assert sign(sqrt(1 - sqrt(3))) == I
|
| 230 |
+
|
| 231 |
+
x = Symbol('x')
|
| 232 |
+
assert sign(x).is_finite is True
|
| 233 |
+
assert sign(x).is_complex is True
|
| 234 |
+
assert sign(x).is_imaginary is None
|
| 235 |
+
assert sign(x).is_integer is None
|
| 236 |
+
assert sign(x).is_real is None
|
| 237 |
+
assert sign(x).is_zero is None
|
| 238 |
+
assert sign(x).doit() == sign(x)
|
| 239 |
+
assert sign(1.2*x) == sign(x)
|
| 240 |
+
assert sign(2*x) == sign(x)
|
| 241 |
+
assert sign(I*x) == I*sign(x)
|
| 242 |
+
assert sign(-2*I*x) == -I*sign(x)
|
| 243 |
+
assert sign(conjugate(x)) == conjugate(sign(x))
|
| 244 |
+
|
| 245 |
+
p = Symbol('p', positive=True)
|
| 246 |
+
n = Symbol('n', negative=True)
|
| 247 |
+
m = Symbol('m', negative=True)
|
| 248 |
+
assert sign(2*p*x) == sign(x)
|
| 249 |
+
assert sign(n*x) == -sign(x)
|
| 250 |
+
assert sign(n*m*x) == sign(x)
|
| 251 |
+
|
| 252 |
+
x = Symbol('x', imaginary=True)
|
| 253 |
+
assert sign(x).is_imaginary is True
|
| 254 |
+
assert sign(x).is_integer is False
|
| 255 |
+
assert sign(x).is_real is False
|
| 256 |
+
assert sign(x).is_zero is False
|
| 257 |
+
assert sign(x).diff(x) == 2*DiracDelta(-I*x)
|
| 258 |
+
assert sign(x).doit() == x / Abs(x)
|
| 259 |
+
assert conjugate(sign(x)) == -sign(x)
|
| 260 |
+
|
| 261 |
+
x = Symbol('x', real=True)
|
| 262 |
+
assert sign(x).is_imaginary is False
|
| 263 |
+
assert sign(x).is_integer is True
|
| 264 |
+
assert sign(x).is_real is True
|
| 265 |
+
assert sign(x).is_zero is None
|
| 266 |
+
assert sign(x).diff(x) == 2*DiracDelta(x)
|
| 267 |
+
assert sign(x).doit() == sign(x)
|
| 268 |
+
assert conjugate(sign(x)) == sign(x)
|
| 269 |
+
|
| 270 |
+
x = Symbol('x', nonzero=True)
|
| 271 |
+
assert sign(x).is_imaginary is False
|
| 272 |
+
assert sign(x).is_integer is True
|
| 273 |
+
assert sign(x).is_real is True
|
| 274 |
+
assert sign(x).is_zero is False
|
| 275 |
+
assert sign(x).doit() == x / Abs(x)
|
| 276 |
+
assert sign(Abs(x)) == 1
|
| 277 |
+
assert Abs(sign(x)) == 1
|
| 278 |
+
|
| 279 |
+
x = Symbol('x', positive=True)
|
| 280 |
+
assert sign(x).is_imaginary is False
|
| 281 |
+
assert sign(x).is_integer is True
|
| 282 |
+
assert sign(x).is_real is True
|
| 283 |
+
assert sign(x).is_zero is False
|
| 284 |
+
assert sign(x).doit() == x / Abs(x)
|
| 285 |
+
assert sign(Abs(x)) == 1
|
| 286 |
+
assert Abs(sign(x)) == 1
|
| 287 |
+
|
| 288 |
+
x = 0
|
| 289 |
+
assert sign(x).is_imaginary is False
|
| 290 |
+
assert sign(x).is_integer is True
|
| 291 |
+
assert sign(x).is_real is True
|
| 292 |
+
assert sign(x).is_zero is True
|
| 293 |
+
assert sign(x).doit() == 0
|
| 294 |
+
assert sign(Abs(x)) == 0
|
| 295 |
+
assert Abs(sign(x)) == 0
|
| 296 |
+
|
| 297 |
+
nz = Symbol('nz', nonzero=True, integer=True)
|
| 298 |
+
assert sign(nz).is_imaginary is False
|
| 299 |
+
assert sign(nz).is_integer is True
|
| 300 |
+
assert sign(nz).is_real is True
|
| 301 |
+
assert sign(nz).is_zero is False
|
| 302 |
+
assert sign(nz)**2 == 1
|
| 303 |
+
assert (sign(nz)**3).args == (sign(nz), 3)
|
| 304 |
+
|
| 305 |
+
assert sign(Symbol('x', nonnegative=True)).is_nonnegative
|
| 306 |
+
assert sign(Symbol('x', nonnegative=True)).is_nonpositive is None
|
| 307 |
+
assert sign(Symbol('x', nonpositive=True)).is_nonnegative is None
|
| 308 |
+
assert sign(Symbol('x', nonpositive=True)).is_nonpositive
|
| 309 |
+
assert sign(Symbol('x', real=True)).is_nonnegative is None
|
| 310 |
+
assert sign(Symbol('x', real=True)).is_nonpositive is None
|
| 311 |
+
assert sign(Symbol('x', real=True, zero=False)).is_nonpositive is None
|
| 312 |
+
|
| 313 |
+
x, y = Symbol('x', real=True), Symbol('y')
|
| 314 |
+
f = Function('f')
|
| 315 |
+
assert sign(x).rewrite(Piecewise) == \
|
| 316 |
+
Piecewise((1, x > 0), (-1, x < 0), (0, True))
|
| 317 |
+
assert sign(y).rewrite(Piecewise) == sign(y)
|
| 318 |
+
assert sign(x).rewrite(Heaviside) == 2*Heaviside(x, H0=S(1)/2) - 1
|
| 319 |
+
assert sign(y).rewrite(Heaviside) == sign(y)
|
| 320 |
+
assert sign(y).rewrite(Abs) == Piecewise((0, Eq(y, 0)), (y/Abs(y), True))
|
| 321 |
+
assert sign(f(y)).rewrite(Abs) == Piecewise((0, Eq(f(y), 0)), (f(y)/Abs(f(y)), True))
|
| 322 |
+
|
| 323 |
+
# evaluate what can be evaluated
|
| 324 |
+
assert sign(exp_polar(I*pi)*pi) is S.NegativeOne
|
| 325 |
+
|
| 326 |
+
eq = -sqrt(10 + 6*sqrt(3)) + sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3))
|
| 327 |
+
# if there is a fast way to know when and when you cannot prove an
|
| 328 |
+
# expression like this is zero then the equality to zero is ok
|
| 329 |
+
assert sign(eq).func is sign or sign(eq) == 0
|
| 330 |
+
# but sometimes it's hard to do this so it's better not to load
|
| 331 |
+
# abs down with tests that will be very slow
|
| 332 |
+
q = 1 + sqrt(2) - 2*sqrt(3) + 1331*sqrt(6)
|
| 333 |
+
p = expand(q**3)**Rational(1, 3)
|
| 334 |
+
d = p - q
|
| 335 |
+
assert sign(d).func is sign or sign(d) == 0
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
def test_as_real_imag():
|
| 339 |
+
n = pi**1000
|
| 340 |
+
# the special code for working out the real
|
| 341 |
+
# and complex parts of a power with Integer exponent
|
| 342 |
+
# should not run if there is no imaginary part, hence
|
| 343 |
+
# this should not hang
|
| 344 |
+
assert n.as_real_imag() == (n, 0)
|
| 345 |
+
|
| 346 |
+
# issue 6261
|
| 347 |
+
x = Symbol('x')
|
| 348 |
+
assert sqrt(x).as_real_imag() == \
|
| 349 |
+
((re(x)**2 + im(x)**2)**Rational(1, 4)*cos(atan2(im(x), re(x))/2),
|
| 350 |
+
(re(x)**2 + im(x)**2)**Rational(1, 4)*sin(atan2(im(x), re(x))/2))
|
| 351 |
+
|
| 352 |
+
# issue 3853
|
| 353 |
+
a, b = symbols('a,b', real=True)
|
| 354 |
+
assert ((1 + sqrt(a + b*I))/2).as_real_imag() == \
|
| 355 |
+
(
|
| 356 |
+
(a**2 + b**2)**Rational(
|
| 357 |
+
1, 4)*cos(atan2(b, a)/2)/2 + S.Half,
|
| 358 |
+
(a**2 + b**2)**Rational(1, 4)*sin(atan2(b, a)/2)/2)
|
| 359 |
+
|
| 360 |
+
assert sqrt(a**2).as_real_imag() == (sqrt(a**2), 0)
|
| 361 |
+
i = symbols('i', imaginary=True)
|
| 362 |
+
assert sqrt(i**2).as_real_imag() == (0, abs(i))
|
| 363 |
+
|
| 364 |
+
assert ((1 + I)/(1 - I)).as_real_imag() == (0, 1)
|
| 365 |
+
assert ((1 + I)**3/(1 - I)).as_real_imag() == (-2, 0)
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
@XFAIL
|
| 369 |
+
def test_sign_issue_3068():
|
| 370 |
+
n = pi**1000
|
| 371 |
+
i = int(n)
|
| 372 |
+
x = Symbol('x')
|
| 373 |
+
assert (n - i).round() == 1 # doesn't hang
|
| 374 |
+
assert sign(n - i) == 1
|
| 375 |
+
# perhaps it's not possible to get the sign right when
|
| 376 |
+
# only 1 digit is being requested for this situation;
|
| 377 |
+
# 2 digits works
|
| 378 |
+
assert (n - x).n(1, subs={x: i}) > 0
|
| 379 |
+
assert (n - x).n(2, subs={x: i}) > 0
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
def test_Abs():
|
| 383 |
+
raises(TypeError, lambda: Abs(Interval(2, 3))) # issue 8717
|
| 384 |
+
|
| 385 |
+
x, y = symbols('x,y')
|
| 386 |
+
assert sign(sign(x)) == sign(x)
|
| 387 |
+
assert sign(x*y).func is sign
|
| 388 |
+
assert Abs(0) == 0
|
| 389 |
+
assert Abs(1) == 1
|
| 390 |
+
assert Abs(-1) == 1
|
| 391 |
+
assert Abs(I) == 1
|
| 392 |
+
assert Abs(-I) == 1
|
| 393 |
+
assert Abs(nan) is nan
|
| 394 |
+
assert Abs(zoo) is oo
|
| 395 |
+
assert Abs(I * pi) == pi
|
| 396 |
+
assert Abs(-I * pi) == pi
|
| 397 |
+
assert Abs(I * x) == Abs(x)
|
| 398 |
+
assert Abs(-I * x) == Abs(x)
|
| 399 |
+
assert Abs(-2*x) == 2*Abs(x)
|
| 400 |
+
assert Abs(-2.0*x) == 2.0*Abs(x)
|
| 401 |
+
assert Abs(2*pi*x*y) == 2*pi*Abs(x*y)
|
| 402 |
+
assert Abs(conjugate(x)) == Abs(x)
|
| 403 |
+
assert conjugate(Abs(x)) == Abs(x)
|
| 404 |
+
assert Abs(x).expand(complex=True) == sqrt(re(x)**2 + im(x)**2)
|
| 405 |
+
|
| 406 |
+
a = Symbol('a', positive=True)
|
| 407 |
+
assert Abs(2*pi*x*a) == 2*pi*a*Abs(x)
|
| 408 |
+
assert Abs(2*pi*I*x*a) == 2*pi*a*Abs(x)
|
| 409 |
+
|
| 410 |
+
x = Symbol('x', real=True)
|
| 411 |
+
n = Symbol('n', integer=True)
|
| 412 |
+
assert Abs((-1)**n) == 1
|
| 413 |
+
assert x**(2*n) == Abs(x)**(2*n)
|
| 414 |
+
assert Abs(x).diff(x) == sign(x)
|
| 415 |
+
assert abs(x) == Abs(x) # Python built-in
|
| 416 |
+
assert Abs(x)**3 == x**2*Abs(x)
|
| 417 |
+
assert Abs(x)**4 == x**4
|
| 418 |
+
assert (
|
| 419 |
+
Abs(x)**(3*n)).args == (Abs(x), 3*n) # leave symbolic odd unchanged
|
| 420 |
+
assert (1/Abs(x)).args == (Abs(x), -1)
|
| 421 |
+
assert 1/Abs(x)**3 == 1/(x**2*Abs(x))
|
| 422 |
+
assert Abs(x)**-3 == Abs(x)/(x**4)
|
| 423 |
+
assert Abs(x**3) == x**2*Abs(x)
|
| 424 |
+
assert Abs(I**I) == exp(-pi/2)
|
| 425 |
+
assert Abs((4 + 5*I)**(6 + 7*I)) == 68921*exp(-7*atan(Rational(5, 4)))
|
| 426 |
+
y = Symbol('y', real=True)
|
| 427 |
+
assert Abs(I**y) == 1
|
| 428 |
+
y = Symbol('y')
|
| 429 |
+
assert Abs(I**y) == exp(-pi*im(y)/2)
|
| 430 |
+
|
| 431 |
+
x = Symbol('x', imaginary=True)
|
| 432 |
+
assert Abs(x).diff(x) == -sign(x)
|
| 433 |
+
|
| 434 |
+
eq = -sqrt(10 + 6*sqrt(3)) + sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3))
|
| 435 |
+
# if there is a fast way to know when you can and when you cannot prove an
|
| 436 |
+
# expression like this is zero then the equality to zero is ok
|
| 437 |
+
assert abs(eq).func is Abs or abs(eq) == 0
|
| 438 |
+
# but sometimes it's hard to do this so it's better not to load
|
| 439 |
+
# abs down with tests that will be very slow
|
| 440 |
+
q = 1 + sqrt(2) - 2*sqrt(3) + 1331*sqrt(6)
|
| 441 |
+
p = expand(q**3)**Rational(1, 3)
|
| 442 |
+
d = p - q
|
| 443 |
+
assert abs(d).func is Abs or abs(d) == 0
|
| 444 |
+
|
| 445 |
+
assert Abs(4*exp(pi*I/4)) == 4
|
| 446 |
+
assert Abs(3**(2 + I)) == 9
|
| 447 |
+
assert Abs((-3)**(1 - I)) == 3*exp(pi)
|
| 448 |
+
|
| 449 |
+
assert Abs(oo) is oo
|
| 450 |
+
assert Abs(-oo) is oo
|
| 451 |
+
assert Abs(oo + I) is oo
|
| 452 |
+
assert Abs(oo + I*oo) is oo
|
| 453 |
+
|
| 454 |
+
a = Symbol('a', algebraic=True)
|
| 455 |
+
t = Symbol('t', transcendental=True)
|
| 456 |
+
x = Symbol('x')
|
| 457 |
+
assert re(a).is_algebraic
|
| 458 |
+
assert re(x).is_algebraic is None
|
| 459 |
+
assert re(t).is_algebraic is False
|
| 460 |
+
assert Abs(x).fdiff() == sign(x)
|
| 461 |
+
raises(ArgumentIndexError, lambda: Abs(x).fdiff(2))
|
| 462 |
+
|
| 463 |
+
# doesn't have recursion error
|
| 464 |
+
arg = sqrt(acos(1 - I)*acos(1 + I))
|
| 465 |
+
assert abs(arg) == arg
|
| 466 |
+
|
| 467 |
+
# special handling to put Abs in denom
|
| 468 |
+
assert abs(1/x) == 1/Abs(x)
|
| 469 |
+
e = abs(2/x**2)
|
| 470 |
+
assert e.is_Mul and e == 2/Abs(x**2)
|
| 471 |
+
assert unchanged(Abs, y/x)
|
| 472 |
+
assert unchanged(Abs, x/(x + 1))
|
| 473 |
+
assert unchanged(Abs, x*y)
|
| 474 |
+
p = Symbol('p', positive=True)
|
| 475 |
+
assert abs(x/p) == abs(x)/p
|
| 476 |
+
|
| 477 |
+
# coverage
|
| 478 |
+
assert unchanged(Abs, Symbol('x', real=True)**y)
|
| 479 |
+
# issue 19627
|
| 480 |
+
f = Function('f', positive=True)
|
| 481 |
+
assert sqrt(f(x)**2) == f(x)
|
| 482 |
+
# issue 21625
|
| 483 |
+
assert unchanged(Abs, S("im(acos(-i + acosh(-g + i)))"))
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
def test_Abs_rewrite():
|
| 487 |
+
x = Symbol('x', real=True)
|
| 488 |
+
a = Abs(x).rewrite(Heaviside).expand()
|
| 489 |
+
assert a == x*Heaviside(x) - x*Heaviside(-x)
|
| 490 |
+
for i in [-2, -1, 0, 1, 2]:
|
| 491 |
+
assert a.subs(x, i) == abs(i)
|
| 492 |
+
y = Symbol('y')
|
| 493 |
+
assert Abs(y).rewrite(Heaviside) == Abs(y)
|
| 494 |
+
|
| 495 |
+
x, y = Symbol('x', real=True), Symbol('y')
|
| 496 |
+
assert Abs(x).rewrite(Piecewise) == Piecewise((x, x >= 0), (-x, True))
|
| 497 |
+
assert Abs(y).rewrite(Piecewise) == Abs(y)
|
| 498 |
+
assert Abs(y).rewrite(sign) == y/sign(y)
|
| 499 |
+
|
| 500 |
+
i = Symbol('i', imaginary=True)
|
| 501 |
+
assert abs(i).rewrite(Piecewise) == Piecewise((I*i, I*i >= 0), (-I*i, True))
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
assert Abs(y).rewrite(conjugate) == sqrt(y*conjugate(y))
|
| 505 |
+
assert Abs(i).rewrite(conjugate) == sqrt(-i**2) # == -I*i
|
| 506 |
+
|
| 507 |
+
y = Symbol('y', extended_real=True)
|
| 508 |
+
assert (Abs(exp(-I*x)-exp(-I*y))**2).rewrite(conjugate) == \
|
| 509 |
+
-exp(I*x)*exp(-I*y) + 2 - exp(-I*x)*exp(I*y)
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
def test_Abs_real():
|
| 513 |
+
# test some properties of abs that only apply
|
| 514 |
+
# to real numbers
|
| 515 |
+
x = Symbol('x', complex=True)
|
| 516 |
+
assert sqrt(x**2) != Abs(x)
|
| 517 |
+
assert Abs(x**2) != x**2
|
| 518 |
+
|
| 519 |
+
x = Symbol('x', real=True)
|
| 520 |
+
assert sqrt(x**2) == Abs(x)
|
| 521 |
+
assert Abs(x**2) == x**2
|
| 522 |
+
|
| 523 |
+
# if the symbol is zero, the following will still apply
|
| 524 |
+
nn = Symbol('nn', nonnegative=True, real=True)
|
| 525 |
+
np = Symbol('np', nonpositive=True, real=True)
|
| 526 |
+
assert Abs(nn) == nn
|
| 527 |
+
assert Abs(np) == -np
|
| 528 |
+
|
| 529 |
+
|
| 530 |
+
def test_Abs_properties():
|
| 531 |
+
x = Symbol('x')
|
| 532 |
+
assert Abs(x).is_real is None
|
| 533 |
+
assert Abs(x).is_extended_real is True
|
| 534 |
+
assert Abs(x).is_rational is None
|
| 535 |
+
assert Abs(x).is_positive is None
|
| 536 |
+
assert Abs(x).is_nonnegative is None
|
| 537 |
+
assert Abs(x).is_extended_positive is None
|
| 538 |
+
assert Abs(x).is_extended_nonnegative is True
|
| 539 |
+
|
| 540 |
+
f = Symbol('x', finite=True)
|
| 541 |
+
assert Abs(f).is_real is True
|
| 542 |
+
assert Abs(f).is_extended_real is True
|
| 543 |
+
assert Abs(f).is_rational is None
|
| 544 |
+
assert Abs(f).is_positive is None
|
| 545 |
+
assert Abs(f).is_nonnegative is True
|
| 546 |
+
assert Abs(f).is_extended_positive is None
|
| 547 |
+
assert Abs(f).is_extended_nonnegative is True
|
| 548 |
+
|
| 549 |
+
z = Symbol('z', complex=True, zero=False)
|
| 550 |
+
assert Abs(z).is_real is True # since complex implies finite
|
| 551 |
+
assert Abs(z).is_extended_real is True
|
| 552 |
+
assert Abs(z).is_rational is None
|
| 553 |
+
assert Abs(z).is_positive is True
|
| 554 |
+
assert Abs(z).is_extended_positive is True
|
| 555 |
+
assert Abs(z).is_zero is False
|
| 556 |
+
|
| 557 |
+
p = Symbol('p', positive=True)
|
| 558 |
+
assert Abs(p).is_real is True
|
| 559 |
+
assert Abs(p).is_extended_real is True
|
| 560 |
+
assert Abs(p).is_rational is None
|
| 561 |
+
assert Abs(p).is_positive is True
|
| 562 |
+
assert Abs(p).is_zero is False
|
| 563 |
+
|
| 564 |
+
q = Symbol('q', rational=True)
|
| 565 |
+
assert Abs(q).is_real is True
|
| 566 |
+
assert Abs(q).is_rational is True
|
| 567 |
+
assert Abs(q).is_integer is None
|
| 568 |
+
assert Abs(q).is_positive is None
|
| 569 |
+
assert Abs(q).is_nonnegative is True
|
| 570 |
+
|
| 571 |
+
i = Symbol('i', integer=True)
|
| 572 |
+
assert Abs(i).is_real is True
|
| 573 |
+
assert Abs(i).is_integer is True
|
| 574 |
+
assert Abs(i).is_positive is None
|
| 575 |
+
assert Abs(i).is_nonnegative is True
|
| 576 |
+
|
| 577 |
+
e = Symbol('n', even=True)
|
| 578 |
+
ne = Symbol('ne', real=True, even=False)
|
| 579 |
+
assert Abs(e).is_even is True
|
| 580 |
+
assert Abs(ne).is_even is False
|
| 581 |
+
assert Abs(i).is_even is None
|
| 582 |
+
|
| 583 |
+
o = Symbol('n', odd=True)
|
| 584 |
+
no = Symbol('no', real=True, odd=False)
|
| 585 |
+
assert Abs(o).is_odd is True
|
| 586 |
+
assert Abs(no).is_odd is False
|
| 587 |
+
assert Abs(i).is_odd is None
|
| 588 |
+
|
| 589 |
+
|
| 590 |
+
def test_abs():
|
| 591 |
+
# this tests that abs calls Abs; don't rename to
|
| 592 |
+
# test_Abs since that test is already above
|
| 593 |
+
a = Symbol('a', positive=True)
|
| 594 |
+
assert abs(I*(1 + a)**2) == (1 + a)**2
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
def test_arg():
|
| 598 |
+
assert arg(0) is nan
|
| 599 |
+
assert arg(1) == 0
|
| 600 |
+
assert arg(-1) == pi
|
| 601 |
+
assert arg(I) == pi/2
|
| 602 |
+
assert arg(-I) == -pi/2
|
| 603 |
+
assert arg(1 + I) == pi/4
|
| 604 |
+
assert arg(-1 + I) == pi*Rational(3, 4)
|
| 605 |
+
assert arg(1 - I) == -pi/4
|
| 606 |
+
assert arg(exp_polar(4*pi*I)) == 4*pi
|
| 607 |
+
assert arg(exp_polar(-7*pi*I)) == -7*pi
|
| 608 |
+
assert arg(exp_polar(5 - 3*pi*I/4)) == pi*Rational(-3, 4)
|
| 609 |
+
|
| 610 |
+
assert arg(exp(I*pi/7)) == pi/7 # issue 17300
|
| 611 |
+
assert arg(exp(16*I)) == 16 - 6*pi
|
| 612 |
+
assert arg(exp(13*I*pi/12)) == -11*pi/12
|
| 613 |
+
assert arg(exp(123 - 5*I)) == -5 + 2*pi
|
| 614 |
+
assert arg(exp(sin(1 + 3*I))) == -2*pi + cos(1)*sinh(3)
|
| 615 |
+
r = Symbol('r', real=True)
|
| 616 |
+
assert arg(exp(r - 2*I)) == -2
|
| 617 |
+
|
| 618 |
+
f = Function('f')
|
| 619 |
+
assert not arg(f(0) + I*f(1)).atoms(re)
|
| 620 |
+
|
| 621 |
+
# check nesting
|
| 622 |
+
x = Symbol('x')
|
| 623 |
+
assert arg(arg(arg(x))) is not S.NaN
|
| 624 |
+
assert arg(arg(arg(arg(x)))) is S.NaN
|
| 625 |
+
r = Symbol('r', extended_real=True)
|
| 626 |
+
assert arg(arg(r)) is not S.NaN
|
| 627 |
+
assert arg(arg(arg(r))) is S.NaN
|
| 628 |
+
|
| 629 |
+
p = Function('p', extended_positive=True)
|
| 630 |
+
assert arg(p(x)) == 0
|
| 631 |
+
assert arg((3 + I)*p(x)) == arg(3 + I)
|
| 632 |
+
|
| 633 |
+
p = Symbol('p', positive=True)
|
| 634 |
+
assert arg(p) == 0
|
| 635 |
+
assert arg(p*I) == pi/2
|
| 636 |
+
|
| 637 |
+
n = Symbol('n', negative=True)
|
| 638 |
+
assert arg(n) == pi
|
| 639 |
+
assert arg(n*I) == -pi/2
|
| 640 |
+
|
| 641 |
+
x = Symbol('x')
|
| 642 |
+
assert conjugate(arg(x)) == arg(x)
|
| 643 |
+
|
| 644 |
+
e = p + I*p**2
|
| 645 |
+
assert arg(e) == arg(1 + p*I)
|
| 646 |
+
# make sure sign doesn't swap
|
| 647 |
+
e = -2*p + 4*I*p**2
|
| 648 |
+
assert arg(e) == arg(-1 + 2*p*I)
|
| 649 |
+
# make sure sign isn't lost
|
| 650 |
+
x = symbols('x', real=True) # could be zero
|
| 651 |
+
e = x + I*x
|
| 652 |
+
assert arg(e) == arg(x*(1 + I))
|
| 653 |
+
assert arg(e/p) == arg(x*(1 + I))
|
| 654 |
+
e = p*cos(p) + I*log(p)*exp(p)
|
| 655 |
+
assert arg(e).args[0] == e
|
| 656 |
+
# keep it simple -- let the user do more advanced cancellation
|
| 657 |
+
e = (p + 1) + I*(p**2 - 1)
|
| 658 |
+
assert arg(e).args[0] == e
|
| 659 |
+
|
| 660 |
+
f = Function('f')
|
| 661 |
+
e = 2*x*(f(0) - 1) - 2*x*f(0)
|
| 662 |
+
assert arg(e) == arg(-2*x)
|
| 663 |
+
assert arg(f(0)).func == arg and arg(f(0)).args == (f(0),)
|
| 664 |
+
|
| 665 |
+
|
| 666 |
+
def test_arg_rewrite():
|
| 667 |
+
assert arg(1 + I) == atan2(1, 1)
|
| 668 |
+
|
| 669 |
+
x = Symbol('x', real=True)
|
| 670 |
+
y = Symbol('y', real=True)
|
| 671 |
+
assert arg(x + I*y).rewrite(atan2) == atan2(y, x)
|
| 672 |
+
|
| 673 |
+
|
| 674 |
+
def test_arg_leading_term_and_series():
|
| 675 |
+
x = Symbol('x')
|
| 676 |
+
assert arg(x).as_leading_term(x, cdir = 1) == 0
|
| 677 |
+
assert arg(x).as_leading_term(x, cdir = -1) == pi
|
| 678 |
+
raises(PoleError, lambda: arg(x + I).as_leading_term(x, cdir = 1))
|
| 679 |
+
raises(PoleError, lambda: arg(2*x).as_leading_term(x, cdir = I))
|
| 680 |
+
|
| 681 |
+
assert arg(x).nseries(x) == 0
|
| 682 |
+
assert arg(x).nseries(x, n=0) == Order(1)
|
| 683 |
+
|
| 684 |
+
|
| 685 |
+
def test_adjoint():
|
| 686 |
+
a = Symbol('a', antihermitian=True)
|
| 687 |
+
b = Symbol('b', hermitian=True)
|
| 688 |
+
assert adjoint(a) == -a
|
| 689 |
+
assert adjoint(I*a) == I*a
|
| 690 |
+
assert adjoint(b) == b
|
| 691 |
+
assert adjoint(I*b) == -I*b
|
| 692 |
+
assert adjoint(a*b) == -b*a
|
| 693 |
+
assert adjoint(I*a*b) == I*b*a
|
| 694 |
+
|
| 695 |
+
x, y = symbols('x y')
|
| 696 |
+
assert adjoint(adjoint(x)) == x
|
| 697 |
+
assert adjoint(x + y) == conjugate(x) + conjugate(y)
|
| 698 |
+
assert adjoint(x - y) == conjugate(x) - conjugate(y)
|
| 699 |
+
assert adjoint(x * y) == conjugate(x) * conjugate(y)
|
| 700 |
+
assert adjoint(x / y) == conjugate(x) / conjugate(y)
|
| 701 |
+
assert adjoint(-x) == -conjugate(x)
|
| 702 |
+
|
| 703 |
+
x, y = symbols('x y', commutative=False)
|
| 704 |
+
assert adjoint(adjoint(x)) == x
|
| 705 |
+
assert adjoint(x + y) == adjoint(x) + adjoint(y)
|
| 706 |
+
assert adjoint(x - y) == adjoint(x) - adjoint(y)
|
| 707 |
+
assert adjoint(x * y) == adjoint(y) * adjoint(x)
|
| 708 |
+
assert adjoint(x / y) == 1 / adjoint(y) * adjoint(x)
|
| 709 |
+
assert adjoint(-x) == -adjoint(x)
|
| 710 |
+
|
| 711 |
+
|
| 712 |
+
def test_conjugate():
|
| 713 |
+
a = Symbol('a', real=True)
|
| 714 |
+
b = Symbol('b', imaginary=True)
|
| 715 |
+
assert conjugate(a) == a
|
| 716 |
+
assert conjugate(I*a) == -I*a
|
| 717 |
+
assert conjugate(b) == -b
|
| 718 |
+
assert conjugate(I*b) == I*b
|
| 719 |
+
assert conjugate(a*b) == -a*b
|
| 720 |
+
assert conjugate(I*a*b) == I*a*b
|
| 721 |
+
|
| 722 |
+
x, y = symbols('x y')
|
| 723 |
+
assert conjugate(conjugate(x)) == x
|
| 724 |
+
assert conjugate(x).inverse() == conjugate
|
| 725 |
+
assert conjugate(x + y) == conjugate(x) + conjugate(y)
|
| 726 |
+
assert conjugate(x - y) == conjugate(x) - conjugate(y)
|
| 727 |
+
assert conjugate(x * y) == conjugate(x) * conjugate(y)
|
| 728 |
+
assert conjugate(x / y) == conjugate(x) / conjugate(y)
|
| 729 |
+
assert conjugate(-x) == -conjugate(x)
|
| 730 |
+
|
| 731 |
+
a = Symbol('a', algebraic=True)
|
| 732 |
+
t = Symbol('t', transcendental=True)
|
| 733 |
+
assert re(a).is_algebraic
|
| 734 |
+
assert re(x).is_algebraic is None
|
| 735 |
+
assert re(t).is_algebraic is False
|
| 736 |
+
|
| 737 |
+
|
| 738 |
+
def test_conjugate_transpose():
|
| 739 |
+
x = Symbol('x', commutative=False)
|
| 740 |
+
assert conjugate(transpose(x)) == adjoint(x)
|
| 741 |
+
assert transpose(conjugate(x)) == adjoint(x)
|
| 742 |
+
assert adjoint(transpose(x)) == conjugate(x)
|
| 743 |
+
assert transpose(adjoint(x)) == conjugate(x)
|
| 744 |
+
assert adjoint(conjugate(x)) == transpose(x)
|
| 745 |
+
assert conjugate(adjoint(x)) == transpose(x)
|
| 746 |
+
|
| 747 |
+
x = Symbol('x')
|
| 748 |
+
assert conjugate(x) == adjoint(x)
|
| 749 |
+
assert transpose(x) == x
|
| 750 |
+
|
| 751 |
+
|
| 752 |
+
def test_transpose():
|
| 753 |
+
a = Symbol('a', complex=True)
|
| 754 |
+
assert transpose(a) == a
|
| 755 |
+
assert transpose(I*a) == I*a
|
| 756 |
+
|
| 757 |
+
x, y = symbols('x y')
|
| 758 |
+
assert transpose(transpose(x)) == x
|
| 759 |
+
assert transpose(x + y) == x + y
|
| 760 |
+
assert transpose(x - y) == x - y
|
| 761 |
+
assert transpose(x * y) == x * y
|
| 762 |
+
assert transpose(x / y) == x / y
|
| 763 |
+
assert transpose(-x) == -x
|
| 764 |
+
|
| 765 |
+
x, y = symbols('x y', commutative=False)
|
| 766 |
+
assert transpose(transpose(x)) == x
|
| 767 |
+
assert transpose(x + y) == transpose(x) + transpose(y)
|
| 768 |
+
assert transpose(x - y) == transpose(x) - transpose(y)
|
| 769 |
+
assert transpose(x * y) == transpose(y) * transpose(x)
|
| 770 |
+
assert transpose(x / y) == 1 / transpose(y) * transpose(x)
|
| 771 |
+
assert transpose(-x) == -transpose(x)
|
| 772 |
+
|
| 773 |
+
|
| 774 |
+
@_both_exp_pow
|
| 775 |
+
def test_polarify():
|
| 776 |
+
from sympy.functions.elementary.complexes import (polar_lift, polarify)
|
| 777 |
+
x = Symbol('x')
|
| 778 |
+
z = Symbol('z', polar=True)
|
| 779 |
+
f = Function('f')
|
| 780 |
+
ES = {}
|
| 781 |
+
|
| 782 |
+
assert polarify(-1) == (polar_lift(-1), ES)
|
| 783 |
+
assert polarify(1 + I) == (polar_lift(1 + I), ES)
|
| 784 |
+
|
| 785 |
+
assert polarify(exp(x), subs=False) == exp(x)
|
| 786 |
+
assert polarify(1 + x, subs=False) == 1 + x
|
| 787 |
+
assert polarify(f(I) + x, subs=False) == f(polar_lift(I)) + x
|
| 788 |
+
|
| 789 |
+
assert polarify(x, lift=True) == polar_lift(x)
|
| 790 |
+
assert polarify(z, lift=True) == z
|
| 791 |
+
assert polarify(f(x), lift=True) == f(polar_lift(x))
|
| 792 |
+
assert polarify(1 + x, lift=True) == polar_lift(1 + x)
|
| 793 |
+
assert polarify(1 + f(x), lift=True) == polar_lift(1 + f(polar_lift(x)))
|
| 794 |
+
|
| 795 |
+
newex, subs = polarify(f(x) + z)
|
| 796 |
+
assert newex.subs(subs) == f(x) + z
|
| 797 |
+
|
| 798 |
+
mu = Symbol("mu")
|
| 799 |
+
sigma = Symbol("sigma", positive=True)
|
| 800 |
+
|
| 801 |
+
# Make sure polarify(lift=True) doesn't try to lift the integration
|
| 802 |
+
# variable
|
| 803 |
+
assert polarify(
|
| 804 |
+
Integral(sqrt(2)*x*exp(-(-mu + x)**2/(2*sigma**2))/(2*sqrt(pi)*sigma),
|
| 805 |
+
(x, -oo, oo)), lift=True) == Integral(sqrt(2)*(sigma*exp_polar(0))**exp_polar(I*pi)*
|
| 806 |
+
exp((sigma*exp_polar(0))**(2*exp_polar(I*pi))*exp_polar(I*pi)*polar_lift(-mu + x)**
|
| 807 |
+
(2*exp_polar(0))/2)*exp_polar(0)*polar_lift(x)/(2*sqrt(pi)), (x, -oo, oo))
|
| 808 |
+
|
| 809 |
+
|
| 810 |
+
def test_unpolarify():
|
| 811 |
+
from sympy.functions.elementary.complexes import (polar_lift, principal_branch, unpolarify)
|
| 812 |
+
from sympy.core.relational import Ne
|
| 813 |
+
from sympy.functions.elementary.hyperbolic import tanh
|
| 814 |
+
from sympy.functions.special.error_functions import erf
|
| 815 |
+
from sympy.functions.special.gamma_functions import (gamma, uppergamma)
|
| 816 |
+
from sympy.abc import x
|
| 817 |
+
p = exp_polar(7*I) + 1
|
| 818 |
+
u = exp(7*I) + 1
|
| 819 |
+
|
| 820 |
+
assert unpolarify(1) == 1
|
| 821 |
+
assert unpolarify(p) == u
|
| 822 |
+
assert unpolarify(p**2) == u**2
|
| 823 |
+
assert unpolarify(p**x) == p**x
|
| 824 |
+
assert unpolarify(p*x) == u*x
|
| 825 |
+
assert unpolarify(p + x) == u + x
|
| 826 |
+
assert unpolarify(sqrt(sin(p))) == sqrt(sin(u))
|
| 827 |
+
|
| 828 |
+
# Test reduction to principal branch 2*pi.
|
| 829 |
+
t = principal_branch(x, 2*pi)
|
| 830 |
+
assert unpolarify(t) == x
|
| 831 |
+
assert unpolarify(sqrt(t)) == sqrt(t)
|
| 832 |
+
|
| 833 |
+
# Test exponents_only.
|
| 834 |
+
assert unpolarify(p**p, exponents_only=True) == p**u
|
| 835 |
+
assert unpolarify(uppergamma(x, p**p)) == uppergamma(x, p**u)
|
| 836 |
+
|
| 837 |
+
# Test functions.
|
| 838 |
+
assert unpolarify(sin(p)) == sin(u)
|
| 839 |
+
assert unpolarify(tanh(p)) == tanh(u)
|
| 840 |
+
assert unpolarify(gamma(p)) == gamma(u)
|
| 841 |
+
assert unpolarify(erf(p)) == erf(u)
|
| 842 |
+
assert unpolarify(uppergamma(x, p)) == uppergamma(x, p)
|
| 843 |
+
|
| 844 |
+
assert unpolarify(uppergamma(sin(p), sin(p + exp_polar(0)))) == \
|
| 845 |
+
uppergamma(sin(u), sin(u + 1))
|
| 846 |
+
assert unpolarify(uppergamma(polar_lift(0), 2*exp_polar(0))) == \
|
| 847 |
+
uppergamma(0, 2)
|
| 848 |
+
|
| 849 |
+
assert unpolarify(Eq(p, 0)) == Eq(u, 0)
|
| 850 |
+
assert unpolarify(Ne(p, 0)) == Ne(u, 0)
|
| 851 |
+
assert unpolarify(polar_lift(x) > 0) == (x > 0)
|
| 852 |
+
|
| 853 |
+
# Test bools
|
| 854 |
+
assert unpolarify(True) is True
|
| 855 |
+
|
| 856 |
+
|
| 857 |
+
def test_issue_4035():
|
| 858 |
+
x = Symbol('x')
|
| 859 |
+
assert Abs(x).expand(trig=True) == Abs(x)
|
| 860 |
+
assert sign(x).expand(trig=True) == sign(x)
|
| 861 |
+
assert arg(x).expand(trig=True) == arg(x)
|
| 862 |
+
|
| 863 |
+
|
| 864 |
+
def test_issue_3206():
|
| 865 |
+
x = Symbol('x')
|
| 866 |
+
assert Abs(Abs(x)) == Abs(x)
|
| 867 |
+
|
| 868 |
+
|
| 869 |
+
def test_issue_4754_derivative_conjugate():
|
| 870 |
+
x = Symbol('x', real=True)
|
| 871 |
+
y = Symbol('y', imaginary=True)
|
| 872 |
+
f = Function('f')
|
| 873 |
+
assert (f(x).conjugate()).diff(x) == (f(x).diff(x)).conjugate()
|
| 874 |
+
assert (f(y).conjugate()).diff(y) == -(f(y).diff(y)).conjugate()
|
| 875 |
+
|
| 876 |
+
|
| 877 |
+
def test_derivatives_issue_4757():
|
| 878 |
+
x = Symbol('x', real=True)
|
| 879 |
+
y = Symbol('y', imaginary=True)
|
| 880 |
+
f = Function('f')
|
| 881 |
+
assert re(f(x)).diff(x) == re(f(x).diff(x))
|
| 882 |
+
assert im(f(x)).diff(x) == im(f(x).diff(x))
|
| 883 |
+
assert re(f(y)).diff(y) == -I*im(f(y).diff(y))
|
| 884 |
+
assert im(f(y)).diff(y) == -I*re(f(y).diff(y))
|
| 885 |
+
assert Abs(f(x)).diff(x).subs(f(x), 1 + I*x).doit() == x/sqrt(1 + x**2)
|
| 886 |
+
assert arg(f(x)).diff(x).subs(f(x), 1 + I*x**2).doit() == 2*x/(1 + x**4)
|
| 887 |
+
assert Abs(f(y)).diff(y).subs(f(y), 1 + y).doit() == -y/sqrt(1 - y**2)
|
| 888 |
+
assert arg(f(y)).diff(y).subs(f(y), I + y**2).doit() == 2*y/(1 + y**4)
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
def test_issue_11413():
|
| 892 |
+
from sympy.simplify.simplify import simplify
|
| 893 |
+
v0 = Symbol('v0')
|
| 894 |
+
v1 = Symbol('v1')
|
| 895 |
+
v2 = Symbol('v2')
|
| 896 |
+
V = Matrix([[v0],[v1],[v2]])
|
| 897 |
+
U = V.normalized()
|
| 898 |
+
assert U == Matrix([
|
| 899 |
+
[v0/sqrt(Abs(v0)**2 + Abs(v1)**2 + Abs(v2)**2)],
|
| 900 |
+
[v1/sqrt(Abs(v0)**2 + Abs(v1)**2 + Abs(v2)**2)],
|
| 901 |
+
[v2/sqrt(Abs(v0)**2 + Abs(v1)**2 + Abs(v2)**2)]])
|
| 902 |
+
U.norm = sqrt(v0**2/(v0**2 + v1**2 + v2**2) + v1**2/(v0**2 + v1**2 + v2**2) + v2**2/(v0**2 + v1**2 + v2**2))
|
| 903 |
+
assert simplify(U.norm) == 1
|
| 904 |
+
|
| 905 |
+
|
| 906 |
+
def test_periodic_argument():
|
| 907 |
+
from sympy.functions.elementary.complexes import (periodic_argument, polar_lift, principal_branch, unbranched_argument)
|
| 908 |
+
x = Symbol('x')
|
| 909 |
+
p = Symbol('p', positive=True)
|
| 910 |
+
|
| 911 |
+
assert unbranched_argument(2 + I) == periodic_argument(2 + I, oo)
|
| 912 |
+
assert unbranched_argument(1 + x) == periodic_argument(1 + x, oo)
|
| 913 |
+
assert N_equals(unbranched_argument((1 + I)**2), pi/2)
|
| 914 |
+
assert N_equals(unbranched_argument((1 - I)**2), -pi/2)
|
| 915 |
+
assert N_equals(periodic_argument((1 + I)**2, 3*pi), pi/2)
|
| 916 |
+
assert N_equals(periodic_argument((1 - I)**2, 3*pi), -pi/2)
|
| 917 |
+
|
| 918 |
+
assert unbranched_argument(principal_branch(x, pi)) == \
|
| 919 |
+
periodic_argument(x, pi)
|
| 920 |
+
|
| 921 |
+
assert unbranched_argument(polar_lift(2 + I)) == unbranched_argument(2 + I)
|
| 922 |
+
assert periodic_argument(polar_lift(2 + I), 2*pi) == \
|
| 923 |
+
periodic_argument(2 + I, 2*pi)
|
| 924 |
+
assert periodic_argument(polar_lift(2 + I), 3*pi) == \
|
| 925 |
+
periodic_argument(2 + I, 3*pi)
|
| 926 |
+
assert periodic_argument(polar_lift(2 + I), pi) == \
|
| 927 |
+
periodic_argument(polar_lift(2 + I), pi)
|
| 928 |
+
|
| 929 |
+
assert unbranched_argument(polar_lift(1 + I)) == pi/4
|
| 930 |
+
assert periodic_argument(2*p, p) == periodic_argument(p, p)
|
| 931 |
+
assert periodic_argument(pi*p, p) == periodic_argument(p, p)
|
| 932 |
+
|
| 933 |
+
assert Abs(polar_lift(1 + I)) == Abs(1 + I)
|
| 934 |
+
|
| 935 |
+
|
| 936 |
+
@XFAIL
|
| 937 |
+
def test_principal_branch_fail():
|
| 938 |
+
# TODO XXX why does abs(x)._eval_evalf() not fall back to global evalf?
|
| 939 |
+
from sympy.functions.elementary.complexes import principal_branch
|
| 940 |
+
assert N_equals(principal_branch((1 + I)**2, pi/2), 0)
|
| 941 |
+
|
| 942 |
+
|
| 943 |
+
def test_principal_branch():
|
| 944 |
+
from sympy.functions.elementary.complexes import (polar_lift, principal_branch)
|
| 945 |
+
p = Symbol('p', positive=True)
|
| 946 |
+
x = Symbol('x')
|
| 947 |
+
neg = Symbol('x', negative=True)
|
| 948 |
+
|
| 949 |
+
assert principal_branch(polar_lift(x), p) == principal_branch(x, p)
|
| 950 |
+
assert principal_branch(polar_lift(2 + I), p) == principal_branch(2 + I, p)
|
| 951 |
+
assert principal_branch(2*x, p) == 2*principal_branch(x, p)
|
| 952 |
+
assert principal_branch(1, pi) == exp_polar(0)
|
| 953 |
+
assert principal_branch(-1, 2*pi) == exp_polar(I*pi)
|
| 954 |
+
assert principal_branch(-1, pi) == exp_polar(0)
|
| 955 |
+
assert principal_branch(exp_polar(3*pi*I)*x, 2*pi) == \
|
| 956 |
+
principal_branch(exp_polar(I*pi)*x, 2*pi)
|
| 957 |
+
assert principal_branch(neg*exp_polar(pi*I), 2*pi) == neg*exp_polar(-I*pi)
|
| 958 |
+
# related to issue #14692
|
| 959 |
+
assert principal_branch(exp_polar(-I*pi/2)/polar_lift(neg), 2*pi) == \
|
| 960 |
+
exp_polar(-I*pi/2)/neg
|
| 961 |
+
|
| 962 |
+
assert N_equals(principal_branch((1 + I)**2, 2*pi), 2*I)
|
| 963 |
+
assert N_equals(principal_branch((1 + I)**2, 3*pi), 2*I)
|
| 964 |
+
assert N_equals(principal_branch((1 + I)**2, 1*pi), 2*I)
|
| 965 |
+
|
| 966 |
+
# test argument sanitization
|
| 967 |
+
assert principal_branch(x, I).func is principal_branch
|
| 968 |
+
assert principal_branch(x, -4).func is principal_branch
|
| 969 |
+
assert principal_branch(x, -oo).func is principal_branch
|
| 970 |
+
assert principal_branch(x, zoo).func is principal_branch
|
| 971 |
+
|
| 972 |
+
|
| 973 |
+
@XFAIL
|
| 974 |
+
def test_issue_6167_6151():
|
| 975 |
+
n = pi**1000
|
| 976 |
+
i = int(n)
|
| 977 |
+
assert sign(n - i) == 1
|
| 978 |
+
assert abs(n - i) == n - i
|
| 979 |
+
x = Symbol('x')
|
| 980 |
+
eps = pi**-1500
|
| 981 |
+
big = pi**1000
|
| 982 |
+
one = cos(x)**2 + sin(x)**2
|
| 983 |
+
e = big*one - big + eps
|
| 984 |
+
from sympy.simplify.simplify import simplify
|
| 985 |
+
assert sign(simplify(e)) == 1
|
| 986 |
+
for xi in (111, 11, 1, Rational(1, 10)):
|
| 987 |
+
assert sign(e.subs(x, xi)) == 1
|
| 988 |
+
|
| 989 |
+
|
| 990 |
+
def test_issue_14216():
|
| 991 |
+
from sympy.functions.elementary.complexes import unpolarify
|
| 992 |
+
A = MatrixSymbol("A", 2, 2)
|
| 993 |
+
assert unpolarify(A[0, 0]) == A[0, 0]
|
| 994 |
+
assert unpolarify(A[0, 0]*A[1, 0]) == A[0, 0]*A[1, 0]
|
| 995 |
+
|
| 996 |
+
|
| 997 |
+
def test_issue_14238():
|
| 998 |
+
# doesn't cause recursion error
|
| 999 |
+
r = Symbol('r', real=True)
|
| 1000 |
+
assert Abs(r + Piecewise((0, r > 0), (1 - r, True)))
|
| 1001 |
+
|
| 1002 |
+
|
| 1003 |
+
def test_issue_22189():
|
| 1004 |
+
x = Symbol('x')
|
| 1005 |
+
for a in (sqrt(7 - 2*x) - 2, 1 - x):
|
| 1006 |
+
assert Abs(a) - Abs(-a) == 0, a
|
| 1007 |
+
|
| 1008 |
+
|
| 1009 |
+
def test_zero_assumptions():
|
| 1010 |
+
nr = Symbol('nonreal', real=False, finite=True)
|
| 1011 |
+
ni = Symbol('nonimaginary', imaginary=False)
|
| 1012 |
+
# imaginary implies not zero
|
| 1013 |
+
nzni = Symbol('nonzerononimaginary', zero=False, imaginary=False)
|
| 1014 |
+
|
| 1015 |
+
assert re(nr).is_zero is None
|
| 1016 |
+
assert im(nr).is_zero is False
|
| 1017 |
+
|
| 1018 |
+
assert re(ni).is_zero is None
|
| 1019 |
+
assert im(ni).is_zero is None
|
| 1020 |
+
|
| 1021 |
+
assert re(nzni).is_zero is False
|
| 1022 |
+
assert im(nzni).is_zero is None
|
| 1023 |
+
|
| 1024 |
+
|
| 1025 |
+
@_both_exp_pow
|
| 1026 |
+
def test_issue_15893():
|
| 1027 |
+
f = Function('f', real=True)
|
| 1028 |
+
x = Symbol('x', real=True)
|
| 1029 |
+
eq = Derivative(Abs(f(x)), f(x))
|
| 1030 |
+
assert eq.doit() == sign(f(x))
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/tests/test_miscellaneous.py
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools as it
|
| 2 |
+
|
| 3 |
+
from sympy.core.expr import unchanged
|
| 4 |
+
from sympy.core.function import Function
|
| 5 |
+
from sympy.core.numbers import I, oo, Rational
|
| 6 |
+
from sympy.core.power import Pow
|
| 7 |
+
from sympy.core.singleton import S
|
| 8 |
+
from sympy.core.symbol import Symbol
|
| 9 |
+
from sympy.external import import_module
|
| 10 |
+
from sympy.functions.elementary.exponential import log
|
| 11 |
+
from sympy.functions.elementary.integers import floor, ceiling
|
| 12 |
+
from sympy.functions.elementary.miscellaneous import (sqrt, cbrt, root, Min,
|
| 13 |
+
Max, real_root, Rem)
|
| 14 |
+
from sympy.functions.elementary.trigonometric import cos, sin
|
| 15 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 16 |
+
|
| 17 |
+
from sympy.utilities.lambdify import lambdify
|
| 18 |
+
from sympy.testing.pytest import raises, skip, ignore_warnings
|
| 19 |
+
|
| 20 |
+
def test_Min():
|
| 21 |
+
from sympy.abc import x, y, z
|
| 22 |
+
n = Symbol('n', negative=True)
|
| 23 |
+
n_ = Symbol('n_', negative=True)
|
| 24 |
+
nn = Symbol('nn', nonnegative=True)
|
| 25 |
+
nn_ = Symbol('nn_', nonnegative=True)
|
| 26 |
+
p = Symbol('p', positive=True)
|
| 27 |
+
p_ = Symbol('p_', positive=True)
|
| 28 |
+
np = Symbol('np', nonpositive=True)
|
| 29 |
+
np_ = Symbol('np_', nonpositive=True)
|
| 30 |
+
r = Symbol('r', real=True)
|
| 31 |
+
|
| 32 |
+
assert Min(5, 4) == 4
|
| 33 |
+
assert Min(-oo, -oo) is -oo
|
| 34 |
+
assert Min(-oo, n) is -oo
|
| 35 |
+
assert Min(n, -oo) is -oo
|
| 36 |
+
assert Min(-oo, np) is -oo
|
| 37 |
+
assert Min(np, -oo) is -oo
|
| 38 |
+
assert Min(-oo, 0) is -oo
|
| 39 |
+
assert Min(0, -oo) is -oo
|
| 40 |
+
assert Min(-oo, nn) is -oo
|
| 41 |
+
assert Min(nn, -oo) is -oo
|
| 42 |
+
assert Min(-oo, p) is -oo
|
| 43 |
+
assert Min(p, -oo) is -oo
|
| 44 |
+
assert Min(-oo, oo) is -oo
|
| 45 |
+
assert Min(oo, -oo) is -oo
|
| 46 |
+
assert Min(n, n) == n
|
| 47 |
+
assert unchanged(Min, n, np)
|
| 48 |
+
assert Min(np, n) == Min(n, np)
|
| 49 |
+
assert Min(n, 0) == n
|
| 50 |
+
assert Min(0, n) == n
|
| 51 |
+
assert Min(n, nn) == n
|
| 52 |
+
assert Min(nn, n) == n
|
| 53 |
+
assert Min(n, p) == n
|
| 54 |
+
assert Min(p, n) == n
|
| 55 |
+
assert Min(n, oo) == n
|
| 56 |
+
assert Min(oo, n) == n
|
| 57 |
+
assert Min(np, np) == np
|
| 58 |
+
assert Min(np, 0) == np
|
| 59 |
+
assert Min(0, np) == np
|
| 60 |
+
assert Min(np, nn) == np
|
| 61 |
+
assert Min(nn, np) == np
|
| 62 |
+
assert Min(np, p) == np
|
| 63 |
+
assert Min(p, np) == np
|
| 64 |
+
assert Min(np, oo) == np
|
| 65 |
+
assert Min(oo, np) == np
|
| 66 |
+
assert Min(0, 0) == 0
|
| 67 |
+
assert Min(0, nn) == 0
|
| 68 |
+
assert Min(nn, 0) == 0
|
| 69 |
+
assert Min(0, p) == 0
|
| 70 |
+
assert Min(p, 0) == 0
|
| 71 |
+
assert Min(0, oo) == 0
|
| 72 |
+
assert Min(oo, 0) == 0
|
| 73 |
+
assert Min(nn, nn) == nn
|
| 74 |
+
assert unchanged(Min, nn, p)
|
| 75 |
+
assert Min(p, nn) == Min(nn, p)
|
| 76 |
+
assert Min(nn, oo) == nn
|
| 77 |
+
assert Min(oo, nn) == nn
|
| 78 |
+
assert Min(p, p) == p
|
| 79 |
+
assert Min(p, oo) == p
|
| 80 |
+
assert Min(oo, p) == p
|
| 81 |
+
assert Min(oo, oo) is oo
|
| 82 |
+
|
| 83 |
+
assert Min(n, n_).func is Min
|
| 84 |
+
assert Min(nn, nn_).func is Min
|
| 85 |
+
assert Min(np, np_).func is Min
|
| 86 |
+
assert Min(p, p_).func is Min
|
| 87 |
+
|
| 88 |
+
# lists
|
| 89 |
+
assert Min() is S.Infinity
|
| 90 |
+
assert Min(x) == x
|
| 91 |
+
assert Min(x, y) == Min(y, x)
|
| 92 |
+
assert Min(x, y, z) == Min(z, y, x)
|
| 93 |
+
assert Min(x, Min(y, z)) == Min(z, y, x)
|
| 94 |
+
assert Min(x, Max(y, -oo)) == Min(x, y)
|
| 95 |
+
assert Min(p, oo, n, p, p, p_) == n
|
| 96 |
+
assert Min(p_, n_, p) == n_
|
| 97 |
+
assert Min(n, oo, -7, p, p, 2) == Min(n, -7)
|
| 98 |
+
assert Min(2, x, p, n, oo, n_, p, 2, -2, -2) == Min(-2, x, n, n_)
|
| 99 |
+
assert Min(0, x, 1, y) == Min(0, x, y)
|
| 100 |
+
assert Min(1000, 100, -100, x, p, n) == Min(n, x, -100)
|
| 101 |
+
assert unchanged(Min, sin(x), cos(x))
|
| 102 |
+
assert Min(sin(x), cos(x)) == Min(cos(x), sin(x))
|
| 103 |
+
assert Min(cos(x), sin(x)).subs(x, 1) == cos(1)
|
| 104 |
+
assert Min(cos(x), sin(x)).subs(x, S.Half) == sin(S.Half)
|
| 105 |
+
raises(ValueError, lambda: Min(cos(x), sin(x)).subs(x, I))
|
| 106 |
+
raises(ValueError, lambda: Min(I))
|
| 107 |
+
raises(ValueError, lambda: Min(I, x))
|
| 108 |
+
raises(ValueError, lambda: Min(S.ComplexInfinity, x))
|
| 109 |
+
|
| 110 |
+
assert Min(1, x).diff(x) == Heaviside(1 - x)
|
| 111 |
+
assert Min(x, 1).diff(x) == Heaviside(1 - x)
|
| 112 |
+
assert Min(0, -x, 1 - 2*x).diff(x) == -Heaviside(x + Min(0, -2*x + 1)) \
|
| 113 |
+
- 2*Heaviside(2*x + Min(0, -x) - 1)
|
| 114 |
+
|
| 115 |
+
# issue 7619
|
| 116 |
+
f = Function('f')
|
| 117 |
+
assert Min(1, 2*Min(f(1), 2)) # doesn't fail
|
| 118 |
+
|
| 119 |
+
# issue 7233
|
| 120 |
+
e = Min(0, x)
|
| 121 |
+
assert e.n().args == (0, x)
|
| 122 |
+
|
| 123 |
+
# issue 8643
|
| 124 |
+
m = Min(n, p_, n_, r)
|
| 125 |
+
assert m.is_positive is False
|
| 126 |
+
assert m.is_nonnegative is False
|
| 127 |
+
assert m.is_negative is True
|
| 128 |
+
|
| 129 |
+
m = Min(p, p_)
|
| 130 |
+
assert m.is_positive is True
|
| 131 |
+
assert m.is_nonnegative is True
|
| 132 |
+
assert m.is_negative is False
|
| 133 |
+
|
| 134 |
+
m = Min(p, nn_, p_)
|
| 135 |
+
assert m.is_positive is None
|
| 136 |
+
assert m.is_nonnegative is True
|
| 137 |
+
assert m.is_negative is False
|
| 138 |
+
|
| 139 |
+
m = Min(nn, p, r)
|
| 140 |
+
assert m.is_positive is None
|
| 141 |
+
assert m.is_nonnegative is None
|
| 142 |
+
assert m.is_negative is None
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def test_Max():
|
| 146 |
+
from sympy.abc import x, y, z
|
| 147 |
+
n = Symbol('n', negative=True)
|
| 148 |
+
n_ = Symbol('n_', negative=True)
|
| 149 |
+
nn = Symbol('nn', nonnegative=True)
|
| 150 |
+
p = Symbol('p', positive=True)
|
| 151 |
+
p_ = Symbol('p_', positive=True)
|
| 152 |
+
r = Symbol('r', real=True)
|
| 153 |
+
|
| 154 |
+
assert Max(5, 4) == 5
|
| 155 |
+
|
| 156 |
+
# lists
|
| 157 |
+
|
| 158 |
+
assert Max() is S.NegativeInfinity
|
| 159 |
+
assert Max(x) == x
|
| 160 |
+
assert Max(x, y) == Max(y, x)
|
| 161 |
+
assert Max(x, y, z) == Max(z, y, x)
|
| 162 |
+
assert Max(x, Max(y, z)) == Max(z, y, x)
|
| 163 |
+
assert Max(x, Min(y, oo)) == Max(x, y)
|
| 164 |
+
assert Max(n, -oo, n_, p, 2) == Max(p, 2)
|
| 165 |
+
assert Max(n, -oo, n_, p) == p
|
| 166 |
+
assert Max(2, x, p, n, -oo, S.NegativeInfinity, n_, p, 2) == Max(2, x, p)
|
| 167 |
+
assert Max(0, x, 1, y) == Max(1, x, y)
|
| 168 |
+
assert Max(r, r + 1, r - 1) == 1 + r
|
| 169 |
+
assert Max(1000, 100, -100, x, p, n) == Max(p, x, 1000)
|
| 170 |
+
assert Max(cos(x), sin(x)) == Max(sin(x), cos(x))
|
| 171 |
+
assert Max(cos(x), sin(x)).subs(x, 1) == sin(1)
|
| 172 |
+
assert Max(cos(x), sin(x)).subs(x, S.Half) == cos(S.Half)
|
| 173 |
+
raises(ValueError, lambda: Max(cos(x), sin(x)).subs(x, I))
|
| 174 |
+
raises(ValueError, lambda: Max(I))
|
| 175 |
+
raises(ValueError, lambda: Max(I, x))
|
| 176 |
+
raises(ValueError, lambda: Max(S.ComplexInfinity, 1))
|
| 177 |
+
assert Max(n, -oo, n_, p, 2) == Max(p, 2)
|
| 178 |
+
assert Max(n, -oo, n_, p, 1000) == Max(p, 1000)
|
| 179 |
+
|
| 180 |
+
assert Max(1, x).diff(x) == Heaviside(x - 1)
|
| 181 |
+
assert Max(x, 1).diff(x) == Heaviside(x - 1)
|
| 182 |
+
assert Max(x**2, 1 + x, 1).diff(x) == \
|
| 183 |
+
2*x*Heaviside(x**2 - Max(1, x + 1)) \
|
| 184 |
+
+ Heaviside(x - Max(1, x**2) + 1)
|
| 185 |
+
|
| 186 |
+
e = Max(0, x)
|
| 187 |
+
assert e.n().args == (0, x)
|
| 188 |
+
|
| 189 |
+
# issue 8643
|
| 190 |
+
m = Max(p, p_, n, r)
|
| 191 |
+
assert m.is_positive is True
|
| 192 |
+
assert m.is_nonnegative is True
|
| 193 |
+
assert m.is_negative is False
|
| 194 |
+
|
| 195 |
+
m = Max(n, n_)
|
| 196 |
+
assert m.is_positive is False
|
| 197 |
+
assert m.is_nonnegative is False
|
| 198 |
+
assert m.is_negative is True
|
| 199 |
+
|
| 200 |
+
m = Max(n, n_, r)
|
| 201 |
+
assert m.is_positive is None
|
| 202 |
+
assert m.is_nonnegative is None
|
| 203 |
+
assert m.is_negative is None
|
| 204 |
+
|
| 205 |
+
m = Max(n, nn, r)
|
| 206 |
+
assert m.is_positive is None
|
| 207 |
+
assert m.is_nonnegative is True
|
| 208 |
+
assert m.is_negative is False
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def test_minmax_assumptions():
|
| 212 |
+
r = Symbol('r', real=True)
|
| 213 |
+
a = Symbol('a', real=True, algebraic=True)
|
| 214 |
+
t = Symbol('t', real=True, transcendental=True)
|
| 215 |
+
q = Symbol('q', rational=True)
|
| 216 |
+
p = Symbol('p', irrational=True)
|
| 217 |
+
n = Symbol('n', rational=True, integer=False)
|
| 218 |
+
i = Symbol('i', integer=True)
|
| 219 |
+
o = Symbol('o', odd=True)
|
| 220 |
+
e = Symbol('e', even=True)
|
| 221 |
+
k = Symbol('k', prime=True)
|
| 222 |
+
reals = [r, a, t, q, p, n, i, o, e, k]
|
| 223 |
+
|
| 224 |
+
for ext in (Max, Min):
|
| 225 |
+
for x, y in it.product(reals, repeat=2):
|
| 226 |
+
|
| 227 |
+
# Must be real
|
| 228 |
+
assert ext(x, y).is_real
|
| 229 |
+
|
| 230 |
+
# Algebraic?
|
| 231 |
+
if x.is_algebraic and y.is_algebraic:
|
| 232 |
+
assert ext(x, y).is_algebraic
|
| 233 |
+
elif x.is_transcendental and y.is_transcendental:
|
| 234 |
+
assert ext(x, y).is_transcendental
|
| 235 |
+
else:
|
| 236 |
+
assert ext(x, y).is_algebraic is None
|
| 237 |
+
|
| 238 |
+
# Rational?
|
| 239 |
+
if x.is_rational and y.is_rational:
|
| 240 |
+
assert ext(x, y).is_rational
|
| 241 |
+
elif x.is_irrational and y.is_irrational:
|
| 242 |
+
assert ext(x, y).is_irrational
|
| 243 |
+
else:
|
| 244 |
+
assert ext(x, y).is_rational is None
|
| 245 |
+
|
| 246 |
+
# Integer?
|
| 247 |
+
if x.is_integer and y.is_integer:
|
| 248 |
+
assert ext(x, y).is_integer
|
| 249 |
+
elif x.is_noninteger and y.is_noninteger:
|
| 250 |
+
assert ext(x, y).is_noninteger
|
| 251 |
+
else:
|
| 252 |
+
assert ext(x, y).is_integer is None
|
| 253 |
+
|
| 254 |
+
# Odd?
|
| 255 |
+
if x.is_odd and y.is_odd:
|
| 256 |
+
assert ext(x, y).is_odd
|
| 257 |
+
elif x.is_odd is False and y.is_odd is False:
|
| 258 |
+
assert ext(x, y).is_odd is False
|
| 259 |
+
else:
|
| 260 |
+
assert ext(x, y).is_odd is None
|
| 261 |
+
|
| 262 |
+
# Even?
|
| 263 |
+
if x.is_even and y.is_even:
|
| 264 |
+
assert ext(x, y).is_even
|
| 265 |
+
elif x.is_even is False and y.is_even is False:
|
| 266 |
+
assert ext(x, y).is_even is False
|
| 267 |
+
else:
|
| 268 |
+
assert ext(x, y).is_even is None
|
| 269 |
+
|
| 270 |
+
# Prime?
|
| 271 |
+
if x.is_prime and y.is_prime:
|
| 272 |
+
assert ext(x, y).is_prime
|
| 273 |
+
elif x.is_prime is False and y.is_prime is False:
|
| 274 |
+
assert ext(x, y).is_prime is False
|
| 275 |
+
else:
|
| 276 |
+
assert ext(x, y).is_prime is None
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
def test_issue_8413():
|
| 280 |
+
x = Symbol('x', real=True)
|
| 281 |
+
# we can't evaluate in general because non-reals are not
|
| 282 |
+
# comparable: Min(floor(3.2 + I), 3.2 + I) -> ValueError
|
| 283 |
+
assert Min(floor(x), x) == floor(x)
|
| 284 |
+
assert Min(ceiling(x), x) == x
|
| 285 |
+
assert Max(floor(x), x) == x
|
| 286 |
+
assert Max(ceiling(x), x) == ceiling(x)
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
def test_root():
|
| 290 |
+
from sympy.abc import x
|
| 291 |
+
n = Symbol('n', integer=True)
|
| 292 |
+
k = Symbol('k', integer=True)
|
| 293 |
+
|
| 294 |
+
assert root(2, 2) == sqrt(2)
|
| 295 |
+
assert root(2, 1) == 2
|
| 296 |
+
assert root(2, 3) == 2**Rational(1, 3)
|
| 297 |
+
assert root(2, 3) == cbrt(2)
|
| 298 |
+
assert root(2, -5) == 2**Rational(4, 5)/2
|
| 299 |
+
|
| 300 |
+
assert root(-2, 1) == -2
|
| 301 |
+
|
| 302 |
+
assert root(-2, 2) == sqrt(2)*I
|
| 303 |
+
assert root(-2, 1) == -2
|
| 304 |
+
|
| 305 |
+
assert root(x, 2) == sqrt(x)
|
| 306 |
+
assert root(x, 1) == x
|
| 307 |
+
assert root(x, 3) == x**Rational(1, 3)
|
| 308 |
+
assert root(x, 3) == cbrt(x)
|
| 309 |
+
assert root(x, -5) == x**Rational(-1, 5)
|
| 310 |
+
|
| 311 |
+
assert root(x, n) == x**(1/n)
|
| 312 |
+
assert root(x, -n) == x**(-1/n)
|
| 313 |
+
|
| 314 |
+
assert root(x, n, k) == (-1)**(2*k/n)*x**(1/n)
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
def test_real_root():
|
| 318 |
+
assert real_root(-8, 3) == -2
|
| 319 |
+
assert real_root(-16, 4) == root(-16, 4)
|
| 320 |
+
r = root(-7, 4)
|
| 321 |
+
assert real_root(r) == r
|
| 322 |
+
r1 = root(-1, 3)
|
| 323 |
+
r2 = r1**2
|
| 324 |
+
r3 = root(-1, 4)
|
| 325 |
+
assert real_root(r1 + r2 + r3) == -1 + r2 + r3
|
| 326 |
+
assert real_root(root(-2, 3)) == -root(2, 3)
|
| 327 |
+
assert real_root(-8., 3) == -2.0
|
| 328 |
+
x = Symbol('x')
|
| 329 |
+
n = Symbol('n')
|
| 330 |
+
g = real_root(x, n)
|
| 331 |
+
assert g.subs({"x": -8, "n": 3}) == -2
|
| 332 |
+
assert g.subs({"x": 8, "n": 3}) == 2
|
| 333 |
+
# give principle root if there is no real root -- if this is not desired
|
| 334 |
+
# then maybe a Root class is needed to raise an error instead
|
| 335 |
+
assert g.subs({"x": I, "n": 3}) == cbrt(I)
|
| 336 |
+
assert g.subs({"x": -8, "n": 2}) == sqrt(-8)
|
| 337 |
+
assert g.subs({"x": I, "n": 2}) == sqrt(I)
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
def test_issue_11463():
|
| 341 |
+
numpy = import_module('numpy')
|
| 342 |
+
if not numpy:
|
| 343 |
+
skip("numpy not installed.")
|
| 344 |
+
x = Symbol('x')
|
| 345 |
+
f = lambdify(x, real_root((log(x/(x-2))), 3), 'numpy')
|
| 346 |
+
# numpy.select evaluates all options before considering conditions,
|
| 347 |
+
# so it raises a warning about root of negative number which does
|
| 348 |
+
# not affect the outcome. This warning is suppressed here
|
| 349 |
+
with ignore_warnings(RuntimeWarning):
|
| 350 |
+
assert f(numpy.array(-1)) < -1
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def test_rewrite_MaxMin_as_Heaviside():
|
| 354 |
+
from sympy.abc import x
|
| 355 |
+
assert Max(0, x).rewrite(Heaviside) == x*Heaviside(x)
|
| 356 |
+
assert Max(3, x).rewrite(Heaviside) == x*Heaviside(x - 3) + \
|
| 357 |
+
3*Heaviside(-x + 3)
|
| 358 |
+
assert Max(0, x+2, 2*x).rewrite(Heaviside) == \
|
| 359 |
+
2*x*Heaviside(2*x)*Heaviside(x - 2) + \
|
| 360 |
+
(x + 2)*Heaviside(-x + 2)*Heaviside(x + 2)
|
| 361 |
+
|
| 362 |
+
assert Min(0, x).rewrite(Heaviside) == x*Heaviside(-x)
|
| 363 |
+
assert Min(3, x).rewrite(Heaviside) == x*Heaviside(-x + 3) + \
|
| 364 |
+
3*Heaviside(x - 3)
|
| 365 |
+
assert Min(x, -x, -2).rewrite(Heaviside) == \
|
| 366 |
+
x*Heaviside(-2*x)*Heaviside(-x - 2) - \
|
| 367 |
+
x*Heaviside(2*x)*Heaviside(x - 2) \
|
| 368 |
+
- 2*Heaviside(-x + 2)*Heaviside(x + 2)
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def test_rewrite_MaxMin_as_Piecewise():
|
| 372 |
+
from sympy.core.symbol import symbols
|
| 373 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 374 |
+
x, y, z, a, b = symbols('x y z a b', real=True)
|
| 375 |
+
vx, vy, va = symbols('vx vy va')
|
| 376 |
+
assert Max(a, b).rewrite(Piecewise) == Piecewise((a, a >= b), (b, True))
|
| 377 |
+
assert Max(x, y, z).rewrite(Piecewise) == Piecewise((x, (x >= y) & (x >= z)), (y, y >= z), (z, True))
|
| 378 |
+
assert Max(x, y, a, b).rewrite(Piecewise) == Piecewise((a, (a >= b) & (a >= x) & (a >= y)),
|
| 379 |
+
(b, (b >= x) & (b >= y)), (x, x >= y), (y, True))
|
| 380 |
+
assert Min(a, b).rewrite(Piecewise) == Piecewise((a, a <= b), (b, True))
|
| 381 |
+
assert Min(x, y, z).rewrite(Piecewise) == Piecewise((x, (x <= y) & (x <= z)), (y, y <= z), (z, True))
|
| 382 |
+
assert Min(x, y, a, b).rewrite(Piecewise) == Piecewise((a, (a <= b) & (a <= x) & (a <= y)),
|
| 383 |
+
(b, (b <= x) & (b <= y)), (x, x <= y), (y, True))
|
| 384 |
+
|
| 385 |
+
# Piecewise rewriting of Min/Max does also takes place for not explicitly real arguments
|
| 386 |
+
assert Max(vx, vy).rewrite(Piecewise) == Piecewise((vx, vx >= vy), (vy, True))
|
| 387 |
+
assert Min(va, vx, vy).rewrite(Piecewise) == Piecewise((va, (va <= vx) & (va <= vy)), (vx, vx <= vy), (vy, True))
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
def test_issue_11099():
|
| 391 |
+
from sympy.abc import x, y
|
| 392 |
+
# some fixed value tests
|
| 393 |
+
fixed_test_data = {x: -2, y: 3}
|
| 394 |
+
assert Min(x, y).evalf(subs=fixed_test_data) == \
|
| 395 |
+
Min(x, y).subs(fixed_test_data).evalf()
|
| 396 |
+
assert Max(x, y).evalf(subs=fixed_test_data) == \
|
| 397 |
+
Max(x, y).subs(fixed_test_data).evalf()
|
| 398 |
+
# randomly generate some test data
|
| 399 |
+
from sympy.core.random import randint
|
| 400 |
+
for i in range(20):
|
| 401 |
+
random_test_data = {x: randint(-100, 100), y: randint(-100, 100)}
|
| 402 |
+
assert Min(x, y).evalf(subs=random_test_data) == \
|
| 403 |
+
Min(x, y).subs(random_test_data).evalf()
|
| 404 |
+
assert Max(x, y).evalf(subs=random_test_data) == \
|
| 405 |
+
Max(x, y).subs(random_test_data).evalf()
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
def test_issue_12638():
|
| 409 |
+
from sympy.abc import a, b, c
|
| 410 |
+
assert Min(a, b, c, Max(a, b)) == Min(a, b, c)
|
| 411 |
+
assert Min(a, b, Max(a, b, c)) == Min(a, b)
|
| 412 |
+
assert Min(a, b, Max(a, c)) == Min(a, b)
|
| 413 |
+
|
| 414 |
+
def test_issue_21399():
|
| 415 |
+
from sympy.abc import a, b, c
|
| 416 |
+
assert Max(Min(a, b), Min(a, b, c)) == Min(a, b)
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def test_instantiation_evaluation():
|
| 420 |
+
from sympy.abc import v, w, x, y, z
|
| 421 |
+
assert Min(1, Max(2, x)) == 1
|
| 422 |
+
assert Max(3, Min(2, x)) == 3
|
| 423 |
+
assert Min(Max(x, y), Max(x, z)) == Max(x, Min(y, z))
|
| 424 |
+
assert set(Min(Max(w, x), Max(y, z)).args) == {
|
| 425 |
+
Max(w, x), Max(y, z)}
|
| 426 |
+
assert Min(Max(x, y), Max(x, z), w) == Min(
|
| 427 |
+
w, Max(x, Min(y, z)))
|
| 428 |
+
A, B = Min, Max
|
| 429 |
+
for i in range(2):
|
| 430 |
+
assert A(x, B(x, y)) == x
|
| 431 |
+
assert A(x, B(y, A(x, w, z))) == A(x, B(y, A(w, z)))
|
| 432 |
+
A, B = B, A
|
| 433 |
+
assert Min(w, Max(x, y), Max(v, x, z)) == Min(
|
| 434 |
+
w, Max(x, Min(y, Max(v, z))))
|
| 435 |
+
|
| 436 |
+
def test_rewrite_as_Abs():
|
| 437 |
+
from itertools import permutations
|
| 438 |
+
from sympy.functions.elementary.complexes import Abs
|
| 439 |
+
from sympy.abc import x, y, z, w
|
| 440 |
+
def test(e):
|
| 441 |
+
free = e.free_symbols
|
| 442 |
+
a = e.rewrite(Abs)
|
| 443 |
+
assert not a.has(Min, Max)
|
| 444 |
+
for i in permutations(range(len(free))):
|
| 445 |
+
reps = dict(zip(free, i))
|
| 446 |
+
assert a.xreplace(reps) == e.xreplace(reps)
|
| 447 |
+
test(Min(x, y))
|
| 448 |
+
test(Max(x, y))
|
| 449 |
+
test(Min(x, y, z))
|
| 450 |
+
test(Min(Max(w, x), Max(y, z)))
|
| 451 |
+
|
| 452 |
+
def test_issue_14000():
|
| 453 |
+
assert isinstance(sqrt(4, evaluate=False), Pow) == True
|
| 454 |
+
assert isinstance(cbrt(3.5, evaluate=False), Pow) == True
|
| 455 |
+
assert isinstance(root(16, 4, evaluate=False), Pow) == True
|
| 456 |
+
|
| 457 |
+
assert sqrt(4, evaluate=False) == Pow(4, S.Half, evaluate=False)
|
| 458 |
+
assert cbrt(3.5, evaluate=False) == Pow(3.5, Rational(1, 3), evaluate=False)
|
| 459 |
+
assert root(4, 2, evaluate=False) == Pow(4, S.Half, evaluate=False)
|
| 460 |
+
|
| 461 |
+
assert root(16, 4, 2, evaluate=False).has(Pow) == True
|
| 462 |
+
assert real_root(-8, 3, evaluate=False).has(Pow) == True
|
| 463 |
+
|
| 464 |
+
def test_issue_6899():
|
| 465 |
+
from sympy.core.function import Lambda
|
| 466 |
+
x = Symbol('x')
|
| 467 |
+
eqn = Lambda(x, x)
|
| 468 |
+
assert eqn.func(*eqn.args) == eqn
|
| 469 |
+
|
| 470 |
+
def test_Rem():
|
| 471 |
+
from sympy.abc import x, y
|
| 472 |
+
assert Rem(5, 3) == 2
|
| 473 |
+
assert Rem(-5, 3) == -2
|
| 474 |
+
assert Rem(5, -3) == 2
|
| 475 |
+
assert Rem(-5, -3) == -2
|
| 476 |
+
assert Rem(x**3, y) == Rem(x**3, y)
|
| 477 |
+
assert Rem(Rem(-5, 3) + 3, 3) == 1
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
def test_minmax_no_evaluate():
|
| 481 |
+
from sympy import evaluate
|
| 482 |
+
p = Symbol('p', positive=True)
|
| 483 |
+
|
| 484 |
+
assert Max(1, 3) == 3
|
| 485 |
+
assert Max(1, 3).args == ()
|
| 486 |
+
assert Max(0, p) == p
|
| 487 |
+
assert Max(0, p).args == ()
|
| 488 |
+
assert Min(0, p) == 0
|
| 489 |
+
assert Min(0, p).args == ()
|
| 490 |
+
|
| 491 |
+
assert Max(1, 3, evaluate=False) != 3
|
| 492 |
+
assert Max(1, 3, evaluate=False).args == (1, 3)
|
| 493 |
+
assert Max(0, p, evaluate=False) != p
|
| 494 |
+
assert Max(0, p, evaluate=False).args == (0, p)
|
| 495 |
+
assert Min(0, p, evaluate=False) != 0
|
| 496 |
+
assert Min(0, p, evaluate=False).args == (0, p)
|
| 497 |
+
|
| 498 |
+
with evaluate(False):
|
| 499 |
+
assert Max(1, 3) != 3
|
| 500 |
+
assert Max(1, 3).args == (1, 3)
|
| 501 |
+
assert Max(0, p) != p
|
| 502 |
+
assert Max(0, p).args == (0, p)
|
| 503 |
+
assert Min(0, p) != 0
|
| 504 |
+
assert Min(0, p).args == (0, p)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/elementary/trigonometric.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Stub __init__.py for the sympy.functions.special package
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/bessel.py
ADDED
|
@@ -0,0 +1,2208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from functools import wraps
|
| 2 |
+
|
| 3 |
+
from sympy.core import S
|
| 4 |
+
from sympy.core.add import Add
|
| 5 |
+
from sympy.core.cache import cacheit
|
| 6 |
+
from sympy.core.expr import Expr
|
| 7 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError, _mexpand
|
| 8 |
+
from sympy.core.logic import fuzzy_or, fuzzy_not
|
| 9 |
+
from sympy.core.numbers import Rational, pi, I
|
| 10 |
+
from sympy.core.power import Pow
|
| 11 |
+
from sympy.core.symbol import Dummy, uniquely_named_symbol, Wild
|
| 12 |
+
from sympy.core.sympify import sympify
|
| 13 |
+
from sympy.functions.combinatorial.factorials import factorial, RisingFactorial
|
| 14 |
+
from sympy.functions.elementary.trigonometric import sin, cos, csc, cot
|
| 15 |
+
from sympy.functions.elementary.integers import ceiling
|
| 16 |
+
from sympy.functions.elementary.exponential import exp, log
|
| 17 |
+
from sympy.functions.elementary.miscellaneous import cbrt, sqrt, root
|
| 18 |
+
from sympy.functions.elementary.complexes import (Abs, re, im, polar_lift, unpolarify)
|
| 19 |
+
from sympy.functions.special.gamma_functions import gamma, digamma, uppergamma
|
| 20 |
+
from sympy.functions.special.hyper import hyper
|
| 21 |
+
from sympy.polys.orthopolys import spherical_bessel_fn
|
| 22 |
+
|
| 23 |
+
from mpmath import mp, workprec
|
| 24 |
+
|
| 25 |
+
# TODO
|
| 26 |
+
# o Scorer functions G1 and G2
|
| 27 |
+
# o Asymptotic expansions
|
| 28 |
+
# These are possible, e.g. for fixed order, but since the bessel type
|
| 29 |
+
# functions are oscillatory they are not actually tractable at
|
| 30 |
+
# infinity, so this is not particularly useful right now.
|
| 31 |
+
# o Nicer series expansions.
|
| 32 |
+
# o More rewriting.
|
| 33 |
+
# o Add solvers to ode.py (or rather add solvers for the hypergeometric equation).
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class BesselBase(DefinedFunction):
|
| 37 |
+
"""
|
| 38 |
+
Abstract base class for Bessel-type functions.
|
| 39 |
+
|
| 40 |
+
This class is meant to reduce code duplication.
|
| 41 |
+
All Bessel-type functions can 1) be differentiated, with the derivatives
|
| 42 |
+
expressed in terms of similar functions, and 2) be rewritten in terms
|
| 43 |
+
of other Bessel-type functions.
|
| 44 |
+
|
| 45 |
+
Here, Bessel-type functions are assumed to have one complex parameter.
|
| 46 |
+
|
| 47 |
+
To use this base class, define class attributes ``_a`` and ``_b`` such that
|
| 48 |
+
``2*F_n' = -_a*F_{n+1} + b*F_{n-1}``.
|
| 49 |
+
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
@property
|
| 53 |
+
def order(self):
|
| 54 |
+
""" The order of the Bessel-type function. """
|
| 55 |
+
return self.args[0]
|
| 56 |
+
|
| 57 |
+
@property
|
| 58 |
+
def argument(self):
|
| 59 |
+
""" The argument of the Bessel-type function. """
|
| 60 |
+
return self.args[1]
|
| 61 |
+
|
| 62 |
+
@classmethod
|
| 63 |
+
def eval(cls, nu, z):
|
| 64 |
+
return
|
| 65 |
+
|
| 66 |
+
def fdiff(self, argindex=2):
|
| 67 |
+
if argindex != 2:
|
| 68 |
+
raise ArgumentIndexError(self, argindex)
|
| 69 |
+
return (self._b/2 * self.__class__(self.order - 1, self.argument) -
|
| 70 |
+
self._a/2 * self.__class__(self.order + 1, self.argument))
|
| 71 |
+
|
| 72 |
+
def _eval_conjugate(self):
|
| 73 |
+
z = self.argument
|
| 74 |
+
if z.is_extended_negative is False:
|
| 75 |
+
return self.__class__(self.order.conjugate(), z.conjugate())
|
| 76 |
+
|
| 77 |
+
def _eval_is_meromorphic(self, x, a):
|
| 78 |
+
nu, z = self.order, self.argument
|
| 79 |
+
|
| 80 |
+
if nu.has(x):
|
| 81 |
+
return False
|
| 82 |
+
if not z._eval_is_meromorphic(x, a):
|
| 83 |
+
return None
|
| 84 |
+
z0 = z.subs(x, a)
|
| 85 |
+
if nu.is_integer:
|
| 86 |
+
if isinstance(self, (besselj, besseli, hn1, hn2, jn, yn)) or not nu.is_zero:
|
| 87 |
+
return fuzzy_not(z0.is_infinite)
|
| 88 |
+
return fuzzy_not(fuzzy_or([z0.is_zero, z0.is_infinite]))
|
| 89 |
+
|
| 90 |
+
def _eval_expand_func(self, **hints):
|
| 91 |
+
nu, z, f = self.order, self.argument, self.__class__
|
| 92 |
+
if nu.is_real:
|
| 93 |
+
if (nu - 1).is_positive:
|
| 94 |
+
return (-self._a*self._b*f(nu - 2, z)._eval_expand_func() +
|
| 95 |
+
2*self._a*(nu - 1)*f(nu - 1, z)._eval_expand_func()/z)
|
| 96 |
+
elif (nu + 1).is_negative:
|
| 97 |
+
return (2*self._b*(nu + 1)*f(nu + 1, z)._eval_expand_func()/z -
|
| 98 |
+
self._a*self._b*f(nu + 2, z)._eval_expand_func())
|
| 99 |
+
return self
|
| 100 |
+
|
| 101 |
+
def _eval_simplify(self, **kwargs):
|
| 102 |
+
from sympy.simplify.simplify import besselsimp
|
| 103 |
+
return besselsimp(self)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class besselj(BesselBase):
|
| 107 |
+
r"""
|
| 108 |
+
Bessel function of the first kind.
|
| 109 |
+
|
| 110 |
+
Explanation
|
| 111 |
+
===========
|
| 112 |
+
|
| 113 |
+
The Bessel $J$ function of order $\nu$ is defined to be the function
|
| 114 |
+
satisfying Bessel's differential equation
|
| 115 |
+
|
| 116 |
+
.. math ::
|
| 117 |
+
z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2}
|
| 118 |
+
+ z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 - \nu^2) w = 0,
|
| 119 |
+
|
| 120 |
+
with Laurent expansion
|
| 121 |
+
|
| 122 |
+
.. math ::
|
| 123 |
+
J_\nu(z) = z^\nu \left(\frac{1}{\Gamma(\nu + 1) 2^\nu} + O(z^2) \right),
|
| 124 |
+
|
| 125 |
+
if $\nu$ is not a negative integer. If $\nu=-n \in \mathbb{Z}_{<0}$
|
| 126 |
+
*is* a negative integer, then the definition is
|
| 127 |
+
|
| 128 |
+
.. math ::
|
| 129 |
+
J_{-n}(z) = (-1)^n J_n(z).
|
| 130 |
+
|
| 131 |
+
Examples
|
| 132 |
+
========
|
| 133 |
+
|
| 134 |
+
Create a Bessel function object:
|
| 135 |
+
|
| 136 |
+
>>> from sympy import besselj, jn
|
| 137 |
+
>>> from sympy.abc import z, n
|
| 138 |
+
>>> b = besselj(n, z)
|
| 139 |
+
|
| 140 |
+
Differentiate it:
|
| 141 |
+
|
| 142 |
+
>>> b.diff(z)
|
| 143 |
+
besselj(n - 1, z)/2 - besselj(n + 1, z)/2
|
| 144 |
+
|
| 145 |
+
Rewrite in terms of spherical Bessel functions:
|
| 146 |
+
|
| 147 |
+
>>> b.rewrite(jn)
|
| 148 |
+
sqrt(2)*sqrt(z)*jn(n - 1/2, z)/sqrt(pi)
|
| 149 |
+
|
| 150 |
+
Access the parameter and argument:
|
| 151 |
+
|
| 152 |
+
>>> b.order
|
| 153 |
+
n
|
| 154 |
+
>>> b.argument
|
| 155 |
+
z
|
| 156 |
+
|
| 157 |
+
See Also
|
| 158 |
+
========
|
| 159 |
+
|
| 160 |
+
bessely, besseli, besselk
|
| 161 |
+
|
| 162 |
+
References
|
| 163 |
+
==========
|
| 164 |
+
|
| 165 |
+
.. [1] Abramowitz, Milton; Stegun, Irene A., eds. (1965), "Chapter 9",
|
| 166 |
+
Handbook of Mathematical Functions with Formulas, Graphs, and
|
| 167 |
+
Mathematical Tables
|
| 168 |
+
.. [2] Luke, Y. L. (1969), The Special Functions and Their
|
| 169 |
+
Approximations, Volume 1
|
| 170 |
+
.. [3] https://en.wikipedia.org/wiki/Bessel_function
|
| 171 |
+
.. [4] https://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/
|
| 172 |
+
|
| 173 |
+
"""
|
| 174 |
+
|
| 175 |
+
_a = S.One
|
| 176 |
+
_b = S.One
|
| 177 |
+
|
| 178 |
+
@classmethod
|
| 179 |
+
def eval(cls, nu, z):
|
| 180 |
+
if z.is_zero:
|
| 181 |
+
if nu.is_zero:
|
| 182 |
+
return S.One
|
| 183 |
+
elif (nu.is_integer and nu.is_zero is False) or re(nu).is_positive:
|
| 184 |
+
return S.Zero
|
| 185 |
+
elif re(nu).is_negative and not (nu.is_integer is True):
|
| 186 |
+
return S.ComplexInfinity
|
| 187 |
+
elif nu.is_imaginary:
|
| 188 |
+
return S.NaN
|
| 189 |
+
if z in (S.Infinity, S.NegativeInfinity):
|
| 190 |
+
return S.Zero
|
| 191 |
+
|
| 192 |
+
if z.could_extract_minus_sign():
|
| 193 |
+
return (z)**nu*(-z)**(-nu)*besselj(nu, -z)
|
| 194 |
+
if nu.is_integer:
|
| 195 |
+
if nu.could_extract_minus_sign():
|
| 196 |
+
return S.NegativeOne**(-nu)*besselj(-nu, z)
|
| 197 |
+
newz = z.extract_multiplicatively(I)
|
| 198 |
+
if newz: # NOTE we don't want to change the function if z==0
|
| 199 |
+
return I**(nu)*besseli(nu, newz)
|
| 200 |
+
|
| 201 |
+
# branch handling:
|
| 202 |
+
if nu.is_integer:
|
| 203 |
+
newz = unpolarify(z)
|
| 204 |
+
if newz != z:
|
| 205 |
+
return besselj(nu, newz)
|
| 206 |
+
else:
|
| 207 |
+
newz, n = z.extract_branch_factor()
|
| 208 |
+
if n != 0:
|
| 209 |
+
return exp(2*n*pi*nu*I)*besselj(nu, newz)
|
| 210 |
+
nnu = unpolarify(nu)
|
| 211 |
+
if nu != nnu:
|
| 212 |
+
return besselj(nnu, z)
|
| 213 |
+
|
| 214 |
+
def _eval_rewrite_as_besseli(self, nu, z, **kwargs):
|
| 215 |
+
return exp(I*pi*nu/2)*besseli(nu, polar_lift(-I)*z)
|
| 216 |
+
|
| 217 |
+
def _eval_rewrite_as_bessely(self, nu, z, **kwargs):
|
| 218 |
+
if nu.is_integer is False:
|
| 219 |
+
return csc(pi*nu)*bessely(-nu, z) - cot(pi*nu)*bessely(nu, z)
|
| 220 |
+
|
| 221 |
+
def _eval_rewrite_as_jn(self, nu, z, **kwargs):
|
| 222 |
+
return sqrt(2*z/pi)*jn(nu - S.Half, self.argument)
|
| 223 |
+
|
| 224 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 225 |
+
nu, z = self.args
|
| 226 |
+
try:
|
| 227 |
+
arg = z.as_leading_term(x)
|
| 228 |
+
except NotImplementedError:
|
| 229 |
+
return self
|
| 230 |
+
c, e = arg.as_coeff_exponent(x)
|
| 231 |
+
|
| 232 |
+
if e.is_positive:
|
| 233 |
+
return arg**nu/(2**nu*gamma(nu + 1))
|
| 234 |
+
elif e.is_negative:
|
| 235 |
+
cdir = 1 if cdir == 0 else cdir
|
| 236 |
+
sign = c*cdir**e
|
| 237 |
+
if not sign.is_negative:
|
| 238 |
+
# Refer Abramowitz and Stegun 1965, p. 364 for more information on
|
| 239 |
+
# asymptotic approximation of besselj function.
|
| 240 |
+
return sqrt(2)*cos(z - pi*(2*nu + 1)/4)/sqrt(pi*z)
|
| 241 |
+
return self
|
| 242 |
+
|
| 243 |
+
return super(besselj, self)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 244 |
+
|
| 245 |
+
def _eval_is_extended_real(self):
|
| 246 |
+
nu, z = self.args
|
| 247 |
+
if nu.is_integer and z.is_extended_real:
|
| 248 |
+
return True
|
| 249 |
+
|
| 250 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 251 |
+
# Refer https://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/06/01/04/01/01/0003/
|
| 252 |
+
# for more information on nseries expansion of besselj function.
|
| 253 |
+
from sympy.series.order import Order
|
| 254 |
+
nu, z = self.args
|
| 255 |
+
|
| 256 |
+
# In case of powers less than 1, number of terms need to be computed
|
| 257 |
+
# separately to avoid repeated callings of _eval_nseries with wrong n
|
| 258 |
+
try:
|
| 259 |
+
_, exp = z.leadterm(x)
|
| 260 |
+
except (ValueError, NotImplementedError):
|
| 261 |
+
return self
|
| 262 |
+
|
| 263 |
+
if exp.is_positive:
|
| 264 |
+
newn = ceiling(n/exp)
|
| 265 |
+
o = Order(x**n, x)
|
| 266 |
+
r = (z/2)._eval_nseries(x, n, logx, cdir).removeO()
|
| 267 |
+
if r is S.Zero:
|
| 268 |
+
return o
|
| 269 |
+
t = (_mexpand(r**2) + o).removeO()
|
| 270 |
+
|
| 271 |
+
term = r**nu/gamma(nu + 1)
|
| 272 |
+
s = [term]
|
| 273 |
+
for k in range(1, (newn + 1)//2):
|
| 274 |
+
term *= -t/(k*(nu + k))
|
| 275 |
+
term = (_mexpand(term) + o).removeO()
|
| 276 |
+
s.append(term)
|
| 277 |
+
return Add(*s) + o
|
| 278 |
+
|
| 279 |
+
return super(besselj, self)._eval_nseries(x, n, logx, cdir)
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
class bessely(BesselBase):
|
| 283 |
+
r"""
|
| 284 |
+
Bessel function of the second kind.
|
| 285 |
+
|
| 286 |
+
Explanation
|
| 287 |
+
===========
|
| 288 |
+
|
| 289 |
+
The Bessel $Y$ function of order $\nu$ is defined as
|
| 290 |
+
|
| 291 |
+
.. math ::
|
| 292 |
+
Y_\nu(z) = \lim_{\mu \to \nu} \frac{J_\mu(z) \cos(\pi \mu)
|
| 293 |
+
- J_{-\mu}(z)}{\sin(\pi \mu)},
|
| 294 |
+
|
| 295 |
+
where $J_\mu(z)$ is the Bessel function of the first kind.
|
| 296 |
+
|
| 297 |
+
It is a solution to Bessel's equation, and linearly independent from
|
| 298 |
+
$J_\nu$.
|
| 299 |
+
|
| 300 |
+
Examples
|
| 301 |
+
========
|
| 302 |
+
|
| 303 |
+
>>> from sympy import bessely, yn
|
| 304 |
+
>>> from sympy.abc import z, n
|
| 305 |
+
>>> b = bessely(n, z)
|
| 306 |
+
>>> b.diff(z)
|
| 307 |
+
bessely(n - 1, z)/2 - bessely(n + 1, z)/2
|
| 308 |
+
>>> b.rewrite(yn)
|
| 309 |
+
sqrt(2)*sqrt(z)*yn(n - 1/2, z)/sqrt(pi)
|
| 310 |
+
|
| 311 |
+
See Also
|
| 312 |
+
========
|
| 313 |
+
|
| 314 |
+
besselj, besseli, besselk
|
| 315 |
+
|
| 316 |
+
References
|
| 317 |
+
==========
|
| 318 |
+
|
| 319 |
+
.. [1] https://functions.wolfram.com/Bessel-TypeFunctions/BesselY/
|
| 320 |
+
|
| 321 |
+
"""
|
| 322 |
+
|
| 323 |
+
_a = S.One
|
| 324 |
+
_b = S.One
|
| 325 |
+
|
| 326 |
+
@classmethod
|
| 327 |
+
def eval(cls, nu, z):
|
| 328 |
+
if z.is_zero:
|
| 329 |
+
if nu.is_zero:
|
| 330 |
+
return S.NegativeInfinity
|
| 331 |
+
elif re(nu).is_zero is False:
|
| 332 |
+
return S.ComplexInfinity
|
| 333 |
+
elif re(nu).is_zero:
|
| 334 |
+
return S.NaN
|
| 335 |
+
if z in (S.Infinity, S.NegativeInfinity):
|
| 336 |
+
return S.Zero
|
| 337 |
+
if z == I*S.Infinity:
|
| 338 |
+
return exp(I*pi*(nu + 1)/2) * S.Infinity
|
| 339 |
+
if z == I*S.NegativeInfinity:
|
| 340 |
+
return exp(-I*pi*(nu + 1)/2) * S.Infinity
|
| 341 |
+
|
| 342 |
+
if nu.is_integer:
|
| 343 |
+
if nu.could_extract_minus_sign():
|
| 344 |
+
return S.NegativeOne**(-nu)*bessely(-nu, z)
|
| 345 |
+
|
| 346 |
+
def _eval_rewrite_as_besselj(self, nu, z, **kwargs):
|
| 347 |
+
if nu.is_integer is False:
|
| 348 |
+
return csc(pi*nu)*(cos(pi*nu)*besselj(nu, z) - besselj(-nu, z))
|
| 349 |
+
|
| 350 |
+
def _eval_rewrite_as_besseli(self, nu, z, **kwargs):
|
| 351 |
+
aj = self._eval_rewrite_as_besselj(*self.args)
|
| 352 |
+
if aj:
|
| 353 |
+
return aj.rewrite(besseli)
|
| 354 |
+
|
| 355 |
+
def _eval_rewrite_as_yn(self, nu, z, **kwargs):
|
| 356 |
+
return sqrt(2*z/pi) * yn(nu - S.Half, self.argument)
|
| 357 |
+
|
| 358 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 359 |
+
nu, z = self.args
|
| 360 |
+
try:
|
| 361 |
+
arg = z.as_leading_term(x)
|
| 362 |
+
except NotImplementedError:
|
| 363 |
+
return self
|
| 364 |
+
c, e = arg.as_coeff_exponent(x)
|
| 365 |
+
|
| 366 |
+
if e.is_positive:
|
| 367 |
+
term_one = ((2/pi)*log(z/2)*besselj(nu, z))
|
| 368 |
+
term_two = -(z/2)**(-nu)*factorial(nu - 1)/pi if (nu).is_positive else S.Zero
|
| 369 |
+
term_three = -(z/2)**nu/(pi*factorial(nu))*(digamma(nu + 1) - S.EulerGamma)
|
| 370 |
+
arg = Add(*[term_one, term_two, term_three]).as_leading_term(x, logx=logx)
|
| 371 |
+
return arg
|
| 372 |
+
elif e.is_negative:
|
| 373 |
+
cdir = 1 if cdir == 0 else cdir
|
| 374 |
+
sign = c*cdir**e
|
| 375 |
+
if not sign.is_negative:
|
| 376 |
+
# Refer Abramowitz and Stegun 1965, p. 364 for more information on
|
| 377 |
+
# asymptotic approximation of bessely function.
|
| 378 |
+
return sqrt(2)*(-sin(pi*nu/2 - z + pi/4) + 3*cos(pi*nu/2 - z + pi/4)/(8*z))*sqrt(1/z)/sqrt(pi)
|
| 379 |
+
return self
|
| 380 |
+
|
| 381 |
+
return super(bessely, self)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 382 |
+
|
| 383 |
+
def _eval_is_extended_real(self):
|
| 384 |
+
nu, z = self.args
|
| 385 |
+
if nu.is_integer and z.is_positive:
|
| 386 |
+
return True
|
| 387 |
+
|
| 388 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 389 |
+
# Refer https://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/02/0008/
|
| 390 |
+
# for more information on nseries expansion of bessely function.
|
| 391 |
+
from sympy.series.order import Order
|
| 392 |
+
nu, z = self.args
|
| 393 |
+
|
| 394 |
+
# In case of powers less than 1, number of terms need to be computed
|
| 395 |
+
# separately to avoid repeated callings of _eval_nseries with wrong n
|
| 396 |
+
try:
|
| 397 |
+
_, exp = z.leadterm(x)
|
| 398 |
+
except (ValueError, NotImplementedError):
|
| 399 |
+
return self
|
| 400 |
+
|
| 401 |
+
if exp.is_positive and nu.is_integer:
|
| 402 |
+
newn = ceiling(n/exp)
|
| 403 |
+
bn = besselj(nu, z)
|
| 404 |
+
a = ((2/pi)*log(z/2)*bn)._eval_nseries(x, n, logx, cdir)
|
| 405 |
+
|
| 406 |
+
b, c = [], []
|
| 407 |
+
o = Order(x**n, x)
|
| 408 |
+
r = (z/2)._eval_nseries(x, n, logx, cdir).removeO()
|
| 409 |
+
if r is S.Zero:
|
| 410 |
+
return o
|
| 411 |
+
t = (_mexpand(r**2) + o).removeO()
|
| 412 |
+
|
| 413 |
+
if nu > S.Zero:
|
| 414 |
+
term = r**(-nu)*factorial(nu - 1)/pi
|
| 415 |
+
b.append(term)
|
| 416 |
+
for k in range(1, nu):
|
| 417 |
+
denom = (nu - k)*k
|
| 418 |
+
if denom == S.Zero:
|
| 419 |
+
term *= t/k
|
| 420 |
+
else:
|
| 421 |
+
term *= t/denom
|
| 422 |
+
term = (_mexpand(term) + o).removeO()
|
| 423 |
+
b.append(term)
|
| 424 |
+
|
| 425 |
+
p = r**nu/(pi*factorial(nu))
|
| 426 |
+
term = p*(digamma(nu + 1) - S.EulerGamma)
|
| 427 |
+
c.append(term)
|
| 428 |
+
for k in range(1, (newn + 1)//2):
|
| 429 |
+
p *= -t/(k*(k + nu))
|
| 430 |
+
p = (_mexpand(p) + o).removeO()
|
| 431 |
+
term = p*(digamma(k + nu + 1) + digamma(k + 1))
|
| 432 |
+
c.append(term)
|
| 433 |
+
return a - Add(*b) - Add(*c) # Order term comes from a
|
| 434 |
+
|
| 435 |
+
return super(bessely, self)._eval_nseries(x, n, logx, cdir)
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
class besseli(BesselBase):
|
| 439 |
+
r"""
|
| 440 |
+
Modified Bessel function of the first kind.
|
| 441 |
+
|
| 442 |
+
Explanation
|
| 443 |
+
===========
|
| 444 |
+
|
| 445 |
+
The Bessel $I$ function is a solution to the modified Bessel equation
|
| 446 |
+
|
| 447 |
+
.. math ::
|
| 448 |
+
z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2}
|
| 449 |
+
+ z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 + \nu^2)^2 w = 0.
|
| 450 |
+
|
| 451 |
+
It can be defined as
|
| 452 |
+
|
| 453 |
+
.. math ::
|
| 454 |
+
I_\nu(z) = i^{-\nu} J_\nu(iz),
|
| 455 |
+
|
| 456 |
+
where $J_\nu(z)$ is the Bessel function of the first kind.
|
| 457 |
+
|
| 458 |
+
Examples
|
| 459 |
+
========
|
| 460 |
+
|
| 461 |
+
>>> from sympy import besseli
|
| 462 |
+
>>> from sympy.abc import z, n
|
| 463 |
+
>>> besseli(n, z).diff(z)
|
| 464 |
+
besseli(n - 1, z)/2 + besseli(n + 1, z)/2
|
| 465 |
+
|
| 466 |
+
See Also
|
| 467 |
+
========
|
| 468 |
+
|
| 469 |
+
besselj, bessely, besselk
|
| 470 |
+
|
| 471 |
+
References
|
| 472 |
+
==========
|
| 473 |
+
|
| 474 |
+
.. [1] https://functions.wolfram.com/Bessel-TypeFunctions/BesselI/
|
| 475 |
+
|
| 476 |
+
"""
|
| 477 |
+
|
| 478 |
+
_a = -S.One
|
| 479 |
+
_b = S.One
|
| 480 |
+
|
| 481 |
+
@classmethod
|
| 482 |
+
def eval(cls, nu, z):
|
| 483 |
+
if z.is_zero:
|
| 484 |
+
if nu.is_zero:
|
| 485 |
+
return S.One
|
| 486 |
+
elif (nu.is_integer and nu.is_zero is False) or re(nu).is_positive:
|
| 487 |
+
return S.Zero
|
| 488 |
+
elif re(nu).is_negative and not (nu.is_integer is True):
|
| 489 |
+
return S.ComplexInfinity
|
| 490 |
+
elif nu.is_imaginary:
|
| 491 |
+
return S.NaN
|
| 492 |
+
if im(z) in (S.Infinity, S.NegativeInfinity):
|
| 493 |
+
return S.Zero
|
| 494 |
+
if z is S.Infinity:
|
| 495 |
+
return S.Infinity
|
| 496 |
+
if z is S.NegativeInfinity:
|
| 497 |
+
return (-1)**nu*S.Infinity
|
| 498 |
+
|
| 499 |
+
if z.could_extract_minus_sign():
|
| 500 |
+
return (z)**nu*(-z)**(-nu)*besseli(nu, -z)
|
| 501 |
+
if nu.is_integer:
|
| 502 |
+
if nu.could_extract_minus_sign():
|
| 503 |
+
return besseli(-nu, z)
|
| 504 |
+
newz = z.extract_multiplicatively(I)
|
| 505 |
+
if newz: # NOTE we don't want to change the function if z==0
|
| 506 |
+
return I**(-nu)*besselj(nu, -newz)
|
| 507 |
+
|
| 508 |
+
# branch handling:
|
| 509 |
+
if nu.is_integer:
|
| 510 |
+
newz = unpolarify(z)
|
| 511 |
+
if newz != z:
|
| 512 |
+
return besseli(nu, newz)
|
| 513 |
+
else:
|
| 514 |
+
newz, n = z.extract_branch_factor()
|
| 515 |
+
if n != 0:
|
| 516 |
+
return exp(2*n*pi*nu*I)*besseli(nu, newz)
|
| 517 |
+
nnu = unpolarify(nu)
|
| 518 |
+
if nu != nnu:
|
| 519 |
+
return besseli(nnu, z)
|
| 520 |
+
|
| 521 |
+
def _eval_rewrite_as_tractable(self, nu, z, limitvar=None, **kwargs):
|
| 522 |
+
if z.is_extended_real:
|
| 523 |
+
return exp(z)*_besseli(nu, z)
|
| 524 |
+
|
| 525 |
+
def _eval_rewrite_as_besselj(self, nu, z, **kwargs):
|
| 526 |
+
return exp(-I*pi*nu/2)*besselj(nu, polar_lift(I)*z)
|
| 527 |
+
|
| 528 |
+
def _eval_rewrite_as_bessely(self, nu, z, **kwargs):
|
| 529 |
+
aj = self._eval_rewrite_as_besselj(*self.args)
|
| 530 |
+
if aj:
|
| 531 |
+
return aj.rewrite(bessely)
|
| 532 |
+
|
| 533 |
+
def _eval_rewrite_as_jn(self, nu, z, **kwargs):
|
| 534 |
+
return self._eval_rewrite_as_besselj(*self.args).rewrite(jn)
|
| 535 |
+
|
| 536 |
+
def _eval_is_extended_real(self):
|
| 537 |
+
nu, z = self.args
|
| 538 |
+
if nu.is_integer and z.is_extended_real:
|
| 539 |
+
return True
|
| 540 |
+
|
| 541 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 542 |
+
nu, z = self.args
|
| 543 |
+
try:
|
| 544 |
+
arg = z.as_leading_term(x)
|
| 545 |
+
except NotImplementedError:
|
| 546 |
+
return self
|
| 547 |
+
c, e = arg.as_coeff_exponent(x)
|
| 548 |
+
|
| 549 |
+
if e.is_positive:
|
| 550 |
+
return arg**nu/(2**nu*gamma(nu + 1))
|
| 551 |
+
elif e.is_negative:
|
| 552 |
+
cdir = 1 if cdir == 0 else cdir
|
| 553 |
+
sign = c*cdir**e
|
| 554 |
+
if not sign.is_negative:
|
| 555 |
+
# Refer Abramowitz and Stegun 1965, p. 377 for more information on
|
| 556 |
+
# asymptotic approximation of besseli function.
|
| 557 |
+
return exp(z)/sqrt(2*pi*z)
|
| 558 |
+
return self
|
| 559 |
+
|
| 560 |
+
return super(besseli, self)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 561 |
+
|
| 562 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 563 |
+
# Refer https://functions.wolfram.com/Bessel-TypeFunctions/BesselI/06/01/04/01/01/0003/
|
| 564 |
+
# for more information on nseries expansion of besseli function.
|
| 565 |
+
from sympy.series.order import Order
|
| 566 |
+
nu, z = self.args
|
| 567 |
+
|
| 568 |
+
# In case of powers less than 1, number of terms need to be computed
|
| 569 |
+
# separately to avoid repeated callings of _eval_nseries with wrong n
|
| 570 |
+
try:
|
| 571 |
+
_, exp = z.leadterm(x)
|
| 572 |
+
except (ValueError, NotImplementedError):
|
| 573 |
+
return self
|
| 574 |
+
|
| 575 |
+
if exp.is_positive:
|
| 576 |
+
newn = ceiling(n/exp)
|
| 577 |
+
o = Order(x**n, x)
|
| 578 |
+
r = (z/2)._eval_nseries(x, n, logx, cdir).removeO()
|
| 579 |
+
if r is S.Zero:
|
| 580 |
+
return o
|
| 581 |
+
t = (_mexpand(r**2) + o).removeO()
|
| 582 |
+
|
| 583 |
+
term = r**nu/gamma(nu + 1)
|
| 584 |
+
s = [term]
|
| 585 |
+
for k in range(1, (newn + 1)//2):
|
| 586 |
+
term *= t/(k*(nu + k))
|
| 587 |
+
term = (_mexpand(term) + o).removeO()
|
| 588 |
+
s.append(term)
|
| 589 |
+
return Add(*s) + o
|
| 590 |
+
|
| 591 |
+
return super(besseli, self)._eval_nseries(x, n, logx, cdir)
|
| 592 |
+
|
| 593 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 594 |
+
from sympy.functions.combinatorial.factorials import RisingFactorial
|
| 595 |
+
from sympy.series.order import Order
|
| 596 |
+
point = args0[1]
|
| 597 |
+
|
| 598 |
+
if point in [S.Infinity, S.NegativeInfinity]:
|
| 599 |
+
nu, z = self.args
|
| 600 |
+
s = [(RisingFactorial(Rational(2*nu - 1, 2), k)*RisingFactorial(Rational(2*nu + 1, 2), k))/\
|
| 601 |
+
((2)**(k)*z**(Rational(2*k + 1, 2))*factorial(k)) for k in range(n)] + [Order(1/z**(Rational(2*n + 1, 2)), x)]
|
| 602 |
+
return exp(z)/sqrt(2*pi) * (Add(*s))
|
| 603 |
+
|
| 604 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
class besselk(BesselBase):
|
| 608 |
+
r"""
|
| 609 |
+
Modified Bessel function of the second kind.
|
| 610 |
+
|
| 611 |
+
Explanation
|
| 612 |
+
===========
|
| 613 |
+
|
| 614 |
+
The Bessel $K$ function of order $\nu$ is defined as
|
| 615 |
+
|
| 616 |
+
.. math ::
|
| 617 |
+
K_\nu(z) = \lim_{\mu \to \nu} \frac{\pi}{2}
|
| 618 |
+
\frac{I_{-\mu}(z) -I_\mu(z)}{\sin(\pi \mu)},
|
| 619 |
+
|
| 620 |
+
where $I_\mu(z)$ is the modified Bessel function of the first kind.
|
| 621 |
+
|
| 622 |
+
It is a solution of the modified Bessel equation, and linearly independent
|
| 623 |
+
from $Y_\nu$.
|
| 624 |
+
|
| 625 |
+
Examples
|
| 626 |
+
========
|
| 627 |
+
|
| 628 |
+
>>> from sympy import besselk
|
| 629 |
+
>>> from sympy.abc import z, n
|
| 630 |
+
>>> besselk(n, z).diff(z)
|
| 631 |
+
-besselk(n - 1, z)/2 - besselk(n + 1, z)/2
|
| 632 |
+
|
| 633 |
+
See Also
|
| 634 |
+
========
|
| 635 |
+
|
| 636 |
+
besselj, besseli, bessely
|
| 637 |
+
|
| 638 |
+
References
|
| 639 |
+
==========
|
| 640 |
+
|
| 641 |
+
.. [1] https://functions.wolfram.com/Bessel-TypeFunctions/BesselK/
|
| 642 |
+
|
| 643 |
+
"""
|
| 644 |
+
|
| 645 |
+
_a = S.One
|
| 646 |
+
_b = -S.One
|
| 647 |
+
|
| 648 |
+
@classmethod
|
| 649 |
+
def eval(cls, nu, z):
|
| 650 |
+
if z.is_zero:
|
| 651 |
+
if nu.is_zero:
|
| 652 |
+
return S.Infinity
|
| 653 |
+
elif re(nu).is_zero is False:
|
| 654 |
+
return S.ComplexInfinity
|
| 655 |
+
elif re(nu).is_zero:
|
| 656 |
+
return S.NaN
|
| 657 |
+
if z in (S.Infinity, I*S.Infinity, I*S.NegativeInfinity):
|
| 658 |
+
return S.Zero
|
| 659 |
+
|
| 660 |
+
if nu.is_integer:
|
| 661 |
+
if nu.could_extract_minus_sign():
|
| 662 |
+
return besselk(-nu, z)
|
| 663 |
+
|
| 664 |
+
def _eval_rewrite_as_besseli(self, nu, z, **kwargs):
|
| 665 |
+
if nu.is_integer is False:
|
| 666 |
+
return pi*csc(pi*nu)*(besseli(-nu, z) - besseli(nu, z))/2
|
| 667 |
+
|
| 668 |
+
def _eval_rewrite_as_besselj(self, nu, z, **kwargs):
|
| 669 |
+
ai = self._eval_rewrite_as_besseli(*self.args)
|
| 670 |
+
if ai:
|
| 671 |
+
return ai.rewrite(besselj)
|
| 672 |
+
|
| 673 |
+
def _eval_rewrite_as_bessely(self, nu, z, **kwargs):
|
| 674 |
+
aj = self._eval_rewrite_as_besselj(*self.args)
|
| 675 |
+
if aj:
|
| 676 |
+
return aj.rewrite(bessely)
|
| 677 |
+
|
| 678 |
+
def _eval_rewrite_as_yn(self, nu, z, **kwargs):
|
| 679 |
+
ay = self._eval_rewrite_as_bessely(*self.args)
|
| 680 |
+
if ay:
|
| 681 |
+
return ay.rewrite(yn)
|
| 682 |
+
|
| 683 |
+
def _eval_is_extended_real(self):
|
| 684 |
+
nu, z = self.args
|
| 685 |
+
if nu.is_integer and z.is_positive:
|
| 686 |
+
return True
|
| 687 |
+
|
| 688 |
+
def _eval_rewrite_as_tractable(self, nu, z, limitvar=None, **kwargs):
|
| 689 |
+
if z.is_extended_real:
|
| 690 |
+
return exp(-z)*_besselk(nu, z)
|
| 691 |
+
|
| 692 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 693 |
+
nu, z = self.args
|
| 694 |
+
try:
|
| 695 |
+
arg = z.as_leading_term(x)
|
| 696 |
+
except NotImplementedError:
|
| 697 |
+
return self
|
| 698 |
+
_, e = arg.as_coeff_exponent(x)
|
| 699 |
+
|
| 700 |
+
if e.is_positive:
|
| 701 |
+
if nu.is_zero:
|
| 702 |
+
# Equation 9.6.8 of Abramowitz and Stegun (10th ed, 1972).
|
| 703 |
+
term = -log(z) - S.EulerGamma + log(2)
|
| 704 |
+
elif nu.is_nonzero:
|
| 705 |
+
# Equation 9.6.9 of Abramowitz and Stegun (10th ed, 1972).
|
| 706 |
+
term = gamma(Abs(nu))*(z/2)**(-Abs(nu))/2
|
| 707 |
+
else:
|
| 708 |
+
raise NotImplementedError(f"Cannot proceed without knowing if {nu} is zero or not.")
|
| 709 |
+
|
| 710 |
+
return term.as_leading_term(x, logx=logx)
|
| 711 |
+
elif e.is_negative:
|
| 712 |
+
# Equation 9.7.2 of Abramowitz and Stegun (10th ed, 1972).
|
| 713 |
+
return sqrt(pi)*exp(-arg)/sqrt(2*arg)
|
| 714 |
+
else:
|
| 715 |
+
return self.func(nu, arg)
|
| 716 |
+
|
| 717 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 718 |
+
from sympy.series.order import Order
|
| 719 |
+
nu, z = self.args
|
| 720 |
+
|
| 721 |
+
try:
|
| 722 |
+
_, exp = z.leadterm(x)
|
| 723 |
+
except (ValueError, NotImplementedError):
|
| 724 |
+
return self
|
| 725 |
+
|
| 726 |
+
# In case of powers less than 1, number of terms need to be computed
|
| 727 |
+
# separately to avoid repeated callings of _eval_nseries with wrong n
|
| 728 |
+
if exp.is_positive:
|
| 729 |
+
r = (z/2)._eval_nseries(x, n, logx, cdir).removeO()
|
| 730 |
+
if r is S.Zero:
|
| 731 |
+
return Order(z**(-nu) + z**nu, x)
|
| 732 |
+
|
| 733 |
+
o = Order(x**n, x)
|
| 734 |
+
if nu.is_integer:
|
| 735 |
+
# Reference: https://functions.wolfram.com/Bessel-TypeFunctions/BesselK/06/01/04/01/02/0008/ (only for integer order)
|
| 736 |
+
newn = ceiling(n/exp)
|
| 737 |
+
bn = besseli(nu, z)
|
| 738 |
+
a = ((-1)**(nu - 1)*log(z/2)*bn)._eval_nseries(x, n, logx, cdir)
|
| 739 |
+
|
| 740 |
+
b, c = [], []
|
| 741 |
+
t = _mexpand(r**2)
|
| 742 |
+
|
| 743 |
+
if nu > S.Zero:
|
| 744 |
+
term = r**(-nu)*factorial(nu - 1)/2
|
| 745 |
+
b.append(term)
|
| 746 |
+
for k in range(1, nu):
|
| 747 |
+
term *= t/((k - nu)*k)
|
| 748 |
+
term = (_mexpand(term) + o).removeO()
|
| 749 |
+
b.append(term)
|
| 750 |
+
|
| 751 |
+
p = r**nu*(-1)**nu/(2*factorial(nu))
|
| 752 |
+
term = p*(digamma(nu + 1) - S.EulerGamma)
|
| 753 |
+
c.append(term)
|
| 754 |
+
for k in range(1, (newn + 1)//2):
|
| 755 |
+
p *= t/(k*(k + nu))
|
| 756 |
+
p = (_mexpand(p) + o).removeO()
|
| 757 |
+
term = p*(digamma(k + nu + 1) + digamma(k + 1))
|
| 758 |
+
c.append(term)
|
| 759 |
+
return a + Add(*b) + Add(*c) + o
|
| 760 |
+
elif nu.is_noninteger:
|
| 761 |
+
# Reference: https://functions.wolfram.com/Bessel-TypeFunctions/BesselK/06/01/04/01/01/0003/
|
| 762 |
+
# (only for non-integer order).
|
| 763 |
+
# While the expression in the reference above seems correct
|
| 764 |
+
# for non-real order as well, it would need some manipulation
|
| 765 |
+
# (not implemented) to be written as a power series in x with
|
| 766 |
+
# real exponents [e.g. Dunster 1990. "Bessel functions
|
| 767 |
+
# of purely imaginary order, with an application to second-order
|
| 768 |
+
# linear differential equations having a large parameter".
|
| 769 |
+
# SIAM J. Math. Anal. Vol 21, No. 4, pp 995-1018.].
|
| 770 |
+
newn_a = ceiling((n+nu)/exp)
|
| 771 |
+
newn_b = ceiling((n-nu)/exp)
|
| 772 |
+
|
| 773 |
+
a, b = [], []
|
| 774 |
+
for k in range((newn_a+1)//2):
|
| 775 |
+
term = gamma(nu)*r**(2*k-nu)/(2*RisingFactorial(1-nu, k)*factorial(k))
|
| 776 |
+
a.append(_mexpand(term))
|
| 777 |
+
for k in range((newn_b+1)//2):
|
| 778 |
+
term = gamma(-nu)*r**(2*k+nu)/(2*RisingFactorial(nu+1, k)*factorial(k))
|
| 779 |
+
b.append(_mexpand(term))
|
| 780 |
+
return Add(*a) + Add(*b) + o
|
| 781 |
+
else:
|
| 782 |
+
raise NotImplementedError("besselk expansion is only implemented for real order")
|
| 783 |
+
|
| 784 |
+
return super(besselk, self)._eval_nseries(x, n, logx, cdir)
|
| 785 |
+
|
| 786 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 787 |
+
from sympy.functions.combinatorial.factorials import RisingFactorial
|
| 788 |
+
from sympy.series.order import Order
|
| 789 |
+
point = args0[1]
|
| 790 |
+
|
| 791 |
+
if point in [S.Infinity, S.NegativeInfinity]:
|
| 792 |
+
nu, z = self.args
|
| 793 |
+
s = [(RisingFactorial(Rational(2*nu - 1, 2), k)*RisingFactorial(Rational(2*nu + 1, 2), k))/\
|
| 794 |
+
((-2)**(k)*z**(Rational(2*k + 1, 2))*factorial(k)) for k in range(n)] +[Order(1/z**(Rational(2*n + 1, 2)), x)]
|
| 795 |
+
return (exp(-z)*sqrt(pi/2))*Add(*s)
|
| 796 |
+
|
| 797 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 798 |
+
|
| 799 |
+
|
| 800 |
+
class hankel1(BesselBase):
|
| 801 |
+
r"""
|
| 802 |
+
Hankel function of the first kind.
|
| 803 |
+
|
| 804 |
+
Explanation
|
| 805 |
+
===========
|
| 806 |
+
|
| 807 |
+
This function is defined as
|
| 808 |
+
|
| 809 |
+
.. math ::
|
| 810 |
+
H_\nu^{(1)} = J_\nu(z) + iY_\nu(z),
|
| 811 |
+
|
| 812 |
+
where $J_\nu(z)$ is the Bessel function of the first kind, and
|
| 813 |
+
$Y_\nu(z)$ is the Bessel function of the second kind.
|
| 814 |
+
|
| 815 |
+
It is a solution to Bessel's equation.
|
| 816 |
+
|
| 817 |
+
Examples
|
| 818 |
+
========
|
| 819 |
+
|
| 820 |
+
>>> from sympy import hankel1
|
| 821 |
+
>>> from sympy.abc import z, n
|
| 822 |
+
>>> hankel1(n, z).diff(z)
|
| 823 |
+
hankel1(n - 1, z)/2 - hankel1(n + 1, z)/2
|
| 824 |
+
|
| 825 |
+
See Also
|
| 826 |
+
========
|
| 827 |
+
|
| 828 |
+
hankel2, besselj, bessely
|
| 829 |
+
|
| 830 |
+
References
|
| 831 |
+
==========
|
| 832 |
+
|
| 833 |
+
.. [1] https://functions.wolfram.com/Bessel-TypeFunctions/HankelH1/
|
| 834 |
+
|
| 835 |
+
"""
|
| 836 |
+
|
| 837 |
+
_a = S.One
|
| 838 |
+
_b = S.One
|
| 839 |
+
|
| 840 |
+
def _eval_conjugate(self):
|
| 841 |
+
z = self.argument
|
| 842 |
+
if z.is_extended_negative is False:
|
| 843 |
+
return hankel2(self.order.conjugate(), z.conjugate())
|
| 844 |
+
|
| 845 |
+
|
| 846 |
+
class hankel2(BesselBase):
|
| 847 |
+
r"""
|
| 848 |
+
Hankel function of the second kind.
|
| 849 |
+
|
| 850 |
+
Explanation
|
| 851 |
+
===========
|
| 852 |
+
|
| 853 |
+
This function is defined as
|
| 854 |
+
|
| 855 |
+
.. math ::
|
| 856 |
+
H_\nu^{(2)} = J_\nu(z) - iY_\nu(z),
|
| 857 |
+
|
| 858 |
+
where $J_\nu(z)$ is the Bessel function of the first kind, and
|
| 859 |
+
$Y_\nu(z)$ is the Bessel function of the second kind.
|
| 860 |
+
|
| 861 |
+
It is a solution to Bessel's equation, and linearly independent from
|
| 862 |
+
$H_\nu^{(1)}$.
|
| 863 |
+
|
| 864 |
+
Examples
|
| 865 |
+
========
|
| 866 |
+
|
| 867 |
+
>>> from sympy import hankel2
|
| 868 |
+
>>> from sympy.abc import z, n
|
| 869 |
+
>>> hankel2(n, z).diff(z)
|
| 870 |
+
hankel2(n - 1, z)/2 - hankel2(n + 1, z)/2
|
| 871 |
+
|
| 872 |
+
See Also
|
| 873 |
+
========
|
| 874 |
+
|
| 875 |
+
hankel1, besselj, bessely
|
| 876 |
+
|
| 877 |
+
References
|
| 878 |
+
==========
|
| 879 |
+
|
| 880 |
+
.. [1] https://functions.wolfram.com/Bessel-TypeFunctions/HankelH2/
|
| 881 |
+
|
| 882 |
+
"""
|
| 883 |
+
|
| 884 |
+
_a = S.One
|
| 885 |
+
_b = S.One
|
| 886 |
+
|
| 887 |
+
def _eval_conjugate(self):
|
| 888 |
+
z = self.argument
|
| 889 |
+
if z.is_extended_negative is False:
|
| 890 |
+
return hankel1(self.order.conjugate(), z.conjugate())
|
| 891 |
+
|
| 892 |
+
|
| 893 |
+
def assume_integer_order(fn):
|
| 894 |
+
@wraps(fn)
|
| 895 |
+
def g(self, nu, z):
|
| 896 |
+
if nu.is_integer:
|
| 897 |
+
return fn(self, nu, z)
|
| 898 |
+
return g
|
| 899 |
+
|
| 900 |
+
|
| 901 |
+
class SphericalBesselBase(BesselBase):
|
| 902 |
+
"""
|
| 903 |
+
Base class for spherical Bessel functions.
|
| 904 |
+
|
| 905 |
+
These are thin wrappers around ordinary Bessel functions,
|
| 906 |
+
since spherical Bessel functions differ from the ordinary
|
| 907 |
+
ones just by a slight change in order.
|
| 908 |
+
|
| 909 |
+
To use this class, define the ``_eval_evalf()`` and ``_expand()`` methods.
|
| 910 |
+
|
| 911 |
+
"""
|
| 912 |
+
|
| 913 |
+
def _expand(self, **hints):
|
| 914 |
+
""" Expand self into a polynomial. Nu is guaranteed to be Integer. """
|
| 915 |
+
raise NotImplementedError('expansion')
|
| 916 |
+
|
| 917 |
+
def _eval_expand_func(self, **hints):
|
| 918 |
+
if self.order.is_Integer:
|
| 919 |
+
return self._expand(**hints)
|
| 920 |
+
return self
|
| 921 |
+
|
| 922 |
+
def fdiff(self, argindex=2):
|
| 923 |
+
if argindex != 2:
|
| 924 |
+
raise ArgumentIndexError(self, argindex)
|
| 925 |
+
return self.__class__(self.order - 1, self.argument) - \
|
| 926 |
+
self * (self.order + 1)/self.argument
|
| 927 |
+
|
| 928 |
+
|
| 929 |
+
def _jn(n, z):
|
| 930 |
+
return (spherical_bessel_fn(n, z)*sin(z) +
|
| 931 |
+
S.NegativeOne**(n + 1)*spherical_bessel_fn(-n - 1, z)*cos(z))
|
| 932 |
+
|
| 933 |
+
|
| 934 |
+
def _yn(n, z):
|
| 935 |
+
# (-1)**(n + 1) * _jn(-n - 1, z)
|
| 936 |
+
return (S.NegativeOne**(n + 1) * spherical_bessel_fn(-n - 1, z)*sin(z) -
|
| 937 |
+
spherical_bessel_fn(n, z)*cos(z))
|
| 938 |
+
|
| 939 |
+
|
| 940 |
+
class jn(SphericalBesselBase):
|
| 941 |
+
r"""
|
| 942 |
+
Spherical Bessel function of the first kind.
|
| 943 |
+
|
| 944 |
+
Explanation
|
| 945 |
+
===========
|
| 946 |
+
|
| 947 |
+
This function is a solution to the spherical Bessel equation
|
| 948 |
+
|
| 949 |
+
.. math ::
|
| 950 |
+
z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2}
|
| 951 |
+
+ 2z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 - \nu(\nu + 1)) w = 0.
|
| 952 |
+
|
| 953 |
+
It can be defined as
|
| 954 |
+
|
| 955 |
+
.. math ::
|
| 956 |
+
j_\nu(z) = \sqrt{\frac{\pi}{2z}} J_{\nu + \frac{1}{2}}(z),
|
| 957 |
+
|
| 958 |
+
where $J_\nu(z)$ is the Bessel function of the first kind.
|
| 959 |
+
|
| 960 |
+
The spherical Bessel functions of integral order are
|
| 961 |
+
calculated using the formula:
|
| 962 |
+
|
| 963 |
+
.. math:: j_n(z) = f_n(z) \sin{z} + (-1)^{n+1} f_{-n-1}(z) \cos{z},
|
| 964 |
+
|
| 965 |
+
where the coefficients $f_n(z)$ are available as
|
| 966 |
+
:func:`sympy.polys.orthopolys.spherical_bessel_fn`.
|
| 967 |
+
|
| 968 |
+
Examples
|
| 969 |
+
========
|
| 970 |
+
|
| 971 |
+
>>> from sympy import Symbol, jn, sin, cos, expand_func, besselj, bessely
|
| 972 |
+
>>> z = Symbol("z")
|
| 973 |
+
>>> nu = Symbol("nu", integer=True)
|
| 974 |
+
>>> print(expand_func(jn(0, z)))
|
| 975 |
+
sin(z)/z
|
| 976 |
+
>>> expand_func(jn(1, z)) == sin(z)/z**2 - cos(z)/z
|
| 977 |
+
True
|
| 978 |
+
>>> expand_func(jn(3, z))
|
| 979 |
+
(-6/z**2 + 15/z**4)*sin(z) + (1/z - 15/z**3)*cos(z)
|
| 980 |
+
>>> jn(nu, z).rewrite(besselj)
|
| 981 |
+
sqrt(2)*sqrt(pi)*sqrt(1/z)*besselj(nu + 1/2, z)/2
|
| 982 |
+
>>> jn(nu, z).rewrite(bessely)
|
| 983 |
+
(-1)**nu*sqrt(2)*sqrt(pi)*sqrt(1/z)*bessely(-nu - 1/2, z)/2
|
| 984 |
+
>>> jn(2, 5.2+0.3j).evalf(20)
|
| 985 |
+
0.099419756723640344491 - 0.054525080242173562897*I
|
| 986 |
+
|
| 987 |
+
See Also
|
| 988 |
+
========
|
| 989 |
+
|
| 990 |
+
besselj, bessely, besselk, yn
|
| 991 |
+
|
| 992 |
+
References
|
| 993 |
+
==========
|
| 994 |
+
|
| 995 |
+
.. [1] https://dlmf.nist.gov/10.47
|
| 996 |
+
|
| 997 |
+
"""
|
| 998 |
+
@classmethod
|
| 999 |
+
def eval(cls, nu, z):
|
| 1000 |
+
if z.is_zero:
|
| 1001 |
+
if nu.is_zero:
|
| 1002 |
+
return S.One
|
| 1003 |
+
elif nu.is_integer:
|
| 1004 |
+
if nu.is_positive:
|
| 1005 |
+
return S.Zero
|
| 1006 |
+
else:
|
| 1007 |
+
return S.ComplexInfinity
|
| 1008 |
+
if z in (S.NegativeInfinity, S.Infinity):
|
| 1009 |
+
return S.Zero
|
| 1010 |
+
|
| 1011 |
+
def _eval_rewrite_as_besselj(self, nu, z, **kwargs):
|
| 1012 |
+
return sqrt(pi/(2*z)) * besselj(nu + S.Half, z)
|
| 1013 |
+
|
| 1014 |
+
def _eval_rewrite_as_bessely(self, nu, z, **kwargs):
|
| 1015 |
+
return S.NegativeOne**nu * sqrt(pi/(2*z)) * bessely(-nu - S.Half, z)
|
| 1016 |
+
|
| 1017 |
+
def _eval_rewrite_as_yn(self, nu, z, **kwargs):
|
| 1018 |
+
return S.NegativeOne**(nu) * yn(-nu - 1, z)
|
| 1019 |
+
|
| 1020 |
+
def _expand(self, **hints):
|
| 1021 |
+
return _jn(self.order, self.argument)
|
| 1022 |
+
|
| 1023 |
+
def _eval_evalf(self, prec):
|
| 1024 |
+
if self.order.is_Integer:
|
| 1025 |
+
return self.rewrite(besselj)._eval_evalf(prec)
|
| 1026 |
+
|
| 1027 |
+
|
| 1028 |
+
class yn(SphericalBesselBase):
|
| 1029 |
+
r"""
|
| 1030 |
+
Spherical Bessel function of the second kind.
|
| 1031 |
+
|
| 1032 |
+
Explanation
|
| 1033 |
+
===========
|
| 1034 |
+
|
| 1035 |
+
This function is another solution to the spherical Bessel equation, and
|
| 1036 |
+
linearly independent from $j_n$. It can be defined as
|
| 1037 |
+
|
| 1038 |
+
.. math ::
|
| 1039 |
+
y_\nu(z) = \sqrt{\frac{\pi}{2z}} Y_{\nu + \frac{1}{2}}(z),
|
| 1040 |
+
|
| 1041 |
+
where $Y_\nu(z)$ is the Bessel function of the second kind.
|
| 1042 |
+
|
| 1043 |
+
For integral orders $n$, $y_n$ is calculated using the formula:
|
| 1044 |
+
|
| 1045 |
+
.. math:: y_n(z) = (-1)^{n+1} j_{-n-1}(z)
|
| 1046 |
+
|
| 1047 |
+
Examples
|
| 1048 |
+
========
|
| 1049 |
+
|
| 1050 |
+
>>> from sympy import Symbol, yn, sin, cos, expand_func, besselj, bessely
|
| 1051 |
+
>>> z = Symbol("z")
|
| 1052 |
+
>>> nu = Symbol("nu", integer=True)
|
| 1053 |
+
>>> print(expand_func(yn(0, z)))
|
| 1054 |
+
-cos(z)/z
|
| 1055 |
+
>>> expand_func(yn(1, z)) == -cos(z)/z**2-sin(z)/z
|
| 1056 |
+
True
|
| 1057 |
+
>>> yn(nu, z).rewrite(besselj)
|
| 1058 |
+
(-1)**(nu + 1)*sqrt(2)*sqrt(pi)*sqrt(1/z)*besselj(-nu - 1/2, z)/2
|
| 1059 |
+
>>> yn(nu, z).rewrite(bessely)
|
| 1060 |
+
sqrt(2)*sqrt(pi)*sqrt(1/z)*bessely(nu + 1/2, z)/2
|
| 1061 |
+
>>> yn(2, 5.2+0.3j).evalf(20)
|
| 1062 |
+
0.18525034196069722536 + 0.014895573969924817587*I
|
| 1063 |
+
|
| 1064 |
+
See Also
|
| 1065 |
+
========
|
| 1066 |
+
|
| 1067 |
+
besselj, bessely, besselk, jn
|
| 1068 |
+
|
| 1069 |
+
References
|
| 1070 |
+
==========
|
| 1071 |
+
|
| 1072 |
+
.. [1] https://dlmf.nist.gov/10.47
|
| 1073 |
+
|
| 1074 |
+
"""
|
| 1075 |
+
@assume_integer_order
|
| 1076 |
+
def _eval_rewrite_as_besselj(self, nu, z, **kwargs):
|
| 1077 |
+
return S.NegativeOne**(nu+1) * sqrt(pi/(2*z)) * besselj(-nu - S.Half, z)
|
| 1078 |
+
|
| 1079 |
+
@assume_integer_order
|
| 1080 |
+
def _eval_rewrite_as_bessely(self, nu, z, **kwargs):
|
| 1081 |
+
return sqrt(pi/(2*z)) * bessely(nu + S.Half, z)
|
| 1082 |
+
|
| 1083 |
+
def _eval_rewrite_as_jn(self, nu, z, **kwargs):
|
| 1084 |
+
return S.NegativeOne**(nu + 1) * jn(-nu - 1, z)
|
| 1085 |
+
|
| 1086 |
+
def _expand(self, **hints):
|
| 1087 |
+
return _yn(self.order, self.argument)
|
| 1088 |
+
|
| 1089 |
+
def _eval_evalf(self, prec):
|
| 1090 |
+
if self.order.is_Integer:
|
| 1091 |
+
return self.rewrite(bessely)._eval_evalf(prec)
|
| 1092 |
+
|
| 1093 |
+
|
| 1094 |
+
class SphericalHankelBase(SphericalBesselBase):
|
| 1095 |
+
|
| 1096 |
+
@assume_integer_order
|
| 1097 |
+
def _eval_rewrite_as_besselj(self, nu, z, **kwargs):
|
| 1098 |
+
# jn +- I*yn
|
| 1099 |
+
# jn as beeselj: sqrt(pi/(2*z)) * besselj(nu + S.Half, z)
|
| 1100 |
+
# yn as besselj: (-1)**(nu+1) * sqrt(pi/(2*z)) * besselj(-nu - S.Half, z)
|
| 1101 |
+
hks = self._hankel_kind_sign
|
| 1102 |
+
return sqrt(pi/(2*z))*(besselj(nu + S.Half, z) +
|
| 1103 |
+
hks*I*S.NegativeOne**(nu+1)*besselj(-nu - S.Half, z))
|
| 1104 |
+
|
| 1105 |
+
@assume_integer_order
|
| 1106 |
+
def _eval_rewrite_as_bessely(self, nu, z, **kwargs):
|
| 1107 |
+
# jn +- I*yn
|
| 1108 |
+
# jn as bessely: (-1)**nu * sqrt(pi/(2*z)) * bessely(-nu - S.Half, z)
|
| 1109 |
+
# yn as bessely: sqrt(pi/(2*z)) * bessely(nu + S.Half, z)
|
| 1110 |
+
hks = self._hankel_kind_sign
|
| 1111 |
+
return sqrt(pi/(2*z))*(S.NegativeOne**nu*bessely(-nu - S.Half, z) +
|
| 1112 |
+
hks*I*bessely(nu + S.Half, z))
|
| 1113 |
+
|
| 1114 |
+
def _eval_rewrite_as_yn(self, nu, z, **kwargs):
|
| 1115 |
+
hks = self._hankel_kind_sign
|
| 1116 |
+
return jn(nu, z).rewrite(yn) + hks*I*yn(nu, z)
|
| 1117 |
+
|
| 1118 |
+
def _eval_rewrite_as_jn(self, nu, z, **kwargs):
|
| 1119 |
+
hks = self._hankel_kind_sign
|
| 1120 |
+
return jn(nu, z) + hks*I*yn(nu, z).rewrite(jn)
|
| 1121 |
+
|
| 1122 |
+
def _eval_expand_func(self, **hints):
|
| 1123 |
+
if self.order.is_Integer:
|
| 1124 |
+
return self._expand(**hints)
|
| 1125 |
+
else:
|
| 1126 |
+
nu = self.order
|
| 1127 |
+
z = self.argument
|
| 1128 |
+
hks = self._hankel_kind_sign
|
| 1129 |
+
return jn(nu, z) + hks*I*yn(nu, z)
|
| 1130 |
+
|
| 1131 |
+
def _expand(self, **hints):
|
| 1132 |
+
n = self.order
|
| 1133 |
+
z = self.argument
|
| 1134 |
+
hks = self._hankel_kind_sign
|
| 1135 |
+
|
| 1136 |
+
# fully expanded version
|
| 1137 |
+
# return ((fn(n, z) * sin(z) +
|
| 1138 |
+
# (-1)**(n + 1) * fn(-n - 1, z) * cos(z)) + # jn
|
| 1139 |
+
# (hks * I * (-1)**(n + 1) *
|
| 1140 |
+
# (fn(-n - 1, z) * hk * I * sin(z) +
|
| 1141 |
+
# (-1)**(-n) * fn(n, z) * I * cos(z))) # +-I*yn
|
| 1142 |
+
# )
|
| 1143 |
+
|
| 1144 |
+
return (_jn(n, z) + hks*I*_yn(n, z)).expand()
|
| 1145 |
+
|
| 1146 |
+
def _eval_evalf(self, prec):
|
| 1147 |
+
if self.order.is_Integer:
|
| 1148 |
+
return self.rewrite(besselj)._eval_evalf(prec)
|
| 1149 |
+
|
| 1150 |
+
|
| 1151 |
+
class hn1(SphericalHankelBase):
|
| 1152 |
+
r"""
|
| 1153 |
+
Spherical Hankel function of the first kind.
|
| 1154 |
+
|
| 1155 |
+
Explanation
|
| 1156 |
+
===========
|
| 1157 |
+
|
| 1158 |
+
This function is defined as
|
| 1159 |
+
|
| 1160 |
+
.. math:: h_\nu^(1)(z) = j_\nu(z) + i y_\nu(z),
|
| 1161 |
+
|
| 1162 |
+
where $j_\nu(z)$ and $y_\nu(z)$ are the spherical
|
| 1163 |
+
Bessel function of the first and second kinds.
|
| 1164 |
+
|
| 1165 |
+
For integral orders $n$, $h_n^(1)$ is calculated using the formula:
|
| 1166 |
+
|
| 1167 |
+
.. math:: h_n^(1)(z) = j_{n}(z) + i (-1)^{n+1} j_{-n-1}(z)
|
| 1168 |
+
|
| 1169 |
+
Examples
|
| 1170 |
+
========
|
| 1171 |
+
|
| 1172 |
+
>>> from sympy import Symbol, hn1, hankel1, expand_func, yn, jn
|
| 1173 |
+
>>> z = Symbol("z")
|
| 1174 |
+
>>> nu = Symbol("nu", integer=True)
|
| 1175 |
+
>>> print(expand_func(hn1(nu, z)))
|
| 1176 |
+
jn(nu, z) + I*yn(nu, z)
|
| 1177 |
+
>>> print(expand_func(hn1(0, z)))
|
| 1178 |
+
sin(z)/z - I*cos(z)/z
|
| 1179 |
+
>>> print(expand_func(hn1(1, z)))
|
| 1180 |
+
-I*sin(z)/z - cos(z)/z + sin(z)/z**2 - I*cos(z)/z**2
|
| 1181 |
+
>>> hn1(nu, z).rewrite(jn)
|
| 1182 |
+
(-1)**(nu + 1)*I*jn(-nu - 1, z) + jn(nu, z)
|
| 1183 |
+
>>> hn1(nu, z).rewrite(yn)
|
| 1184 |
+
(-1)**nu*yn(-nu - 1, z) + I*yn(nu, z)
|
| 1185 |
+
>>> hn1(nu, z).rewrite(hankel1)
|
| 1186 |
+
sqrt(2)*sqrt(pi)*sqrt(1/z)*hankel1(nu, z)/2
|
| 1187 |
+
|
| 1188 |
+
See Also
|
| 1189 |
+
========
|
| 1190 |
+
|
| 1191 |
+
hn2, jn, yn, hankel1, hankel2
|
| 1192 |
+
|
| 1193 |
+
References
|
| 1194 |
+
==========
|
| 1195 |
+
|
| 1196 |
+
.. [1] https://dlmf.nist.gov/10.47
|
| 1197 |
+
|
| 1198 |
+
"""
|
| 1199 |
+
|
| 1200 |
+
_hankel_kind_sign = S.One
|
| 1201 |
+
|
| 1202 |
+
@assume_integer_order
|
| 1203 |
+
def _eval_rewrite_as_hankel1(self, nu, z, **kwargs):
|
| 1204 |
+
return sqrt(pi/(2*z))*hankel1(nu, z)
|
| 1205 |
+
|
| 1206 |
+
|
| 1207 |
+
class hn2(SphericalHankelBase):
|
| 1208 |
+
r"""
|
| 1209 |
+
Spherical Hankel function of the second kind.
|
| 1210 |
+
|
| 1211 |
+
Explanation
|
| 1212 |
+
===========
|
| 1213 |
+
|
| 1214 |
+
This function is defined as
|
| 1215 |
+
|
| 1216 |
+
.. math:: h_\nu^(2)(z) = j_\nu(z) - i y_\nu(z),
|
| 1217 |
+
|
| 1218 |
+
where $j_\nu(z)$ and $y_\nu(z)$ are the spherical
|
| 1219 |
+
Bessel function of the first and second kinds.
|
| 1220 |
+
|
| 1221 |
+
For integral orders $n$, $h_n^(2)$ is calculated using the formula:
|
| 1222 |
+
|
| 1223 |
+
.. math:: h_n^(2)(z) = j_{n} - i (-1)^{n+1} j_{-n-1}(z)
|
| 1224 |
+
|
| 1225 |
+
Examples
|
| 1226 |
+
========
|
| 1227 |
+
|
| 1228 |
+
>>> from sympy import Symbol, hn2, hankel2, expand_func, jn, yn
|
| 1229 |
+
>>> z = Symbol("z")
|
| 1230 |
+
>>> nu = Symbol("nu", integer=True)
|
| 1231 |
+
>>> print(expand_func(hn2(nu, z)))
|
| 1232 |
+
jn(nu, z) - I*yn(nu, z)
|
| 1233 |
+
>>> print(expand_func(hn2(0, z)))
|
| 1234 |
+
sin(z)/z + I*cos(z)/z
|
| 1235 |
+
>>> print(expand_func(hn2(1, z)))
|
| 1236 |
+
I*sin(z)/z - cos(z)/z + sin(z)/z**2 + I*cos(z)/z**2
|
| 1237 |
+
>>> hn2(nu, z).rewrite(hankel2)
|
| 1238 |
+
sqrt(2)*sqrt(pi)*sqrt(1/z)*hankel2(nu, z)/2
|
| 1239 |
+
>>> hn2(nu, z).rewrite(jn)
|
| 1240 |
+
-(-1)**(nu + 1)*I*jn(-nu - 1, z) + jn(nu, z)
|
| 1241 |
+
>>> hn2(nu, z).rewrite(yn)
|
| 1242 |
+
(-1)**nu*yn(-nu - 1, z) - I*yn(nu, z)
|
| 1243 |
+
|
| 1244 |
+
See Also
|
| 1245 |
+
========
|
| 1246 |
+
|
| 1247 |
+
hn1, jn, yn, hankel1, hankel2
|
| 1248 |
+
|
| 1249 |
+
References
|
| 1250 |
+
==========
|
| 1251 |
+
|
| 1252 |
+
.. [1] https://dlmf.nist.gov/10.47
|
| 1253 |
+
|
| 1254 |
+
"""
|
| 1255 |
+
|
| 1256 |
+
_hankel_kind_sign = -S.One
|
| 1257 |
+
|
| 1258 |
+
@assume_integer_order
|
| 1259 |
+
def _eval_rewrite_as_hankel2(self, nu, z, **kwargs):
|
| 1260 |
+
return sqrt(pi/(2*z))*hankel2(nu, z)
|
| 1261 |
+
|
| 1262 |
+
|
| 1263 |
+
def jn_zeros(n, k, method="sympy", dps=15):
|
| 1264 |
+
"""
|
| 1265 |
+
Zeros of the spherical Bessel function of the first kind.
|
| 1266 |
+
|
| 1267 |
+
Explanation
|
| 1268 |
+
===========
|
| 1269 |
+
|
| 1270 |
+
This returns an array of zeros of $jn$ up to the $k$-th zero.
|
| 1271 |
+
|
| 1272 |
+
* method = "sympy": uses `mpmath.besseljzero
|
| 1273 |
+
<https://mpmath.org/doc/current/functions/bessel.html#mpmath.besseljzero>`_
|
| 1274 |
+
* method = "scipy": uses the
|
| 1275 |
+
`SciPy's sph_jn <https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.jn_zeros.html>`_
|
| 1276 |
+
and
|
| 1277 |
+
`newton <https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html>`_
|
| 1278 |
+
to find all
|
| 1279 |
+
roots, which is faster than computing the zeros using a general
|
| 1280 |
+
numerical solver, but it requires SciPy and only works with low
|
| 1281 |
+
precision floating point numbers. (The function used with
|
| 1282 |
+
method="sympy" is a recent addition to mpmath; before that a general
|
| 1283 |
+
solver was used.)
|
| 1284 |
+
|
| 1285 |
+
Examples
|
| 1286 |
+
========
|
| 1287 |
+
|
| 1288 |
+
>>> from sympy import jn_zeros
|
| 1289 |
+
>>> jn_zeros(2, 4, dps=5)
|
| 1290 |
+
[5.7635, 9.095, 12.323, 15.515]
|
| 1291 |
+
|
| 1292 |
+
See Also
|
| 1293 |
+
========
|
| 1294 |
+
|
| 1295 |
+
jn, yn, besselj, besselk, bessely
|
| 1296 |
+
|
| 1297 |
+
Parameters
|
| 1298 |
+
==========
|
| 1299 |
+
|
| 1300 |
+
n : integer
|
| 1301 |
+
order of Bessel function
|
| 1302 |
+
|
| 1303 |
+
k : integer
|
| 1304 |
+
number of zeros to return
|
| 1305 |
+
|
| 1306 |
+
|
| 1307 |
+
"""
|
| 1308 |
+
from math import pi as math_pi
|
| 1309 |
+
|
| 1310 |
+
if method == "sympy":
|
| 1311 |
+
from mpmath import besseljzero
|
| 1312 |
+
from mpmath.libmp.libmpf import dps_to_prec
|
| 1313 |
+
prec = dps_to_prec(dps)
|
| 1314 |
+
return [Expr._from_mpmath(besseljzero(S(n + 0.5)._to_mpmath(prec),
|
| 1315 |
+
int(l)), prec)
|
| 1316 |
+
for l in range(1, k + 1)]
|
| 1317 |
+
elif method == "scipy":
|
| 1318 |
+
from scipy.optimize import newton
|
| 1319 |
+
try:
|
| 1320 |
+
from scipy.special import spherical_jn
|
| 1321 |
+
f = lambda x: spherical_jn(n, x)
|
| 1322 |
+
except ImportError:
|
| 1323 |
+
from scipy.special import sph_jn
|
| 1324 |
+
f = lambda x: sph_jn(n, x)[0][-1]
|
| 1325 |
+
else:
|
| 1326 |
+
raise NotImplementedError("Unknown method.")
|
| 1327 |
+
|
| 1328 |
+
def solver(f, x):
|
| 1329 |
+
if method == "scipy":
|
| 1330 |
+
root = newton(f, x)
|
| 1331 |
+
else:
|
| 1332 |
+
raise NotImplementedError("Unknown method.")
|
| 1333 |
+
return root
|
| 1334 |
+
|
| 1335 |
+
# we need to approximate the position of the first root:
|
| 1336 |
+
root = n + math_pi
|
| 1337 |
+
# determine the first root exactly:
|
| 1338 |
+
root = solver(f, root)
|
| 1339 |
+
roots = [root]
|
| 1340 |
+
for i in range(k - 1):
|
| 1341 |
+
# estimate the position of the next root using the last root + pi:
|
| 1342 |
+
root = solver(f, root + math_pi)
|
| 1343 |
+
roots.append(root)
|
| 1344 |
+
return roots
|
| 1345 |
+
|
| 1346 |
+
|
| 1347 |
+
class AiryBase(DefinedFunction):
|
| 1348 |
+
"""
|
| 1349 |
+
Abstract base class for Airy functions.
|
| 1350 |
+
|
| 1351 |
+
This class is meant to reduce code duplication.
|
| 1352 |
+
|
| 1353 |
+
"""
|
| 1354 |
+
|
| 1355 |
+
def _eval_conjugate(self):
|
| 1356 |
+
return self.func(self.args[0].conjugate())
|
| 1357 |
+
|
| 1358 |
+
def _eval_is_extended_real(self):
|
| 1359 |
+
return self.args[0].is_extended_real
|
| 1360 |
+
|
| 1361 |
+
def as_real_imag(self, deep=True, **hints):
|
| 1362 |
+
z = self.args[0]
|
| 1363 |
+
zc = z.conjugate()
|
| 1364 |
+
f = self.func
|
| 1365 |
+
u = (f(z)+f(zc))/2
|
| 1366 |
+
v = I*(f(zc)-f(z))/2
|
| 1367 |
+
return u, v
|
| 1368 |
+
|
| 1369 |
+
def _eval_expand_complex(self, deep=True, **hints):
|
| 1370 |
+
re_part, im_part = self.as_real_imag(deep=deep, **hints)
|
| 1371 |
+
return re_part + im_part*I
|
| 1372 |
+
|
| 1373 |
+
|
| 1374 |
+
class airyai(AiryBase):
|
| 1375 |
+
r"""
|
| 1376 |
+
The Airy function $\operatorname{Ai}$ of the first kind.
|
| 1377 |
+
|
| 1378 |
+
Explanation
|
| 1379 |
+
===========
|
| 1380 |
+
|
| 1381 |
+
The Airy function $\operatorname{Ai}(z)$ is defined to be the function
|
| 1382 |
+
satisfying Airy's differential equation
|
| 1383 |
+
|
| 1384 |
+
.. math::
|
| 1385 |
+
\frac{\mathrm{d}^2 w(z)}{\mathrm{d}z^2} - z w(z) = 0.
|
| 1386 |
+
|
| 1387 |
+
Equivalently, for real $z$
|
| 1388 |
+
|
| 1389 |
+
.. math::
|
| 1390 |
+
\operatorname{Ai}(z) := \frac{1}{\pi}
|
| 1391 |
+
\int_0^\infty \cos\left(\frac{t^3}{3} + z t\right) \mathrm{d}t.
|
| 1392 |
+
|
| 1393 |
+
Examples
|
| 1394 |
+
========
|
| 1395 |
+
|
| 1396 |
+
Create an Airy function object:
|
| 1397 |
+
|
| 1398 |
+
>>> from sympy import airyai
|
| 1399 |
+
>>> from sympy.abc import z
|
| 1400 |
+
|
| 1401 |
+
>>> airyai(z)
|
| 1402 |
+
airyai(z)
|
| 1403 |
+
|
| 1404 |
+
Several special values are known:
|
| 1405 |
+
|
| 1406 |
+
>>> airyai(0)
|
| 1407 |
+
3**(1/3)/(3*gamma(2/3))
|
| 1408 |
+
>>> from sympy import oo
|
| 1409 |
+
>>> airyai(oo)
|
| 1410 |
+
0
|
| 1411 |
+
>>> airyai(-oo)
|
| 1412 |
+
0
|
| 1413 |
+
|
| 1414 |
+
The Airy function obeys the mirror symmetry:
|
| 1415 |
+
|
| 1416 |
+
>>> from sympy import conjugate
|
| 1417 |
+
>>> conjugate(airyai(z))
|
| 1418 |
+
airyai(conjugate(z))
|
| 1419 |
+
|
| 1420 |
+
Differentiation with respect to $z$ is supported:
|
| 1421 |
+
|
| 1422 |
+
>>> from sympy import diff
|
| 1423 |
+
>>> diff(airyai(z), z)
|
| 1424 |
+
airyaiprime(z)
|
| 1425 |
+
>>> diff(airyai(z), z, 2)
|
| 1426 |
+
z*airyai(z)
|
| 1427 |
+
|
| 1428 |
+
Series expansion is also supported:
|
| 1429 |
+
|
| 1430 |
+
>>> from sympy import series
|
| 1431 |
+
>>> series(airyai(z), z, 0, 3)
|
| 1432 |
+
3**(5/6)*gamma(1/3)/(6*pi) - 3**(1/6)*z*gamma(2/3)/(2*pi) + O(z**3)
|
| 1433 |
+
|
| 1434 |
+
We can numerically evaluate the Airy function to arbitrary precision
|
| 1435 |
+
on the whole complex plane:
|
| 1436 |
+
|
| 1437 |
+
>>> airyai(-2).evalf(50)
|
| 1438 |
+
0.22740742820168557599192443603787379946077222541710
|
| 1439 |
+
|
| 1440 |
+
Rewrite $\operatorname{Ai}(z)$ in terms of hypergeometric functions:
|
| 1441 |
+
|
| 1442 |
+
>>> from sympy import hyper
|
| 1443 |
+
>>> airyai(z).rewrite(hyper)
|
| 1444 |
+
-3**(2/3)*z*hyper((), (4/3,), z**3/9)/(3*gamma(1/3)) + 3**(1/3)*hyper((), (2/3,), z**3/9)/(3*gamma(2/3))
|
| 1445 |
+
|
| 1446 |
+
See Also
|
| 1447 |
+
========
|
| 1448 |
+
|
| 1449 |
+
airybi: Airy function of the second kind.
|
| 1450 |
+
airyaiprime: Derivative of the Airy function of the first kind.
|
| 1451 |
+
airybiprime: Derivative of the Airy function of the second kind.
|
| 1452 |
+
|
| 1453 |
+
References
|
| 1454 |
+
==========
|
| 1455 |
+
|
| 1456 |
+
.. [1] https://en.wikipedia.org/wiki/Airy_function
|
| 1457 |
+
.. [2] https://dlmf.nist.gov/9
|
| 1458 |
+
.. [3] https://encyclopediaofmath.org/wiki/Airy_functions
|
| 1459 |
+
.. [4] https://mathworld.wolfram.com/AiryFunctions.html
|
| 1460 |
+
|
| 1461 |
+
"""
|
| 1462 |
+
|
| 1463 |
+
nargs = 1
|
| 1464 |
+
unbranched = True
|
| 1465 |
+
|
| 1466 |
+
@classmethod
|
| 1467 |
+
def eval(cls, arg):
|
| 1468 |
+
if arg.is_Number:
|
| 1469 |
+
if arg is S.NaN:
|
| 1470 |
+
return S.NaN
|
| 1471 |
+
elif arg is S.Infinity:
|
| 1472 |
+
return S.Zero
|
| 1473 |
+
elif arg is S.NegativeInfinity:
|
| 1474 |
+
return S.Zero
|
| 1475 |
+
elif arg.is_zero:
|
| 1476 |
+
return S.One / (3**Rational(2, 3) * gamma(Rational(2, 3)))
|
| 1477 |
+
if arg.is_zero:
|
| 1478 |
+
return S.One / (3**Rational(2, 3) * gamma(Rational(2, 3)))
|
| 1479 |
+
|
| 1480 |
+
def fdiff(self, argindex=1):
|
| 1481 |
+
if argindex == 1:
|
| 1482 |
+
return airyaiprime(self.args[0])
|
| 1483 |
+
else:
|
| 1484 |
+
raise ArgumentIndexError(self, argindex)
|
| 1485 |
+
|
| 1486 |
+
@staticmethod
|
| 1487 |
+
@cacheit
|
| 1488 |
+
def taylor_term(n, x, *previous_terms):
|
| 1489 |
+
if n < 0:
|
| 1490 |
+
return S.Zero
|
| 1491 |
+
else:
|
| 1492 |
+
x = sympify(x)
|
| 1493 |
+
if len(previous_terms) > 1:
|
| 1494 |
+
p = previous_terms[-1]
|
| 1495 |
+
return ((cbrt(3)*x)**(-n)*(cbrt(3)*x)**(n + 1)*sin(pi*(n*Rational(2, 3) + Rational(4, 3)))*factorial(n) *
|
| 1496 |
+
gamma(n/3 + Rational(2, 3))/(sin(pi*(n*Rational(2, 3) + Rational(2, 3)))*factorial(n + 1)*gamma(n/3 + Rational(1, 3))) * p)
|
| 1497 |
+
else:
|
| 1498 |
+
return (S.One/(3**Rational(2, 3)*pi) * gamma((n+S.One)/S(3)) * sin(Rational(2, 3)*pi*(n+S.One)) /
|
| 1499 |
+
factorial(n) * (cbrt(3)*x)**n)
|
| 1500 |
+
|
| 1501 |
+
def _eval_rewrite_as_besselj(self, z, **kwargs):
|
| 1502 |
+
ot = Rational(1, 3)
|
| 1503 |
+
tt = Rational(2, 3)
|
| 1504 |
+
a = Pow(-z, Rational(3, 2))
|
| 1505 |
+
if re(z).is_negative:
|
| 1506 |
+
return ot*sqrt(-z) * (besselj(-ot, tt*a) + besselj(ot, tt*a))
|
| 1507 |
+
|
| 1508 |
+
def _eval_rewrite_as_besseli(self, z, **kwargs):
|
| 1509 |
+
ot = Rational(1, 3)
|
| 1510 |
+
tt = Rational(2, 3)
|
| 1511 |
+
a = Pow(z, Rational(3, 2))
|
| 1512 |
+
if re(z).is_positive:
|
| 1513 |
+
return ot*sqrt(z) * (besseli(-ot, tt*a) - besseli(ot, tt*a))
|
| 1514 |
+
else:
|
| 1515 |
+
return ot*(Pow(a, ot)*besseli(-ot, tt*a) - z*Pow(a, -ot)*besseli(ot, tt*a))
|
| 1516 |
+
|
| 1517 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 1518 |
+
pf1 = S.One / (3**Rational(2, 3)*gamma(Rational(2, 3)))
|
| 1519 |
+
pf2 = z / (root(3, 3)*gamma(Rational(1, 3)))
|
| 1520 |
+
return pf1 * hyper([], [Rational(2, 3)], z**3/9) - pf2 * hyper([], [Rational(4, 3)], z**3/9)
|
| 1521 |
+
|
| 1522 |
+
def _eval_expand_func(self, **hints):
|
| 1523 |
+
arg = self.args[0]
|
| 1524 |
+
symbs = arg.free_symbols
|
| 1525 |
+
|
| 1526 |
+
if len(symbs) == 1:
|
| 1527 |
+
z = symbs.pop()
|
| 1528 |
+
c = Wild("c", exclude=[z])
|
| 1529 |
+
d = Wild("d", exclude=[z])
|
| 1530 |
+
m = Wild("m", exclude=[z])
|
| 1531 |
+
n = Wild("n", exclude=[z])
|
| 1532 |
+
M = arg.match(c*(d*z**n)**m)
|
| 1533 |
+
if M is not None:
|
| 1534 |
+
m = M[m]
|
| 1535 |
+
# The transformation is given by 03.05.16.0001.01
|
| 1536 |
+
# https://functions.wolfram.com/Bessel-TypeFunctions/AiryAi/16/01/01/0001/
|
| 1537 |
+
if (3*m).is_integer:
|
| 1538 |
+
c = M[c]
|
| 1539 |
+
d = M[d]
|
| 1540 |
+
n = M[n]
|
| 1541 |
+
pf = (d * z**n)**m / (d**m * z**(m*n))
|
| 1542 |
+
newarg = c * d**m * z**(m*n)
|
| 1543 |
+
return S.Half * ((pf + S.One)*airyai(newarg) - (pf - S.One)/sqrt(3)*airybi(newarg))
|
| 1544 |
+
|
| 1545 |
+
|
| 1546 |
+
class airybi(AiryBase):
|
| 1547 |
+
r"""
|
| 1548 |
+
The Airy function $\operatorname{Bi}$ of the second kind.
|
| 1549 |
+
|
| 1550 |
+
Explanation
|
| 1551 |
+
===========
|
| 1552 |
+
|
| 1553 |
+
The Airy function $\operatorname{Bi}(z)$ is defined to be the function
|
| 1554 |
+
satisfying Airy's differential equation
|
| 1555 |
+
|
| 1556 |
+
.. math::
|
| 1557 |
+
\frac{\mathrm{d}^2 w(z)}{\mathrm{d}z^2} - z w(z) = 0.
|
| 1558 |
+
|
| 1559 |
+
Equivalently, for real $z$
|
| 1560 |
+
|
| 1561 |
+
.. math::
|
| 1562 |
+
\operatorname{Bi}(z) := \frac{1}{\pi}
|
| 1563 |
+
\int_0^\infty
|
| 1564 |
+
\exp\left(-\frac{t^3}{3} + z t\right)
|
| 1565 |
+
+ \sin\left(\frac{t^3}{3} + z t\right) \mathrm{d}t.
|
| 1566 |
+
|
| 1567 |
+
Examples
|
| 1568 |
+
========
|
| 1569 |
+
|
| 1570 |
+
Create an Airy function object:
|
| 1571 |
+
|
| 1572 |
+
>>> from sympy import airybi
|
| 1573 |
+
>>> from sympy.abc import z
|
| 1574 |
+
|
| 1575 |
+
>>> airybi(z)
|
| 1576 |
+
airybi(z)
|
| 1577 |
+
|
| 1578 |
+
Several special values are known:
|
| 1579 |
+
|
| 1580 |
+
>>> airybi(0)
|
| 1581 |
+
3**(5/6)/(3*gamma(2/3))
|
| 1582 |
+
>>> from sympy import oo
|
| 1583 |
+
>>> airybi(oo)
|
| 1584 |
+
oo
|
| 1585 |
+
>>> airybi(-oo)
|
| 1586 |
+
0
|
| 1587 |
+
|
| 1588 |
+
The Airy function obeys the mirror symmetry:
|
| 1589 |
+
|
| 1590 |
+
>>> from sympy import conjugate
|
| 1591 |
+
>>> conjugate(airybi(z))
|
| 1592 |
+
airybi(conjugate(z))
|
| 1593 |
+
|
| 1594 |
+
Differentiation with respect to $z$ is supported:
|
| 1595 |
+
|
| 1596 |
+
>>> from sympy import diff
|
| 1597 |
+
>>> diff(airybi(z), z)
|
| 1598 |
+
airybiprime(z)
|
| 1599 |
+
>>> diff(airybi(z), z, 2)
|
| 1600 |
+
z*airybi(z)
|
| 1601 |
+
|
| 1602 |
+
Series expansion is also supported:
|
| 1603 |
+
|
| 1604 |
+
>>> from sympy import series
|
| 1605 |
+
>>> series(airybi(z), z, 0, 3)
|
| 1606 |
+
3**(1/3)*gamma(1/3)/(2*pi) + 3**(2/3)*z*gamma(2/3)/(2*pi) + O(z**3)
|
| 1607 |
+
|
| 1608 |
+
We can numerically evaluate the Airy function to arbitrary precision
|
| 1609 |
+
on the whole complex plane:
|
| 1610 |
+
|
| 1611 |
+
>>> airybi(-2).evalf(50)
|
| 1612 |
+
-0.41230258795639848808323405461146104203453483447240
|
| 1613 |
+
|
| 1614 |
+
Rewrite $\operatorname{Bi}(z)$ in terms of hypergeometric functions:
|
| 1615 |
+
|
| 1616 |
+
>>> from sympy import hyper
|
| 1617 |
+
>>> airybi(z).rewrite(hyper)
|
| 1618 |
+
3**(1/6)*z*hyper((), (4/3,), z**3/9)/gamma(1/3) + 3**(5/6)*hyper((), (2/3,), z**3/9)/(3*gamma(2/3))
|
| 1619 |
+
|
| 1620 |
+
See Also
|
| 1621 |
+
========
|
| 1622 |
+
|
| 1623 |
+
airyai: Airy function of the first kind.
|
| 1624 |
+
airyaiprime: Derivative of the Airy function of the first kind.
|
| 1625 |
+
airybiprime: Derivative of the Airy function of the second kind.
|
| 1626 |
+
|
| 1627 |
+
References
|
| 1628 |
+
==========
|
| 1629 |
+
|
| 1630 |
+
.. [1] https://en.wikipedia.org/wiki/Airy_function
|
| 1631 |
+
.. [2] https://dlmf.nist.gov/9
|
| 1632 |
+
.. [3] https://encyclopediaofmath.org/wiki/Airy_functions
|
| 1633 |
+
.. [4] https://mathworld.wolfram.com/AiryFunctions.html
|
| 1634 |
+
|
| 1635 |
+
"""
|
| 1636 |
+
|
| 1637 |
+
nargs = 1
|
| 1638 |
+
unbranched = True
|
| 1639 |
+
|
| 1640 |
+
@classmethod
|
| 1641 |
+
def eval(cls, arg):
|
| 1642 |
+
if arg.is_Number:
|
| 1643 |
+
if arg is S.NaN:
|
| 1644 |
+
return S.NaN
|
| 1645 |
+
elif arg is S.Infinity:
|
| 1646 |
+
return S.Infinity
|
| 1647 |
+
elif arg is S.NegativeInfinity:
|
| 1648 |
+
return S.Zero
|
| 1649 |
+
elif arg.is_zero:
|
| 1650 |
+
return S.One / (3**Rational(1, 6) * gamma(Rational(2, 3)))
|
| 1651 |
+
|
| 1652 |
+
if arg.is_zero:
|
| 1653 |
+
return S.One / (3**Rational(1, 6) * gamma(Rational(2, 3)))
|
| 1654 |
+
|
| 1655 |
+
def fdiff(self, argindex=1):
|
| 1656 |
+
if argindex == 1:
|
| 1657 |
+
return airybiprime(self.args[0])
|
| 1658 |
+
else:
|
| 1659 |
+
raise ArgumentIndexError(self, argindex)
|
| 1660 |
+
|
| 1661 |
+
@staticmethod
|
| 1662 |
+
@cacheit
|
| 1663 |
+
def taylor_term(n, x, *previous_terms):
|
| 1664 |
+
if n < 0:
|
| 1665 |
+
return S.Zero
|
| 1666 |
+
else:
|
| 1667 |
+
x = sympify(x)
|
| 1668 |
+
if len(previous_terms) > 1:
|
| 1669 |
+
p = previous_terms[-1]
|
| 1670 |
+
return (cbrt(3)*x * Abs(sin(Rational(2, 3)*pi*(n + S.One))) * factorial((n - S.One)/S(3)) /
|
| 1671 |
+
((n + S.One) * Abs(cos(Rational(2, 3)*pi*(n + S.Half))) * factorial((n - 2)/S(3))) * p)
|
| 1672 |
+
else:
|
| 1673 |
+
return (S.One/(root(3, 6)*pi) * gamma((n + S.One)/S(3)) * Abs(sin(Rational(2, 3)*pi*(n + S.One))) /
|
| 1674 |
+
factorial(n) * (cbrt(3)*x)**n)
|
| 1675 |
+
|
| 1676 |
+
def _eval_rewrite_as_besselj(self, z, **kwargs):
|
| 1677 |
+
ot = Rational(1, 3)
|
| 1678 |
+
tt = Rational(2, 3)
|
| 1679 |
+
a = Pow(-z, Rational(3, 2))
|
| 1680 |
+
if re(z).is_negative:
|
| 1681 |
+
return sqrt(-z/3) * (besselj(-ot, tt*a) - besselj(ot, tt*a))
|
| 1682 |
+
|
| 1683 |
+
def _eval_rewrite_as_besseli(self, z, **kwargs):
|
| 1684 |
+
ot = Rational(1, 3)
|
| 1685 |
+
tt = Rational(2, 3)
|
| 1686 |
+
a = Pow(z, Rational(3, 2))
|
| 1687 |
+
if re(z).is_positive:
|
| 1688 |
+
return sqrt(z)/sqrt(3) * (besseli(-ot, tt*a) + besseli(ot, tt*a))
|
| 1689 |
+
else:
|
| 1690 |
+
b = Pow(a, ot)
|
| 1691 |
+
c = Pow(a, -ot)
|
| 1692 |
+
return sqrt(ot)*(b*besseli(-ot, tt*a) + z*c*besseli(ot, tt*a))
|
| 1693 |
+
|
| 1694 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 1695 |
+
pf1 = S.One / (root(3, 6)*gamma(Rational(2, 3)))
|
| 1696 |
+
pf2 = z*root(3, 6) / gamma(Rational(1, 3))
|
| 1697 |
+
return pf1 * hyper([], [Rational(2, 3)], z**3/9) + pf2 * hyper([], [Rational(4, 3)], z**3/9)
|
| 1698 |
+
|
| 1699 |
+
def _eval_expand_func(self, **hints):
|
| 1700 |
+
arg = self.args[0]
|
| 1701 |
+
symbs = arg.free_symbols
|
| 1702 |
+
|
| 1703 |
+
if len(symbs) == 1:
|
| 1704 |
+
z = symbs.pop()
|
| 1705 |
+
c = Wild("c", exclude=[z])
|
| 1706 |
+
d = Wild("d", exclude=[z])
|
| 1707 |
+
m = Wild("m", exclude=[z])
|
| 1708 |
+
n = Wild("n", exclude=[z])
|
| 1709 |
+
M = arg.match(c*(d*z**n)**m)
|
| 1710 |
+
if M is not None:
|
| 1711 |
+
m = M[m]
|
| 1712 |
+
# The transformation is given by 03.06.16.0001.01
|
| 1713 |
+
# https://functions.wolfram.com/Bessel-TypeFunctions/AiryBi/16/01/01/0001/
|
| 1714 |
+
if (3*m).is_integer:
|
| 1715 |
+
c = M[c]
|
| 1716 |
+
d = M[d]
|
| 1717 |
+
n = M[n]
|
| 1718 |
+
pf = (d * z**n)**m / (d**m * z**(m*n))
|
| 1719 |
+
newarg = c * d**m * z**(m*n)
|
| 1720 |
+
return S.Half * (sqrt(3)*(S.One - pf)*airyai(newarg) + (S.One + pf)*airybi(newarg))
|
| 1721 |
+
|
| 1722 |
+
|
| 1723 |
+
class airyaiprime(AiryBase):
|
| 1724 |
+
r"""
|
| 1725 |
+
The derivative $\operatorname{Ai}^\prime$ of the Airy function of the first
|
| 1726 |
+
kind.
|
| 1727 |
+
|
| 1728 |
+
Explanation
|
| 1729 |
+
===========
|
| 1730 |
+
|
| 1731 |
+
The Airy function $\operatorname{Ai}^\prime(z)$ is defined to be the
|
| 1732 |
+
function
|
| 1733 |
+
|
| 1734 |
+
.. math::
|
| 1735 |
+
\operatorname{Ai}^\prime(z) := \frac{\mathrm{d} \operatorname{Ai}(z)}{\mathrm{d} z}.
|
| 1736 |
+
|
| 1737 |
+
Examples
|
| 1738 |
+
========
|
| 1739 |
+
|
| 1740 |
+
Create an Airy function object:
|
| 1741 |
+
|
| 1742 |
+
>>> from sympy import airyaiprime
|
| 1743 |
+
>>> from sympy.abc import z
|
| 1744 |
+
|
| 1745 |
+
>>> airyaiprime(z)
|
| 1746 |
+
airyaiprime(z)
|
| 1747 |
+
|
| 1748 |
+
Several special values are known:
|
| 1749 |
+
|
| 1750 |
+
>>> airyaiprime(0)
|
| 1751 |
+
-3**(2/3)/(3*gamma(1/3))
|
| 1752 |
+
>>> from sympy import oo
|
| 1753 |
+
>>> airyaiprime(oo)
|
| 1754 |
+
0
|
| 1755 |
+
|
| 1756 |
+
The Airy function obeys the mirror symmetry:
|
| 1757 |
+
|
| 1758 |
+
>>> from sympy import conjugate
|
| 1759 |
+
>>> conjugate(airyaiprime(z))
|
| 1760 |
+
airyaiprime(conjugate(z))
|
| 1761 |
+
|
| 1762 |
+
Differentiation with respect to $z$ is supported:
|
| 1763 |
+
|
| 1764 |
+
>>> from sympy import diff
|
| 1765 |
+
>>> diff(airyaiprime(z), z)
|
| 1766 |
+
z*airyai(z)
|
| 1767 |
+
>>> diff(airyaiprime(z), z, 2)
|
| 1768 |
+
z*airyaiprime(z) + airyai(z)
|
| 1769 |
+
|
| 1770 |
+
Series expansion is also supported:
|
| 1771 |
+
|
| 1772 |
+
>>> from sympy import series
|
| 1773 |
+
>>> series(airyaiprime(z), z, 0, 3)
|
| 1774 |
+
-3**(2/3)/(3*gamma(1/3)) + 3**(1/3)*z**2/(6*gamma(2/3)) + O(z**3)
|
| 1775 |
+
|
| 1776 |
+
We can numerically evaluate the Airy function to arbitrary precision
|
| 1777 |
+
on the whole complex plane:
|
| 1778 |
+
|
| 1779 |
+
>>> airyaiprime(-2).evalf(50)
|
| 1780 |
+
0.61825902074169104140626429133247528291577794512415
|
| 1781 |
+
|
| 1782 |
+
Rewrite $\operatorname{Ai}^\prime(z)$ in terms of hypergeometric functions:
|
| 1783 |
+
|
| 1784 |
+
>>> from sympy import hyper
|
| 1785 |
+
>>> airyaiprime(z).rewrite(hyper)
|
| 1786 |
+
3**(1/3)*z**2*hyper((), (5/3,), z**3/9)/(6*gamma(2/3)) - 3**(2/3)*hyper((), (1/3,), z**3/9)/(3*gamma(1/3))
|
| 1787 |
+
|
| 1788 |
+
See Also
|
| 1789 |
+
========
|
| 1790 |
+
|
| 1791 |
+
airyai: Airy function of the first kind.
|
| 1792 |
+
airybi: Airy function of the second kind.
|
| 1793 |
+
airybiprime: Derivative of the Airy function of the second kind.
|
| 1794 |
+
|
| 1795 |
+
References
|
| 1796 |
+
==========
|
| 1797 |
+
|
| 1798 |
+
.. [1] https://en.wikipedia.org/wiki/Airy_function
|
| 1799 |
+
.. [2] https://dlmf.nist.gov/9
|
| 1800 |
+
.. [3] https://encyclopediaofmath.org/wiki/Airy_functions
|
| 1801 |
+
.. [4] https://mathworld.wolfram.com/AiryFunctions.html
|
| 1802 |
+
|
| 1803 |
+
"""
|
| 1804 |
+
|
| 1805 |
+
nargs = 1
|
| 1806 |
+
unbranched = True
|
| 1807 |
+
|
| 1808 |
+
@classmethod
|
| 1809 |
+
def eval(cls, arg):
|
| 1810 |
+
if arg.is_Number:
|
| 1811 |
+
if arg is S.NaN:
|
| 1812 |
+
return S.NaN
|
| 1813 |
+
elif arg is S.Infinity:
|
| 1814 |
+
return S.Zero
|
| 1815 |
+
|
| 1816 |
+
if arg.is_zero:
|
| 1817 |
+
return S.NegativeOne / (3**Rational(1, 3) * gamma(Rational(1, 3)))
|
| 1818 |
+
|
| 1819 |
+
def fdiff(self, argindex=1):
|
| 1820 |
+
if argindex == 1:
|
| 1821 |
+
return self.args[0]*airyai(self.args[0])
|
| 1822 |
+
else:
|
| 1823 |
+
raise ArgumentIndexError(self, argindex)
|
| 1824 |
+
|
| 1825 |
+
def _eval_evalf(self, prec):
|
| 1826 |
+
z = self.args[0]._to_mpmath(prec)
|
| 1827 |
+
with workprec(prec):
|
| 1828 |
+
res = mp.airyai(z, derivative=1)
|
| 1829 |
+
return Expr._from_mpmath(res, prec)
|
| 1830 |
+
|
| 1831 |
+
def _eval_rewrite_as_besselj(self, z, **kwargs):
|
| 1832 |
+
tt = Rational(2, 3)
|
| 1833 |
+
a = Pow(-z, Rational(3, 2))
|
| 1834 |
+
if re(z).is_negative:
|
| 1835 |
+
return z/3 * (besselj(-tt, tt*a) - besselj(tt, tt*a))
|
| 1836 |
+
|
| 1837 |
+
def _eval_rewrite_as_besseli(self, z, **kwargs):
|
| 1838 |
+
ot = Rational(1, 3)
|
| 1839 |
+
tt = Rational(2, 3)
|
| 1840 |
+
a = tt * Pow(z, Rational(3, 2))
|
| 1841 |
+
if re(z).is_positive:
|
| 1842 |
+
return z/3 * (besseli(tt, a) - besseli(-tt, a))
|
| 1843 |
+
else:
|
| 1844 |
+
a = Pow(z, Rational(3, 2))
|
| 1845 |
+
b = Pow(a, tt)
|
| 1846 |
+
c = Pow(a, -tt)
|
| 1847 |
+
return ot * (z**2*c*besseli(tt, tt*a) - b*besseli(-ot, tt*a))
|
| 1848 |
+
|
| 1849 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 1850 |
+
pf1 = z**2 / (2*3**Rational(2, 3)*gamma(Rational(2, 3)))
|
| 1851 |
+
pf2 = 1 / (root(3, 3)*gamma(Rational(1, 3)))
|
| 1852 |
+
return pf1 * hyper([], [Rational(5, 3)], z**3/9) - pf2 * hyper([], [Rational(1, 3)], z**3/9)
|
| 1853 |
+
|
| 1854 |
+
def _eval_expand_func(self, **hints):
|
| 1855 |
+
arg = self.args[0]
|
| 1856 |
+
symbs = arg.free_symbols
|
| 1857 |
+
|
| 1858 |
+
if len(symbs) == 1:
|
| 1859 |
+
z = symbs.pop()
|
| 1860 |
+
c = Wild("c", exclude=[z])
|
| 1861 |
+
d = Wild("d", exclude=[z])
|
| 1862 |
+
m = Wild("m", exclude=[z])
|
| 1863 |
+
n = Wild("n", exclude=[z])
|
| 1864 |
+
M = arg.match(c*(d*z**n)**m)
|
| 1865 |
+
if M is not None:
|
| 1866 |
+
m = M[m]
|
| 1867 |
+
# The transformation is in principle
|
| 1868 |
+
# given by 03.07.16.0001.01 but note
|
| 1869 |
+
# that there is an error in this formula.
|
| 1870 |
+
# https://functions.wolfram.com/Bessel-TypeFunctions/AiryAiPrime/16/01/01/0001/
|
| 1871 |
+
if (3*m).is_integer:
|
| 1872 |
+
c = M[c]
|
| 1873 |
+
d = M[d]
|
| 1874 |
+
n = M[n]
|
| 1875 |
+
pf = (d**m * z**(n*m)) / (d * z**n)**m
|
| 1876 |
+
newarg = c * d**m * z**(n*m)
|
| 1877 |
+
return S.Half * ((pf + S.One)*airyaiprime(newarg) + (pf - S.One)/sqrt(3)*airybiprime(newarg))
|
| 1878 |
+
|
| 1879 |
+
|
| 1880 |
+
class airybiprime(AiryBase):
|
| 1881 |
+
r"""
|
| 1882 |
+
The derivative $\operatorname{Bi}^\prime$ of the Airy function of the first
|
| 1883 |
+
kind.
|
| 1884 |
+
|
| 1885 |
+
Explanation
|
| 1886 |
+
===========
|
| 1887 |
+
|
| 1888 |
+
The Airy function $\operatorname{Bi}^\prime(z)$ is defined to be the
|
| 1889 |
+
function
|
| 1890 |
+
|
| 1891 |
+
.. math::
|
| 1892 |
+
\operatorname{Bi}^\prime(z) := \frac{\mathrm{d} \operatorname{Bi}(z)}{\mathrm{d} z}.
|
| 1893 |
+
|
| 1894 |
+
Examples
|
| 1895 |
+
========
|
| 1896 |
+
|
| 1897 |
+
Create an Airy function object:
|
| 1898 |
+
|
| 1899 |
+
>>> from sympy import airybiprime
|
| 1900 |
+
>>> from sympy.abc import z
|
| 1901 |
+
|
| 1902 |
+
>>> airybiprime(z)
|
| 1903 |
+
airybiprime(z)
|
| 1904 |
+
|
| 1905 |
+
Several special values are known:
|
| 1906 |
+
|
| 1907 |
+
>>> airybiprime(0)
|
| 1908 |
+
3**(1/6)/gamma(1/3)
|
| 1909 |
+
>>> from sympy import oo
|
| 1910 |
+
>>> airybiprime(oo)
|
| 1911 |
+
oo
|
| 1912 |
+
>>> airybiprime(-oo)
|
| 1913 |
+
0
|
| 1914 |
+
|
| 1915 |
+
The Airy function obeys the mirror symmetry:
|
| 1916 |
+
|
| 1917 |
+
>>> from sympy import conjugate
|
| 1918 |
+
>>> conjugate(airybiprime(z))
|
| 1919 |
+
airybiprime(conjugate(z))
|
| 1920 |
+
|
| 1921 |
+
Differentiation with respect to $z$ is supported:
|
| 1922 |
+
|
| 1923 |
+
>>> from sympy import diff
|
| 1924 |
+
>>> diff(airybiprime(z), z)
|
| 1925 |
+
z*airybi(z)
|
| 1926 |
+
>>> diff(airybiprime(z), z, 2)
|
| 1927 |
+
z*airybiprime(z) + airybi(z)
|
| 1928 |
+
|
| 1929 |
+
Series expansion is also supported:
|
| 1930 |
+
|
| 1931 |
+
>>> from sympy import series
|
| 1932 |
+
>>> series(airybiprime(z), z, 0, 3)
|
| 1933 |
+
3**(1/6)/gamma(1/3) + 3**(5/6)*z**2/(6*gamma(2/3)) + O(z**3)
|
| 1934 |
+
|
| 1935 |
+
We can numerically evaluate the Airy function to arbitrary precision
|
| 1936 |
+
on the whole complex plane:
|
| 1937 |
+
|
| 1938 |
+
>>> airybiprime(-2).evalf(50)
|
| 1939 |
+
0.27879516692116952268509756941098324140300059345163
|
| 1940 |
+
|
| 1941 |
+
Rewrite $\operatorname{Bi}^\prime(z)$ in terms of hypergeometric functions:
|
| 1942 |
+
|
| 1943 |
+
>>> from sympy import hyper
|
| 1944 |
+
>>> airybiprime(z).rewrite(hyper)
|
| 1945 |
+
3**(5/6)*z**2*hyper((), (5/3,), z**3/9)/(6*gamma(2/3)) + 3**(1/6)*hyper((), (1/3,), z**3/9)/gamma(1/3)
|
| 1946 |
+
|
| 1947 |
+
See Also
|
| 1948 |
+
========
|
| 1949 |
+
|
| 1950 |
+
airyai: Airy function of the first kind.
|
| 1951 |
+
airybi: Airy function of the second kind.
|
| 1952 |
+
airyaiprime: Derivative of the Airy function of the first kind.
|
| 1953 |
+
|
| 1954 |
+
References
|
| 1955 |
+
==========
|
| 1956 |
+
|
| 1957 |
+
.. [1] https://en.wikipedia.org/wiki/Airy_function
|
| 1958 |
+
.. [2] https://dlmf.nist.gov/9
|
| 1959 |
+
.. [3] https://encyclopediaofmath.org/wiki/Airy_functions
|
| 1960 |
+
.. [4] https://mathworld.wolfram.com/AiryFunctions.html
|
| 1961 |
+
|
| 1962 |
+
"""
|
| 1963 |
+
|
| 1964 |
+
nargs = 1
|
| 1965 |
+
unbranched = True
|
| 1966 |
+
|
| 1967 |
+
@classmethod
|
| 1968 |
+
def eval(cls, arg):
|
| 1969 |
+
if arg.is_Number:
|
| 1970 |
+
if arg is S.NaN:
|
| 1971 |
+
return S.NaN
|
| 1972 |
+
elif arg is S.Infinity:
|
| 1973 |
+
return S.Infinity
|
| 1974 |
+
elif arg is S.NegativeInfinity:
|
| 1975 |
+
return S.Zero
|
| 1976 |
+
elif arg.is_zero:
|
| 1977 |
+
return 3**Rational(1, 6) / gamma(Rational(1, 3))
|
| 1978 |
+
|
| 1979 |
+
if arg.is_zero:
|
| 1980 |
+
return 3**Rational(1, 6) / gamma(Rational(1, 3))
|
| 1981 |
+
|
| 1982 |
+
|
| 1983 |
+
def fdiff(self, argindex=1):
|
| 1984 |
+
if argindex == 1:
|
| 1985 |
+
return self.args[0]*airybi(self.args[0])
|
| 1986 |
+
else:
|
| 1987 |
+
raise ArgumentIndexError(self, argindex)
|
| 1988 |
+
|
| 1989 |
+
def _eval_evalf(self, prec):
|
| 1990 |
+
z = self.args[0]._to_mpmath(prec)
|
| 1991 |
+
with workprec(prec):
|
| 1992 |
+
res = mp.airybi(z, derivative=1)
|
| 1993 |
+
return Expr._from_mpmath(res, prec)
|
| 1994 |
+
|
| 1995 |
+
def _eval_rewrite_as_besselj(self, z, **kwargs):
|
| 1996 |
+
tt = Rational(2, 3)
|
| 1997 |
+
a = tt * Pow(-z, Rational(3, 2))
|
| 1998 |
+
if re(z).is_negative:
|
| 1999 |
+
return -z/sqrt(3) * (besselj(-tt, a) + besselj(tt, a))
|
| 2000 |
+
|
| 2001 |
+
def _eval_rewrite_as_besseli(self, z, **kwargs):
|
| 2002 |
+
ot = Rational(1, 3)
|
| 2003 |
+
tt = Rational(2, 3)
|
| 2004 |
+
a = tt * Pow(z, Rational(3, 2))
|
| 2005 |
+
if re(z).is_positive:
|
| 2006 |
+
return z/sqrt(3) * (besseli(-tt, a) + besseli(tt, a))
|
| 2007 |
+
else:
|
| 2008 |
+
a = Pow(z, Rational(3, 2))
|
| 2009 |
+
b = Pow(a, tt)
|
| 2010 |
+
c = Pow(a, -tt)
|
| 2011 |
+
return sqrt(ot) * (b*besseli(-tt, tt*a) + z**2*c*besseli(tt, tt*a))
|
| 2012 |
+
|
| 2013 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 2014 |
+
pf1 = z**2 / (2*root(3, 6)*gamma(Rational(2, 3)))
|
| 2015 |
+
pf2 = root(3, 6) / gamma(Rational(1, 3))
|
| 2016 |
+
return pf1 * hyper([], [Rational(5, 3)], z**3/9) + pf2 * hyper([], [Rational(1, 3)], z**3/9)
|
| 2017 |
+
|
| 2018 |
+
def _eval_expand_func(self, **hints):
|
| 2019 |
+
arg = self.args[0]
|
| 2020 |
+
symbs = arg.free_symbols
|
| 2021 |
+
|
| 2022 |
+
if len(symbs) == 1:
|
| 2023 |
+
z = symbs.pop()
|
| 2024 |
+
c = Wild("c", exclude=[z])
|
| 2025 |
+
d = Wild("d", exclude=[z])
|
| 2026 |
+
m = Wild("m", exclude=[z])
|
| 2027 |
+
n = Wild("n", exclude=[z])
|
| 2028 |
+
M = arg.match(c*(d*z**n)**m)
|
| 2029 |
+
if M is not None:
|
| 2030 |
+
m = M[m]
|
| 2031 |
+
# The transformation is in principle
|
| 2032 |
+
# given by 03.08.16.0001.01 but note
|
| 2033 |
+
# that there is an error in this formula.
|
| 2034 |
+
# https://functions.wolfram.com/Bessel-TypeFunctions/AiryBiPrime/16/01/01/0001/
|
| 2035 |
+
if (3*m).is_integer:
|
| 2036 |
+
c = M[c]
|
| 2037 |
+
d = M[d]
|
| 2038 |
+
n = M[n]
|
| 2039 |
+
pf = (d**m * z**(n*m)) / (d * z**n)**m
|
| 2040 |
+
newarg = c * d**m * z**(n*m)
|
| 2041 |
+
return S.Half * (sqrt(3)*(pf - S.One)*airyaiprime(newarg) + (pf + S.One)*airybiprime(newarg))
|
| 2042 |
+
|
| 2043 |
+
|
| 2044 |
+
class marcumq(DefinedFunction):
|
| 2045 |
+
r"""
|
| 2046 |
+
The Marcum Q-function.
|
| 2047 |
+
|
| 2048 |
+
Explanation
|
| 2049 |
+
===========
|
| 2050 |
+
|
| 2051 |
+
The Marcum Q-function is defined by the meromorphic continuation of
|
| 2052 |
+
|
| 2053 |
+
.. math::
|
| 2054 |
+
Q_m(a, b) = a^{- m + 1} \int_{b}^{\infty} x^{m} e^{- \frac{a^{2}}{2} - \frac{x^{2}}{2}} I_{m - 1}\left(a x\right)\, dx
|
| 2055 |
+
|
| 2056 |
+
Examples
|
| 2057 |
+
========
|
| 2058 |
+
|
| 2059 |
+
>>> from sympy import marcumq
|
| 2060 |
+
>>> from sympy.abc import m, a, b
|
| 2061 |
+
>>> marcumq(m, a, b)
|
| 2062 |
+
marcumq(m, a, b)
|
| 2063 |
+
|
| 2064 |
+
Special values:
|
| 2065 |
+
|
| 2066 |
+
>>> marcumq(m, 0, b)
|
| 2067 |
+
uppergamma(m, b**2/2)/gamma(m)
|
| 2068 |
+
>>> marcumq(0, 0, 0)
|
| 2069 |
+
0
|
| 2070 |
+
>>> marcumq(0, a, 0)
|
| 2071 |
+
1 - exp(-a**2/2)
|
| 2072 |
+
>>> marcumq(1, a, a)
|
| 2073 |
+
1/2 + exp(-a**2)*besseli(0, a**2)/2
|
| 2074 |
+
>>> marcumq(2, a, a)
|
| 2075 |
+
1/2 + exp(-a**2)*besseli(0, a**2)/2 + exp(-a**2)*besseli(1, a**2)
|
| 2076 |
+
|
| 2077 |
+
Differentiation with respect to $a$ and $b$ is supported:
|
| 2078 |
+
|
| 2079 |
+
>>> from sympy import diff
|
| 2080 |
+
>>> diff(marcumq(m, a, b), a)
|
| 2081 |
+
a*(-marcumq(m, a, b) + marcumq(m + 1, a, b))
|
| 2082 |
+
>>> diff(marcumq(m, a, b), b)
|
| 2083 |
+
-a**(1 - m)*b**m*exp(-a**2/2 - b**2/2)*besseli(m - 1, a*b)
|
| 2084 |
+
|
| 2085 |
+
References
|
| 2086 |
+
==========
|
| 2087 |
+
|
| 2088 |
+
.. [1] https://en.wikipedia.org/wiki/Marcum_Q-function
|
| 2089 |
+
.. [2] https://mathworld.wolfram.com/MarcumQ-Function.html
|
| 2090 |
+
|
| 2091 |
+
"""
|
| 2092 |
+
|
| 2093 |
+
@classmethod
|
| 2094 |
+
def eval(cls, m, a, b):
|
| 2095 |
+
if a is S.Zero:
|
| 2096 |
+
if m is S.Zero and b is S.Zero:
|
| 2097 |
+
return S.Zero
|
| 2098 |
+
return uppergamma(m, b**2 * S.Half) / gamma(m)
|
| 2099 |
+
|
| 2100 |
+
if m is S.Zero and b is S.Zero:
|
| 2101 |
+
return 1 - 1 / exp(a**2 * S.Half)
|
| 2102 |
+
|
| 2103 |
+
if a == b:
|
| 2104 |
+
if m is S.One:
|
| 2105 |
+
return (1 + exp(-a**2) * besseli(0, a**2))*S.Half
|
| 2106 |
+
if m == 2:
|
| 2107 |
+
return S.Half + S.Half * exp(-a**2) * besseli(0, a**2) + exp(-a**2) * besseli(1, a**2)
|
| 2108 |
+
|
| 2109 |
+
if a.is_zero:
|
| 2110 |
+
if m.is_zero and b.is_zero:
|
| 2111 |
+
return S.Zero
|
| 2112 |
+
return uppergamma(m, b**2*S.Half) / gamma(m)
|
| 2113 |
+
|
| 2114 |
+
if m.is_zero and b.is_zero:
|
| 2115 |
+
return 1 - 1 / exp(a**2*S.Half)
|
| 2116 |
+
|
| 2117 |
+
def fdiff(self, argindex=2):
|
| 2118 |
+
m, a, b = self.args
|
| 2119 |
+
if argindex == 2:
|
| 2120 |
+
return a * (-marcumq(m, a, b) + marcumq(1+m, a, b))
|
| 2121 |
+
elif argindex == 3:
|
| 2122 |
+
return (-b**m / a**(m-1)) * exp(-(a**2 + b**2)/2) * besseli(m-1, a*b)
|
| 2123 |
+
else:
|
| 2124 |
+
raise ArgumentIndexError(self, argindex)
|
| 2125 |
+
|
| 2126 |
+
def _eval_rewrite_as_Integral(self, m, a, b, **kwargs):
|
| 2127 |
+
from sympy.integrals.integrals import Integral
|
| 2128 |
+
x = kwargs.get('x', Dummy(uniquely_named_symbol('x').name))
|
| 2129 |
+
return a ** (1 - m) * \
|
| 2130 |
+
Integral(x**m * exp(-(x**2 + a**2)/2) * besseli(m-1, a*x), [x, b, S.Infinity])
|
| 2131 |
+
|
| 2132 |
+
def _eval_rewrite_as_Sum(self, m, a, b, **kwargs):
|
| 2133 |
+
from sympy.concrete.summations import Sum
|
| 2134 |
+
k = kwargs.get('k', Dummy('k'))
|
| 2135 |
+
return exp(-(a**2 + b**2) / 2) * Sum((a/b)**k * besseli(k, a*b), [k, 1-m, S.Infinity])
|
| 2136 |
+
|
| 2137 |
+
def _eval_rewrite_as_besseli(self, m, a, b, **kwargs):
|
| 2138 |
+
if a == b:
|
| 2139 |
+
if m == 1:
|
| 2140 |
+
return (1 + exp(-a**2) * besseli(0, a**2)) / 2
|
| 2141 |
+
if m.is_Integer and m >= 2:
|
| 2142 |
+
s = sum(besseli(i, a**2) for i in range(1, m))
|
| 2143 |
+
return S.Half + exp(-a**2) * besseli(0, a**2) / 2 + exp(-a**2) * s
|
| 2144 |
+
|
| 2145 |
+
def _eval_is_zero(self):
|
| 2146 |
+
if all(arg.is_zero for arg in self.args):
|
| 2147 |
+
return True
|
| 2148 |
+
|
| 2149 |
+
class _besseli(DefinedFunction):
|
| 2150 |
+
"""
|
| 2151 |
+
Helper function to make the $\\mathrm{besseli}(nu, z)$
|
| 2152 |
+
function tractable for the Gruntz algorithm.
|
| 2153 |
+
|
| 2154 |
+
"""
|
| 2155 |
+
|
| 2156 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 2157 |
+
from sympy.functions.combinatorial.factorials import RisingFactorial
|
| 2158 |
+
from sympy.series.order import Order
|
| 2159 |
+
point = args0[1]
|
| 2160 |
+
|
| 2161 |
+
if point in [S.Infinity, S.NegativeInfinity]:
|
| 2162 |
+
nu, z = self.args
|
| 2163 |
+
l = [((RisingFactorial(Rational(2*nu - 1, 2), k)*RisingFactorial(
|
| 2164 |
+
Rational(2*nu + 1, 2), k))/((2)**(k)*z**(Rational(2*k + 1, 2))*factorial(k))) for k in range(n)]
|
| 2165 |
+
return sqrt(pi/(2))*(Add(*l)) + Order(1/z**(Rational(2*n + 1, 2)), x)
|
| 2166 |
+
|
| 2167 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 2168 |
+
|
| 2169 |
+
def _eval_rewrite_as_intractable(self, nu, z, **kwargs):
|
| 2170 |
+
return exp(-z)*besseli(nu, z)
|
| 2171 |
+
|
| 2172 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 2173 |
+
x0 = self.args[0].limit(x, 0)
|
| 2174 |
+
if x0.is_zero:
|
| 2175 |
+
f = self._eval_rewrite_as_intractable(*self.args)
|
| 2176 |
+
return f._eval_nseries(x, n, logx)
|
| 2177 |
+
return super()._eval_nseries(x, n, logx)
|
| 2178 |
+
|
| 2179 |
+
|
| 2180 |
+
class _besselk(DefinedFunction):
|
| 2181 |
+
"""
|
| 2182 |
+
Helper function to make the $\\mathrm{besselk}(nu, z)$
|
| 2183 |
+
function tractable for the Gruntz algorithm.
|
| 2184 |
+
|
| 2185 |
+
"""
|
| 2186 |
+
|
| 2187 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 2188 |
+
from sympy.functions.combinatorial.factorials import RisingFactorial
|
| 2189 |
+
from sympy.series.order import Order
|
| 2190 |
+
point = args0[1]
|
| 2191 |
+
|
| 2192 |
+
if point in [S.Infinity, S.NegativeInfinity]:
|
| 2193 |
+
nu, z = self.args
|
| 2194 |
+
l = [((RisingFactorial(Rational(2*nu - 1, 2), k)*RisingFactorial(
|
| 2195 |
+
Rational(2*nu + 1, 2), k))/((-2)**(k)*z**(Rational(2*k + 1, 2))*factorial(k))) for k in range(n)]
|
| 2196 |
+
return sqrt(pi/(2))*(Add(*l)) + Order(1/z**(Rational(2*n + 1, 2)), x)
|
| 2197 |
+
|
| 2198 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 2199 |
+
|
| 2200 |
+
def _eval_rewrite_as_intractable(self,nu, z, **kwargs):
|
| 2201 |
+
return exp(z)*besselk(nu, z)
|
| 2202 |
+
|
| 2203 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 2204 |
+
x0 = self.args[0].limit(x, 0)
|
| 2205 |
+
if x0.is_zero:
|
| 2206 |
+
f = self._eval_rewrite_as_intractable(*self.args)
|
| 2207 |
+
return f._eval_nseries(x, n, logx)
|
| 2208 |
+
return super()._eval_nseries(x, n, logx)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/beta_functions.py
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S
|
| 2 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 3 |
+
from sympy.core.symbol import Dummy, uniquely_named_symbol
|
| 4 |
+
from sympy.functions.special.gamma_functions import gamma, digamma
|
| 5 |
+
from sympy.functions.combinatorial.numbers import catalan
|
| 6 |
+
from sympy.functions.elementary.complexes import conjugate
|
| 7 |
+
|
| 8 |
+
# See mpmath #569 and SymPy #20569
|
| 9 |
+
def betainc_mpmath_fix(a, b, x1, x2, reg=0):
|
| 10 |
+
from mpmath import betainc, mpf
|
| 11 |
+
if x1 == x2:
|
| 12 |
+
return mpf(0)
|
| 13 |
+
else:
|
| 14 |
+
return betainc(a, b, x1, x2, reg)
|
| 15 |
+
|
| 16 |
+
###############################################################################
|
| 17 |
+
############################ COMPLETE BETA FUNCTION ##########################
|
| 18 |
+
###############################################################################
|
| 19 |
+
|
| 20 |
+
class beta(DefinedFunction):
|
| 21 |
+
r"""
|
| 22 |
+
The beta integral is called the Eulerian integral of the first kind by
|
| 23 |
+
Legendre:
|
| 24 |
+
|
| 25 |
+
.. math::
|
| 26 |
+
\mathrm{B}(x,y) \int^{1}_{0} t^{x-1} (1-t)^{y-1} \mathrm{d}t.
|
| 27 |
+
|
| 28 |
+
Explanation
|
| 29 |
+
===========
|
| 30 |
+
|
| 31 |
+
The Beta function or Euler's first integral is closely associated
|
| 32 |
+
with the gamma function. The Beta function is often used in probability
|
| 33 |
+
theory and mathematical statistics. It satisfies properties like:
|
| 34 |
+
|
| 35 |
+
.. math::
|
| 36 |
+
\mathrm{B}(a,1) = \frac{1}{a} \\
|
| 37 |
+
\mathrm{B}(a,b) = \mathrm{B}(b,a) \\
|
| 38 |
+
\mathrm{B}(a,b) = \frac{\Gamma(a) \Gamma(b)}{\Gamma(a+b)}
|
| 39 |
+
|
| 40 |
+
Therefore for integral values of $a$ and $b$:
|
| 41 |
+
|
| 42 |
+
.. math::
|
| 43 |
+
\mathrm{B} = \frac{(a-1)! (b-1)!}{(a+b-1)!}
|
| 44 |
+
|
| 45 |
+
A special case of the Beta function when `x = y` is the
|
| 46 |
+
Central Beta function. It satisfies properties like:
|
| 47 |
+
|
| 48 |
+
.. math::
|
| 49 |
+
\mathrm{B}(x) = 2^{1 - 2x}\mathrm{B}(x, \frac{1}{2})
|
| 50 |
+
\mathrm{B}(x) = 2^{1 - 2x} cos(\pi x) \mathrm{B}(\frac{1}{2} - x, x)
|
| 51 |
+
\mathrm{B}(x) = \int_{0}^{1} \frac{t^x}{(1 + t)^{2x}} dt
|
| 52 |
+
\mathrm{B}(x) = \frac{2}{x} \prod_{n = 1}^{\infty} \frac{n(n + 2x)}{(n + x)^2}
|
| 53 |
+
|
| 54 |
+
Examples
|
| 55 |
+
========
|
| 56 |
+
|
| 57 |
+
>>> from sympy import I, pi
|
| 58 |
+
>>> from sympy.abc import x, y
|
| 59 |
+
|
| 60 |
+
The Beta function obeys the mirror symmetry:
|
| 61 |
+
|
| 62 |
+
>>> from sympy import beta, conjugate
|
| 63 |
+
>>> conjugate(beta(x, y))
|
| 64 |
+
beta(conjugate(x), conjugate(y))
|
| 65 |
+
|
| 66 |
+
Differentiation with respect to both $x$ and $y$ is supported:
|
| 67 |
+
|
| 68 |
+
>>> from sympy import beta, diff
|
| 69 |
+
>>> diff(beta(x, y), x)
|
| 70 |
+
(polygamma(0, x) - polygamma(0, x + y))*beta(x, y)
|
| 71 |
+
|
| 72 |
+
>>> diff(beta(x, y), y)
|
| 73 |
+
(polygamma(0, y) - polygamma(0, x + y))*beta(x, y)
|
| 74 |
+
|
| 75 |
+
>>> diff(beta(x), x)
|
| 76 |
+
2*(polygamma(0, x) - polygamma(0, 2*x))*beta(x, x)
|
| 77 |
+
|
| 78 |
+
We can numerically evaluate the Beta function to
|
| 79 |
+
arbitrary precision for any complex numbers x and y:
|
| 80 |
+
|
| 81 |
+
>>> from sympy import beta
|
| 82 |
+
>>> beta(pi).evalf(40)
|
| 83 |
+
0.02671848900111377452242355235388489324562
|
| 84 |
+
|
| 85 |
+
>>> beta(1 + I).evalf(20)
|
| 86 |
+
-0.2112723729365330143 - 0.7655283165378005676*I
|
| 87 |
+
|
| 88 |
+
See Also
|
| 89 |
+
========
|
| 90 |
+
|
| 91 |
+
gamma: Gamma function.
|
| 92 |
+
uppergamma: Upper incomplete gamma function.
|
| 93 |
+
lowergamma: Lower incomplete gamma function.
|
| 94 |
+
polygamma: Polygamma function.
|
| 95 |
+
loggamma: Log Gamma function.
|
| 96 |
+
digamma: Digamma function.
|
| 97 |
+
trigamma: Trigamma function.
|
| 98 |
+
|
| 99 |
+
References
|
| 100 |
+
==========
|
| 101 |
+
|
| 102 |
+
.. [1] https://en.wikipedia.org/wiki/Beta_function
|
| 103 |
+
.. [2] https://mathworld.wolfram.com/BetaFunction.html
|
| 104 |
+
.. [3] https://dlmf.nist.gov/5.12
|
| 105 |
+
|
| 106 |
+
"""
|
| 107 |
+
unbranched = True
|
| 108 |
+
|
| 109 |
+
def fdiff(self, argindex):
|
| 110 |
+
x, y = self.args
|
| 111 |
+
if argindex == 1:
|
| 112 |
+
# Diff wrt x
|
| 113 |
+
return beta(x, y)*(digamma(x) - digamma(x + y))
|
| 114 |
+
elif argindex == 2:
|
| 115 |
+
# Diff wrt y
|
| 116 |
+
return beta(x, y)*(digamma(y) - digamma(x + y))
|
| 117 |
+
else:
|
| 118 |
+
raise ArgumentIndexError(self, argindex)
|
| 119 |
+
|
| 120 |
+
@classmethod
|
| 121 |
+
def eval(cls, x, y=None):
|
| 122 |
+
if y is None:
|
| 123 |
+
return beta(x, x)
|
| 124 |
+
if x.is_Number and y.is_Number:
|
| 125 |
+
return beta(x, y, evaluate=False).doit()
|
| 126 |
+
|
| 127 |
+
def doit(self, **hints):
|
| 128 |
+
x = xold = self.args[0]
|
| 129 |
+
# Deal with unevaluated single argument beta
|
| 130 |
+
single_argument = len(self.args) == 1
|
| 131 |
+
y = yold = self.args[0] if single_argument else self.args[1]
|
| 132 |
+
if hints.get('deep', True):
|
| 133 |
+
x = x.doit(**hints)
|
| 134 |
+
y = y.doit(**hints)
|
| 135 |
+
if y.is_zero or x.is_zero:
|
| 136 |
+
return S.ComplexInfinity
|
| 137 |
+
if y is S.One:
|
| 138 |
+
return 1/x
|
| 139 |
+
if x is S.One:
|
| 140 |
+
return 1/y
|
| 141 |
+
if y == x + 1:
|
| 142 |
+
return 1/(x*y*catalan(x))
|
| 143 |
+
s = x + y
|
| 144 |
+
if (s.is_integer and s.is_negative and x.is_integer is False and
|
| 145 |
+
y.is_integer is False):
|
| 146 |
+
return S.Zero
|
| 147 |
+
if x == xold and y == yold and not single_argument:
|
| 148 |
+
return self
|
| 149 |
+
return beta(x, y)
|
| 150 |
+
|
| 151 |
+
def _eval_expand_func(self, **hints):
|
| 152 |
+
x, y = self.args
|
| 153 |
+
return gamma(x)*gamma(y) / gamma(x + y)
|
| 154 |
+
|
| 155 |
+
def _eval_is_real(self):
|
| 156 |
+
return self.args[0].is_real and self.args[1].is_real
|
| 157 |
+
|
| 158 |
+
def _eval_conjugate(self):
|
| 159 |
+
return self.func(self.args[0].conjugate(), self.args[1].conjugate())
|
| 160 |
+
|
| 161 |
+
def _eval_rewrite_as_gamma(self, x, y, piecewise=True, **kwargs):
|
| 162 |
+
return self._eval_expand_func(**kwargs)
|
| 163 |
+
|
| 164 |
+
def _eval_rewrite_as_Integral(self, x, y, **kwargs):
|
| 165 |
+
from sympy.integrals.integrals import Integral
|
| 166 |
+
t = Dummy(uniquely_named_symbol('t', [x, y]).name)
|
| 167 |
+
return Integral(t**(x - 1)*(1 - t)**(y - 1), (t, 0, 1))
|
| 168 |
+
|
| 169 |
+
###############################################################################
|
| 170 |
+
########################## INCOMPLETE BETA FUNCTION ###########################
|
| 171 |
+
###############################################################################
|
| 172 |
+
|
| 173 |
+
class betainc(DefinedFunction):
|
| 174 |
+
r"""
|
| 175 |
+
The Generalized Incomplete Beta function is defined as
|
| 176 |
+
|
| 177 |
+
.. math::
|
| 178 |
+
\mathrm{B}_{(x_1, x_2)}(a, b) = \int_{x_1}^{x_2} t^{a - 1} (1 - t)^{b - 1} dt
|
| 179 |
+
|
| 180 |
+
The Incomplete Beta function is a special case
|
| 181 |
+
of the Generalized Incomplete Beta function :
|
| 182 |
+
|
| 183 |
+
.. math:: \mathrm{B}_z (a, b) = \mathrm{B}_{(0, z)}(a, b)
|
| 184 |
+
|
| 185 |
+
The Incomplete Beta function satisfies :
|
| 186 |
+
|
| 187 |
+
.. math:: \mathrm{B}_z (a, b) = (-1)^a \mathrm{B}_{\frac{z}{z - 1}} (a, 1 - a - b)
|
| 188 |
+
|
| 189 |
+
The Beta function is a special case of the Incomplete Beta function :
|
| 190 |
+
|
| 191 |
+
.. math:: \mathrm{B}(a, b) = \mathrm{B}_{1}(a, b)
|
| 192 |
+
|
| 193 |
+
Examples
|
| 194 |
+
========
|
| 195 |
+
|
| 196 |
+
>>> from sympy import betainc, symbols, conjugate
|
| 197 |
+
>>> a, b, x, x1, x2 = symbols('a b x x1 x2')
|
| 198 |
+
|
| 199 |
+
The Generalized Incomplete Beta function is given by:
|
| 200 |
+
|
| 201 |
+
>>> betainc(a, b, x1, x2)
|
| 202 |
+
betainc(a, b, x1, x2)
|
| 203 |
+
|
| 204 |
+
The Incomplete Beta function can be obtained as follows:
|
| 205 |
+
|
| 206 |
+
>>> betainc(a, b, 0, x)
|
| 207 |
+
betainc(a, b, 0, x)
|
| 208 |
+
|
| 209 |
+
The Incomplete Beta function obeys the mirror symmetry:
|
| 210 |
+
|
| 211 |
+
>>> conjugate(betainc(a, b, x1, x2))
|
| 212 |
+
betainc(conjugate(a), conjugate(b), conjugate(x1), conjugate(x2))
|
| 213 |
+
|
| 214 |
+
We can numerically evaluate the Incomplete Beta function to
|
| 215 |
+
arbitrary precision for any complex numbers a, b, x1 and x2:
|
| 216 |
+
|
| 217 |
+
>>> from sympy import betainc, I
|
| 218 |
+
>>> betainc(2, 3, 4, 5).evalf(10)
|
| 219 |
+
56.08333333
|
| 220 |
+
>>> betainc(0.75, 1 - 4*I, 0, 2 + 3*I).evalf(25)
|
| 221 |
+
0.2241657956955709603655887 + 0.3619619242700451992411724*I
|
| 222 |
+
|
| 223 |
+
The Generalized Incomplete Beta function can be expressed
|
| 224 |
+
in terms of the Generalized Hypergeometric function.
|
| 225 |
+
|
| 226 |
+
>>> from sympy import hyper
|
| 227 |
+
>>> betainc(a, b, x1, x2).rewrite(hyper)
|
| 228 |
+
(-x1**a*hyper((a, 1 - b), (a + 1,), x1) + x2**a*hyper((a, 1 - b), (a + 1,), x2))/a
|
| 229 |
+
|
| 230 |
+
See Also
|
| 231 |
+
========
|
| 232 |
+
|
| 233 |
+
beta: Beta function
|
| 234 |
+
hyper: Generalized Hypergeometric function
|
| 235 |
+
|
| 236 |
+
References
|
| 237 |
+
==========
|
| 238 |
+
|
| 239 |
+
.. [1] https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function
|
| 240 |
+
.. [2] https://dlmf.nist.gov/8.17
|
| 241 |
+
.. [3] https://functions.wolfram.com/GammaBetaErf/Beta4/
|
| 242 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/BetaRegularized4/02/
|
| 243 |
+
|
| 244 |
+
"""
|
| 245 |
+
nargs = 4
|
| 246 |
+
unbranched = True
|
| 247 |
+
|
| 248 |
+
def fdiff(self, argindex):
|
| 249 |
+
a, b, x1, x2 = self.args
|
| 250 |
+
if argindex == 3:
|
| 251 |
+
# Diff wrt x1
|
| 252 |
+
return -(1 - x1)**(b - 1)*x1**(a - 1)
|
| 253 |
+
elif argindex == 4:
|
| 254 |
+
# Diff wrt x2
|
| 255 |
+
return (1 - x2)**(b - 1)*x2**(a - 1)
|
| 256 |
+
else:
|
| 257 |
+
raise ArgumentIndexError(self, argindex)
|
| 258 |
+
|
| 259 |
+
def _eval_mpmath(self):
|
| 260 |
+
return betainc_mpmath_fix, self.args
|
| 261 |
+
|
| 262 |
+
def _eval_is_real(self):
|
| 263 |
+
if all(arg.is_real for arg in self.args):
|
| 264 |
+
return True
|
| 265 |
+
|
| 266 |
+
def _eval_conjugate(self):
|
| 267 |
+
return self.func(*map(conjugate, self.args))
|
| 268 |
+
|
| 269 |
+
def _eval_rewrite_as_Integral(self, a, b, x1, x2, **kwargs):
|
| 270 |
+
from sympy.integrals.integrals import Integral
|
| 271 |
+
t = Dummy(uniquely_named_symbol('t', [a, b, x1, x2]).name)
|
| 272 |
+
return Integral(t**(a - 1)*(1 - t)**(b - 1), (t, x1, x2))
|
| 273 |
+
|
| 274 |
+
def _eval_rewrite_as_hyper(self, a, b, x1, x2, **kwargs):
|
| 275 |
+
from sympy.functions.special.hyper import hyper
|
| 276 |
+
return (x2**a * hyper((a, 1 - b), (a + 1,), x2) - x1**a * hyper((a, 1 - b), (a + 1,), x1)) / a
|
| 277 |
+
|
| 278 |
+
###############################################################################
|
| 279 |
+
#################### REGULARIZED INCOMPLETE BETA FUNCTION #####################
|
| 280 |
+
###############################################################################
|
| 281 |
+
|
| 282 |
+
class betainc_regularized(DefinedFunction):
|
| 283 |
+
r"""
|
| 284 |
+
The Generalized Regularized Incomplete Beta function is given by
|
| 285 |
+
|
| 286 |
+
.. math::
|
| 287 |
+
\mathrm{I}_{(x_1, x_2)}(a, b) = \frac{\mathrm{B}_{(x_1, x_2)}(a, b)}{\mathrm{B}(a, b)}
|
| 288 |
+
|
| 289 |
+
The Regularized Incomplete Beta function is a special case
|
| 290 |
+
of the Generalized Regularized Incomplete Beta function :
|
| 291 |
+
|
| 292 |
+
.. math:: \mathrm{I}_z (a, b) = \mathrm{I}_{(0, z)}(a, b)
|
| 293 |
+
|
| 294 |
+
The Regularized Incomplete Beta function is the cumulative distribution
|
| 295 |
+
function of the beta distribution.
|
| 296 |
+
|
| 297 |
+
Examples
|
| 298 |
+
========
|
| 299 |
+
|
| 300 |
+
>>> from sympy import betainc_regularized, symbols, conjugate
|
| 301 |
+
>>> a, b, x, x1, x2 = symbols('a b x x1 x2')
|
| 302 |
+
|
| 303 |
+
The Generalized Regularized Incomplete Beta
|
| 304 |
+
function is given by:
|
| 305 |
+
|
| 306 |
+
>>> betainc_regularized(a, b, x1, x2)
|
| 307 |
+
betainc_regularized(a, b, x1, x2)
|
| 308 |
+
|
| 309 |
+
The Regularized Incomplete Beta function
|
| 310 |
+
can be obtained as follows:
|
| 311 |
+
|
| 312 |
+
>>> betainc_regularized(a, b, 0, x)
|
| 313 |
+
betainc_regularized(a, b, 0, x)
|
| 314 |
+
|
| 315 |
+
The Regularized Incomplete Beta function
|
| 316 |
+
obeys the mirror symmetry:
|
| 317 |
+
|
| 318 |
+
>>> conjugate(betainc_regularized(a, b, x1, x2))
|
| 319 |
+
betainc_regularized(conjugate(a), conjugate(b), conjugate(x1), conjugate(x2))
|
| 320 |
+
|
| 321 |
+
We can numerically evaluate the Regularized Incomplete Beta function
|
| 322 |
+
to arbitrary precision for any complex numbers a, b, x1 and x2:
|
| 323 |
+
|
| 324 |
+
>>> from sympy import betainc_regularized, pi, E
|
| 325 |
+
>>> betainc_regularized(1, 2, 0, 0.25).evalf(10)
|
| 326 |
+
0.4375000000
|
| 327 |
+
>>> betainc_regularized(pi, E, 0, 1).evalf(5)
|
| 328 |
+
1.00000
|
| 329 |
+
|
| 330 |
+
The Generalized Regularized Incomplete Beta function can be
|
| 331 |
+
expressed in terms of the Generalized Hypergeometric function.
|
| 332 |
+
|
| 333 |
+
>>> from sympy import hyper
|
| 334 |
+
>>> betainc_regularized(a, b, x1, x2).rewrite(hyper)
|
| 335 |
+
(-x1**a*hyper((a, 1 - b), (a + 1,), x1) + x2**a*hyper((a, 1 - b), (a + 1,), x2))/(a*beta(a, b))
|
| 336 |
+
|
| 337 |
+
See Also
|
| 338 |
+
========
|
| 339 |
+
|
| 340 |
+
beta: Beta function
|
| 341 |
+
hyper: Generalized Hypergeometric function
|
| 342 |
+
|
| 343 |
+
References
|
| 344 |
+
==========
|
| 345 |
+
|
| 346 |
+
.. [1] https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function
|
| 347 |
+
.. [2] https://dlmf.nist.gov/8.17
|
| 348 |
+
.. [3] https://functions.wolfram.com/GammaBetaErf/Beta4/
|
| 349 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/BetaRegularized4/02/
|
| 350 |
+
|
| 351 |
+
"""
|
| 352 |
+
nargs = 4
|
| 353 |
+
unbranched = True
|
| 354 |
+
|
| 355 |
+
def __new__(cls, a, b, x1, x2):
|
| 356 |
+
return super().__new__(cls, a, b, x1, x2)
|
| 357 |
+
|
| 358 |
+
def _eval_mpmath(self):
|
| 359 |
+
return betainc_mpmath_fix, (*self.args, S(1))
|
| 360 |
+
|
| 361 |
+
def fdiff(self, argindex):
|
| 362 |
+
a, b, x1, x2 = self.args
|
| 363 |
+
if argindex == 3:
|
| 364 |
+
# Diff wrt x1
|
| 365 |
+
return -(1 - x1)**(b - 1)*x1**(a - 1) / beta(a, b)
|
| 366 |
+
elif argindex == 4:
|
| 367 |
+
# Diff wrt x2
|
| 368 |
+
return (1 - x2)**(b - 1)*x2**(a - 1) / beta(a, b)
|
| 369 |
+
else:
|
| 370 |
+
raise ArgumentIndexError(self, argindex)
|
| 371 |
+
|
| 372 |
+
def _eval_is_real(self):
|
| 373 |
+
if all(arg.is_real for arg in self.args):
|
| 374 |
+
return True
|
| 375 |
+
|
| 376 |
+
def _eval_conjugate(self):
|
| 377 |
+
return self.func(*map(conjugate, self.args))
|
| 378 |
+
|
| 379 |
+
def _eval_rewrite_as_Integral(self, a, b, x1, x2, **kwargs):
|
| 380 |
+
from sympy.integrals.integrals import Integral
|
| 381 |
+
t = Dummy(uniquely_named_symbol('t', [a, b, x1, x2]).name)
|
| 382 |
+
integrand = t**(a - 1)*(1 - t)**(b - 1)
|
| 383 |
+
expr = Integral(integrand, (t, x1, x2))
|
| 384 |
+
return expr / Integral(integrand, (t, 0, 1))
|
| 385 |
+
|
| 386 |
+
def _eval_rewrite_as_hyper(self, a, b, x1, x2, **kwargs):
|
| 387 |
+
from sympy.functions.special.hyper import hyper
|
| 388 |
+
expr = (x2**a * hyper((a, 1 - b), (a + 1,), x2) - x1**a * hyper((a, 1 - b), (a + 1,), x1)) / a
|
| 389 |
+
return expr / beta(a, b)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/bsplines.py
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S, sympify
|
| 2 |
+
from sympy.core.symbol import (Dummy, symbols)
|
| 3 |
+
from sympy.functions import Piecewise, piecewise_fold
|
| 4 |
+
from sympy.logic.boolalg import And
|
| 5 |
+
from sympy.sets.sets import Interval
|
| 6 |
+
|
| 7 |
+
from functools import lru_cache
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def _ivl(cond, x):
|
| 11 |
+
"""return the interval corresponding to the condition
|
| 12 |
+
|
| 13 |
+
Conditions in spline's Piecewise give the range over
|
| 14 |
+
which an expression is valid like (lo <= x) & (x <= hi).
|
| 15 |
+
This function returns (lo, hi).
|
| 16 |
+
"""
|
| 17 |
+
if isinstance(cond, And) and len(cond.args) == 2:
|
| 18 |
+
a, b = cond.args
|
| 19 |
+
if a.lts == x:
|
| 20 |
+
a, b = b, a
|
| 21 |
+
return a.lts, b.gts
|
| 22 |
+
raise TypeError('unexpected cond type: %s' % cond)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _add_splines(c, b1, d, b2, x):
|
| 26 |
+
"""Construct c*b1 + d*b2."""
|
| 27 |
+
|
| 28 |
+
if S.Zero in (b1, c):
|
| 29 |
+
rv = piecewise_fold(d * b2)
|
| 30 |
+
elif S.Zero in (b2, d):
|
| 31 |
+
rv = piecewise_fold(c * b1)
|
| 32 |
+
else:
|
| 33 |
+
new_args = []
|
| 34 |
+
# Just combining the Piecewise without any fancy optimization
|
| 35 |
+
p1 = piecewise_fold(c * b1)
|
| 36 |
+
p2 = piecewise_fold(d * b2)
|
| 37 |
+
|
| 38 |
+
# Search all Piecewise arguments except (0, True)
|
| 39 |
+
p2args = list(p2.args[:-1])
|
| 40 |
+
|
| 41 |
+
# This merging algorithm assumes the conditions in
|
| 42 |
+
# p1 and p2 are sorted
|
| 43 |
+
for arg in p1.args[:-1]:
|
| 44 |
+
expr = arg.expr
|
| 45 |
+
cond = arg.cond
|
| 46 |
+
|
| 47 |
+
lower = _ivl(cond, x)[0]
|
| 48 |
+
|
| 49 |
+
# Check p2 for matching conditions that can be merged
|
| 50 |
+
for i, arg2 in enumerate(p2args):
|
| 51 |
+
expr2 = arg2.expr
|
| 52 |
+
cond2 = arg2.cond
|
| 53 |
+
|
| 54 |
+
lower_2, upper_2 = _ivl(cond2, x)
|
| 55 |
+
if cond2 == cond:
|
| 56 |
+
# Conditions match, join expressions
|
| 57 |
+
expr += expr2
|
| 58 |
+
# Remove matching element
|
| 59 |
+
del p2args[i]
|
| 60 |
+
# No need to check the rest
|
| 61 |
+
break
|
| 62 |
+
elif lower_2 < lower and upper_2 <= lower:
|
| 63 |
+
# Check if arg2 condition smaller than arg1,
|
| 64 |
+
# add to new_args by itself (no match expected
|
| 65 |
+
# in p1)
|
| 66 |
+
new_args.append(arg2)
|
| 67 |
+
del p2args[i]
|
| 68 |
+
break
|
| 69 |
+
|
| 70 |
+
# Checked all, add expr and cond
|
| 71 |
+
new_args.append((expr, cond))
|
| 72 |
+
|
| 73 |
+
# Add remaining items from p2args
|
| 74 |
+
new_args.extend(p2args)
|
| 75 |
+
|
| 76 |
+
# Add final (0, True)
|
| 77 |
+
new_args.append((0, True))
|
| 78 |
+
|
| 79 |
+
rv = Piecewise(*new_args, evaluate=False)
|
| 80 |
+
|
| 81 |
+
return rv.expand()
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
@lru_cache(maxsize=128)
|
| 85 |
+
def bspline_basis(d, knots, n, x):
|
| 86 |
+
"""
|
| 87 |
+
The $n$-th B-spline at $x$ of degree $d$ with knots.
|
| 88 |
+
|
| 89 |
+
Explanation
|
| 90 |
+
===========
|
| 91 |
+
|
| 92 |
+
B-Splines are piecewise polynomials of degree $d$. They are defined on a
|
| 93 |
+
set of knots, which is a sequence of integers or floats.
|
| 94 |
+
|
| 95 |
+
Examples
|
| 96 |
+
========
|
| 97 |
+
|
| 98 |
+
The 0th degree splines have a value of 1 on a single interval:
|
| 99 |
+
|
| 100 |
+
>>> from sympy import bspline_basis
|
| 101 |
+
>>> from sympy.abc import x
|
| 102 |
+
>>> d = 0
|
| 103 |
+
>>> knots = tuple(range(5))
|
| 104 |
+
>>> bspline_basis(d, knots, 0, x)
|
| 105 |
+
Piecewise((1, (x >= 0) & (x <= 1)), (0, True))
|
| 106 |
+
|
| 107 |
+
For a given ``(d, knots)`` there are ``len(knots)-d-1`` B-splines
|
| 108 |
+
defined, that are indexed by ``n`` (starting at 0).
|
| 109 |
+
|
| 110 |
+
Here is an example of a cubic B-spline:
|
| 111 |
+
|
| 112 |
+
>>> bspline_basis(3, tuple(range(5)), 0, x)
|
| 113 |
+
Piecewise((x**3/6, (x >= 0) & (x <= 1)),
|
| 114 |
+
(-x**3/2 + 2*x**2 - 2*x + 2/3,
|
| 115 |
+
(x >= 1) & (x <= 2)),
|
| 116 |
+
(x**3/2 - 4*x**2 + 10*x - 22/3,
|
| 117 |
+
(x >= 2) & (x <= 3)),
|
| 118 |
+
(-x**3/6 + 2*x**2 - 8*x + 32/3,
|
| 119 |
+
(x >= 3) & (x <= 4)),
|
| 120 |
+
(0, True))
|
| 121 |
+
|
| 122 |
+
By repeating knot points, you can introduce discontinuities in the
|
| 123 |
+
B-splines and their derivatives:
|
| 124 |
+
|
| 125 |
+
>>> d = 1
|
| 126 |
+
>>> knots = (0, 0, 2, 3, 4)
|
| 127 |
+
>>> bspline_basis(d, knots, 0, x)
|
| 128 |
+
Piecewise((1 - x/2, (x >= 0) & (x <= 2)), (0, True))
|
| 129 |
+
|
| 130 |
+
It is quite time consuming to construct and evaluate B-splines. If
|
| 131 |
+
you need to evaluate a B-spline many times, it is best to lambdify them
|
| 132 |
+
first:
|
| 133 |
+
|
| 134 |
+
>>> from sympy import lambdify
|
| 135 |
+
>>> d = 3
|
| 136 |
+
>>> knots = tuple(range(10))
|
| 137 |
+
>>> b0 = bspline_basis(d, knots, 0, x)
|
| 138 |
+
>>> f = lambdify(x, b0)
|
| 139 |
+
>>> y = f(0.5)
|
| 140 |
+
|
| 141 |
+
Parameters
|
| 142 |
+
==========
|
| 143 |
+
|
| 144 |
+
d : integer
|
| 145 |
+
degree of bspline
|
| 146 |
+
|
| 147 |
+
knots : list of integer values
|
| 148 |
+
list of knots points of bspline
|
| 149 |
+
|
| 150 |
+
n : integer
|
| 151 |
+
$n$-th B-spline
|
| 152 |
+
|
| 153 |
+
x : symbol
|
| 154 |
+
|
| 155 |
+
See Also
|
| 156 |
+
========
|
| 157 |
+
|
| 158 |
+
bspline_basis_set
|
| 159 |
+
|
| 160 |
+
References
|
| 161 |
+
==========
|
| 162 |
+
|
| 163 |
+
.. [1] https://en.wikipedia.org/wiki/B-spline
|
| 164 |
+
|
| 165 |
+
"""
|
| 166 |
+
# make sure x has no assumptions so conditions don't evaluate
|
| 167 |
+
xvar = x
|
| 168 |
+
x = Dummy()
|
| 169 |
+
|
| 170 |
+
knots = tuple(sympify(k) for k in knots)
|
| 171 |
+
d = int(d)
|
| 172 |
+
n = int(n)
|
| 173 |
+
n_knots = len(knots)
|
| 174 |
+
n_intervals = n_knots - 1
|
| 175 |
+
if n + d + 1 > n_intervals:
|
| 176 |
+
raise ValueError("n + d + 1 must not exceed len(knots) - 1")
|
| 177 |
+
if d == 0:
|
| 178 |
+
result = Piecewise(
|
| 179 |
+
(S.One, Interval(knots[n], knots[n + 1]).contains(x)), (0, True)
|
| 180 |
+
)
|
| 181 |
+
elif d > 0:
|
| 182 |
+
denom = knots[n + d + 1] - knots[n + 1]
|
| 183 |
+
if denom != S.Zero:
|
| 184 |
+
B = (knots[n + d + 1] - x) / denom
|
| 185 |
+
b2 = bspline_basis(d - 1, knots, n + 1, x)
|
| 186 |
+
else:
|
| 187 |
+
b2 = B = S.Zero
|
| 188 |
+
|
| 189 |
+
denom = knots[n + d] - knots[n]
|
| 190 |
+
if denom != S.Zero:
|
| 191 |
+
A = (x - knots[n]) / denom
|
| 192 |
+
b1 = bspline_basis(d - 1, knots, n, x)
|
| 193 |
+
else:
|
| 194 |
+
b1 = A = S.Zero
|
| 195 |
+
|
| 196 |
+
result = _add_splines(A, b1, B, b2, x)
|
| 197 |
+
else:
|
| 198 |
+
raise ValueError("degree must be non-negative: %r" % n)
|
| 199 |
+
|
| 200 |
+
# return result with user-given x
|
| 201 |
+
return result.xreplace({x: xvar})
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def bspline_basis_set(d, knots, x):
|
| 205 |
+
"""
|
| 206 |
+
Return the ``len(knots)-d-1`` B-splines at *x* of degree *d*
|
| 207 |
+
with *knots*.
|
| 208 |
+
|
| 209 |
+
Explanation
|
| 210 |
+
===========
|
| 211 |
+
|
| 212 |
+
This function returns a list of piecewise polynomials that are the
|
| 213 |
+
``len(knots)-d-1`` B-splines of degree *d* for the given knots.
|
| 214 |
+
This function calls ``bspline_basis(d, knots, n, x)`` for different
|
| 215 |
+
values of *n*.
|
| 216 |
+
|
| 217 |
+
Examples
|
| 218 |
+
========
|
| 219 |
+
|
| 220 |
+
>>> from sympy import bspline_basis_set
|
| 221 |
+
>>> from sympy.abc import x
|
| 222 |
+
>>> d = 2
|
| 223 |
+
>>> knots = range(5)
|
| 224 |
+
>>> splines = bspline_basis_set(d, knots, x)
|
| 225 |
+
>>> splines
|
| 226 |
+
[Piecewise((x**2/2, (x >= 0) & (x <= 1)),
|
| 227 |
+
(-x**2 + 3*x - 3/2, (x >= 1) & (x <= 2)),
|
| 228 |
+
(x**2/2 - 3*x + 9/2, (x >= 2) & (x <= 3)),
|
| 229 |
+
(0, True)),
|
| 230 |
+
Piecewise((x**2/2 - x + 1/2, (x >= 1) & (x <= 2)),
|
| 231 |
+
(-x**2 + 5*x - 11/2, (x >= 2) & (x <= 3)),
|
| 232 |
+
(x**2/2 - 4*x + 8, (x >= 3) & (x <= 4)),
|
| 233 |
+
(0, True))]
|
| 234 |
+
|
| 235 |
+
Parameters
|
| 236 |
+
==========
|
| 237 |
+
|
| 238 |
+
d : integer
|
| 239 |
+
degree of bspline
|
| 240 |
+
|
| 241 |
+
knots : list of integers
|
| 242 |
+
list of knots points of bspline
|
| 243 |
+
|
| 244 |
+
x : symbol
|
| 245 |
+
|
| 246 |
+
See Also
|
| 247 |
+
========
|
| 248 |
+
|
| 249 |
+
bspline_basis
|
| 250 |
+
|
| 251 |
+
"""
|
| 252 |
+
n_splines = len(knots) - d - 1
|
| 253 |
+
return [bspline_basis(d, tuple(knots), i, x) for i in range(n_splines)]
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def interpolating_spline(d, x, X, Y):
|
| 257 |
+
"""
|
| 258 |
+
Return spline of degree *d*, passing through the given *X*
|
| 259 |
+
and *Y* values.
|
| 260 |
+
|
| 261 |
+
Explanation
|
| 262 |
+
===========
|
| 263 |
+
|
| 264 |
+
This function returns a piecewise function such that each part is
|
| 265 |
+
a polynomial of degree not greater than *d*. The value of *d*
|
| 266 |
+
must be 1 or greater and the values of *X* must be strictly
|
| 267 |
+
increasing.
|
| 268 |
+
|
| 269 |
+
Examples
|
| 270 |
+
========
|
| 271 |
+
|
| 272 |
+
>>> from sympy import interpolating_spline
|
| 273 |
+
>>> from sympy.abc import x
|
| 274 |
+
>>> interpolating_spline(1, x, [1, 2, 4, 7], [3, 6, 5, 7])
|
| 275 |
+
Piecewise((3*x, (x >= 1) & (x <= 2)),
|
| 276 |
+
(7 - x/2, (x >= 2) & (x <= 4)),
|
| 277 |
+
(2*x/3 + 7/3, (x >= 4) & (x <= 7)))
|
| 278 |
+
>>> interpolating_spline(3, x, [-2, 0, 1, 3, 4], [4, 2, 1, 1, 3])
|
| 279 |
+
Piecewise((7*x**3/117 + 7*x**2/117 - 131*x/117 + 2, (x >= -2) & (x <= 1)),
|
| 280 |
+
(10*x**3/117 - 2*x**2/117 - 122*x/117 + 77/39, (x >= 1) & (x <= 4)))
|
| 281 |
+
|
| 282 |
+
Parameters
|
| 283 |
+
==========
|
| 284 |
+
|
| 285 |
+
d : integer
|
| 286 |
+
Degree of Bspline strictly greater than equal to one
|
| 287 |
+
|
| 288 |
+
x : symbol
|
| 289 |
+
|
| 290 |
+
X : list of strictly increasing real values
|
| 291 |
+
list of X coordinates through which the spline passes
|
| 292 |
+
|
| 293 |
+
Y : list of real values
|
| 294 |
+
list of corresponding Y coordinates through which the spline passes
|
| 295 |
+
|
| 296 |
+
See Also
|
| 297 |
+
========
|
| 298 |
+
|
| 299 |
+
bspline_basis_set, interpolating_poly
|
| 300 |
+
|
| 301 |
+
"""
|
| 302 |
+
from sympy.solvers.solveset import linsolve
|
| 303 |
+
from sympy.matrices.dense import Matrix
|
| 304 |
+
|
| 305 |
+
# Input sanitization
|
| 306 |
+
d = sympify(d)
|
| 307 |
+
if not (d.is_Integer and d.is_positive):
|
| 308 |
+
raise ValueError("Spline degree must be a positive integer, not %s." % d)
|
| 309 |
+
if len(X) != len(Y):
|
| 310 |
+
raise ValueError("Number of X and Y coordinates must be the same.")
|
| 311 |
+
if len(X) < d + 1:
|
| 312 |
+
raise ValueError("Degree must be less than the number of control points.")
|
| 313 |
+
if not all(a < b for a, b in zip(X, X[1:])):
|
| 314 |
+
raise ValueError("The x-coordinates must be strictly increasing.")
|
| 315 |
+
X = [sympify(i) for i in X]
|
| 316 |
+
|
| 317 |
+
# Evaluating knots value
|
| 318 |
+
if d.is_odd:
|
| 319 |
+
j = (d + 1) // 2
|
| 320 |
+
interior_knots = X[j:-j]
|
| 321 |
+
else:
|
| 322 |
+
j = d // 2
|
| 323 |
+
interior_knots = [
|
| 324 |
+
(a + b)/2 for a, b in zip(X[j : -j - 1], X[j + 1 : -j])
|
| 325 |
+
]
|
| 326 |
+
|
| 327 |
+
knots = [X[0]] * (d + 1) + list(interior_knots) + [X[-1]] * (d + 1)
|
| 328 |
+
|
| 329 |
+
basis = bspline_basis_set(d, knots, x)
|
| 330 |
+
|
| 331 |
+
A = [[b.subs(x, v) for b in basis] for v in X]
|
| 332 |
+
|
| 333 |
+
coeff = linsolve((Matrix(A), Matrix(Y)), symbols("c0:{}".format(len(X)), cls=Dummy))
|
| 334 |
+
coeff = list(coeff)[0]
|
| 335 |
+
intervals = {c for b in basis for (e, c) in b.args if c != True}
|
| 336 |
+
|
| 337 |
+
# Sorting the intervals
|
| 338 |
+
# ival contains the end-points of each interval
|
| 339 |
+
intervals = sorted(intervals, key=lambda c: _ivl(c, x))
|
| 340 |
+
|
| 341 |
+
basis_dicts = [{c: e for (e, c) in b.args} for b in basis]
|
| 342 |
+
spline = []
|
| 343 |
+
for i in intervals:
|
| 344 |
+
piece = sum(
|
| 345 |
+
[c * d.get(i, S.Zero) for (c, d) in zip(coeff, basis_dicts)], S.Zero
|
| 346 |
+
)
|
| 347 |
+
spline.append((piece, i))
|
| 348 |
+
return Piecewise(*spline)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/delta_functions.py
ADDED
|
@@ -0,0 +1,664 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S, diff
|
| 2 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 3 |
+
from sympy.core.logic import fuzzy_not
|
| 4 |
+
from sympy.core.relational import Eq, Ne
|
| 5 |
+
from sympy.functions.elementary.complexes import im, sign
|
| 6 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 7 |
+
from sympy.polys.polyerrors import PolynomialError
|
| 8 |
+
from sympy.polys.polyroots import roots
|
| 9 |
+
from sympy.utilities.misc import filldedent
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
###############################################################################
|
| 13 |
+
################################ DELTA FUNCTION ###############################
|
| 14 |
+
###############################################################################
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class DiracDelta(DefinedFunction):
|
| 18 |
+
r"""
|
| 19 |
+
The DiracDelta function and its derivatives.
|
| 20 |
+
|
| 21 |
+
Explanation
|
| 22 |
+
===========
|
| 23 |
+
|
| 24 |
+
DiracDelta is not an ordinary function. It can be rigorously defined either
|
| 25 |
+
as a distribution or as a measure.
|
| 26 |
+
|
| 27 |
+
DiracDelta only makes sense in definite integrals, and in particular,
|
| 28 |
+
integrals of the form ``Integral(f(x)*DiracDelta(x - x0), (x, a, b))``,
|
| 29 |
+
where it equals ``f(x0)`` if ``a <= x0 <= b`` and ``0`` otherwise. Formally,
|
| 30 |
+
DiracDelta acts in some ways like a function that is ``0`` everywhere except
|
| 31 |
+
at ``0``, but in many ways it also does not. It can often be useful to treat
|
| 32 |
+
DiracDelta in formal ways, building up and manipulating expressions with
|
| 33 |
+
delta functions (which may eventually be integrated), but care must be taken
|
| 34 |
+
to not treat it as a real function. SymPy's ``oo`` is similar. It only
|
| 35 |
+
truly makes sense formally in certain contexts (such as integration limits),
|
| 36 |
+
but SymPy allows its use everywhere, and it tries to be consistent with
|
| 37 |
+
operations on it (like ``1/oo``), but it is easy to get into trouble and get
|
| 38 |
+
wrong results if ``oo`` is treated too much like a number. Similarly, if
|
| 39 |
+
DiracDelta is treated too much like a function, it is easy to get wrong or
|
| 40 |
+
nonsensical results.
|
| 41 |
+
|
| 42 |
+
DiracDelta function has the following properties:
|
| 43 |
+
|
| 44 |
+
1) $\frac{d}{d x} \theta(x) = \delta(x)$
|
| 45 |
+
2) $\int_{-\infty}^\infty \delta(x - a)f(x)\, dx = f(a)$ and $\int_{a-
|
| 46 |
+
\epsilon}^{a+\epsilon} \delta(x - a)f(x)\, dx = f(a)$
|
| 47 |
+
3) $\delta(x) = 0$ for all $x \neq 0$
|
| 48 |
+
4) $\delta(g(x)) = \sum_i \frac{\delta(x - x_i)}{\|g'(x_i)\|}$ where $x_i$
|
| 49 |
+
are the roots of $g$
|
| 50 |
+
5) $\delta(-x) = \delta(x)$
|
| 51 |
+
|
| 52 |
+
Derivatives of ``k``-th order of DiracDelta have the following properties:
|
| 53 |
+
|
| 54 |
+
6) $\delta(x, k) = 0$ for all $x \neq 0$
|
| 55 |
+
7) $\delta(-x, k) = -\delta(x, k)$ for odd $k$
|
| 56 |
+
8) $\delta(-x, k) = \delta(x, k)$ for even $k$
|
| 57 |
+
|
| 58 |
+
Examples
|
| 59 |
+
========
|
| 60 |
+
|
| 61 |
+
>>> from sympy import DiracDelta, diff, pi
|
| 62 |
+
>>> from sympy.abc import x, y
|
| 63 |
+
|
| 64 |
+
>>> DiracDelta(x)
|
| 65 |
+
DiracDelta(x)
|
| 66 |
+
>>> DiracDelta(1)
|
| 67 |
+
0
|
| 68 |
+
>>> DiracDelta(-1)
|
| 69 |
+
0
|
| 70 |
+
>>> DiracDelta(pi)
|
| 71 |
+
0
|
| 72 |
+
>>> DiracDelta(x - 4).subs(x, 4)
|
| 73 |
+
DiracDelta(0)
|
| 74 |
+
>>> diff(DiracDelta(x))
|
| 75 |
+
DiracDelta(x, 1)
|
| 76 |
+
>>> diff(DiracDelta(x - 1), x, 2)
|
| 77 |
+
DiracDelta(x - 1, 2)
|
| 78 |
+
>>> diff(DiracDelta(x**2 - 1), x, 2)
|
| 79 |
+
2*(2*x**2*DiracDelta(x**2 - 1, 2) + DiracDelta(x**2 - 1, 1))
|
| 80 |
+
>>> DiracDelta(3*x).is_simple(x)
|
| 81 |
+
True
|
| 82 |
+
>>> DiracDelta(x**2).is_simple(x)
|
| 83 |
+
False
|
| 84 |
+
>>> DiracDelta((x**2 - 1)*y).expand(diracdelta=True, wrt=x)
|
| 85 |
+
DiracDelta(x - 1)/(2*Abs(y)) + DiracDelta(x + 1)/(2*Abs(y))
|
| 86 |
+
|
| 87 |
+
See Also
|
| 88 |
+
========
|
| 89 |
+
|
| 90 |
+
Heaviside
|
| 91 |
+
sympy.simplify.simplify.simplify, is_simple
|
| 92 |
+
sympy.functions.special.tensor_functions.KroneckerDelta
|
| 93 |
+
|
| 94 |
+
References
|
| 95 |
+
==========
|
| 96 |
+
|
| 97 |
+
.. [1] https://mathworld.wolfram.com/DeltaFunction.html
|
| 98 |
+
|
| 99 |
+
"""
|
| 100 |
+
|
| 101 |
+
is_real = True
|
| 102 |
+
|
| 103 |
+
def fdiff(self, argindex=1):
|
| 104 |
+
"""
|
| 105 |
+
Returns the first derivative of a DiracDelta Function.
|
| 106 |
+
|
| 107 |
+
Explanation
|
| 108 |
+
===========
|
| 109 |
+
|
| 110 |
+
The difference between ``diff()`` and ``fdiff()`` is: ``diff()`` is the
|
| 111 |
+
user-level function and ``fdiff()`` is an object method. ``fdiff()`` is
|
| 112 |
+
a convenience method available in the ``Function`` class. It returns
|
| 113 |
+
the derivative of the function without considering the chain rule.
|
| 114 |
+
``diff(function, x)`` calls ``Function._eval_derivative`` which in turn
|
| 115 |
+
calls ``fdiff()`` internally to compute the derivative of the function.
|
| 116 |
+
|
| 117 |
+
Examples
|
| 118 |
+
========
|
| 119 |
+
|
| 120 |
+
>>> from sympy import DiracDelta, diff
|
| 121 |
+
>>> from sympy.abc import x
|
| 122 |
+
|
| 123 |
+
>>> DiracDelta(x).fdiff()
|
| 124 |
+
DiracDelta(x, 1)
|
| 125 |
+
|
| 126 |
+
>>> DiracDelta(x, 1).fdiff()
|
| 127 |
+
DiracDelta(x, 2)
|
| 128 |
+
|
| 129 |
+
>>> DiracDelta(x**2 - 1).fdiff()
|
| 130 |
+
DiracDelta(x**2 - 1, 1)
|
| 131 |
+
|
| 132 |
+
>>> diff(DiracDelta(x, 1)).fdiff()
|
| 133 |
+
DiracDelta(x, 3)
|
| 134 |
+
|
| 135 |
+
Parameters
|
| 136 |
+
==========
|
| 137 |
+
|
| 138 |
+
argindex : integer
|
| 139 |
+
degree of derivative
|
| 140 |
+
|
| 141 |
+
"""
|
| 142 |
+
if argindex == 1:
|
| 143 |
+
#I didn't know if there is a better way to handle default arguments
|
| 144 |
+
k = 0
|
| 145 |
+
if len(self.args) > 1:
|
| 146 |
+
k = self.args[1]
|
| 147 |
+
return self.func(self.args[0], k + 1)
|
| 148 |
+
else:
|
| 149 |
+
raise ArgumentIndexError(self, argindex)
|
| 150 |
+
|
| 151 |
+
@classmethod
|
| 152 |
+
def eval(cls, arg, k=S.Zero):
|
| 153 |
+
"""
|
| 154 |
+
Returns a simplified form or a value of DiracDelta depending on the
|
| 155 |
+
argument passed by the DiracDelta object.
|
| 156 |
+
|
| 157 |
+
Explanation
|
| 158 |
+
===========
|
| 159 |
+
|
| 160 |
+
The ``eval()`` method is automatically called when the ``DiracDelta``
|
| 161 |
+
class is about to be instantiated and it returns either some simplified
|
| 162 |
+
instance or the unevaluated instance depending on the argument passed.
|
| 163 |
+
In other words, ``eval()`` method is not needed to be called explicitly,
|
| 164 |
+
it is being called and evaluated once the object is called.
|
| 165 |
+
|
| 166 |
+
Examples
|
| 167 |
+
========
|
| 168 |
+
|
| 169 |
+
>>> from sympy import DiracDelta, S
|
| 170 |
+
>>> from sympy.abc import x
|
| 171 |
+
|
| 172 |
+
>>> DiracDelta(x)
|
| 173 |
+
DiracDelta(x)
|
| 174 |
+
|
| 175 |
+
>>> DiracDelta(-x, 1)
|
| 176 |
+
-DiracDelta(x, 1)
|
| 177 |
+
|
| 178 |
+
>>> DiracDelta(1)
|
| 179 |
+
0
|
| 180 |
+
|
| 181 |
+
>>> DiracDelta(5, 1)
|
| 182 |
+
0
|
| 183 |
+
|
| 184 |
+
>>> DiracDelta(0)
|
| 185 |
+
DiracDelta(0)
|
| 186 |
+
|
| 187 |
+
>>> DiracDelta(-1)
|
| 188 |
+
0
|
| 189 |
+
|
| 190 |
+
>>> DiracDelta(S.NaN)
|
| 191 |
+
nan
|
| 192 |
+
|
| 193 |
+
>>> DiracDelta(x - 100).subs(x, 5)
|
| 194 |
+
0
|
| 195 |
+
|
| 196 |
+
>>> DiracDelta(x - 100).subs(x, 100)
|
| 197 |
+
DiracDelta(0)
|
| 198 |
+
|
| 199 |
+
Parameters
|
| 200 |
+
==========
|
| 201 |
+
|
| 202 |
+
k : integer
|
| 203 |
+
order of derivative
|
| 204 |
+
|
| 205 |
+
arg : argument passed to DiracDelta
|
| 206 |
+
|
| 207 |
+
"""
|
| 208 |
+
if not k.is_Integer or k.is_negative:
|
| 209 |
+
raise ValueError("Error: the second argument of DiracDelta must be \
|
| 210 |
+
a non-negative integer, %s given instead." % (k,))
|
| 211 |
+
if arg is S.NaN:
|
| 212 |
+
return S.NaN
|
| 213 |
+
if arg.is_nonzero:
|
| 214 |
+
return S.Zero
|
| 215 |
+
if fuzzy_not(im(arg).is_zero):
|
| 216 |
+
raise ValueError(filldedent('''
|
| 217 |
+
Function defined only for Real Values.
|
| 218 |
+
Complex part: %s found in %s .''' % (
|
| 219 |
+
repr(im(arg)), repr(arg))))
|
| 220 |
+
c, nc = arg.args_cnc()
|
| 221 |
+
if c and c[0] is S.NegativeOne:
|
| 222 |
+
# keep this fast and simple instead of using
|
| 223 |
+
# could_extract_minus_sign
|
| 224 |
+
if k.is_odd:
|
| 225 |
+
return -cls(-arg, k)
|
| 226 |
+
elif k.is_even:
|
| 227 |
+
return cls(-arg, k) if k else cls(-arg)
|
| 228 |
+
elif k.is_zero:
|
| 229 |
+
return cls(arg, evaluate=False)
|
| 230 |
+
|
| 231 |
+
def _eval_expand_diracdelta(self, **hints):
|
| 232 |
+
"""
|
| 233 |
+
Compute a simplified representation of the function using
|
| 234 |
+
property number 4. Pass ``wrt`` as a hint to expand the expression
|
| 235 |
+
with respect to a particular variable.
|
| 236 |
+
|
| 237 |
+
Explanation
|
| 238 |
+
===========
|
| 239 |
+
|
| 240 |
+
``wrt`` is:
|
| 241 |
+
|
| 242 |
+
- a variable with respect to which a DiracDelta expression will
|
| 243 |
+
get expanded.
|
| 244 |
+
|
| 245 |
+
Examples
|
| 246 |
+
========
|
| 247 |
+
|
| 248 |
+
>>> from sympy import DiracDelta
|
| 249 |
+
>>> from sympy.abc import x, y
|
| 250 |
+
|
| 251 |
+
>>> DiracDelta(x*y).expand(diracdelta=True, wrt=x)
|
| 252 |
+
DiracDelta(x)/Abs(y)
|
| 253 |
+
>>> DiracDelta(x*y).expand(diracdelta=True, wrt=y)
|
| 254 |
+
DiracDelta(y)/Abs(x)
|
| 255 |
+
|
| 256 |
+
>>> DiracDelta(x**2 + x - 2).expand(diracdelta=True, wrt=x)
|
| 257 |
+
DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3
|
| 258 |
+
|
| 259 |
+
See Also
|
| 260 |
+
========
|
| 261 |
+
|
| 262 |
+
is_simple, Diracdelta
|
| 263 |
+
|
| 264 |
+
"""
|
| 265 |
+
wrt = hints.get('wrt', None)
|
| 266 |
+
if wrt is None:
|
| 267 |
+
free = self.free_symbols
|
| 268 |
+
if len(free) == 1:
|
| 269 |
+
wrt = free.pop()
|
| 270 |
+
else:
|
| 271 |
+
raise TypeError(filldedent('''
|
| 272 |
+
When there is more than 1 free symbol or variable in the expression,
|
| 273 |
+
the 'wrt' keyword is required as a hint to expand when using the
|
| 274 |
+
DiracDelta hint.'''))
|
| 275 |
+
|
| 276 |
+
if not self.args[0].has(wrt) or (len(self.args) > 1 and self.args[1] != 0 ):
|
| 277 |
+
return self
|
| 278 |
+
try:
|
| 279 |
+
argroots = roots(self.args[0], wrt)
|
| 280 |
+
result = 0
|
| 281 |
+
valid = True
|
| 282 |
+
darg = abs(diff(self.args[0], wrt))
|
| 283 |
+
for r, m in argroots.items():
|
| 284 |
+
if r.is_real is not False and m == 1:
|
| 285 |
+
result += self.func(wrt - r)/darg.subs(wrt, r)
|
| 286 |
+
else:
|
| 287 |
+
# don't handle non-real and if m != 1 then
|
| 288 |
+
# a polynomial will have a zero in the derivative (darg)
|
| 289 |
+
# at r
|
| 290 |
+
valid = False
|
| 291 |
+
break
|
| 292 |
+
if valid:
|
| 293 |
+
return result
|
| 294 |
+
except PolynomialError:
|
| 295 |
+
pass
|
| 296 |
+
return self
|
| 297 |
+
|
| 298 |
+
def is_simple(self, x):
|
| 299 |
+
"""
|
| 300 |
+
Tells whether the argument(args[0]) of DiracDelta is a linear
|
| 301 |
+
expression in *x*.
|
| 302 |
+
|
| 303 |
+
Examples
|
| 304 |
+
========
|
| 305 |
+
|
| 306 |
+
>>> from sympy import DiracDelta, cos
|
| 307 |
+
>>> from sympy.abc import x, y
|
| 308 |
+
|
| 309 |
+
>>> DiracDelta(x*y).is_simple(x)
|
| 310 |
+
True
|
| 311 |
+
>>> DiracDelta(x*y).is_simple(y)
|
| 312 |
+
True
|
| 313 |
+
|
| 314 |
+
>>> DiracDelta(x**2 + x - 2).is_simple(x)
|
| 315 |
+
False
|
| 316 |
+
|
| 317 |
+
>>> DiracDelta(cos(x)).is_simple(x)
|
| 318 |
+
False
|
| 319 |
+
|
| 320 |
+
Parameters
|
| 321 |
+
==========
|
| 322 |
+
|
| 323 |
+
x : can be a symbol
|
| 324 |
+
|
| 325 |
+
See Also
|
| 326 |
+
========
|
| 327 |
+
|
| 328 |
+
sympy.simplify.simplify.simplify, DiracDelta
|
| 329 |
+
|
| 330 |
+
"""
|
| 331 |
+
p = self.args[0].as_poly(x)
|
| 332 |
+
if p:
|
| 333 |
+
return p.degree() == 1
|
| 334 |
+
return False
|
| 335 |
+
|
| 336 |
+
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
| 337 |
+
"""
|
| 338 |
+
Represents DiracDelta in a piecewise form.
|
| 339 |
+
|
| 340 |
+
Examples
|
| 341 |
+
========
|
| 342 |
+
|
| 343 |
+
>>> from sympy import DiracDelta, Piecewise, Symbol
|
| 344 |
+
>>> x = Symbol('x')
|
| 345 |
+
|
| 346 |
+
>>> DiracDelta(x).rewrite(Piecewise)
|
| 347 |
+
Piecewise((DiracDelta(0), Eq(x, 0)), (0, True))
|
| 348 |
+
|
| 349 |
+
>>> DiracDelta(x - 5).rewrite(Piecewise)
|
| 350 |
+
Piecewise((DiracDelta(0), Eq(x, 5)), (0, True))
|
| 351 |
+
|
| 352 |
+
>>> DiracDelta(x**2 - 5).rewrite(Piecewise)
|
| 353 |
+
Piecewise((DiracDelta(0), Eq(x**2, 5)), (0, True))
|
| 354 |
+
|
| 355 |
+
>>> DiracDelta(x - 5, 4).rewrite(Piecewise)
|
| 356 |
+
DiracDelta(x - 5, 4)
|
| 357 |
+
|
| 358 |
+
"""
|
| 359 |
+
if len(args) == 1:
|
| 360 |
+
return Piecewise((DiracDelta(0), Eq(args[0], 0)), (0, True))
|
| 361 |
+
|
| 362 |
+
def _eval_rewrite_as_SingularityFunction(self, *args, **kwargs):
|
| 363 |
+
"""
|
| 364 |
+
Returns the DiracDelta expression written in the form of Singularity
|
| 365 |
+
Functions.
|
| 366 |
+
|
| 367 |
+
"""
|
| 368 |
+
from sympy.solvers import solve
|
| 369 |
+
from sympy.functions.special.singularity_functions import SingularityFunction
|
| 370 |
+
if self == DiracDelta(0):
|
| 371 |
+
return SingularityFunction(0, 0, -1)
|
| 372 |
+
if self == DiracDelta(0, 1):
|
| 373 |
+
return SingularityFunction(0, 0, -2)
|
| 374 |
+
free = self.free_symbols
|
| 375 |
+
if len(free) == 1:
|
| 376 |
+
x = (free.pop())
|
| 377 |
+
if len(args) == 1:
|
| 378 |
+
return SingularityFunction(x, solve(args[0], x)[0], -1)
|
| 379 |
+
return SingularityFunction(x, solve(args[0], x)[0], -args[1] - 1)
|
| 380 |
+
else:
|
| 381 |
+
# I don't know how to handle the case for DiracDelta expressions
|
| 382 |
+
# having arguments with more than one variable.
|
| 383 |
+
raise TypeError(filldedent('''
|
| 384 |
+
rewrite(SingularityFunction) does not support
|
| 385 |
+
arguments with more that one variable.'''))
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
###############################################################################
|
| 389 |
+
############################## HEAVISIDE FUNCTION #############################
|
| 390 |
+
###############################################################################
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
class Heaviside(DefinedFunction):
|
| 394 |
+
r"""
|
| 395 |
+
Heaviside step function.
|
| 396 |
+
|
| 397 |
+
Explanation
|
| 398 |
+
===========
|
| 399 |
+
|
| 400 |
+
The Heaviside step function has the following properties:
|
| 401 |
+
|
| 402 |
+
1) $\frac{d}{d x} \theta(x) = \delta(x)$
|
| 403 |
+
2) $\theta(x) = \begin{cases} 0 & \text{for}\: x < 0 \\ \frac{1}{2} &
|
| 404 |
+
\text{for}\: x = 0 \\1 & \text{for}\: x > 0 \end{cases}$
|
| 405 |
+
3) $\frac{d}{d x} \max(x, 0) = \theta(x)$
|
| 406 |
+
|
| 407 |
+
Heaviside(x) is printed as $\theta(x)$ with the SymPy LaTeX printer.
|
| 408 |
+
|
| 409 |
+
The value at 0 is set differently in different fields. SymPy uses 1/2,
|
| 410 |
+
which is a convention from electronics and signal processing, and is
|
| 411 |
+
consistent with solving improper integrals by Fourier transform and
|
| 412 |
+
convolution.
|
| 413 |
+
|
| 414 |
+
To specify a different value of Heaviside at ``x=0``, a second argument
|
| 415 |
+
can be given. Using ``Heaviside(x, nan)`` gives an expression that will
|
| 416 |
+
evaluate to nan for x=0.
|
| 417 |
+
|
| 418 |
+
.. versionchanged:: 1.9 ``Heaviside(0)`` now returns 1/2 (before: undefined)
|
| 419 |
+
|
| 420 |
+
Examples
|
| 421 |
+
========
|
| 422 |
+
|
| 423 |
+
>>> from sympy import Heaviside, nan
|
| 424 |
+
>>> from sympy.abc import x
|
| 425 |
+
>>> Heaviside(9)
|
| 426 |
+
1
|
| 427 |
+
>>> Heaviside(-9)
|
| 428 |
+
0
|
| 429 |
+
>>> Heaviside(0)
|
| 430 |
+
1/2
|
| 431 |
+
>>> Heaviside(0, nan)
|
| 432 |
+
nan
|
| 433 |
+
>>> (Heaviside(x) + 1).replace(Heaviside(x), Heaviside(x, 1))
|
| 434 |
+
Heaviside(x, 1) + 1
|
| 435 |
+
|
| 436 |
+
See Also
|
| 437 |
+
========
|
| 438 |
+
|
| 439 |
+
DiracDelta
|
| 440 |
+
|
| 441 |
+
References
|
| 442 |
+
==========
|
| 443 |
+
|
| 444 |
+
.. [1] https://mathworld.wolfram.com/HeavisideStepFunction.html
|
| 445 |
+
.. [2] https://dlmf.nist.gov/1.16#iv
|
| 446 |
+
|
| 447 |
+
"""
|
| 448 |
+
|
| 449 |
+
is_real = True
|
| 450 |
+
|
| 451 |
+
def fdiff(self, argindex=1):
|
| 452 |
+
"""
|
| 453 |
+
Returns the first derivative of a Heaviside Function.
|
| 454 |
+
|
| 455 |
+
Examples
|
| 456 |
+
========
|
| 457 |
+
|
| 458 |
+
>>> from sympy import Heaviside, diff
|
| 459 |
+
>>> from sympy.abc import x
|
| 460 |
+
|
| 461 |
+
>>> Heaviside(x).fdiff()
|
| 462 |
+
DiracDelta(x)
|
| 463 |
+
|
| 464 |
+
>>> Heaviside(x**2 - 1).fdiff()
|
| 465 |
+
DiracDelta(x**2 - 1)
|
| 466 |
+
|
| 467 |
+
>>> diff(Heaviside(x)).fdiff()
|
| 468 |
+
DiracDelta(x, 1)
|
| 469 |
+
|
| 470 |
+
Parameters
|
| 471 |
+
==========
|
| 472 |
+
|
| 473 |
+
argindex : integer
|
| 474 |
+
order of derivative
|
| 475 |
+
|
| 476 |
+
"""
|
| 477 |
+
if argindex == 1:
|
| 478 |
+
return DiracDelta(self.args[0])
|
| 479 |
+
else:
|
| 480 |
+
raise ArgumentIndexError(self, argindex)
|
| 481 |
+
|
| 482 |
+
def __new__(cls, arg, H0=S.Half, **options):
|
| 483 |
+
if isinstance(H0, Heaviside) and len(H0.args) == 1:
|
| 484 |
+
H0 = S.Half
|
| 485 |
+
return super(cls, cls).__new__(cls, arg, H0, **options)
|
| 486 |
+
|
| 487 |
+
@property
|
| 488 |
+
def pargs(self):
|
| 489 |
+
"""Args without default S.Half"""
|
| 490 |
+
args = self.args
|
| 491 |
+
if args[1] is S.Half:
|
| 492 |
+
args = args[:1]
|
| 493 |
+
return args
|
| 494 |
+
|
| 495 |
+
@classmethod
|
| 496 |
+
def eval(cls, arg, H0=S.Half):
|
| 497 |
+
"""
|
| 498 |
+
Returns a simplified form or a value of Heaviside depending on the
|
| 499 |
+
argument passed by the Heaviside object.
|
| 500 |
+
|
| 501 |
+
Explanation
|
| 502 |
+
===========
|
| 503 |
+
|
| 504 |
+
The ``eval()`` method is automatically called when the ``Heaviside``
|
| 505 |
+
class is about to be instantiated and it returns either some simplified
|
| 506 |
+
instance or the unevaluated instance depending on the argument passed.
|
| 507 |
+
In other words, ``eval()`` method is not needed to be called explicitly,
|
| 508 |
+
it is being called and evaluated once the object is called.
|
| 509 |
+
|
| 510 |
+
Examples
|
| 511 |
+
========
|
| 512 |
+
|
| 513 |
+
>>> from sympy import Heaviside, S
|
| 514 |
+
>>> from sympy.abc import x
|
| 515 |
+
|
| 516 |
+
>>> Heaviside(x)
|
| 517 |
+
Heaviside(x)
|
| 518 |
+
|
| 519 |
+
>>> Heaviside(19)
|
| 520 |
+
1
|
| 521 |
+
|
| 522 |
+
>>> Heaviside(0)
|
| 523 |
+
1/2
|
| 524 |
+
|
| 525 |
+
>>> Heaviside(0, 1)
|
| 526 |
+
1
|
| 527 |
+
|
| 528 |
+
>>> Heaviside(-5)
|
| 529 |
+
0
|
| 530 |
+
|
| 531 |
+
>>> Heaviside(S.NaN)
|
| 532 |
+
nan
|
| 533 |
+
|
| 534 |
+
>>> Heaviside(x - 100).subs(x, 5)
|
| 535 |
+
0
|
| 536 |
+
|
| 537 |
+
>>> Heaviside(x - 100).subs(x, 105)
|
| 538 |
+
1
|
| 539 |
+
|
| 540 |
+
Parameters
|
| 541 |
+
==========
|
| 542 |
+
|
| 543 |
+
arg : argument passed by Heaviside object
|
| 544 |
+
|
| 545 |
+
H0 : value of Heaviside(0)
|
| 546 |
+
|
| 547 |
+
"""
|
| 548 |
+
if arg.is_extended_negative:
|
| 549 |
+
return S.Zero
|
| 550 |
+
elif arg.is_extended_positive:
|
| 551 |
+
return S.One
|
| 552 |
+
elif arg.is_zero:
|
| 553 |
+
return H0
|
| 554 |
+
elif arg is S.NaN:
|
| 555 |
+
return S.NaN
|
| 556 |
+
elif fuzzy_not(im(arg).is_zero):
|
| 557 |
+
raise ValueError("Function defined only for Real Values. Complex part: %s found in %s ." % (repr(im(arg)), repr(arg)) )
|
| 558 |
+
|
| 559 |
+
def _eval_rewrite_as_Piecewise(self, arg, H0=None, **kwargs):
|
| 560 |
+
"""
|
| 561 |
+
Represents Heaviside in a Piecewise form.
|
| 562 |
+
|
| 563 |
+
Examples
|
| 564 |
+
========
|
| 565 |
+
|
| 566 |
+
>>> from sympy import Heaviside, Piecewise, Symbol, nan
|
| 567 |
+
>>> x = Symbol('x')
|
| 568 |
+
|
| 569 |
+
>>> Heaviside(x).rewrite(Piecewise)
|
| 570 |
+
Piecewise((0, x < 0), (1/2, Eq(x, 0)), (1, True))
|
| 571 |
+
|
| 572 |
+
>>> Heaviside(x,nan).rewrite(Piecewise)
|
| 573 |
+
Piecewise((0, x < 0), (nan, Eq(x, 0)), (1, True))
|
| 574 |
+
|
| 575 |
+
>>> Heaviside(x - 5).rewrite(Piecewise)
|
| 576 |
+
Piecewise((0, x < 5), (1/2, Eq(x, 5)), (1, True))
|
| 577 |
+
|
| 578 |
+
>>> Heaviside(x**2 - 1).rewrite(Piecewise)
|
| 579 |
+
Piecewise((0, x**2 < 1), (1/2, Eq(x**2, 1)), (1, True))
|
| 580 |
+
|
| 581 |
+
"""
|
| 582 |
+
if H0 == 0:
|
| 583 |
+
return Piecewise((0, arg <= 0), (1, True))
|
| 584 |
+
if H0 == 1:
|
| 585 |
+
return Piecewise((0, arg < 0), (1, True))
|
| 586 |
+
return Piecewise((0, arg < 0), (H0, Eq(arg, 0)), (1, True))
|
| 587 |
+
|
| 588 |
+
def _eval_rewrite_as_sign(self, arg, H0=S.Half, **kwargs):
|
| 589 |
+
"""
|
| 590 |
+
Represents the Heaviside function in the form of sign function.
|
| 591 |
+
|
| 592 |
+
Explanation
|
| 593 |
+
===========
|
| 594 |
+
|
| 595 |
+
The value of Heaviside(0) must be 1/2 for rewriting as sign to be
|
| 596 |
+
strictly equivalent. For easier usage, we also allow this rewriting
|
| 597 |
+
when Heaviside(0) is undefined.
|
| 598 |
+
|
| 599 |
+
Examples
|
| 600 |
+
========
|
| 601 |
+
|
| 602 |
+
>>> from sympy import Heaviside, Symbol, sign, nan
|
| 603 |
+
>>> x = Symbol('x', real=True)
|
| 604 |
+
>>> y = Symbol('y')
|
| 605 |
+
|
| 606 |
+
>>> Heaviside(x).rewrite(sign)
|
| 607 |
+
sign(x)/2 + 1/2
|
| 608 |
+
|
| 609 |
+
>>> Heaviside(x, 0).rewrite(sign)
|
| 610 |
+
Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (0, True))
|
| 611 |
+
|
| 612 |
+
>>> Heaviside(x, nan).rewrite(sign)
|
| 613 |
+
Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (nan, True))
|
| 614 |
+
|
| 615 |
+
>>> Heaviside(x - 2).rewrite(sign)
|
| 616 |
+
sign(x - 2)/2 + 1/2
|
| 617 |
+
|
| 618 |
+
>>> Heaviside(x**2 - 2*x + 1).rewrite(sign)
|
| 619 |
+
sign(x**2 - 2*x + 1)/2 + 1/2
|
| 620 |
+
|
| 621 |
+
>>> Heaviside(y).rewrite(sign)
|
| 622 |
+
Heaviside(y)
|
| 623 |
+
|
| 624 |
+
>>> Heaviside(y**2 - 2*y + 1).rewrite(sign)
|
| 625 |
+
Heaviside(y**2 - 2*y + 1)
|
| 626 |
+
|
| 627 |
+
See Also
|
| 628 |
+
========
|
| 629 |
+
|
| 630 |
+
sign
|
| 631 |
+
|
| 632 |
+
"""
|
| 633 |
+
if arg.is_extended_real:
|
| 634 |
+
pw1 = Piecewise(
|
| 635 |
+
((sign(arg) + 1)/2, Ne(arg, 0)),
|
| 636 |
+
(Heaviside(0, H0=H0), True))
|
| 637 |
+
pw2 = Piecewise(
|
| 638 |
+
((sign(arg) + 1)/2, Eq(Heaviside(0, H0=H0), S.Half)),
|
| 639 |
+
(pw1, True))
|
| 640 |
+
return pw2
|
| 641 |
+
|
| 642 |
+
def _eval_rewrite_as_SingularityFunction(self, args, H0=S.Half, **kwargs):
|
| 643 |
+
"""
|
| 644 |
+
Returns the Heaviside expression written in the form of Singularity
|
| 645 |
+
Functions.
|
| 646 |
+
|
| 647 |
+
"""
|
| 648 |
+
from sympy.solvers import solve
|
| 649 |
+
from sympy.functions.special.singularity_functions import SingularityFunction
|
| 650 |
+
if self == Heaviside(0):
|
| 651 |
+
return SingularityFunction(0, 0, 0)
|
| 652 |
+
free = self.free_symbols
|
| 653 |
+
if len(free) == 1:
|
| 654 |
+
x = (free.pop())
|
| 655 |
+
return SingularityFunction(x, solve(args, x)[0], 0)
|
| 656 |
+
# TODO
|
| 657 |
+
# ((x - 5)**3*Heaviside(x - 5)).rewrite(SingularityFunction) should output
|
| 658 |
+
# SingularityFunction(x, 5, 0) instead of (x - 5)**3*SingularityFunction(x, 5, 0)
|
| 659 |
+
else:
|
| 660 |
+
# I don't know how to handle the case for Heaviside expressions
|
| 661 |
+
# having arguments with more than one variable.
|
| 662 |
+
raise TypeError(filldedent('''
|
| 663 |
+
rewrite(SingularityFunction) does not
|
| 664 |
+
support arguments with more that one variable.'''))
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/elliptic_integrals.py
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Elliptic Integrals. """
|
| 2 |
+
|
| 3 |
+
from sympy.core import S, pi, I, Rational
|
| 4 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 5 |
+
from sympy.core.symbol import Dummy,uniquely_named_symbol
|
| 6 |
+
from sympy.functions.elementary.complexes import sign
|
| 7 |
+
from sympy.functions.elementary.hyperbolic import atanh
|
| 8 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 9 |
+
from sympy.functions.elementary.trigonometric import sin, tan
|
| 10 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 11 |
+
from sympy.functions.special.hyper import hyper, meijerg
|
| 12 |
+
|
| 13 |
+
class elliptic_k(DefinedFunction):
|
| 14 |
+
r"""
|
| 15 |
+
The complete elliptic integral of the first kind, defined by
|
| 16 |
+
|
| 17 |
+
.. math:: K(m) = F\left(\tfrac{\pi}{2}\middle| m\right)
|
| 18 |
+
|
| 19 |
+
where $F\left(z\middle| m\right)$ is the Legendre incomplete
|
| 20 |
+
elliptic integral of the first kind.
|
| 21 |
+
|
| 22 |
+
Explanation
|
| 23 |
+
===========
|
| 24 |
+
|
| 25 |
+
The function $K(m)$ is a single-valued function on the complex
|
| 26 |
+
plane with branch cut along the interval $(1, \infty)$.
|
| 27 |
+
|
| 28 |
+
Note that our notation defines the incomplete elliptic integral
|
| 29 |
+
in terms of the parameter $m$ instead of the elliptic modulus
|
| 30 |
+
(eccentricity) $k$.
|
| 31 |
+
In this case, the parameter $m$ is defined as $m=k^2$.
|
| 32 |
+
|
| 33 |
+
Examples
|
| 34 |
+
========
|
| 35 |
+
|
| 36 |
+
>>> from sympy import elliptic_k, I
|
| 37 |
+
>>> from sympy.abc import m
|
| 38 |
+
>>> elliptic_k(0)
|
| 39 |
+
pi/2
|
| 40 |
+
>>> elliptic_k(1.0 + I)
|
| 41 |
+
1.50923695405127 + 0.625146415202697*I
|
| 42 |
+
>>> elliptic_k(m).series(n=3)
|
| 43 |
+
pi/2 + pi*m/8 + 9*pi*m**2/128 + O(m**3)
|
| 44 |
+
|
| 45 |
+
See Also
|
| 46 |
+
========
|
| 47 |
+
|
| 48 |
+
elliptic_f
|
| 49 |
+
|
| 50 |
+
References
|
| 51 |
+
==========
|
| 52 |
+
|
| 53 |
+
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
| 54 |
+
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticK
|
| 55 |
+
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
@classmethod
|
| 59 |
+
def eval(cls, m):
|
| 60 |
+
if m.is_zero:
|
| 61 |
+
return pi*S.Half
|
| 62 |
+
elif m is S.Half:
|
| 63 |
+
return 8*pi**Rational(3, 2)/gamma(Rational(-1, 4))**2
|
| 64 |
+
elif m is S.One:
|
| 65 |
+
return S.ComplexInfinity
|
| 66 |
+
elif m is S.NegativeOne:
|
| 67 |
+
return gamma(Rational(1, 4))**2/(4*sqrt(2*pi))
|
| 68 |
+
elif m in (S.Infinity, S.NegativeInfinity, I*S.Infinity,
|
| 69 |
+
I*S.NegativeInfinity, S.ComplexInfinity):
|
| 70 |
+
return S.Zero
|
| 71 |
+
|
| 72 |
+
def fdiff(self, argindex=1):
|
| 73 |
+
m = self.args[0]
|
| 74 |
+
return (elliptic_e(m) - (1 - m)*elliptic_k(m))/(2*m*(1 - m))
|
| 75 |
+
|
| 76 |
+
def _eval_conjugate(self):
|
| 77 |
+
m = self.args[0]
|
| 78 |
+
if (m.is_real and (m - 1).is_positive) is False:
|
| 79 |
+
return self.func(m.conjugate())
|
| 80 |
+
|
| 81 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 82 |
+
from sympy.simplify import hyperexpand
|
| 83 |
+
return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx))
|
| 84 |
+
|
| 85 |
+
def _eval_rewrite_as_hyper(self, m, **kwargs):
|
| 86 |
+
return pi*S.Half*hyper((S.Half, S.Half), (S.One,), m)
|
| 87 |
+
|
| 88 |
+
def _eval_rewrite_as_meijerg(self, m, **kwargs):
|
| 89 |
+
return meijerg(((S.Half, S.Half), []), ((S.Zero,), (S.Zero,)), -m)/2
|
| 90 |
+
|
| 91 |
+
def _eval_is_zero(self):
|
| 92 |
+
m = self.args[0]
|
| 93 |
+
if m.is_infinite:
|
| 94 |
+
return True
|
| 95 |
+
|
| 96 |
+
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
| 97 |
+
from sympy.integrals.integrals import Integral
|
| 98 |
+
t = Dummy(uniquely_named_symbol('t', args).name)
|
| 99 |
+
m = self.args[0]
|
| 100 |
+
return Integral(1/sqrt(1 - m*sin(t)**2), (t, 0, pi/2))
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class elliptic_f(DefinedFunction):
|
| 104 |
+
r"""
|
| 105 |
+
The Legendre incomplete elliptic integral of the first
|
| 106 |
+
kind, defined by
|
| 107 |
+
|
| 108 |
+
.. math:: F\left(z\middle| m\right) =
|
| 109 |
+
\int_0^z \frac{dt}{\sqrt{1 - m \sin^2 t}}
|
| 110 |
+
|
| 111 |
+
Explanation
|
| 112 |
+
===========
|
| 113 |
+
|
| 114 |
+
This function reduces to a complete elliptic integral of
|
| 115 |
+
the first kind, $K(m)$, when $z = \pi/2$.
|
| 116 |
+
|
| 117 |
+
Note that our notation defines the incomplete elliptic integral
|
| 118 |
+
in terms of the parameter $m$ instead of the elliptic modulus
|
| 119 |
+
(eccentricity) $k$.
|
| 120 |
+
In this case, the parameter $m$ is defined as $m=k^2$.
|
| 121 |
+
|
| 122 |
+
Examples
|
| 123 |
+
========
|
| 124 |
+
|
| 125 |
+
>>> from sympy import elliptic_f, I
|
| 126 |
+
>>> from sympy.abc import z, m
|
| 127 |
+
>>> elliptic_f(z, m).series(z)
|
| 128 |
+
z + z**5*(3*m**2/40 - m/30) + m*z**3/6 + O(z**6)
|
| 129 |
+
>>> elliptic_f(3.0 + I/2, 1.0 + I)
|
| 130 |
+
2.909449841483 + 1.74720545502474*I
|
| 131 |
+
|
| 132 |
+
See Also
|
| 133 |
+
========
|
| 134 |
+
|
| 135 |
+
elliptic_k
|
| 136 |
+
|
| 137 |
+
References
|
| 138 |
+
==========
|
| 139 |
+
|
| 140 |
+
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
| 141 |
+
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticF
|
| 142 |
+
|
| 143 |
+
"""
|
| 144 |
+
|
| 145 |
+
@classmethod
|
| 146 |
+
def eval(cls, z, m):
|
| 147 |
+
if z.is_zero:
|
| 148 |
+
return S.Zero
|
| 149 |
+
if m.is_zero:
|
| 150 |
+
return z
|
| 151 |
+
k = 2*z/pi
|
| 152 |
+
if k.is_integer:
|
| 153 |
+
return k*elliptic_k(m)
|
| 154 |
+
elif m in (S.Infinity, S.NegativeInfinity):
|
| 155 |
+
return S.Zero
|
| 156 |
+
elif z.could_extract_minus_sign():
|
| 157 |
+
return -elliptic_f(-z, m)
|
| 158 |
+
|
| 159 |
+
def fdiff(self, argindex=1):
|
| 160 |
+
z, m = self.args
|
| 161 |
+
fm = sqrt(1 - m*sin(z)**2)
|
| 162 |
+
if argindex == 1:
|
| 163 |
+
return 1/fm
|
| 164 |
+
elif argindex == 2:
|
| 165 |
+
return (elliptic_e(z, m)/(2*m*(1 - m)) - elliptic_f(z, m)/(2*m) -
|
| 166 |
+
sin(2*z)/(4*(1 - m)*fm))
|
| 167 |
+
raise ArgumentIndexError(self, argindex)
|
| 168 |
+
|
| 169 |
+
def _eval_conjugate(self):
|
| 170 |
+
z, m = self.args
|
| 171 |
+
if (m.is_real and (m - 1).is_positive) is False:
|
| 172 |
+
return self.func(z.conjugate(), m.conjugate())
|
| 173 |
+
|
| 174 |
+
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
| 175 |
+
from sympy.integrals.integrals import Integral
|
| 176 |
+
t = Dummy(uniquely_named_symbol('t', args).name)
|
| 177 |
+
z, m = self.args[0], self.args[1]
|
| 178 |
+
return Integral(1/(sqrt(1 - m*sin(t)**2)), (t, 0, z))
|
| 179 |
+
|
| 180 |
+
def _eval_is_zero(self):
|
| 181 |
+
z, m = self.args
|
| 182 |
+
if z.is_zero:
|
| 183 |
+
return True
|
| 184 |
+
if m.is_extended_real and m.is_infinite:
|
| 185 |
+
return True
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class elliptic_e(DefinedFunction):
|
| 189 |
+
r"""
|
| 190 |
+
Called with two arguments $z$ and $m$, evaluates the
|
| 191 |
+
incomplete elliptic integral of the second kind, defined by
|
| 192 |
+
|
| 193 |
+
.. math:: E\left(z\middle| m\right) = \int_0^z \sqrt{1 - m \sin^2 t} dt
|
| 194 |
+
|
| 195 |
+
Called with a single argument $m$, evaluates the Legendre complete
|
| 196 |
+
elliptic integral of the second kind
|
| 197 |
+
|
| 198 |
+
.. math:: E(m) = E\left(\tfrac{\pi}{2}\middle| m\right)
|
| 199 |
+
|
| 200 |
+
Explanation
|
| 201 |
+
===========
|
| 202 |
+
|
| 203 |
+
The function $E(m)$ is a single-valued function on the complex
|
| 204 |
+
plane with branch cut along the interval $(1, \infty)$.
|
| 205 |
+
|
| 206 |
+
Note that our notation defines the incomplete elliptic integral
|
| 207 |
+
in terms of the parameter $m$ instead of the elliptic modulus
|
| 208 |
+
(eccentricity) $k$.
|
| 209 |
+
In this case, the parameter $m$ is defined as $m=k^2$.
|
| 210 |
+
|
| 211 |
+
Examples
|
| 212 |
+
========
|
| 213 |
+
|
| 214 |
+
>>> from sympy import elliptic_e, I
|
| 215 |
+
>>> from sympy.abc import z, m
|
| 216 |
+
>>> elliptic_e(z, m).series(z)
|
| 217 |
+
z + z**5*(-m**2/40 + m/30) - m*z**3/6 + O(z**6)
|
| 218 |
+
>>> elliptic_e(m).series(n=4)
|
| 219 |
+
pi/2 - pi*m/8 - 3*pi*m**2/128 - 5*pi*m**3/512 + O(m**4)
|
| 220 |
+
>>> elliptic_e(1 + I, 2 - I/2).n()
|
| 221 |
+
1.55203744279187 + 0.290764986058437*I
|
| 222 |
+
>>> elliptic_e(0)
|
| 223 |
+
pi/2
|
| 224 |
+
>>> elliptic_e(2.0 - I)
|
| 225 |
+
0.991052601328069 + 0.81879421395609*I
|
| 226 |
+
|
| 227 |
+
References
|
| 228 |
+
==========
|
| 229 |
+
|
| 230 |
+
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
| 231 |
+
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticE2
|
| 232 |
+
.. [3] https://functions.wolfram.com/EllipticIntegrals/EllipticE
|
| 233 |
+
|
| 234 |
+
"""
|
| 235 |
+
|
| 236 |
+
@classmethod
|
| 237 |
+
def eval(cls, m, z=None):
|
| 238 |
+
if z is not None:
|
| 239 |
+
z, m = m, z
|
| 240 |
+
k = 2*z/pi
|
| 241 |
+
if m.is_zero:
|
| 242 |
+
return z
|
| 243 |
+
if z.is_zero:
|
| 244 |
+
return S.Zero
|
| 245 |
+
elif k.is_integer:
|
| 246 |
+
return k*elliptic_e(m)
|
| 247 |
+
elif m in (S.Infinity, S.NegativeInfinity):
|
| 248 |
+
return S.ComplexInfinity
|
| 249 |
+
elif z.could_extract_minus_sign():
|
| 250 |
+
return -elliptic_e(-z, m)
|
| 251 |
+
else:
|
| 252 |
+
if m.is_zero:
|
| 253 |
+
return pi/2
|
| 254 |
+
elif m is S.One:
|
| 255 |
+
return S.One
|
| 256 |
+
elif m is S.Infinity:
|
| 257 |
+
return I*S.Infinity
|
| 258 |
+
elif m is S.NegativeInfinity:
|
| 259 |
+
return S.Infinity
|
| 260 |
+
elif m is S.ComplexInfinity:
|
| 261 |
+
return S.ComplexInfinity
|
| 262 |
+
|
| 263 |
+
def fdiff(self, argindex=1):
|
| 264 |
+
if len(self.args) == 2:
|
| 265 |
+
z, m = self.args
|
| 266 |
+
if argindex == 1:
|
| 267 |
+
return sqrt(1 - m*sin(z)**2)
|
| 268 |
+
elif argindex == 2:
|
| 269 |
+
return (elliptic_e(z, m) - elliptic_f(z, m))/(2*m)
|
| 270 |
+
else:
|
| 271 |
+
m = self.args[0]
|
| 272 |
+
if argindex == 1:
|
| 273 |
+
return (elliptic_e(m) - elliptic_k(m))/(2*m)
|
| 274 |
+
raise ArgumentIndexError(self, argindex)
|
| 275 |
+
|
| 276 |
+
def _eval_conjugate(self):
|
| 277 |
+
if len(self.args) == 2:
|
| 278 |
+
z, m = self.args
|
| 279 |
+
if (m.is_real and (m - 1).is_positive) is False:
|
| 280 |
+
return self.func(z.conjugate(), m.conjugate())
|
| 281 |
+
else:
|
| 282 |
+
m = self.args[0]
|
| 283 |
+
if (m.is_real and (m - 1).is_positive) is False:
|
| 284 |
+
return self.func(m.conjugate())
|
| 285 |
+
|
| 286 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 287 |
+
from sympy.simplify import hyperexpand
|
| 288 |
+
if len(self.args) == 1:
|
| 289 |
+
return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx))
|
| 290 |
+
return super()._eval_nseries(x, n=n, logx=logx)
|
| 291 |
+
|
| 292 |
+
def _eval_rewrite_as_hyper(self, *args, **kwargs):
|
| 293 |
+
if len(args) == 1:
|
| 294 |
+
m = args[0]
|
| 295 |
+
return (pi/2)*hyper((Rational(-1, 2), S.Half), (S.One,), m)
|
| 296 |
+
|
| 297 |
+
def _eval_rewrite_as_meijerg(self, *args, **kwargs):
|
| 298 |
+
if len(args) == 1:
|
| 299 |
+
m = args[0]
|
| 300 |
+
return -meijerg(((S.Half, Rational(3, 2)), []), \
|
| 301 |
+
((S.Zero,), (S.Zero,)), -m)/4
|
| 302 |
+
|
| 303 |
+
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
| 304 |
+
from sympy.integrals.integrals import Integral
|
| 305 |
+
z, m = (pi/2, self.args[0]) if len(self.args) == 1 else self.args
|
| 306 |
+
t = Dummy(uniquely_named_symbol('t', args).name)
|
| 307 |
+
return Integral(sqrt(1 - m*sin(t)**2), (t, 0, z))
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
class elliptic_pi(DefinedFunction):
|
| 311 |
+
r"""
|
| 312 |
+
Called with three arguments $n$, $z$ and $m$, evaluates the
|
| 313 |
+
Legendre incomplete elliptic integral of the third kind, defined by
|
| 314 |
+
|
| 315 |
+
.. math:: \Pi\left(n; z\middle| m\right) = \int_0^z \frac{dt}
|
| 316 |
+
{\left(1 - n \sin^2 t\right) \sqrt{1 - m \sin^2 t}}
|
| 317 |
+
|
| 318 |
+
Called with two arguments $n$ and $m$, evaluates the complete
|
| 319 |
+
elliptic integral of the third kind:
|
| 320 |
+
|
| 321 |
+
.. math:: \Pi\left(n\middle| m\right) =
|
| 322 |
+
\Pi\left(n; \tfrac{\pi}{2}\middle| m\right)
|
| 323 |
+
|
| 324 |
+
Explanation
|
| 325 |
+
===========
|
| 326 |
+
|
| 327 |
+
Note that our notation defines the incomplete elliptic integral
|
| 328 |
+
in terms of the parameter $m$ instead of the elliptic modulus
|
| 329 |
+
(eccentricity) $k$.
|
| 330 |
+
In this case, the parameter $m$ is defined as $m=k^2$.
|
| 331 |
+
|
| 332 |
+
Examples
|
| 333 |
+
========
|
| 334 |
+
|
| 335 |
+
>>> from sympy import elliptic_pi, I
|
| 336 |
+
>>> from sympy.abc import z, n, m
|
| 337 |
+
>>> elliptic_pi(n, z, m).series(z, n=4)
|
| 338 |
+
z + z**3*(m/6 + n/3) + O(z**4)
|
| 339 |
+
>>> elliptic_pi(0.5 + I, 1.0 - I, 1.2)
|
| 340 |
+
2.50232379629182 - 0.760939574180767*I
|
| 341 |
+
>>> elliptic_pi(0, 0)
|
| 342 |
+
pi/2
|
| 343 |
+
>>> elliptic_pi(1.0 - I/3, 2.0 + I)
|
| 344 |
+
3.29136443417283 + 0.32555634906645*I
|
| 345 |
+
|
| 346 |
+
References
|
| 347 |
+
==========
|
| 348 |
+
|
| 349 |
+
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
| 350 |
+
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticPi3
|
| 351 |
+
.. [3] https://functions.wolfram.com/EllipticIntegrals/EllipticPi
|
| 352 |
+
|
| 353 |
+
"""
|
| 354 |
+
|
| 355 |
+
@classmethod
|
| 356 |
+
def eval(cls, n, m, z=None):
|
| 357 |
+
if z is not None:
|
| 358 |
+
z, m = m, z
|
| 359 |
+
if n.is_zero:
|
| 360 |
+
return elliptic_f(z, m)
|
| 361 |
+
elif n is S.One:
|
| 362 |
+
return (elliptic_f(z, m) +
|
| 363 |
+
(sqrt(1 - m*sin(z)**2)*tan(z) -
|
| 364 |
+
elliptic_e(z, m))/(1 - m))
|
| 365 |
+
k = 2*z/pi
|
| 366 |
+
if k.is_integer:
|
| 367 |
+
return k*elliptic_pi(n, m)
|
| 368 |
+
elif m.is_zero:
|
| 369 |
+
return atanh(sqrt(n - 1)*tan(z))/sqrt(n - 1)
|
| 370 |
+
elif n == m:
|
| 371 |
+
return (elliptic_f(z, n) - elliptic_pi(1, z, n) +
|
| 372 |
+
tan(z)/sqrt(1 - n*sin(z)**2))
|
| 373 |
+
elif n in (S.Infinity, S.NegativeInfinity):
|
| 374 |
+
return S.Zero
|
| 375 |
+
elif m in (S.Infinity, S.NegativeInfinity):
|
| 376 |
+
return S.Zero
|
| 377 |
+
elif z.could_extract_minus_sign():
|
| 378 |
+
return -elliptic_pi(n, -z, m)
|
| 379 |
+
if n.is_zero:
|
| 380 |
+
return elliptic_f(z, m)
|
| 381 |
+
if m.is_extended_real and m.is_infinite or \
|
| 382 |
+
n.is_extended_real and n.is_infinite:
|
| 383 |
+
return S.Zero
|
| 384 |
+
else:
|
| 385 |
+
if n.is_zero:
|
| 386 |
+
return elliptic_k(m)
|
| 387 |
+
elif n is S.One:
|
| 388 |
+
return S.ComplexInfinity
|
| 389 |
+
elif m.is_zero:
|
| 390 |
+
return pi/(2*sqrt(1 - n))
|
| 391 |
+
elif m == S.One:
|
| 392 |
+
return S.NegativeInfinity/sign(n - 1)
|
| 393 |
+
elif n == m:
|
| 394 |
+
return elliptic_e(n)/(1 - n)
|
| 395 |
+
elif n in (S.Infinity, S.NegativeInfinity):
|
| 396 |
+
return S.Zero
|
| 397 |
+
elif m in (S.Infinity, S.NegativeInfinity):
|
| 398 |
+
return S.Zero
|
| 399 |
+
if n.is_zero:
|
| 400 |
+
return elliptic_k(m)
|
| 401 |
+
if m.is_extended_real and m.is_infinite or \
|
| 402 |
+
n.is_extended_real and n.is_infinite:
|
| 403 |
+
return S.Zero
|
| 404 |
+
|
| 405 |
+
def _eval_conjugate(self):
|
| 406 |
+
if len(self.args) == 3:
|
| 407 |
+
n, z, m = self.args
|
| 408 |
+
if (n.is_real and (n - 1).is_positive) is False and \
|
| 409 |
+
(m.is_real and (m - 1).is_positive) is False:
|
| 410 |
+
return self.func(n.conjugate(), z.conjugate(), m.conjugate())
|
| 411 |
+
else:
|
| 412 |
+
n, m = self.args
|
| 413 |
+
return self.func(n.conjugate(), m.conjugate())
|
| 414 |
+
|
| 415 |
+
def fdiff(self, argindex=1):
|
| 416 |
+
if len(self.args) == 3:
|
| 417 |
+
n, z, m = self.args
|
| 418 |
+
fm, fn = sqrt(1 - m*sin(z)**2), 1 - n*sin(z)**2
|
| 419 |
+
if argindex == 1:
|
| 420 |
+
return (elliptic_e(z, m) + (m - n)*elliptic_f(z, m)/n +
|
| 421 |
+
(n**2 - m)*elliptic_pi(n, z, m)/n -
|
| 422 |
+
n*fm*sin(2*z)/(2*fn))/(2*(m - n)*(n - 1))
|
| 423 |
+
elif argindex == 2:
|
| 424 |
+
return 1/(fm*fn)
|
| 425 |
+
elif argindex == 3:
|
| 426 |
+
return (elliptic_e(z, m)/(m - 1) +
|
| 427 |
+
elliptic_pi(n, z, m) -
|
| 428 |
+
m*sin(2*z)/(2*(m - 1)*fm))/(2*(n - m))
|
| 429 |
+
else:
|
| 430 |
+
n, m = self.args
|
| 431 |
+
if argindex == 1:
|
| 432 |
+
return (elliptic_e(m) + (m - n)*elliptic_k(m)/n +
|
| 433 |
+
(n**2 - m)*elliptic_pi(n, m)/n)/(2*(m - n)*(n - 1))
|
| 434 |
+
elif argindex == 2:
|
| 435 |
+
return (elliptic_e(m)/(m - 1) + elliptic_pi(n, m))/(2*(n - m))
|
| 436 |
+
raise ArgumentIndexError(self, argindex)
|
| 437 |
+
|
| 438 |
+
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
| 439 |
+
from sympy.integrals.integrals import Integral
|
| 440 |
+
if len(self.args) == 2:
|
| 441 |
+
n, m, z = self.args[0], self.args[1], pi/2
|
| 442 |
+
else:
|
| 443 |
+
n, z, m = self.args
|
| 444 |
+
t = Dummy(uniquely_named_symbol('t', args).name)
|
| 445 |
+
return Integral(1/((1 - n*sin(t)**2)*sqrt(1 - m*sin(t)**2)), (t, 0, z))
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/error_functions.py
ADDED
|
@@ -0,0 +1,2801 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" This module contains various functions that are special cases
|
| 2 |
+
of incomplete gamma functions. It should probably be renamed. """
|
| 3 |
+
|
| 4 |
+
from sympy.core import EulerGamma # Must be imported from core, not core.numbers
|
| 5 |
+
from sympy.core.add import Add
|
| 6 |
+
from sympy.core.cache import cacheit
|
| 7 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError, expand_mul
|
| 8 |
+
from sympy.core.logic import fuzzy_or
|
| 9 |
+
from sympy.core.numbers import I, pi, Rational, Integer
|
| 10 |
+
from sympy.core.relational import is_eq
|
| 11 |
+
from sympy.core.power import Pow
|
| 12 |
+
from sympy.core.singleton import S
|
| 13 |
+
from sympy.core.symbol import Dummy, uniquely_named_symbol
|
| 14 |
+
from sympy.core.sympify import sympify
|
| 15 |
+
from sympy.functions.combinatorial.factorials import factorial, factorial2, RisingFactorial
|
| 16 |
+
from sympy.functions.elementary.complexes import polar_lift, re, unpolarify
|
| 17 |
+
from sympy.functions.elementary.integers import ceiling, floor
|
| 18 |
+
from sympy.functions.elementary.miscellaneous import sqrt, root
|
| 19 |
+
from sympy.functions.elementary.exponential import exp, log, exp_polar
|
| 20 |
+
from sympy.functions.elementary.hyperbolic import cosh, sinh
|
| 21 |
+
from sympy.functions.elementary.trigonometric import cos, sin, sinc
|
| 22 |
+
from sympy.functions.special.hyper import hyper, meijerg
|
| 23 |
+
|
| 24 |
+
# TODO series expansions
|
| 25 |
+
# TODO see the "Note:" in Ei
|
| 26 |
+
|
| 27 |
+
# Helper function
|
| 28 |
+
def real_to_real_as_real_imag(self, deep=True, **hints):
|
| 29 |
+
if self.args[0].is_extended_real:
|
| 30 |
+
if deep:
|
| 31 |
+
hints['complex'] = False
|
| 32 |
+
return (self.expand(deep, **hints), S.Zero)
|
| 33 |
+
else:
|
| 34 |
+
return (self, S.Zero)
|
| 35 |
+
if deep:
|
| 36 |
+
x, y = self.args[0].expand(deep, **hints).as_real_imag()
|
| 37 |
+
else:
|
| 38 |
+
x, y = self.args[0].as_real_imag()
|
| 39 |
+
re = (self.func(x + I*y) + self.func(x - I*y))/2
|
| 40 |
+
im = (self.func(x + I*y) - self.func(x - I*y))/(2*I)
|
| 41 |
+
return (re, im)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
###############################################################################
|
| 45 |
+
################################ ERROR FUNCTION ###############################
|
| 46 |
+
###############################################################################
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class erf(DefinedFunction):
|
| 50 |
+
r"""
|
| 51 |
+
The Gauss error function.
|
| 52 |
+
|
| 53 |
+
Explanation
|
| 54 |
+
===========
|
| 55 |
+
|
| 56 |
+
This function is defined as:
|
| 57 |
+
|
| 58 |
+
.. math ::
|
| 59 |
+
\mathrm{erf}(x) = \frac{2}{\sqrt{\pi}} \int_0^x e^{-t^2} \mathrm{d}t.
|
| 60 |
+
|
| 61 |
+
Examples
|
| 62 |
+
========
|
| 63 |
+
|
| 64 |
+
>>> from sympy import I, oo, erf
|
| 65 |
+
>>> from sympy.abc import z
|
| 66 |
+
|
| 67 |
+
Several special values are known:
|
| 68 |
+
|
| 69 |
+
>>> erf(0)
|
| 70 |
+
0
|
| 71 |
+
>>> erf(oo)
|
| 72 |
+
1
|
| 73 |
+
>>> erf(-oo)
|
| 74 |
+
-1
|
| 75 |
+
>>> erf(I*oo)
|
| 76 |
+
oo*I
|
| 77 |
+
>>> erf(-I*oo)
|
| 78 |
+
-oo*I
|
| 79 |
+
|
| 80 |
+
In general one can pull out factors of -1 and $I$ from the argument:
|
| 81 |
+
|
| 82 |
+
>>> erf(-z)
|
| 83 |
+
-erf(z)
|
| 84 |
+
|
| 85 |
+
The error function obeys the mirror symmetry:
|
| 86 |
+
|
| 87 |
+
>>> from sympy import conjugate
|
| 88 |
+
>>> conjugate(erf(z))
|
| 89 |
+
erf(conjugate(z))
|
| 90 |
+
|
| 91 |
+
Differentiation with respect to $z$ is supported:
|
| 92 |
+
|
| 93 |
+
>>> from sympy import diff
|
| 94 |
+
>>> diff(erf(z), z)
|
| 95 |
+
2*exp(-z**2)/sqrt(pi)
|
| 96 |
+
|
| 97 |
+
We can numerically evaluate the error function to arbitrary precision
|
| 98 |
+
on the whole complex plane:
|
| 99 |
+
|
| 100 |
+
>>> erf(4).evalf(30)
|
| 101 |
+
0.999999984582742099719981147840
|
| 102 |
+
|
| 103 |
+
>>> erf(-4*I).evalf(30)
|
| 104 |
+
-1296959.73071763923152794095062*I
|
| 105 |
+
|
| 106 |
+
See Also
|
| 107 |
+
========
|
| 108 |
+
|
| 109 |
+
erfc: Complementary error function.
|
| 110 |
+
erfi: Imaginary error function.
|
| 111 |
+
erf2: Two-argument error function.
|
| 112 |
+
erfinv: Inverse error function.
|
| 113 |
+
erfcinv: Inverse Complementary error function.
|
| 114 |
+
erf2inv: Inverse two-argument error function.
|
| 115 |
+
|
| 116 |
+
References
|
| 117 |
+
==========
|
| 118 |
+
|
| 119 |
+
.. [1] https://en.wikipedia.org/wiki/Error_function
|
| 120 |
+
.. [2] https://dlmf.nist.gov/7
|
| 121 |
+
.. [3] https://mathworld.wolfram.com/Erf.html
|
| 122 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/Erf
|
| 123 |
+
|
| 124 |
+
"""
|
| 125 |
+
|
| 126 |
+
unbranched = True
|
| 127 |
+
|
| 128 |
+
def fdiff(self, argindex=1):
|
| 129 |
+
if argindex == 1:
|
| 130 |
+
return 2*exp(-self.args[0]**2)/sqrt(pi)
|
| 131 |
+
else:
|
| 132 |
+
raise ArgumentIndexError(self, argindex)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def inverse(self, argindex=1):
|
| 136 |
+
"""
|
| 137 |
+
Returns the inverse of this function.
|
| 138 |
+
|
| 139 |
+
"""
|
| 140 |
+
return erfinv
|
| 141 |
+
|
| 142 |
+
@classmethod
|
| 143 |
+
def eval(cls, arg):
|
| 144 |
+
if arg.is_Number:
|
| 145 |
+
if arg is S.NaN:
|
| 146 |
+
return S.NaN
|
| 147 |
+
elif arg is S.Infinity:
|
| 148 |
+
return S.One
|
| 149 |
+
elif arg is S.NegativeInfinity:
|
| 150 |
+
return S.NegativeOne
|
| 151 |
+
elif arg.is_zero:
|
| 152 |
+
return S.Zero
|
| 153 |
+
|
| 154 |
+
if isinstance(arg, erfinv):
|
| 155 |
+
return arg.args[0]
|
| 156 |
+
|
| 157 |
+
if isinstance(arg, erfcinv):
|
| 158 |
+
return S.One - arg.args[0]
|
| 159 |
+
|
| 160 |
+
if arg.is_zero:
|
| 161 |
+
return S.Zero
|
| 162 |
+
|
| 163 |
+
# Only happens with unevaluated erf2inv
|
| 164 |
+
if isinstance(arg, erf2inv) and arg.args[0].is_zero:
|
| 165 |
+
return arg.args[1]
|
| 166 |
+
|
| 167 |
+
# Try to pull out factors of I
|
| 168 |
+
t = arg.extract_multiplicatively(I)
|
| 169 |
+
if t in (S.Infinity, S.NegativeInfinity):
|
| 170 |
+
return arg
|
| 171 |
+
|
| 172 |
+
# Try to pull out factors of -1
|
| 173 |
+
if arg.could_extract_minus_sign():
|
| 174 |
+
return -cls(-arg)
|
| 175 |
+
|
| 176 |
+
@staticmethod
|
| 177 |
+
@cacheit
|
| 178 |
+
def taylor_term(n, x, *previous_terms):
|
| 179 |
+
if n < 0 or n % 2 == 0:
|
| 180 |
+
return S.Zero
|
| 181 |
+
else:
|
| 182 |
+
x = sympify(x)
|
| 183 |
+
k = floor((n - 1)/S(2))
|
| 184 |
+
if len(previous_terms) > 2:
|
| 185 |
+
return -previous_terms[-2] * x**2 * (n - 2)/(n*k)
|
| 186 |
+
else:
|
| 187 |
+
return 2*S.NegativeOne**k * x**n/(n*factorial(k)*sqrt(pi))
|
| 188 |
+
|
| 189 |
+
def _eval_conjugate(self):
|
| 190 |
+
return self.func(self.args[0].conjugate())
|
| 191 |
+
|
| 192 |
+
def _eval_is_real(self):
|
| 193 |
+
if self.args[0].is_extended_real is True:
|
| 194 |
+
return True
|
| 195 |
+
# There are cases where erf(z) becomes a real number
|
| 196 |
+
# even if z is a complex number
|
| 197 |
+
|
| 198 |
+
def _eval_is_imaginary(self):
|
| 199 |
+
if self.args[0].is_imaginary is True:
|
| 200 |
+
return True
|
| 201 |
+
|
| 202 |
+
def _eval_is_finite(self):
|
| 203 |
+
z = self.args[0]
|
| 204 |
+
return fuzzy_or([z.is_finite, z.is_extended_real])
|
| 205 |
+
|
| 206 |
+
def _eval_is_zero(self):
|
| 207 |
+
if self.args[0].is_extended_real is True:
|
| 208 |
+
return self.args[0].is_zero
|
| 209 |
+
|
| 210 |
+
def _eval_is_positive(self):
|
| 211 |
+
if self.args[0].is_extended_real is True:
|
| 212 |
+
return self.args[0].is_extended_positive
|
| 213 |
+
|
| 214 |
+
def _eval_is_negative(self):
|
| 215 |
+
if self.args[0].is_extended_real is True:
|
| 216 |
+
return self.args[0].is_extended_negative
|
| 217 |
+
|
| 218 |
+
def _eval_rewrite_as_uppergamma(self, z, **kwargs):
|
| 219 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 220 |
+
return sqrt(z**2)/z*(S.One - uppergamma(S.Half, z**2)/sqrt(pi))
|
| 221 |
+
|
| 222 |
+
def _eval_rewrite_as_fresnels(self, z, **kwargs):
|
| 223 |
+
arg = (S.One - I)*z/sqrt(pi)
|
| 224 |
+
return (S.One + I)*(fresnelc(arg) - I*fresnels(arg))
|
| 225 |
+
|
| 226 |
+
def _eval_rewrite_as_fresnelc(self, z, **kwargs):
|
| 227 |
+
arg = (S.One - I)*z/sqrt(pi)
|
| 228 |
+
return (S.One + I)*(fresnelc(arg) - I*fresnels(arg))
|
| 229 |
+
|
| 230 |
+
def _eval_rewrite_as_meijerg(self, z, **kwargs):
|
| 231 |
+
return z/sqrt(pi)*meijerg([S.Half], [], [0], [Rational(-1, 2)], z**2)
|
| 232 |
+
|
| 233 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 234 |
+
return 2*z/sqrt(pi)*hyper([S.Half], [3*S.Half], -z**2)
|
| 235 |
+
|
| 236 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 237 |
+
return sqrt(z**2)/z - z*expint(S.Half, z**2)/sqrt(pi)
|
| 238 |
+
|
| 239 |
+
def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs):
|
| 240 |
+
from sympy.series.limits import limit
|
| 241 |
+
if limitvar:
|
| 242 |
+
lim = limit(z, limitvar, S.Infinity)
|
| 243 |
+
if lim is S.NegativeInfinity:
|
| 244 |
+
return S.NegativeOne + _erfs(-z)*exp(-z**2)
|
| 245 |
+
return S.One - _erfs(z)*exp(-z**2)
|
| 246 |
+
|
| 247 |
+
def _eval_rewrite_as_erfc(self, z, **kwargs):
|
| 248 |
+
return S.One - erfc(z)
|
| 249 |
+
|
| 250 |
+
def _eval_rewrite_as_erfi(self, z, **kwargs):
|
| 251 |
+
return -I*erfi(I*z)
|
| 252 |
+
|
| 253 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 254 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 255 |
+
arg0 = arg.subs(x, 0)
|
| 256 |
+
|
| 257 |
+
if arg0 is S.ComplexInfinity:
|
| 258 |
+
arg0 = arg.limit(x, 0, dir='-' if cdir == -1 else '+')
|
| 259 |
+
if x in arg.free_symbols and arg0.is_zero:
|
| 260 |
+
return 2*arg/sqrt(pi)
|
| 261 |
+
else:
|
| 262 |
+
return self.func(arg0)
|
| 263 |
+
|
| 264 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 265 |
+
from sympy.series.order import Order
|
| 266 |
+
point = args0[0]
|
| 267 |
+
|
| 268 |
+
if point in [S.Infinity, S.NegativeInfinity]:
|
| 269 |
+
z = self.args[0]
|
| 270 |
+
|
| 271 |
+
try:
|
| 272 |
+
_, ex = z.leadterm(x)
|
| 273 |
+
except (ValueError, NotImplementedError):
|
| 274 |
+
return self
|
| 275 |
+
|
| 276 |
+
ex = -ex # as x->1/x for aseries
|
| 277 |
+
if ex.is_positive:
|
| 278 |
+
newn = ceiling(n/ex)
|
| 279 |
+
s = [S.NegativeOne**k * factorial2(2*k - 1) / (z**(2*k + 1) * 2**k)
|
| 280 |
+
for k in range(newn)] + [Order(1/z**newn, x)]
|
| 281 |
+
return S.One - (exp(-z**2)/sqrt(pi)) * Add(*s)
|
| 282 |
+
|
| 283 |
+
return super(erf, self)._eval_aseries(n, args0, x, logx)
|
| 284 |
+
|
| 285 |
+
as_real_imag = real_to_real_as_real_imag
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
class erfc(DefinedFunction):
|
| 289 |
+
r"""
|
| 290 |
+
Complementary Error Function.
|
| 291 |
+
|
| 292 |
+
Explanation
|
| 293 |
+
===========
|
| 294 |
+
|
| 295 |
+
The function is defined as:
|
| 296 |
+
|
| 297 |
+
.. math ::
|
| 298 |
+
\mathrm{erfc}(x) = \frac{2}{\sqrt{\pi}} \int_x^\infty e^{-t^2} \mathrm{d}t
|
| 299 |
+
|
| 300 |
+
Examples
|
| 301 |
+
========
|
| 302 |
+
|
| 303 |
+
>>> from sympy import I, oo, erfc
|
| 304 |
+
>>> from sympy.abc import z
|
| 305 |
+
|
| 306 |
+
Several special values are known:
|
| 307 |
+
|
| 308 |
+
>>> erfc(0)
|
| 309 |
+
1
|
| 310 |
+
>>> erfc(oo)
|
| 311 |
+
0
|
| 312 |
+
>>> erfc(-oo)
|
| 313 |
+
2
|
| 314 |
+
>>> erfc(I*oo)
|
| 315 |
+
-oo*I
|
| 316 |
+
>>> erfc(-I*oo)
|
| 317 |
+
oo*I
|
| 318 |
+
|
| 319 |
+
The error function obeys the mirror symmetry:
|
| 320 |
+
|
| 321 |
+
>>> from sympy import conjugate
|
| 322 |
+
>>> conjugate(erfc(z))
|
| 323 |
+
erfc(conjugate(z))
|
| 324 |
+
|
| 325 |
+
Differentiation with respect to $z$ is supported:
|
| 326 |
+
|
| 327 |
+
>>> from sympy import diff
|
| 328 |
+
>>> diff(erfc(z), z)
|
| 329 |
+
-2*exp(-z**2)/sqrt(pi)
|
| 330 |
+
|
| 331 |
+
It also follows
|
| 332 |
+
|
| 333 |
+
>>> erfc(-z)
|
| 334 |
+
2 - erfc(z)
|
| 335 |
+
|
| 336 |
+
We can numerically evaluate the complementary error function to arbitrary
|
| 337 |
+
precision on the whole complex plane:
|
| 338 |
+
|
| 339 |
+
>>> erfc(4).evalf(30)
|
| 340 |
+
0.0000000154172579002800188521596734869
|
| 341 |
+
|
| 342 |
+
>>> erfc(4*I).evalf(30)
|
| 343 |
+
1.0 - 1296959.73071763923152794095062*I
|
| 344 |
+
|
| 345 |
+
See Also
|
| 346 |
+
========
|
| 347 |
+
|
| 348 |
+
erf: Gaussian error function.
|
| 349 |
+
erfi: Imaginary error function.
|
| 350 |
+
erf2: Two-argument error function.
|
| 351 |
+
erfinv: Inverse error function.
|
| 352 |
+
erfcinv: Inverse Complementary error function.
|
| 353 |
+
erf2inv: Inverse two-argument error function.
|
| 354 |
+
|
| 355 |
+
References
|
| 356 |
+
==========
|
| 357 |
+
|
| 358 |
+
.. [1] https://en.wikipedia.org/wiki/Error_function
|
| 359 |
+
.. [2] https://dlmf.nist.gov/7
|
| 360 |
+
.. [3] https://mathworld.wolfram.com/Erfc.html
|
| 361 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/Erfc
|
| 362 |
+
|
| 363 |
+
"""
|
| 364 |
+
|
| 365 |
+
unbranched = True
|
| 366 |
+
|
| 367 |
+
def fdiff(self, argindex=1):
|
| 368 |
+
if argindex == 1:
|
| 369 |
+
return -2*exp(-self.args[0]**2)/sqrt(pi)
|
| 370 |
+
else:
|
| 371 |
+
raise ArgumentIndexError(self, argindex)
|
| 372 |
+
|
| 373 |
+
def inverse(self, argindex=1):
|
| 374 |
+
"""
|
| 375 |
+
Returns the inverse of this function.
|
| 376 |
+
|
| 377 |
+
"""
|
| 378 |
+
return erfcinv
|
| 379 |
+
|
| 380 |
+
@classmethod
|
| 381 |
+
def eval(cls, arg):
|
| 382 |
+
if arg.is_Number:
|
| 383 |
+
if arg is S.NaN:
|
| 384 |
+
return S.NaN
|
| 385 |
+
elif arg is S.Infinity:
|
| 386 |
+
return S.Zero
|
| 387 |
+
elif arg.is_zero:
|
| 388 |
+
return S.One
|
| 389 |
+
|
| 390 |
+
if isinstance(arg, erfinv):
|
| 391 |
+
return S.One - arg.args[0]
|
| 392 |
+
|
| 393 |
+
if isinstance(arg, erfcinv):
|
| 394 |
+
return arg.args[0]
|
| 395 |
+
|
| 396 |
+
if arg.is_zero:
|
| 397 |
+
return S.One
|
| 398 |
+
|
| 399 |
+
# Try to pull out factors of I
|
| 400 |
+
t = arg.extract_multiplicatively(I)
|
| 401 |
+
if t in (S.Infinity, S.NegativeInfinity):
|
| 402 |
+
return -arg
|
| 403 |
+
|
| 404 |
+
# Try to pull out factors of -1
|
| 405 |
+
if arg.could_extract_minus_sign():
|
| 406 |
+
return 2 - cls(-arg)
|
| 407 |
+
|
| 408 |
+
@staticmethod
|
| 409 |
+
@cacheit
|
| 410 |
+
def taylor_term(n, x, *previous_terms):
|
| 411 |
+
if n == 0:
|
| 412 |
+
return S.One
|
| 413 |
+
elif n < 0 or n % 2 == 0:
|
| 414 |
+
return S.Zero
|
| 415 |
+
else:
|
| 416 |
+
x = sympify(x)
|
| 417 |
+
k = floor((n - 1)/S(2))
|
| 418 |
+
if len(previous_terms) > 2:
|
| 419 |
+
return -previous_terms[-2] * x**2 * (n - 2)/(n*k)
|
| 420 |
+
else:
|
| 421 |
+
return -2*S.NegativeOne**k * x**n/(n*factorial(k)*sqrt(pi))
|
| 422 |
+
|
| 423 |
+
def _eval_conjugate(self):
|
| 424 |
+
return self.func(self.args[0].conjugate())
|
| 425 |
+
|
| 426 |
+
def _eval_is_real(self):
|
| 427 |
+
if self.args[0].is_extended_real is True:
|
| 428 |
+
return True
|
| 429 |
+
if self.args[0].is_imaginary is True:
|
| 430 |
+
return False
|
| 431 |
+
|
| 432 |
+
def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs):
|
| 433 |
+
return self.rewrite(erf).rewrite("tractable", deep=True, limitvar=limitvar)
|
| 434 |
+
|
| 435 |
+
def _eval_rewrite_as_erf(self, z, **kwargs):
|
| 436 |
+
return S.One - erf(z)
|
| 437 |
+
|
| 438 |
+
def _eval_rewrite_as_erfi(self, z, **kwargs):
|
| 439 |
+
return S.One + I*erfi(I*z)
|
| 440 |
+
|
| 441 |
+
def _eval_rewrite_as_fresnels(self, z, **kwargs):
|
| 442 |
+
arg = (S.One - I)*z/sqrt(pi)
|
| 443 |
+
return S.One - (S.One + I)*(fresnelc(arg) - I*fresnels(arg))
|
| 444 |
+
|
| 445 |
+
def _eval_rewrite_as_fresnelc(self, z, **kwargs):
|
| 446 |
+
arg = (S.One-I)*z/sqrt(pi)
|
| 447 |
+
return S.One - (S.One + I)*(fresnelc(arg) - I*fresnels(arg))
|
| 448 |
+
|
| 449 |
+
def _eval_rewrite_as_meijerg(self, z, **kwargs):
|
| 450 |
+
return S.One - z/sqrt(pi)*meijerg([S.Half], [], [0], [Rational(-1, 2)], z**2)
|
| 451 |
+
|
| 452 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 453 |
+
return S.One - 2*z/sqrt(pi)*hyper([S.Half], [3*S.Half], -z**2)
|
| 454 |
+
|
| 455 |
+
def _eval_rewrite_as_uppergamma(self, z, **kwargs):
|
| 456 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 457 |
+
return S.One - sqrt(z**2)/z*(S.One - uppergamma(S.Half, z**2)/sqrt(pi))
|
| 458 |
+
|
| 459 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 460 |
+
return S.One - sqrt(z**2)/z + z*expint(S.Half, z**2)/sqrt(pi)
|
| 461 |
+
|
| 462 |
+
def _eval_expand_func(self, **hints):
|
| 463 |
+
return self.rewrite(erf)
|
| 464 |
+
|
| 465 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 466 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 467 |
+
arg0 = arg.subs(x, 0)
|
| 468 |
+
|
| 469 |
+
if arg0 is S.ComplexInfinity:
|
| 470 |
+
arg0 = arg.limit(x, 0, dir='-' if cdir == -1 else '+')
|
| 471 |
+
if arg0.is_zero:
|
| 472 |
+
return S.One
|
| 473 |
+
else:
|
| 474 |
+
return self.func(arg0)
|
| 475 |
+
|
| 476 |
+
as_real_imag = real_to_real_as_real_imag
|
| 477 |
+
|
| 478 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 479 |
+
return S.One - erf(*self.args)._eval_aseries(n, args0, x, logx)
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
class erfi(DefinedFunction):
|
| 483 |
+
r"""
|
| 484 |
+
Imaginary error function.
|
| 485 |
+
|
| 486 |
+
Explanation
|
| 487 |
+
===========
|
| 488 |
+
|
| 489 |
+
The function erfi is defined as:
|
| 490 |
+
|
| 491 |
+
.. math ::
|
| 492 |
+
\mathrm{erfi}(x) = \frac{2}{\sqrt{\pi}} \int_0^x e^{t^2} \mathrm{d}t
|
| 493 |
+
|
| 494 |
+
Examples
|
| 495 |
+
========
|
| 496 |
+
|
| 497 |
+
>>> from sympy import I, oo, erfi
|
| 498 |
+
>>> from sympy.abc import z
|
| 499 |
+
|
| 500 |
+
Several special values are known:
|
| 501 |
+
|
| 502 |
+
>>> erfi(0)
|
| 503 |
+
0
|
| 504 |
+
>>> erfi(oo)
|
| 505 |
+
oo
|
| 506 |
+
>>> erfi(-oo)
|
| 507 |
+
-oo
|
| 508 |
+
>>> erfi(I*oo)
|
| 509 |
+
I
|
| 510 |
+
>>> erfi(-I*oo)
|
| 511 |
+
-I
|
| 512 |
+
|
| 513 |
+
In general one can pull out factors of -1 and $I$ from the argument:
|
| 514 |
+
|
| 515 |
+
>>> erfi(-z)
|
| 516 |
+
-erfi(z)
|
| 517 |
+
|
| 518 |
+
>>> from sympy import conjugate
|
| 519 |
+
>>> conjugate(erfi(z))
|
| 520 |
+
erfi(conjugate(z))
|
| 521 |
+
|
| 522 |
+
Differentiation with respect to $z$ is supported:
|
| 523 |
+
|
| 524 |
+
>>> from sympy import diff
|
| 525 |
+
>>> diff(erfi(z), z)
|
| 526 |
+
2*exp(z**2)/sqrt(pi)
|
| 527 |
+
|
| 528 |
+
We can numerically evaluate the imaginary error function to arbitrary
|
| 529 |
+
precision on the whole complex plane:
|
| 530 |
+
|
| 531 |
+
>>> erfi(2).evalf(30)
|
| 532 |
+
18.5648024145755525987042919132
|
| 533 |
+
|
| 534 |
+
>>> erfi(-2*I).evalf(30)
|
| 535 |
+
-0.995322265018952734162069256367*I
|
| 536 |
+
|
| 537 |
+
See Also
|
| 538 |
+
========
|
| 539 |
+
|
| 540 |
+
erf: Gaussian error function.
|
| 541 |
+
erfc: Complementary error function.
|
| 542 |
+
erf2: Two-argument error function.
|
| 543 |
+
erfinv: Inverse error function.
|
| 544 |
+
erfcinv: Inverse Complementary error function.
|
| 545 |
+
erf2inv: Inverse two-argument error function.
|
| 546 |
+
|
| 547 |
+
References
|
| 548 |
+
==========
|
| 549 |
+
|
| 550 |
+
.. [1] https://en.wikipedia.org/wiki/Error_function
|
| 551 |
+
.. [2] https://mathworld.wolfram.com/Erfi.html
|
| 552 |
+
.. [3] https://functions.wolfram.com/GammaBetaErf/Erfi
|
| 553 |
+
|
| 554 |
+
"""
|
| 555 |
+
|
| 556 |
+
unbranched = True
|
| 557 |
+
|
| 558 |
+
def fdiff(self, argindex=1):
|
| 559 |
+
if argindex == 1:
|
| 560 |
+
return 2*exp(self.args[0]**2)/sqrt(pi)
|
| 561 |
+
else:
|
| 562 |
+
raise ArgumentIndexError(self, argindex)
|
| 563 |
+
|
| 564 |
+
@classmethod
|
| 565 |
+
def eval(cls, z):
|
| 566 |
+
if z.is_Number:
|
| 567 |
+
if z is S.NaN:
|
| 568 |
+
return S.NaN
|
| 569 |
+
elif z.is_zero:
|
| 570 |
+
return S.Zero
|
| 571 |
+
elif z is S.Infinity:
|
| 572 |
+
return S.Infinity
|
| 573 |
+
|
| 574 |
+
if z.is_zero:
|
| 575 |
+
return S.Zero
|
| 576 |
+
|
| 577 |
+
# Try to pull out factors of -1
|
| 578 |
+
if z.could_extract_minus_sign():
|
| 579 |
+
return -cls(-z)
|
| 580 |
+
|
| 581 |
+
# Try to pull out factors of I
|
| 582 |
+
nz = z.extract_multiplicatively(I)
|
| 583 |
+
if nz is not None:
|
| 584 |
+
if nz is S.Infinity:
|
| 585 |
+
return I
|
| 586 |
+
if isinstance(nz, erfinv):
|
| 587 |
+
return I*nz.args[0]
|
| 588 |
+
if isinstance(nz, erfcinv):
|
| 589 |
+
return I*(S.One - nz.args[0])
|
| 590 |
+
# Only happens with unevaluated erf2inv
|
| 591 |
+
if isinstance(nz, erf2inv) and nz.args[0].is_zero:
|
| 592 |
+
return I*nz.args[1]
|
| 593 |
+
|
| 594 |
+
@staticmethod
|
| 595 |
+
@cacheit
|
| 596 |
+
def taylor_term(n, x, *previous_terms):
|
| 597 |
+
if n < 0 or n % 2 == 0:
|
| 598 |
+
return S.Zero
|
| 599 |
+
else:
|
| 600 |
+
x = sympify(x)
|
| 601 |
+
k = floor((n - 1)/S(2))
|
| 602 |
+
if len(previous_terms) > 2:
|
| 603 |
+
return previous_terms[-2] * x**2 * (n - 2)/(n*k)
|
| 604 |
+
else:
|
| 605 |
+
return 2 * x**n/(n*factorial(k)*sqrt(pi))
|
| 606 |
+
|
| 607 |
+
def _eval_conjugate(self):
|
| 608 |
+
return self.func(self.args[0].conjugate())
|
| 609 |
+
|
| 610 |
+
def _eval_is_extended_real(self):
|
| 611 |
+
return self.args[0].is_extended_real
|
| 612 |
+
|
| 613 |
+
def _eval_is_zero(self):
|
| 614 |
+
return self.args[0].is_zero
|
| 615 |
+
|
| 616 |
+
def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs):
|
| 617 |
+
return self.rewrite(erf).rewrite("tractable", deep=True, limitvar=limitvar)
|
| 618 |
+
|
| 619 |
+
def _eval_rewrite_as_erf(self, z, **kwargs):
|
| 620 |
+
return -I*erf(I*z)
|
| 621 |
+
|
| 622 |
+
def _eval_rewrite_as_erfc(self, z, **kwargs):
|
| 623 |
+
return I*erfc(I*z) - I
|
| 624 |
+
|
| 625 |
+
def _eval_rewrite_as_fresnels(self, z, **kwargs):
|
| 626 |
+
arg = (S.One + I)*z/sqrt(pi)
|
| 627 |
+
return (S.One - I)*(fresnelc(arg) - I*fresnels(arg))
|
| 628 |
+
|
| 629 |
+
def _eval_rewrite_as_fresnelc(self, z, **kwargs):
|
| 630 |
+
arg = (S.One + I)*z/sqrt(pi)
|
| 631 |
+
return (S.One - I)*(fresnelc(arg) - I*fresnels(arg))
|
| 632 |
+
|
| 633 |
+
def _eval_rewrite_as_meijerg(self, z, **kwargs):
|
| 634 |
+
return z/sqrt(pi)*meijerg([S.Half], [], [0], [Rational(-1, 2)], -z**2)
|
| 635 |
+
|
| 636 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 637 |
+
return 2*z/sqrt(pi)*hyper([S.Half], [3*S.Half], z**2)
|
| 638 |
+
|
| 639 |
+
def _eval_rewrite_as_uppergamma(self, z, **kwargs):
|
| 640 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 641 |
+
return sqrt(-z**2)/z*(uppergamma(S.Half, -z**2)/sqrt(pi) - S.One)
|
| 642 |
+
|
| 643 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 644 |
+
return sqrt(-z**2)/z - z*expint(S.Half, -z**2)/sqrt(pi)
|
| 645 |
+
|
| 646 |
+
def _eval_expand_func(self, **hints):
|
| 647 |
+
return self.rewrite(erf)
|
| 648 |
+
|
| 649 |
+
as_real_imag = real_to_real_as_real_imag
|
| 650 |
+
|
| 651 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 652 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 653 |
+
arg0 = arg.subs(x, 0)
|
| 654 |
+
|
| 655 |
+
if x in arg.free_symbols and arg0.is_zero:
|
| 656 |
+
return 2*arg/sqrt(pi)
|
| 657 |
+
elif arg0.is_finite:
|
| 658 |
+
return self.func(arg0)
|
| 659 |
+
return self.func(arg)
|
| 660 |
+
|
| 661 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 662 |
+
from sympy.series.order import Order
|
| 663 |
+
point = args0[0]
|
| 664 |
+
|
| 665 |
+
if point is S.Infinity:
|
| 666 |
+
z = self.args[0]
|
| 667 |
+
s = [factorial2(2*k - 1) / (2**k * z**(2*k + 1))
|
| 668 |
+
for k in range(n)] + [Order(1/z**n, x)]
|
| 669 |
+
return -I + (exp(z**2)/sqrt(pi)) * Add(*s)
|
| 670 |
+
|
| 671 |
+
return super(erfi, self)._eval_aseries(n, args0, x, logx)
|
| 672 |
+
|
| 673 |
+
|
| 674 |
+
class erf2(DefinedFunction):
|
| 675 |
+
r"""
|
| 676 |
+
Two-argument error function.
|
| 677 |
+
|
| 678 |
+
Explanation
|
| 679 |
+
===========
|
| 680 |
+
|
| 681 |
+
This function is defined as:
|
| 682 |
+
|
| 683 |
+
.. math ::
|
| 684 |
+
\mathrm{erf2}(x, y) = \frac{2}{\sqrt{\pi}} \int_x^y e^{-t^2} \mathrm{d}t
|
| 685 |
+
|
| 686 |
+
Examples
|
| 687 |
+
========
|
| 688 |
+
|
| 689 |
+
>>> from sympy import oo, erf2
|
| 690 |
+
>>> from sympy.abc import x, y
|
| 691 |
+
|
| 692 |
+
Several special values are known:
|
| 693 |
+
|
| 694 |
+
>>> erf2(0, 0)
|
| 695 |
+
0
|
| 696 |
+
>>> erf2(x, x)
|
| 697 |
+
0
|
| 698 |
+
>>> erf2(x, oo)
|
| 699 |
+
1 - erf(x)
|
| 700 |
+
>>> erf2(x, -oo)
|
| 701 |
+
-erf(x) - 1
|
| 702 |
+
>>> erf2(oo, y)
|
| 703 |
+
erf(y) - 1
|
| 704 |
+
>>> erf2(-oo, y)
|
| 705 |
+
erf(y) + 1
|
| 706 |
+
|
| 707 |
+
In general one can pull out factors of -1:
|
| 708 |
+
|
| 709 |
+
>>> erf2(-x, -y)
|
| 710 |
+
-erf2(x, y)
|
| 711 |
+
|
| 712 |
+
The error function obeys the mirror symmetry:
|
| 713 |
+
|
| 714 |
+
>>> from sympy import conjugate
|
| 715 |
+
>>> conjugate(erf2(x, y))
|
| 716 |
+
erf2(conjugate(x), conjugate(y))
|
| 717 |
+
|
| 718 |
+
Differentiation with respect to $x$, $y$ is supported:
|
| 719 |
+
|
| 720 |
+
>>> from sympy import diff
|
| 721 |
+
>>> diff(erf2(x, y), x)
|
| 722 |
+
-2*exp(-x**2)/sqrt(pi)
|
| 723 |
+
>>> diff(erf2(x, y), y)
|
| 724 |
+
2*exp(-y**2)/sqrt(pi)
|
| 725 |
+
|
| 726 |
+
See Also
|
| 727 |
+
========
|
| 728 |
+
|
| 729 |
+
erf: Gaussian error function.
|
| 730 |
+
erfc: Complementary error function.
|
| 731 |
+
erfi: Imaginary error function.
|
| 732 |
+
erfinv: Inverse error function.
|
| 733 |
+
erfcinv: Inverse Complementary error function.
|
| 734 |
+
erf2inv: Inverse two-argument error function.
|
| 735 |
+
|
| 736 |
+
References
|
| 737 |
+
==========
|
| 738 |
+
|
| 739 |
+
.. [1] https://functions.wolfram.com/GammaBetaErf/Erf2/
|
| 740 |
+
|
| 741 |
+
"""
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
def fdiff(self, argindex):
|
| 745 |
+
x, y = self.args
|
| 746 |
+
if argindex == 1:
|
| 747 |
+
return -2*exp(-x**2)/sqrt(pi)
|
| 748 |
+
elif argindex == 2:
|
| 749 |
+
return 2*exp(-y**2)/sqrt(pi)
|
| 750 |
+
else:
|
| 751 |
+
raise ArgumentIndexError(self, argindex)
|
| 752 |
+
|
| 753 |
+
@classmethod
|
| 754 |
+
def eval(cls, x, y):
|
| 755 |
+
chk = (S.Infinity, S.NegativeInfinity, S.Zero)
|
| 756 |
+
if x is S.NaN or y is S.NaN:
|
| 757 |
+
return S.NaN
|
| 758 |
+
elif x == y:
|
| 759 |
+
return S.Zero
|
| 760 |
+
elif x in chk or y in chk:
|
| 761 |
+
return erf(y) - erf(x)
|
| 762 |
+
|
| 763 |
+
if isinstance(y, erf2inv) and y.args[0] == x:
|
| 764 |
+
return y.args[1]
|
| 765 |
+
|
| 766 |
+
if x.is_zero or y.is_zero or x.is_extended_real and x.is_infinite or \
|
| 767 |
+
y.is_extended_real and y.is_infinite:
|
| 768 |
+
return erf(y) - erf(x)
|
| 769 |
+
|
| 770 |
+
#Try to pull out -1 factor
|
| 771 |
+
sign_x = x.could_extract_minus_sign()
|
| 772 |
+
sign_y = y.could_extract_minus_sign()
|
| 773 |
+
if (sign_x and sign_y):
|
| 774 |
+
return -cls(-x, -y)
|
| 775 |
+
elif (sign_x or sign_y):
|
| 776 |
+
return erf(y)-erf(x)
|
| 777 |
+
|
| 778 |
+
def _eval_conjugate(self):
|
| 779 |
+
return self.func(self.args[0].conjugate(), self.args[1].conjugate())
|
| 780 |
+
|
| 781 |
+
def _eval_is_extended_real(self):
|
| 782 |
+
return self.args[0].is_extended_real and self.args[1].is_extended_real
|
| 783 |
+
|
| 784 |
+
def _eval_rewrite_as_erf(self, x, y, **kwargs):
|
| 785 |
+
return erf(y) - erf(x)
|
| 786 |
+
|
| 787 |
+
def _eval_rewrite_as_erfc(self, x, y, **kwargs):
|
| 788 |
+
return erfc(x) - erfc(y)
|
| 789 |
+
|
| 790 |
+
def _eval_rewrite_as_erfi(self, x, y, **kwargs):
|
| 791 |
+
return I*(erfi(I*x)-erfi(I*y))
|
| 792 |
+
|
| 793 |
+
def _eval_rewrite_as_fresnels(self, x, y, **kwargs):
|
| 794 |
+
return erf(y).rewrite(fresnels) - erf(x).rewrite(fresnels)
|
| 795 |
+
|
| 796 |
+
def _eval_rewrite_as_fresnelc(self, x, y, **kwargs):
|
| 797 |
+
return erf(y).rewrite(fresnelc) - erf(x).rewrite(fresnelc)
|
| 798 |
+
|
| 799 |
+
def _eval_rewrite_as_meijerg(self, x, y, **kwargs):
|
| 800 |
+
return erf(y).rewrite(meijerg) - erf(x).rewrite(meijerg)
|
| 801 |
+
|
| 802 |
+
def _eval_rewrite_as_hyper(self, x, y, **kwargs):
|
| 803 |
+
return erf(y).rewrite(hyper) - erf(x).rewrite(hyper)
|
| 804 |
+
|
| 805 |
+
def _eval_rewrite_as_uppergamma(self, x, y, **kwargs):
|
| 806 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 807 |
+
return (sqrt(y**2)/y*(S.One - uppergamma(S.Half, y**2)/sqrt(pi)) -
|
| 808 |
+
sqrt(x**2)/x*(S.One - uppergamma(S.Half, x**2)/sqrt(pi)))
|
| 809 |
+
|
| 810 |
+
def _eval_rewrite_as_expint(self, x, y, **kwargs):
|
| 811 |
+
return erf(y).rewrite(expint) - erf(x).rewrite(expint)
|
| 812 |
+
|
| 813 |
+
def _eval_expand_func(self, **hints):
|
| 814 |
+
return self.rewrite(erf)
|
| 815 |
+
|
| 816 |
+
def _eval_is_zero(self):
|
| 817 |
+
return is_eq(*self.args)
|
| 818 |
+
|
| 819 |
+
class erfinv(DefinedFunction):
|
| 820 |
+
r"""
|
| 821 |
+
Inverse Error Function. The erfinv function is defined as:
|
| 822 |
+
|
| 823 |
+
.. math ::
|
| 824 |
+
\mathrm{erf}(x) = y \quad \Rightarrow \quad \mathrm{erfinv}(y) = x
|
| 825 |
+
|
| 826 |
+
Examples
|
| 827 |
+
========
|
| 828 |
+
|
| 829 |
+
>>> from sympy import erfinv
|
| 830 |
+
>>> from sympy.abc import x
|
| 831 |
+
|
| 832 |
+
Several special values are known:
|
| 833 |
+
|
| 834 |
+
>>> erfinv(0)
|
| 835 |
+
0
|
| 836 |
+
>>> erfinv(1)
|
| 837 |
+
oo
|
| 838 |
+
|
| 839 |
+
Differentiation with respect to $x$ is supported:
|
| 840 |
+
|
| 841 |
+
>>> from sympy import diff
|
| 842 |
+
>>> diff(erfinv(x), x)
|
| 843 |
+
sqrt(pi)*exp(erfinv(x)**2)/2
|
| 844 |
+
|
| 845 |
+
We can numerically evaluate the inverse error function to arbitrary
|
| 846 |
+
precision on [-1, 1]:
|
| 847 |
+
|
| 848 |
+
>>> erfinv(0.2).evalf(30)
|
| 849 |
+
0.179143454621291692285822705344
|
| 850 |
+
|
| 851 |
+
See Also
|
| 852 |
+
========
|
| 853 |
+
|
| 854 |
+
erf: Gaussian error function.
|
| 855 |
+
erfc: Complementary error function.
|
| 856 |
+
erfi: Imaginary error function.
|
| 857 |
+
erf2: Two-argument error function.
|
| 858 |
+
erfcinv: Inverse Complementary error function.
|
| 859 |
+
erf2inv: Inverse two-argument error function.
|
| 860 |
+
|
| 861 |
+
References
|
| 862 |
+
==========
|
| 863 |
+
|
| 864 |
+
.. [1] https://en.wikipedia.org/wiki/Error_function#Inverse_functions
|
| 865 |
+
.. [2] https://functions.wolfram.com/GammaBetaErf/InverseErf/
|
| 866 |
+
|
| 867 |
+
"""
|
| 868 |
+
|
| 869 |
+
|
| 870 |
+
def fdiff(self, argindex =1):
|
| 871 |
+
if argindex == 1:
|
| 872 |
+
return sqrt(pi)*exp(self.func(self.args[0])**2)*S.Half
|
| 873 |
+
else :
|
| 874 |
+
raise ArgumentIndexError(self, argindex)
|
| 875 |
+
|
| 876 |
+
def inverse(self, argindex=1):
|
| 877 |
+
"""
|
| 878 |
+
Returns the inverse of this function.
|
| 879 |
+
|
| 880 |
+
"""
|
| 881 |
+
return erf
|
| 882 |
+
|
| 883 |
+
@classmethod
|
| 884 |
+
def eval(cls, z):
|
| 885 |
+
if z is S.NaN:
|
| 886 |
+
return S.NaN
|
| 887 |
+
elif z is S.NegativeOne:
|
| 888 |
+
return S.NegativeInfinity
|
| 889 |
+
elif z.is_zero:
|
| 890 |
+
return S.Zero
|
| 891 |
+
elif z is S.One:
|
| 892 |
+
return S.Infinity
|
| 893 |
+
|
| 894 |
+
if isinstance(z, erf) and z.args[0].is_extended_real:
|
| 895 |
+
return z.args[0]
|
| 896 |
+
|
| 897 |
+
if z.is_zero:
|
| 898 |
+
return S.Zero
|
| 899 |
+
|
| 900 |
+
# Try to pull out factors of -1
|
| 901 |
+
nz = z.extract_multiplicatively(-1)
|
| 902 |
+
if nz is not None and (isinstance(nz, erf) and (nz.args[0]).is_extended_real):
|
| 903 |
+
return -nz.args[0]
|
| 904 |
+
|
| 905 |
+
def _eval_rewrite_as_erfcinv(self, z, **kwargs):
|
| 906 |
+
return erfcinv(1-z)
|
| 907 |
+
|
| 908 |
+
def _eval_is_zero(self):
|
| 909 |
+
return self.args[0].is_zero
|
| 910 |
+
|
| 911 |
+
|
| 912 |
+
class erfcinv (DefinedFunction):
|
| 913 |
+
r"""
|
| 914 |
+
Inverse Complementary Error Function. The erfcinv function is defined as:
|
| 915 |
+
|
| 916 |
+
.. math ::
|
| 917 |
+
\mathrm{erfc}(x) = y \quad \Rightarrow \quad \mathrm{erfcinv}(y) = x
|
| 918 |
+
|
| 919 |
+
Examples
|
| 920 |
+
========
|
| 921 |
+
|
| 922 |
+
>>> from sympy import erfcinv
|
| 923 |
+
>>> from sympy.abc import x
|
| 924 |
+
|
| 925 |
+
Several special values are known:
|
| 926 |
+
|
| 927 |
+
>>> erfcinv(1)
|
| 928 |
+
0
|
| 929 |
+
>>> erfcinv(0)
|
| 930 |
+
oo
|
| 931 |
+
|
| 932 |
+
Differentiation with respect to $x$ is supported:
|
| 933 |
+
|
| 934 |
+
>>> from sympy import diff
|
| 935 |
+
>>> diff(erfcinv(x), x)
|
| 936 |
+
-sqrt(pi)*exp(erfcinv(x)**2)/2
|
| 937 |
+
|
| 938 |
+
See Also
|
| 939 |
+
========
|
| 940 |
+
|
| 941 |
+
erf: Gaussian error function.
|
| 942 |
+
erfc: Complementary error function.
|
| 943 |
+
erfi: Imaginary error function.
|
| 944 |
+
erf2: Two-argument error function.
|
| 945 |
+
erfinv: Inverse error function.
|
| 946 |
+
erf2inv: Inverse two-argument error function.
|
| 947 |
+
|
| 948 |
+
References
|
| 949 |
+
==========
|
| 950 |
+
|
| 951 |
+
.. [1] https://en.wikipedia.org/wiki/Error_function#Inverse_functions
|
| 952 |
+
.. [2] https://functions.wolfram.com/GammaBetaErf/InverseErfc/
|
| 953 |
+
|
| 954 |
+
"""
|
| 955 |
+
|
| 956 |
+
|
| 957 |
+
def fdiff(self, argindex =1):
|
| 958 |
+
if argindex == 1:
|
| 959 |
+
return -sqrt(pi)*exp(self.func(self.args[0])**2)*S.Half
|
| 960 |
+
else:
|
| 961 |
+
raise ArgumentIndexError(self, argindex)
|
| 962 |
+
|
| 963 |
+
def inverse(self, argindex=1):
|
| 964 |
+
"""
|
| 965 |
+
Returns the inverse of this function.
|
| 966 |
+
|
| 967 |
+
"""
|
| 968 |
+
return erfc
|
| 969 |
+
|
| 970 |
+
@classmethod
|
| 971 |
+
def eval(cls, z):
|
| 972 |
+
if z is S.NaN:
|
| 973 |
+
return S.NaN
|
| 974 |
+
elif z.is_zero:
|
| 975 |
+
return S.Infinity
|
| 976 |
+
elif z is S.One:
|
| 977 |
+
return S.Zero
|
| 978 |
+
elif z == 2:
|
| 979 |
+
return S.NegativeInfinity
|
| 980 |
+
|
| 981 |
+
if z.is_zero:
|
| 982 |
+
return S.Infinity
|
| 983 |
+
|
| 984 |
+
def _eval_rewrite_as_erfinv(self, z, **kwargs):
|
| 985 |
+
return erfinv(1-z)
|
| 986 |
+
|
| 987 |
+
def _eval_is_zero(self):
|
| 988 |
+
return (self.args[0] - 1).is_zero
|
| 989 |
+
|
| 990 |
+
def _eval_is_infinite(self):
|
| 991 |
+
z = self.args[0]
|
| 992 |
+
return fuzzy_or([z.is_zero, is_eq(z, Integer(2))])
|
| 993 |
+
|
| 994 |
+
|
| 995 |
+
class erf2inv(DefinedFunction):
|
| 996 |
+
r"""
|
| 997 |
+
Two-argument Inverse error function. The erf2inv function is defined as:
|
| 998 |
+
|
| 999 |
+
.. math ::
|
| 1000 |
+
\mathrm{erf2}(x, w) = y \quad \Rightarrow \quad \mathrm{erf2inv}(x, y) = w
|
| 1001 |
+
|
| 1002 |
+
Examples
|
| 1003 |
+
========
|
| 1004 |
+
|
| 1005 |
+
>>> from sympy import erf2inv, oo
|
| 1006 |
+
>>> from sympy.abc import x, y
|
| 1007 |
+
|
| 1008 |
+
Several special values are known:
|
| 1009 |
+
|
| 1010 |
+
>>> erf2inv(0, 0)
|
| 1011 |
+
0
|
| 1012 |
+
>>> erf2inv(1, 0)
|
| 1013 |
+
1
|
| 1014 |
+
>>> erf2inv(0, 1)
|
| 1015 |
+
oo
|
| 1016 |
+
>>> erf2inv(0, y)
|
| 1017 |
+
erfinv(y)
|
| 1018 |
+
>>> erf2inv(oo, y)
|
| 1019 |
+
erfcinv(-y)
|
| 1020 |
+
|
| 1021 |
+
Differentiation with respect to $x$ and $y$ is supported:
|
| 1022 |
+
|
| 1023 |
+
>>> from sympy import diff
|
| 1024 |
+
>>> diff(erf2inv(x, y), x)
|
| 1025 |
+
exp(-x**2 + erf2inv(x, y)**2)
|
| 1026 |
+
>>> diff(erf2inv(x, y), y)
|
| 1027 |
+
sqrt(pi)*exp(erf2inv(x, y)**2)/2
|
| 1028 |
+
|
| 1029 |
+
See Also
|
| 1030 |
+
========
|
| 1031 |
+
|
| 1032 |
+
erf: Gaussian error function.
|
| 1033 |
+
erfc: Complementary error function.
|
| 1034 |
+
erfi: Imaginary error function.
|
| 1035 |
+
erf2: Two-argument error function.
|
| 1036 |
+
erfinv: Inverse error function.
|
| 1037 |
+
erfcinv: Inverse complementary error function.
|
| 1038 |
+
|
| 1039 |
+
References
|
| 1040 |
+
==========
|
| 1041 |
+
|
| 1042 |
+
.. [1] https://functions.wolfram.com/GammaBetaErf/InverseErf2/
|
| 1043 |
+
|
| 1044 |
+
"""
|
| 1045 |
+
|
| 1046 |
+
|
| 1047 |
+
def fdiff(self, argindex):
|
| 1048 |
+
x, y = self.args
|
| 1049 |
+
if argindex == 1:
|
| 1050 |
+
return exp(self.func(x,y)**2-x**2)
|
| 1051 |
+
elif argindex == 2:
|
| 1052 |
+
return sqrt(pi)*S.Half*exp(self.func(x,y)**2)
|
| 1053 |
+
else:
|
| 1054 |
+
raise ArgumentIndexError(self, argindex)
|
| 1055 |
+
|
| 1056 |
+
@classmethod
|
| 1057 |
+
def eval(cls, x, y):
|
| 1058 |
+
if x is S.NaN or y is S.NaN:
|
| 1059 |
+
return S.NaN
|
| 1060 |
+
elif x.is_zero and y.is_zero:
|
| 1061 |
+
return S.Zero
|
| 1062 |
+
elif x.is_zero and y is S.One:
|
| 1063 |
+
return S.Infinity
|
| 1064 |
+
elif x is S.One and y.is_zero:
|
| 1065 |
+
return S.One
|
| 1066 |
+
elif x.is_zero:
|
| 1067 |
+
return erfinv(y)
|
| 1068 |
+
elif x is S.Infinity:
|
| 1069 |
+
return erfcinv(-y)
|
| 1070 |
+
elif y.is_zero:
|
| 1071 |
+
return x
|
| 1072 |
+
elif y is S.Infinity:
|
| 1073 |
+
return erfinv(x)
|
| 1074 |
+
|
| 1075 |
+
if x.is_zero:
|
| 1076 |
+
if y.is_zero:
|
| 1077 |
+
return S.Zero
|
| 1078 |
+
else:
|
| 1079 |
+
return erfinv(y)
|
| 1080 |
+
if y.is_zero:
|
| 1081 |
+
return x
|
| 1082 |
+
|
| 1083 |
+
def _eval_is_zero(self):
|
| 1084 |
+
x, y = self.args
|
| 1085 |
+
if x.is_zero and y.is_zero:
|
| 1086 |
+
return True
|
| 1087 |
+
|
| 1088 |
+
###############################################################################
|
| 1089 |
+
#################### EXPONENTIAL INTEGRALS ####################################
|
| 1090 |
+
###############################################################################
|
| 1091 |
+
|
| 1092 |
+
class Ei(DefinedFunction):
|
| 1093 |
+
r"""
|
| 1094 |
+
The classical exponential integral.
|
| 1095 |
+
|
| 1096 |
+
Explanation
|
| 1097 |
+
===========
|
| 1098 |
+
|
| 1099 |
+
For use in SymPy, this function is defined as
|
| 1100 |
+
|
| 1101 |
+
.. math:: \operatorname{Ei}(x) = \sum_{n=1}^\infty \frac{x^n}{n\, n!}
|
| 1102 |
+
+ \log(x) + \gamma,
|
| 1103 |
+
|
| 1104 |
+
where $\gamma$ is the Euler-Mascheroni constant.
|
| 1105 |
+
|
| 1106 |
+
If $x$ is a polar number, this defines an analytic function on the
|
| 1107 |
+
Riemann surface of the logarithm. Otherwise this defines an analytic
|
| 1108 |
+
function in the cut plane $\mathbb{C} \setminus (-\infty, 0]$.
|
| 1109 |
+
|
| 1110 |
+
**Background**
|
| 1111 |
+
|
| 1112 |
+
The name exponential integral comes from the following statement:
|
| 1113 |
+
|
| 1114 |
+
.. math:: \operatorname{Ei}(x) = \int_{-\infty}^x \frac{e^t}{t} \mathrm{d}t
|
| 1115 |
+
|
| 1116 |
+
If the integral is interpreted as a Cauchy principal value, this statement
|
| 1117 |
+
holds for $x > 0$ and $\operatorname{Ei}(x)$ as defined above.
|
| 1118 |
+
|
| 1119 |
+
Examples
|
| 1120 |
+
========
|
| 1121 |
+
|
| 1122 |
+
>>> from sympy import Ei, polar_lift, exp_polar, I, pi
|
| 1123 |
+
>>> from sympy.abc import x
|
| 1124 |
+
|
| 1125 |
+
>>> Ei(-1)
|
| 1126 |
+
Ei(-1)
|
| 1127 |
+
|
| 1128 |
+
This yields a real value:
|
| 1129 |
+
|
| 1130 |
+
>>> Ei(-1).n(chop=True)
|
| 1131 |
+
-0.219383934395520
|
| 1132 |
+
|
| 1133 |
+
On the other hand the analytic continuation is not real:
|
| 1134 |
+
|
| 1135 |
+
>>> Ei(polar_lift(-1)).n(chop=True)
|
| 1136 |
+
-0.21938393439552 + 3.14159265358979*I
|
| 1137 |
+
|
| 1138 |
+
The exponential integral has a logarithmic branch point at the origin:
|
| 1139 |
+
|
| 1140 |
+
>>> Ei(x*exp_polar(2*I*pi))
|
| 1141 |
+
Ei(x) + 2*I*pi
|
| 1142 |
+
|
| 1143 |
+
Differentiation is supported:
|
| 1144 |
+
|
| 1145 |
+
>>> Ei(x).diff(x)
|
| 1146 |
+
exp(x)/x
|
| 1147 |
+
|
| 1148 |
+
The exponential integral is related to many other special functions.
|
| 1149 |
+
For example:
|
| 1150 |
+
|
| 1151 |
+
>>> from sympy import expint, Shi
|
| 1152 |
+
>>> Ei(x).rewrite(expint)
|
| 1153 |
+
-expint(1, x*exp_polar(I*pi)) - I*pi
|
| 1154 |
+
>>> Ei(x).rewrite(Shi)
|
| 1155 |
+
Chi(x) + Shi(x)
|
| 1156 |
+
|
| 1157 |
+
See Also
|
| 1158 |
+
========
|
| 1159 |
+
|
| 1160 |
+
expint: Generalised exponential integral.
|
| 1161 |
+
E1: Special case of the generalised exponential integral.
|
| 1162 |
+
li: Logarithmic integral.
|
| 1163 |
+
Li: Offset logarithmic integral.
|
| 1164 |
+
Si: Sine integral.
|
| 1165 |
+
Ci: Cosine integral.
|
| 1166 |
+
Shi: Hyperbolic sine integral.
|
| 1167 |
+
Chi: Hyperbolic cosine integral.
|
| 1168 |
+
uppergamma: Upper incomplete gamma function.
|
| 1169 |
+
|
| 1170 |
+
References
|
| 1171 |
+
==========
|
| 1172 |
+
|
| 1173 |
+
.. [1] https://dlmf.nist.gov/6.6
|
| 1174 |
+
.. [2] https://en.wikipedia.org/wiki/Exponential_integral
|
| 1175 |
+
.. [3] Abramowitz & Stegun, section 5: https://web.archive.org/web/20201128173312/http://people.math.sfu.ca/~cbm/aands/page_228.htm
|
| 1176 |
+
|
| 1177 |
+
"""
|
| 1178 |
+
|
| 1179 |
+
|
| 1180 |
+
@classmethod
|
| 1181 |
+
def eval(cls, z):
|
| 1182 |
+
if z.is_zero:
|
| 1183 |
+
return S.NegativeInfinity
|
| 1184 |
+
elif z is S.Infinity:
|
| 1185 |
+
return S.Infinity
|
| 1186 |
+
elif z is S.NegativeInfinity:
|
| 1187 |
+
return S.Zero
|
| 1188 |
+
|
| 1189 |
+
if z.is_zero:
|
| 1190 |
+
return S.NegativeInfinity
|
| 1191 |
+
|
| 1192 |
+
nz, n = z.extract_branch_factor()
|
| 1193 |
+
if n:
|
| 1194 |
+
return Ei(nz) + 2*I*pi*n
|
| 1195 |
+
|
| 1196 |
+
def fdiff(self, argindex=1):
|
| 1197 |
+
arg = unpolarify(self.args[0])
|
| 1198 |
+
if argindex == 1:
|
| 1199 |
+
return exp(arg)/arg
|
| 1200 |
+
else:
|
| 1201 |
+
raise ArgumentIndexError(self, argindex)
|
| 1202 |
+
|
| 1203 |
+
def _eval_evalf(self, prec):
|
| 1204 |
+
if (self.args[0]/polar_lift(-1)).is_positive:
|
| 1205 |
+
return super()._eval_evalf(prec) + (I*pi)._eval_evalf(prec)
|
| 1206 |
+
return super()._eval_evalf(prec)
|
| 1207 |
+
|
| 1208 |
+
def _eval_rewrite_as_uppergamma(self, z, **kwargs):
|
| 1209 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 1210 |
+
# XXX this does not currently work usefully because uppergamma
|
| 1211 |
+
# immediately turns into expint
|
| 1212 |
+
return -uppergamma(0, polar_lift(-1)*z) - I*pi
|
| 1213 |
+
|
| 1214 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 1215 |
+
return -expint(1, polar_lift(-1)*z) - I*pi
|
| 1216 |
+
|
| 1217 |
+
def _eval_rewrite_as_li(self, z, **kwargs):
|
| 1218 |
+
if isinstance(z, log):
|
| 1219 |
+
return li(z.args[0])
|
| 1220 |
+
# TODO:
|
| 1221 |
+
# Actually it only holds that:
|
| 1222 |
+
# Ei(z) = li(exp(z))
|
| 1223 |
+
# for -pi < imag(z) <= pi
|
| 1224 |
+
return li(exp(z))
|
| 1225 |
+
|
| 1226 |
+
def _eval_rewrite_as_Si(self, z, **kwargs):
|
| 1227 |
+
if z.is_negative:
|
| 1228 |
+
return Shi(z) + Chi(z) - I*pi
|
| 1229 |
+
else:
|
| 1230 |
+
return Shi(z) + Chi(z)
|
| 1231 |
+
_eval_rewrite_as_Ci = _eval_rewrite_as_Si
|
| 1232 |
+
_eval_rewrite_as_Chi = _eval_rewrite_as_Si
|
| 1233 |
+
_eval_rewrite_as_Shi = _eval_rewrite_as_Si
|
| 1234 |
+
|
| 1235 |
+
def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs):
|
| 1236 |
+
return exp(z) * _eis(z)
|
| 1237 |
+
|
| 1238 |
+
def _eval_rewrite_as_Integral(self, z, **kwargs):
|
| 1239 |
+
from sympy.integrals.integrals import Integral
|
| 1240 |
+
t = Dummy(uniquely_named_symbol('t', [z]).name)
|
| 1241 |
+
return Integral(S.Exp1**t/t, (t, S.NegativeInfinity, z))
|
| 1242 |
+
|
| 1243 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1244 |
+
from sympy import re
|
| 1245 |
+
x0 = self.args[0].limit(x, 0)
|
| 1246 |
+
arg = self.args[0].as_leading_term(x, cdir=cdir)
|
| 1247 |
+
cdir = arg.dir(x, cdir)
|
| 1248 |
+
if x0.is_zero:
|
| 1249 |
+
c, e = arg.as_coeff_exponent(x)
|
| 1250 |
+
logx = log(x) if logx is None else logx
|
| 1251 |
+
return log(c) + e*logx + EulerGamma - (
|
| 1252 |
+
I*pi if re(cdir).is_negative else S.Zero)
|
| 1253 |
+
return super()._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 1254 |
+
|
| 1255 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 1256 |
+
x0 = self.args[0].limit(x, 0)
|
| 1257 |
+
if x0.is_zero:
|
| 1258 |
+
f = self._eval_rewrite_as_Si(*self.args)
|
| 1259 |
+
return f._eval_nseries(x, n, logx)
|
| 1260 |
+
return super()._eval_nseries(x, n, logx)
|
| 1261 |
+
|
| 1262 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 1263 |
+
from sympy.series.order import Order
|
| 1264 |
+
point = args0[0]
|
| 1265 |
+
|
| 1266 |
+
if point in (S.Infinity, S.NegativeInfinity):
|
| 1267 |
+
z = self.args[0]
|
| 1268 |
+
s = [factorial(k) / (z)**k for k in range(n)] + \
|
| 1269 |
+
[Order(1/z**n, x)]
|
| 1270 |
+
return (exp(z)/z) * Add(*s)
|
| 1271 |
+
|
| 1272 |
+
return super(Ei, self)._eval_aseries(n, args0, x, logx)
|
| 1273 |
+
|
| 1274 |
+
|
| 1275 |
+
class expint(DefinedFunction):
|
| 1276 |
+
r"""
|
| 1277 |
+
Generalized exponential integral.
|
| 1278 |
+
|
| 1279 |
+
Explanation
|
| 1280 |
+
===========
|
| 1281 |
+
|
| 1282 |
+
This function is defined as
|
| 1283 |
+
|
| 1284 |
+
.. math:: \operatorname{E}_\nu(z) = z^{\nu - 1} \Gamma(1 - \nu, z),
|
| 1285 |
+
|
| 1286 |
+
where $\Gamma(1 - \nu, z)$ is the upper incomplete gamma function
|
| 1287 |
+
(``uppergamma``).
|
| 1288 |
+
|
| 1289 |
+
Hence for $z$ with positive real part we have
|
| 1290 |
+
|
| 1291 |
+
.. math:: \operatorname{E}_\nu(z)
|
| 1292 |
+
= \int_1^\infty \frac{e^{-zt}}{t^\nu} \mathrm{d}t,
|
| 1293 |
+
|
| 1294 |
+
which explains the name.
|
| 1295 |
+
|
| 1296 |
+
The representation as an incomplete gamma function provides an analytic
|
| 1297 |
+
continuation for $\operatorname{E}_\nu(z)$. If $\nu$ is a
|
| 1298 |
+
non-positive integer, the exponential integral is thus an unbranched
|
| 1299 |
+
function of $z$, otherwise there is a branch point at the origin.
|
| 1300 |
+
Refer to the incomplete gamma function documentation for details of the
|
| 1301 |
+
branching behavior.
|
| 1302 |
+
|
| 1303 |
+
Examples
|
| 1304 |
+
========
|
| 1305 |
+
|
| 1306 |
+
>>> from sympy import expint, S
|
| 1307 |
+
>>> from sympy.abc import nu, z
|
| 1308 |
+
|
| 1309 |
+
Differentiation is supported. Differentiation with respect to $z$ further
|
| 1310 |
+
explains the name: for integral orders, the exponential integral is an
|
| 1311 |
+
iterated integral of the exponential function.
|
| 1312 |
+
|
| 1313 |
+
>>> expint(nu, z).diff(z)
|
| 1314 |
+
-expint(nu - 1, z)
|
| 1315 |
+
|
| 1316 |
+
Differentiation with respect to $\nu$ has no classical expression:
|
| 1317 |
+
|
| 1318 |
+
>>> expint(nu, z).diff(nu)
|
| 1319 |
+
-z**(nu - 1)*meijerg(((), (1, 1)), ((0, 0, 1 - nu), ()), z)
|
| 1320 |
+
|
| 1321 |
+
At non-postive integer orders, the exponential integral reduces to the
|
| 1322 |
+
exponential function:
|
| 1323 |
+
|
| 1324 |
+
>>> expint(0, z)
|
| 1325 |
+
exp(-z)/z
|
| 1326 |
+
>>> expint(-1, z)
|
| 1327 |
+
exp(-z)/z + exp(-z)/z**2
|
| 1328 |
+
|
| 1329 |
+
At half-integers it reduces to error functions:
|
| 1330 |
+
|
| 1331 |
+
>>> expint(S(1)/2, z)
|
| 1332 |
+
sqrt(pi)*erfc(sqrt(z))/sqrt(z)
|
| 1333 |
+
|
| 1334 |
+
At positive integer orders it can be rewritten in terms of exponentials
|
| 1335 |
+
and ``expint(1, z)``. Use ``expand_func()`` to do this:
|
| 1336 |
+
|
| 1337 |
+
>>> from sympy import expand_func
|
| 1338 |
+
>>> expand_func(expint(5, z))
|
| 1339 |
+
z**4*expint(1, z)/24 + (-z**3 + z**2 - 2*z + 6)*exp(-z)/24
|
| 1340 |
+
|
| 1341 |
+
The generalised exponential integral is essentially equivalent to the
|
| 1342 |
+
incomplete gamma function:
|
| 1343 |
+
|
| 1344 |
+
>>> from sympy import uppergamma
|
| 1345 |
+
>>> expint(nu, z).rewrite(uppergamma)
|
| 1346 |
+
z**(nu - 1)*uppergamma(1 - nu, z)
|
| 1347 |
+
|
| 1348 |
+
As such it is branched at the origin:
|
| 1349 |
+
|
| 1350 |
+
>>> from sympy import exp_polar, pi, I
|
| 1351 |
+
>>> expint(4, z*exp_polar(2*pi*I))
|
| 1352 |
+
I*pi*z**3/3 + expint(4, z)
|
| 1353 |
+
>>> expint(nu, z*exp_polar(2*pi*I))
|
| 1354 |
+
z**(nu - 1)*(exp(2*I*pi*nu) - 1)*gamma(1 - nu) + expint(nu, z)
|
| 1355 |
+
|
| 1356 |
+
See Also
|
| 1357 |
+
========
|
| 1358 |
+
|
| 1359 |
+
Ei: Another related function called exponential integral.
|
| 1360 |
+
E1: The classical case, returns expint(1, z).
|
| 1361 |
+
li: Logarithmic integral.
|
| 1362 |
+
Li: Offset logarithmic integral.
|
| 1363 |
+
Si: Sine integral.
|
| 1364 |
+
Ci: Cosine integral.
|
| 1365 |
+
Shi: Hyperbolic sine integral.
|
| 1366 |
+
Chi: Hyperbolic cosine integral.
|
| 1367 |
+
uppergamma
|
| 1368 |
+
|
| 1369 |
+
References
|
| 1370 |
+
==========
|
| 1371 |
+
|
| 1372 |
+
.. [1] https://dlmf.nist.gov/8.19
|
| 1373 |
+
.. [2] https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/
|
| 1374 |
+
.. [3] https://en.wikipedia.org/wiki/Exponential_integral
|
| 1375 |
+
|
| 1376 |
+
"""
|
| 1377 |
+
|
| 1378 |
+
|
| 1379 |
+
@classmethod
|
| 1380 |
+
def eval(cls, nu, z):
|
| 1381 |
+
from sympy.functions.special.gamma_functions import (gamma, uppergamma)
|
| 1382 |
+
nu2 = unpolarify(nu)
|
| 1383 |
+
if nu != nu2:
|
| 1384 |
+
return expint(nu2, z)
|
| 1385 |
+
if nu.is_Integer and nu <= 0 or (not nu.is_Integer and (2*nu).is_Integer):
|
| 1386 |
+
return unpolarify(expand_mul(z**(nu - 1)*uppergamma(1 - nu, z)))
|
| 1387 |
+
|
| 1388 |
+
# Extract branching information. This can be deduced from what is
|
| 1389 |
+
# explained in lowergamma.eval().
|
| 1390 |
+
z, n = z.extract_branch_factor()
|
| 1391 |
+
if n is S.Zero:
|
| 1392 |
+
return
|
| 1393 |
+
if nu.is_integer:
|
| 1394 |
+
if not nu > 0:
|
| 1395 |
+
return
|
| 1396 |
+
return expint(nu, z) \
|
| 1397 |
+
- 2*pi*I*n*S.NegativeOne**(nu - 1)/factorial(nu - 1)*unpolarify(z)**(nu - 1)
|
| 1398 |
+
else:
|
| 1399 |
+
return (exp(2*I*pi*nu*n) - 1)*z**(nu - 1)*gamma(1 - nu) + expint(nu, z)
|
| 1400 |
+
|
| 1401 |
+
def fdiff(self, argindex):
|
| 1402 |
+
nu, z = self.args
|
| 1403 |
+
if argindex == 1:
|
| 1404 |
+
return -z**(nu - 1)*meijerg([], [1, 1], [0, 0, 1 - nu], [], z)
|
| 1405 |
+
elif argindex == 2:
|
| 1406 |
+
return -expint(nu - 1, z)
|
| 1407 |
+
else:
|
| 1408 |
+
raise ArgumentIndexError(self, argindex)
|
| 1409 |
+
|
| 1410 |
+
def _eval_rewrite_as_uppergamma(self, nu, z, **kwargs):
|
| 1411 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 1412 |
+
return z**(nu - 1)*uppergamma(1 - nu, z)
|
| 1413 |
+
|
| 1414 |
+
def _eval_rewrite_as_Ei(self, nu, z, **kwargs):
|
| 1415 |
+
if nu == 1:
|
| 1416 |
+
return -Ei(z*exp_polar(-I*pi)) - I*pi
|
| 1417 |
+
elif nu.is_Integer and nu > 1:
|
| 1418 |
+
# DLMF, 8.19.7
|
| 1419 |
+
x = -unpolarify(z)
|
| 1420 |
+
return x**(nu - 1)/factorial(nu - 1)*E1(z).rewrite(Ei) + \
|
| 1421 |
+
exp(x)/factorial(nu - 1) * \
|
| 1422 |
+
Add(*[factorial(nu - k - 2)*x**k for k in range(nu - 1)])
|
| 1423 |
+
else:
|
| 1424 |
+
return self
|
| 1425 |
+
|
| 1426 |
+
def _eval_expand_func(self, **hints):
|
| 1427 |
+
return self.rewrite(Ei).rewrite(expint, **hints)
|
| 1428 |
+
|
| 1429 |
+
def _eval_rewrite_as_Si(self, nu, z, **kwargs):
|
| 1430 |
+
if nu != 1:
|
| 1431 |
+
return self
|
| 1432 |
+
return Shi(z) - Chi(z)
|
| 1433 |
+
_eval_rewrite_as_Ci = _eval_rewrite_as_Si
|
| 1434 |
+
_eval_rewrite_as_Chi = _eval_rewrite_as_Si
|
| 1435 |
+
_eval_rewrite_as_Shi = _eval_rewrite_as_Si
|
| 1436 |
+
|
| 1437 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 1438 |
+
if not self.args[0].has(x):
|
| 1439 |
+
nu = self.args[0]
|
| 1440 |
+
if nu == 1:
|
| 1441 |
+
f = self._eval_rewrite_as_Si(*self.args)
|
| 1442 |
+
return f._eval_nseries(x, n, logx)
|
| 1443 |
+
elif nu.is_Integer and nu > 1:
|
| 1444 |
+
f = self._eval_rewrite_as_Ei(*self.args)
|
| 1445 |
+
return f._eval_nseries(x, n, logx)
|
| 1446 |
+
return super()._eval_nseries(x, n, logx)
|
| 1447 |
+
|
| 1448 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 1449 |
+
from sympy.series.order import Order
|
| 1450 |
+
point = args0[1]
|
| 1451 |
+
nu = self.args[0]
|
| 1452 |
+
|
| 1453 |
+
if point is S.Infinity:
|
| 1454 |
+
z = self.args[1]
|
| 1455 |
+
s = [S.NegativeOne**k * RisingFactorial(nu, k) / z**k for k in range(n)] + [Order(1/z**n, x)]
|
| 1456 |
+
return (exp(-z)/z) * Add(*s)
|
| 1457 |
+
|
| 1458 |
+
return super(expint, self)._eval_aseries(n, args0, x, logx)
|
| 1459 |
+
|
| 1460 |
+
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
| 1461 |
+
from sympy.integrals.integrals import Integral
|
| 1462 |
+
n, x = self.args
|
| 1463 |
+
t = Dummy(uniquely_named_symbol('t', args).name)
|
| 1464 |
+
return Integral(t**-n * exp(-t*x), (t, 1, S.Infinity))
|
| 1465 |
+
|
| 1466 |
+
|
| 1467 |
+
def E1(z):
|
| 1468 |
+
"""
|
| 1469 |
+
Classical case of the generalized exponential integral.
|
| 1470 |
+
|
| 1471 |
+
Explanation
|
| 1472 |
+
===========
|
| 1473 |
+
|
| 1474 |
+
This is equivalent to ``expint(1, z)``.
|
| 1475 |
+
|
| 1476 |
+
Examples
|
| 1477 |
+
========
|
| 1478 |
+
|
| 1479 |
+
>>> from sympy import E1
|
| 1480 |
+
>>> E1(0)
|
| 1481 |
+
expint(1, 0)
|
| 1482 |
+
|
| 1483 |
+
>>> E1(5)
|
| 1484 |
+
expint(1, 5)
|
| 1485 |
+
|
| 1486 |
+
See Also
|
| 1487 |
+
========
|
| 1488 |
+
|
| 1489 |
+
Ei: Exponential integral.
|
| 1490 |
+
expint: Generalised exponential integral.
|
| 1491 |
+
li: Logarithmic integral.
|
| 1492 |
+
Li: Offset logarithmic integral.
|
| 1493 |
+
Si: Sine integral.
|
| 1494 |
+
Ci: Cosine integral.
|
| 1495 |
+
Shi: Hyperbolic sine integral.
|
| 1496 |
+
Chi: Hyperbolic cosine integral.
|
| 1497 |
+
|
| 1498 |
+
"""
|
| 1499 |
+
return expint(1, z)
|
| 1500 |
+
|
| 1501 |
+
|
| 1502 |
+
class li(DefinedFunction):
|
| 1503 |
+
r"""
|
| 1504 |
+
The classical logarithmic integral.
|
| 1505 |
+
|
| 1506 |
+
Explanation
|
| 1507 |
+
===========
|
| 1508 |
+
|
| 1509 |
+
For use in SymPy, this function is defined as
|
| 1510 |
+
|
| 1511 |
+
.. math:: \operatorname{li}(x) = \int_0^x \frac{1}{\log(t)} \mathrm{d}t \,.
|
| 1512 |
+
|
| 1513 |
+
Examples
|
| 1514 |
+
========
|
| 1515 |
+
|
| 1516 |
+
>>> from sympy import I, oo, li
|
| 1517 |
+
>>> from sympy.abc import z
|
| 1518 |
+
|
| 1519 |
+
Several special values are known:
|
| 1520 |
+
|
| 1521 |
+
>>> li(0)
|
| 1522 |
+
0
|
| 1523 |
+
>>> li(1)
|
| 1524 |
+
-oo
|
| 1525 |
+
>>> li(oo)
|
| 1526 |
+
oo
|
| 1527 |
+
|
| 1528 |
+
Differentiation with respect to $z$ is supported:
|
| 1529 |
+
|
| 1530 |
+
>>> from sympy import diff
|
| 1531 |
+
>>> diff(li(z), z)
|
| 1532 |
+
1/log(z)
|
| 1533 |
+
|
| 1534 |
+
Defining the ``li`` function via an integral:
|
| 1535 |
+
>>> from sympy import integrate
|
| 1536 |
+
>>> integrate(li(z))
|
| 1537 |
+
z*li(z) - Ei(2*log(z))
|
| 1538 |
+
|
| 1539 |
+
>>> integrate(li(z),z)
|
| 1540 |
+
z*li(z) - Ei(2*log(z))
|
| 1541 |
+
|
| 1542 |
+
|
| 1543 |
+
The logarithmic integral can also be defined in terms of ``Ei``:
|
| 1544 |
+
|
| 1545 |
+
>>> from sympy import Ei
|
| 1546 |
+
>>> li(z).rewrite(Ei)
|
| 1547 |
+
Ei(log(z))
|
| 1548 |
+
>>> diff(li(z).rewrite(Ei), z)
|
| 1549 |
+
1/log(z)
|
| 1550 |
+
|
| 1551 |
+
We can numerically evaluate the logarithmic integral to arbitrary precision
|
| 1552 |
+
on the whole complex plane (except the singular points):
|
| 1553 |
+
|
| 1554 |
+
>>> li(2).evalf(30)
|
| 1555 |
+
1.04516378011749278484458888919
|
| 1556 |
+
|
| 1557 |
+
>>> li(2*I).evalf(30)
|
| 1558 |
+
1.0652795784357498247001125598 + 3.08346052231061726610939702133*I
|
| 1559 |
+
|
| 1560 |
+
We can even compute Soldner's constant by the help of mpmath:
|
| 1561 |
+
|
| 1562 |
+
>>> from mpmath import findroot
|
| 1563 |
+
>>> findroot(li, 2)
|
| 1564 |
+
1.45136923488338
|
| 1565 |
+
|
| 1566 |
+
Further transformations include rewriting ``li`` in terms of
|
| 1567 |
+
the trigonometric integrals ``Si``, ``Ci``, ``Shi`` and ``Chi``:
|
| 1568 |
+
|
| 1569 |
+
>>> from sympy import Si, Ci, Shi, Chi
|
| 1570 |
+
>>> li(z).rewrite(Si)
|
| 1571 |
+
-log(I*log(z)) - log(1/log(z))/2 + log(log(z))/2 + Ci(I*log(z)) + Shi(log(z))
|
| 1572 |
+
>>> li(z).rewrite(Ci)
|
| 1573 |
+
-log(I*log(z)) - log(1/log(z))/2 + log(log(z))/2 + Ci(I*log(z)) + Shi(log(z))
|
| 1574 |
+
>>> li(z).rewrite(Shi)
|
| 1575 |
+
-log(1/log(z))/2 + log(log(z))/2 + Chi(log(z)) - Shi(log(z))
|
| 1576 |
+
>>> li(z).rewrite(Chi)
|
| 1577 |
+
-log(1/log(z))/2 + log(log(z))/2 + Chi(log(z)) - Shi(log(z))
|
| 1578 |
+
|
| 1579 |
+
See Also
|
| 1580 |
+
========
|
| 1581 |
+
|
| 1582 |
+
Li: Offset logarithmic integral.
|
| 1583 |
+
Ei: Exponential integral.
|
| 1584 |
+
expint: Generalised exponential integral.
|
| 1585 |
+
E1: Special case of the generalised exponential integral.
|
| 1586 |
+
Si: Sine integral.
|
| 1587 |
+
Ci: Cosine integral.
|
| 1588 |
+
Shi: Hyperbolic sine integral.
|
| 1589 |
+
Chi: Hyperbolic cosine integral.
|
| 1590 |
+
|
| 1591 |
+
References
|
| 1592 |
+
==========
|
| 1593 |
+
|
| 1594 |
+
.. [1] https://en.wikipedia.org/wiki/Logarithmic_integral
|
| 1595 |
+
.. [2] https://mathworld.wolfram.com/LogarithmicIntegral.html
|
| 1596 |
+
.. [3] https://dlmf.nist.gov/6
|
| 1597 |
+
.. [4] https://mathworld.wolfram.com/SoldnersConstant.html
|
| 1598 |
+
|
| 1599 |
+
"""
|
| 1600 |
+
|
| 1601 |
+
|
| 1602 |
+
@classmethod
|
| 1603 |
+
def eval(cls, z):
|
| 1604 |
+
if z.is_zero:
|
| 1605 |
+
return S.Zero
|
| 1606 |
+
elif z is S.One:
|
| 1607 |
+
return S.NegativeInfinity
|
| 1608 |
+
elif z is S.Infinity:
|
| 1609 |
+
return S.Infinity
|
| 1610 |
+
if z.is_zero:
|
| 1611 |
+
return S.Zero
|
| 1612 |
+
|
| 1613 |
+
def fdiff(self, argindex=1):
|
| 1614 |
+
arg = self.args[0]
|
| 1615 |
+
if argindex == 1:
|
| 1616 |
+
return S.One / log(arg)
|
| 1617 |
+
else:
|
| 1618 |
+
raise ArgumentIndexError(self, argindex)
|
| 1619 |
+
|
| 1620 |
+
def _eval_conjugate(self):
|
| 1621 |
+
z = self.args[0]
|
| 1622 |
+
# Exclude values on the branch cut (-oo, 0)
|
| 1623 |
+
if not z.is_extended_negative:
|
| 1624 |
+
return self.func(z.conjugate())
|
| 1625 |
+
|
| 1626 |
+
def _eval_rewrite_as_Li(self, z, **kwargs):
|
| 1627 |
+
return Li(z) + li(2)
|
| 1628 |
+
|
| 1629 |
+
def _eval_rewrite_as_Ei(self, z, **kwargs):
|
| 1630 |
+
return Ei(log(z))
|
| 1631 |
+
|
| 1632 |
+
def _eval_rewrite_as_uppergamma(self, z, **kwargs):
|
| 1633 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 1634 |
+
return (-uppergamma(0, -log(z)) +
|
| 1635 |
+
S.Half*(log(log(z)) - log(S.One/log(z))) - log(-log(z)))
|
| 1636 |
+
|
| 1637 |
+
def _eval_rewrite_as_Si(self, z, **kwargs):
|
| 1638 |
+
return (Ci(I*log(z)) - I*Si(I*log(z)) -
|
| 1639 |
+
S.Half*(log(S.One/log(z)) - log(log(z))) - log(I*log(z)))
|
| 1640 |
+
|
| 1641 |
+
_eval_rewrite_as_Ci = _eval_rewrite_as_Si
|
| 1642 |
+
|
| 1643 |
+
def _eval_rewrite_as_Shi(self, z, **kwargs):
|
| 1644 |
+
return (Chi(log(z)) - Shi(log(z)) - S.Half*(log(S.One/log(z)) - log(log(z))))
|
| 1645 |
+
|
| 1646 |
+
_eval_rewrite_as_Chi = _eval_rewrite_as_Shi
|
| 1647 |
+
|
| 1648 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 1649 |
+
return (log(z)*hyper((1, 1), (2, 2), log(z)) +
|
| 1650 |
+
S.Half*(log(log(z)) - log(S.One/log(z))) + EulerGamma)
|
| 1651 |
+
|
| 1652 |
+
def _eval_rewrite_as_meijerg(self, z, **kwargs):
|
| 1653 |
+
return (-log(-log(z)) - S.Half*(log(S.One/log(z)) - log(log(z)))
|
| 1654 |
+
- meijerg(((), (1,)), ((0, 0), ()), -log(z)))
|
| 1655 |
+
|
| 1656 |
+
def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs):
|
| 1657 |
+
return z * _eis(log(z))
|
| 1658 |
+
|
| 1659 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 1660 |
+
z = self.args[0]
|
| 1661 |
+
s = [(log(z))**k / (factorial(k) * k) for k in range(1, n)]
|
| 1662 |
+
return EulerGamma + log(log(z)) + Add(*s)
|
| 1663 |
+
|
| 1664 |
+
def _eval_is_zero(self):
|
| 1665 |
+
z = self.args[0]
|
| 1666 |
+
if z.is_zero:
|
| 1667 |
+
return True
|
| 1668 |
+
|
| 1669 |
+
class Li(DefinedFunction):
|
| 1670 |
+
r"""
|
| 1671 |
+
The offset logarithmic integral.
|
| 1672 |
+
|
| 1673 |
+
Explanation
|
| 1674 |
+
===========
|
| 1675 |
+
|
| 1676 |
+
For use in SymPy, this function is defined as
|
| 1677 |
+
|
| 1678 |
+
.. math:: \operatorname{Li}(x) = \operatorname{li}(x) - \operatorname{li}(2)
|
| 1679 |
+
|
| 1680 |
+
Examples
|
| 1681 |
+
========
|
| 1682 |
+
|
| 1683 |
+
>>> from sympy import Li
|
| 1684 |
+
>>> from sympy.abc import z
|
| 1685 |
+
|
| 1686 |
+
The following special value is known:
|
| 1687 |
+
|
| 1688 |
+
>>> Li(2)
|
| 1689 |
+
0
|
| 1690 |
+
|
| 1691 |
+
Differentiation with respect to $z$ is supported:
|
| 1692 |
+
|
| 1693 |
+
>>> from sympy import diff
|
| 1694 |
+
>>> diff(Li(z), z)
|
| 1695 |
+
1/log(z)
|
| 1696 |
+
|
| 1697 |
+
The shifted logarithmic integral can be written in terms of $li(z)$:
|
| 1698 |
+
|
| 1699 |
+
>>> from sympy import li
|
| 1700 |
+
>>> Li(z).rewrite(li)
|
| 1701 |
+
li(z) - li(2)
|
| 1702 |
+
|
| 1703 |
+
We can numerically evaluate the logarithmic integral to arbitrary precision
|
| 1704 |
+
on the whole complex plane (except the singular points):
|
| 1705 |
+
|
| 1706 |
+
>>> Li(2).evalf(30)
|
| 1707 |
+
0
|
| 1708 |
+
|
| 1709 |
+
>>> Li(4).evalf(30)
|
| 1710 |
+
1.92242131492155809316615998938
|
| 1711 |
+
|
| 1712 |
+
See Also
|
| 1713 |
+
========
|
| 1714 |
+
|
| 1715 |
+
li: Logarithmic integral.
|
| 1716 |
+
Ei: Exponential integral.
|
| 1717 |
+
expint: Generalised exponential integral.
|
| 1718 |
+
E1: Special case of the generalised exponential integral.
|
| 1719 |
+
Si: Sine integral.
|
| 1720 |
+
Ci: Cosine integral.
|
| 1721 |
+
Shi: Hyperbolic sine integral.
|
| 1722 |
+
Chi: Hyperbolic cosine integral.
|
| 1723 |
+
|
| 1724 |
+
References
|
| 1725 |
+
==========
|
| 1726 |
+
|
| 1727 |
+
.. [1] https://en.wikipedia.org/wiki/Logarithmic_integral
|
| 1728 |
+
.. [2] https://mathworld.wolfram.com/LogarithmicIntegral.html
|
| 1729 |
+
.. [3] https://dlmf.nist.gov/6
|
| 1730 |
+
|
| 1731 |
+
"""
|
| 1732 |
+
|
| 1733 |
+
|
| 1734 |
+
@classmethod
|
| 1735 |
+
def eval(cls, z):
|
| 1736 |
+
if z is S.Infinity:
|
| 1737 |
+
return S.Infinity
|
| 1738 |
+
elif z == S(2):
|
| 1739 |
+
return S.Zero
|
| 1740 |
+
|
| 1741 |
+
def fdiff(self, argindex=1):
|
| 1742 |
+
arg = self.args[0]
|
| 1743 |
+
if argindex == 1:
|
| 1744 |
+
return S.One / log(arg)
|
| 1745 |
+
else:
|
| 1746 |
+
raise ArgumentIndexError(self, argindex)
|
| 1747 |
+
|
| 1748 |
+
def _eval_evalf(self, prec):
|
| 1749 |
+
return self.rewrite(li).evalf(prec)
|
| 1750 |
+
|
| 1751 |
+
def _eval_rewrite_as_li(self, z, **kwargs):
|
| 1752 |
+
return li(z) - li(2)
|
| 1753 |
+
|
| 1754 |
+
def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs):
|
| 1755 |
+
return self.rewrite(li).rewrite("tractable", deep=True)
|
| 1756 |
+
|
| 1757 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 1758 |
+
f = self._eval_rewrite_as_li(*self.args)
|
| 1759 |
+
return f._eval_nseries(x, n, logx)
|
| 1760 |
+
|
| 1761 |
+
###############################################################################
|
| 1762 |
+
#################### TRIGONOMETRIC INTEGRALS ##################################
|
| 1763 |
+
###############################################################################
|
| 1764 |
+
|
| 1765 |
+
class TrigonometricIntegral(DefinedFunction):
|
| 1766 |
+
""" Base class for trigonometric integrals. """
|
| 1767 |
+
|
| 1768 |
+
|
| 1769 |
+
@classmethod
|
| 1770 |
+
def eval(cls, z):
|
| 1771 |
+
if z is S.Zero:
|
| 1772 |
+
return cls._atzero
|
| 1773 |
+
elif z is S.Infinity:
|
| 1774 |
+
return cls._atinf()
|
| 1775 |
+
elif z is S.NegativeInfinity:
|
| 1776 |
+
return cls._atneginf()
|
| 1777 |
+
|
| 1778 |
+
if z.is_zero:
|
| 1779 |
+
return cls._atzero
|
| 1780 |
+
|
| 1781 |
+
nz = z.extract_multiplicatively(polar_lift(I))
|
| 1782 |
+
if nz is None and cls._trigfunc(0) == 0:
|
| 1783 |
+
nz = z.extract_multiplicatively(I)
|
| 1784 |
+
if nz is not None:
|
| 1785 |
+
return cls._Ifactor(nz, 1)
|
| 1786 |
+
nz = z.extract_multiplicatively(polar_lift(-I))
|
| 1787 |
+
if nz is not None:
|
| 1788 |
+
return cls._Ifactor(nz, -1)
|
| 1789 |
+
|
| 1790 |
+
nz = z.extract_multiplicatively(polar_lift(-1))
|
| 1791 |
+
if nz is None and cls._trigfunc(0) == 0:
|
| 1792 |
+
nz = z.extract_multiplicatively(-1)
|
| 1793 |
+
if nz is not None:
|
| 1794 |
+
return cls._minusfactor(nz)
|
| 1795 |
+
|
| 1796 |
+
nz, n = z.extract_branch_factor()
|
| 1797 |
+
if n == 0 and nz == z:
|
| 1798 |
+
return
|
| 1799 |
+
return 2*pi*I*n*cls._trigfunc(0) + cls(nz)
|
| 1800 |
+
|
| 1801 |
+
def fdiff(self, argindex=1):
|
| 1802 |
+
arg = unpolarify(self.args[0])
|
| 1803 |
+
if argindex == 1:
|
| 1804 |
+
return self._trigfunc(arg)/arg
|
| 1805 |
+
else:
|
| 1806 |
+
raise ArgumentIndexError(self, argindex)
|
| 1807 |
+
|
| 1808 |
+
def _eval_rewrite_as_Ei(self, z, **kwargs):
|
| 1809 |
+
return self._eval_rewrite_as_expint(z).rewrite(Ei)
|
| 1810 |
+
|
| 1811 |
+
def _eval_rewrite_as_uppergamma(self, z, **kwargs):
|
| 1812 |
+
from sympy.functions.special.gamma_functions import uppergamma
|
| 1813 |
+
return self._eval_rewrite_as_expint(z).rewrite(uppergamma)
|
| 1814 |
+
|
| 1815 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 1816 |
+
# NOTE this is fairly inefficient
|
| 1817 |
+
if self.args[0].subs(x, 0) != 0:
|
| 1818 |
+
return super()._eval_nseries(x, n, logx)
|
| 1819 |
+
baseseries = self._trigfunc(x)._eval_nseries(x, n, logx)
|
| 1820 |
+
if self._trigfunc(0) != 0:
|
| 1821 |
+
baseseries -= 1
|
| 1822 |
+
baseseries = baseseries.replace(Pow, lambda t, n: t**n/n, simultaneous=False)
|
| 1823 |
+
if self._trigfunc(0) != 0:
|
| 1824 |
+
baseseries += EulerGamma + log(x)
|
| 1825 |
+
return baseseries.subs(x, self.args[0])._eval_nseries(x, n, logx)
|
| 1826 |
+
|
| 1827 |
+
|
| 1828 |
+
class Si(TrigonometricIntegral):
|
| 1829 |
+
r"""
|
| 1830 |
+
Sine integral.
|
| 1831 |
+
|
| 1832 |
+
Explanation
|
| 1833 |
+
===========
|
| 1834 |
+
|
| 1835 |
+
This function is defined by
|
| 1836 |
+
|
| 1837 |
+
.. math:: \operatorname{Si}(z) = \int_0^z \frac{\sin{t}}{t} \mathrm{d}t.
|
| 1838 |
+
|
| 1839 |
+
It is an entire function.
|
| 1840 |
+
|
| 1841 |
+
Examples
|
| 1842 |
+
========
|
| 1843 |
+
|
| 1844 |
+
>>> from sympy import Si
|
| 1845 |
+
>>> from sympy.abc import z
|
| 1846 |
+
|
| 1847 |
+
The sine integral is an antiderivative of $sin(z)/z$:
|
| 1848 |
+
|
| 1849 |
+
>>> Si(z).diff(z)
|
| 1850 |
+
sin(z)/z
|
| 1851 |
+
|
| 1852 |
+
It is unbranched:
|
| 1853 |
+
|
| 1854 |
+
>>> from sympy import exp_polar, I, pi
|
| 1855 |
+
>>> Si(z*exp_polar(2*I*pi))
|
| 1856 |
+
Si(z)
|
| 1857 |
+
|
| 1858 |
+
Sine integral behaves much like ordinary sine under multiplication by ``I``:
|
| 1859 |
+
|
| 1860 |
+
>>> Si(I*z)
|
| 1861 |
+
I*Shi(z)
|
| 1862 |
+
>>> Si(-z)
|
| 1863 |
+
-Si(z)
|
| 1864 |
+
|
| 1865 |
+
It can also be expressed in terms of exponential integrals, but beware
|
| 1866 |
+
that the latter is branched:
|
| 1867 |
+
|
| 1868 |
+
>>> from sympy import expint
|
| 1869 |
+
>>> Si(z).rewrite(expint)
|
| 1870 |
+
-I*(-expint(1, z*exp_polar(-I*pi/2))/2 +
|
| 1871 |
+
expint(1, z*exp_polar(I*pi/2))/2) + pi/2
|
| 1872 |
+
|
| 1873 |
+
It can be rewritten in the form of sinc function (by definition):
|
| 1874 |
+
|
| 1875 |
+
>>> from sympy import sinc
|
| 1876 |
+
>>> Si(z).rewrite(sinc)
|
| 1877 |
+
Integral(sinc(_t), (_t, 0, z))
|
| 1878 |
+
|
| 1879 |
+
See Also
|
| 1880 |
+
========
|
| 1881 |
+
|
| 1882 |
+
Ci: Cosine integral.
|
| 1883 |
+
Shi: Hyperbolic sine integral.
|
| 1884 |
+
Chi: Hyperbolic cosine integral.
|
| 1885 |
+
Ei: Exponential integral.
|
| 1886 |
+
expint: Generalised exponential integral.
|
| 1887 |
+
sinc: unnormalized sinc function
|
| 1888 |
+
E1: Special case of the generalised exponential integral.
|
| 1889 |
+
li: Logarithmic integral.
|
| 1890 |
+
Li: Offset logarithmic integral.
|
| 1891 |
+
|
| 1892 |
+
References
|
| 1893 |
+
==========
|
| 1894 |
+
|
| 1895 |
+
.. [1] https://en.wikipedia.org/wiki/Trigonometric_integral
|
| 1896 |
+
|
| 1897 |
+
"""
|
| 1898 |
+
|
| 1899 |
+
_trigfunc = sin
|
| 1900 |
+
_atzero = S.Zero
|
| 1901 |
+
|
| 1902 |
+
@classmethod
|
| 1903 |
+
def _atinf(cls):
|
| 1904 |
+
return pi*S.Half
|
| 1905 |
+
|
| 1906 |
+
@classmethod
|
| 1907 |
+
def _atneginf(cls):
|
| 1908 |
+
return -pi*S.Half
|
| 1909 |
+
|
| 1910 |
+
@classmethod
|
| 1911 |
+
def _minusfactor(cls, z):
|
| 1912 |
+
return -Si(z)
|
| 1913 |
+
|
| 1914 |
+
@classmethod
|
| 1915 |
+
def _Ifactor(cls, z, sign):
|
| 1916 |
+
return I*Shi(z)*sign
|
| 1917 |
+
|
| 1918 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 1919 |
+
# XXX should we polarify z?
|
| 1920 |
+
return pi/2 + (E1(polar_lift(I)*z) - E1(polar_lift(-I)*z))/2/I
|
| 1921 |
+
|
| 1922 |
+
def _eval_rewrite_as_Integral(self, z, **kwargs):
|
| 1923 |
+
from sympy.integrals.integrals import Integral
|
| 1924 |
+
t = Dummy(uniquely_named_symbol('t', [z]).name)
|
| 1925 |
+
return Integral(sinc(t), (t, 0, z))
|
| 1926 |
+
|
| 1927 |
+
_eval_rewrite_as_sinc = _eval_rewrite_as_Integral
|
| 1928 |
+
|
| 1929 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1930 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 1931 |
+
arg0 = arg.subs(x, 0)
|
| 1932 |
+
|
| 1933 |
+
if arg0 is S.NaN:
|
| 1934 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 1935 |
+
if arg0.is_zero:
|
| 1936 |
+
return arg
|
| 1937 |
+
elif not arg0.is_infinite:
|
| 1938 |
+
return self.func(arg0)
|
| 1939 |
+
else:
|
| 1940 |
+
return self
|
| 1941 |
+
|
| 1942 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 1943 |
+
from sympy.series.order import Order
|
| 1944 |
+
point = args0[0]
|
| 1945 |
+
|
| 1946 |
+
# Expansion at oo
|
| 1947 |
+
if point is S.Infinity:
|
| 1948 |
+
z = self.args[0]
|
| 1949 |
+
p = [S.NegativeOne**k * factorial(2*k) / z**(2*k + 1)
|
| 1950 |
+
for k in range(n//2 + 1)] + [Order(1/z**n, x)]
|
| 1951 |
+
q = [S.NegativeOne**k * factorial(2*k + 1) / z**(2*(k + 1))
|
| 1952 |
+
for k in range(n//2)] + [Order(1/z**n, x)]
|
| 1953 |
+
return pi/2 - cos(z)*Add(*p) - sin(z)*Add(*q)
|
| 1954 |
+
|
| 1955 |
+
# All other points are not handled
|
| 1956 |
+
return super(Si, self)._eval_aseries(n, args0, x, logx)
|
| 1957 |
+
|
| 1958 |
+
def _eval_is_zero(self):
|
| 1959 |
+
z = self.args[0]
|
| 1960 |
+
if z.is_zero:
|
| 1961 |
+
return True
|
| 1962 |
+
|
| 1963 |
+
|
| 1964 |
+
class Ci(TrigonometricIntegral):
|
| 1965 |
+
r"""
|
| 1966 |
+
Cosine integral.
|
| 1967 |
+
|
| 1968 |
+
Explanation
|
| 1969 |
+
===========
|
| 1970 |
+
|
| 1971 |
+
This function is defined for positive $x$ by
|
| 1972 |
+
|
| 1973 |
+
.. math:: \operatorname{Ci}(x) = \gamma + \log{x}
|
| 1974 |
+
+ \int_0^x \frac{\cos{t} - 1}{t} \mathrm{d}t
|
| 1975 |
+
= -\int_x^\infty \frac{\cos{t}}{t} \mathrm{d}t,
|
| 1976 |
+
|
| 1977 |
+
where $\gamma$ is the Euler-Mascheroni constant.
|
| 1978 |
+
|
| 1979 |
+
We have
|
| 1980 |
+
|
| 1981 |
+
.. math:: \operatorname{Ci}(z) =
|
| 1982 |
+
-\frac{\operatorname{E}_1\left(e^{i\pi/2} z\right)
|
| 1983 |
+
+ \operatorname{E}_1\left(e^{-i \pi/2} z\right)}{2}
|
| 1984 |
+
|
| 1985 |
+
which holds for all polar $z$ and thus provides an analytic
|
| 1986 |
+
continuation to the Riemann surface of the logarithm.
|
| 1987 |
+
|
| 1988 |
+
The formula also holds as stated
|
| 1989 |
+
for $z \in \mathbb{C}$ with $\Re(z) > 0$.
|
| 1990 |
+
By lifting to the principal branch, we obtain an analytic function on the
|
| 1991 |
+
cut complex plane.
|
| 1992 |
+
|
| 1993 |
+
Examples
|
| 1994 |
+
========
|
| 1995 |
+
|
| 1996 |
+
>>> from sympy import Ci
|
| 1997 |
+
>>> from sympy.abc import z
|
| 1998 |
+
|
| 1999 |
+
The cosine integral is a primitive of $\cos(z)/z$:
|
| 2000 |
+
|
| 2001 |
+
>>> Ci(z).diff(z)
|
| 2002 |
+
cos(z)/z
|
| 2003 |
+
|
| 2004 |
+
It has a logarithmic branch point at the origin:
|
| 2005 |
+
|
| 2006 |
+
>>> from sympy import exp_polar, I, pi
|
| 2007 |
+
>>> Ci(z*exp_polar(2*I*pi))
|
| 2008 |
+
Ci(z) + 2*I*pi
|
| 2009 |
+
|
| 2010 |
+
The cosine integral behaves somewhat like ordinary $\cos$ under
|
| 2011 |
+
multiplication by $i$:
|
| 2012 |
+
|
| 2013 |
+
>>> from sympy import polar_lift
|
| 2014 |
+
>>> Ci(polar_lift(I)*z)
|
| 2015 |
+
Chi(z) + I*pi/2
|
| 2016 |
+
>>> Ci(polar_lift(-1)*z)
|
| 2017 |
+
Ci(z) + I*pi
|
| 2018 |
+
|
| 2019 |
+
It can also be expressed in terms of exponential integrals:
|
| 2020 |
+
|
| 2021 |
+
>>> from sympy import expint
|
| 2022 |
+
>>> Ci(z).rewrite(expint)
|
| 2023 |
+
-expint(1, z*exp_polar(-I*pi/2))/2 - expint(1, z*exp_polar(I*pi/2))/2
|
| 2024 |
+
|
| 2025 |
+
See Also
|
| 2026 |
+
========
|
| 2027 |
+
|
| 2028 |
+
Si: Sine integral.
|
| 2029 |
+
Shi: Hyperbolic sine integral.
|
| 2030 |
+
Chi: Hyperbolic cosine integral.
|
| 2031 |
+
Ei: Exponential integral.
|
| 2032 |
+
expint: Generalised exponential integral.
|
| 2033 |
+
E1: Special case of the generalised exponential integral.
|
| 2034 |
+
li: Logarithmic integral.
|
| 2035 |
+
Li: Offset logarithmic integral.
|
| 2036 |
+
|
| 2037 |
+
References
|
| 2038 |
+
==========
|
| 2039 |
+
|
| 2040 |
+
.. [1] https://en.wikipedia.org/wiki/Trigonometric_integral
|
| 2041 |
+
|
| 2042 |
+
"""
|
| 2043 |
+
|
| 2044 |
+
_trigfunc = cos
|
| 2045 |
+
_atzero = S.ComplexInfinity
|
| 2046 |
+
|
| 2047 |
+
@classmethod
|
| 2048 |
+
def _atinf(cls):
|
| 2049 |
+
return S.Zero
|
| 2050 |
+
|
| 2051 |
+
@classmethod
|
| 2052 |
+
def _atneginf(cls):
|
| 2053 |
+
return I*pi
|
| 2054 |
+
|
| 2055 |
+
@classmethod
|
| 2056 |
+
def _minusfactor(cls, z):
|
| 2057 |
+
return Ci(z) + I*pi
|
| 2058 |
+
|
| 2059 |
+
@classmethod
|
| 2060 |
+
def _Ifactor(cls, z, sign):
|
| 2061 |
+
return Chi(z) + I*pi/2*sign
|
| 2062 |
+
|
| 2063 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 2064 |
+
return -(E1(polar_lift(I)*z) + E1(polar_lift(-I)*z))/2
|
| 2065 |
+
|
| 2066 |
+
def _eval_rewrite_as_Integral(self, z, **kwargs):
|
| 2067 |
+
from sympy.integrals.integrals import Integral
|
| 2068 |
+
t = Dummy(uniquely_named_symbol('t', [z]).name)
|
| 2069 |
+
return S.EulerGamma + log(z) - Integral((1-cos(t))/t, (t, 0, z))
|
| 2070 |
+
|
| 2071 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 2072 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 2073 |
+
arg0 = arg.subs(x, 0)
|
| 2074 |
+
|
| 2075 |
+
if arg0 is S.NaN:
|
| 2076 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 2077 |
+
if arg0.is_zero:
|
| 2078 |
+
c, e = arg.as_coeff_exponent(x)
|
| 2079 |
+
logx = log(x) if logx is None else logx
|
| 2080 |
+
return log(c) + e*logx + EulerGamma
|
| 2081 |
+
elif arg0.is_finite:
|
| 2082 |
+
return self.func(arg0)
|
| 2083 |
+
else:
|
| 2084 |
+
return self
|
| 2085 |
+
|
| 2086 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 2087 |
+
from sympy.series.order import Order
|
| 2088 |
+
point = args0[0]
|
| 2089 |
+
|
| 2090 |
+
if point in (S.Infinity, S.NegativeInfinity):
|
| 2091 |
+
z = self.args[0]
|
| 2092 |
+
p = [S.NegativeOne**k * factorial(2*k) / z**(2*k + 1)
|
| 2093 |
+
for k in range(n//2 + 1)] + [Order(1/z**n, x)]
|
| 2094 |
+
q = [S.NegativeOne**k * factorial(2*k + 1) / z**(2*(k + 1))
|
| 2095 |
+
for k in range(n//2)] + [Order(1/z**n, x)]
|
| 2096 |
+
result = sin(z)*(Add(*p)) - cos(z)*(Add(*q))
|
| 2097 |
+
|
| 2098 |
+
if point is S.NegativeInfinity:
|
| 2099 |
+
result += I*pi
|
| 2100 |
+
return result
|
| 2101 |
+
|
| 2102 |
+
return super(Ci, self)._eval_aseries(n, args0, x, logx)
|
| 2103 |
+
|
| 2104 |
+
class Shi(TrigonometricIntegral):
|
| 2105 |
+
r"""
|
| 2106 |
+
Sinh integral.
|
| 2107 |
+
|
| 2108 |
+
Explanation
|
| 2109 |
+
===========
|
| 2110 |
+
|
| 2111 |
+
This function is defined by
|
| 2112 |
+
|
| 2113 |
+
.. math:: \operatorname{Shi}(z) = \int_0^z \frac{\sinh{t}}{t} \mathrm{d}t.
|
| 2114 |
+
|
| 2115 |
+
It is an entire function.
|
| 2116 |
+
|
| 2117 |
+
Examples
|
| 2118 |
+
========
|
| 2119 |
+
|
| 2120 |
+
>>> from sympy import Shi
|
| 2121 |
+
>>> from sympy.abc import z
|
| 2122 |
+
|
| 2123 |
+
The Sinh integral is a primitive of $\sinh(z)/z$:
|
| 2124 |
+
|
| 2125 |
+
>>> Shi(z).diff(z)
|
| 2126 |
+
sinh(z)/z
|
| 2127 |
+
|
| 2128 |
+
It is unbranched:
|
| 2129 |
+
|
| 2130 |
+
>>> from sympy import exp_polar, I, pi
|
| 2131 |
+
>>> Shi(z*exp_polar(2*I*pi))
|
| 2132 |
+
Shi(z)
|
| 2133 |
+
|
| 2134 |
+
The $\sinh$ integral behaves much like ordinary $\sinh$ under
|
| 2135 |
+
multiplication by $i$:
|
| 2136 |
+
|
| 2137 |
+
>>> Shi(I*z)
|
| 2138 |
+
I*Si(z)
|
| 2139 |
+
>>> Shi(-z)
|
| 2140 |
+
-Shi(z)
|
| 2141 |
+
|
| 2142 |
+
It can also be expressed in terms of exponential integrals, but beware
|
| 2143 |
+
that the latter is branched:
|
| 2144 |
+
|
| 2145 |
+
>>> from sympy import expint
|
| 2146 |
+
>>> Shi(z).rewrite(expint)
|
| 2147 |
+
expint(1, z)/2 - expint(1, z*exp_polar(I*pi))/2 - I*pi/2
|
| 2148 |
+
|
| 2149 |
+
See Also
|
| 2150 |
+
========
|
| 2151 |
+
|
| 2152 |
+
Si: Sine integral.
|
| 2153 |
+
Ci: Cosine integral.
|
| 2154 |
+
Chi: Hyperbolic cosine integral.
|
| 2155 |
+
Ei: Exponential integral.
|
| 2156 |
+
expint: Generalised exponential integral.
|
| 2157 |
+
E1: Special case of the generalised exponential integral.
|
| 2158 |
+
li: Logarithmic integral.
|
| 2159 |
+
Li: Offset logarithmic integral.
|
| 2160 |
+
|
| 2161 |
+
References
|
| 2162 |
+
==========
|
| 2163 |
+
|
| 2164 |
+
.. [1] https://en.wikipedia.org/wiki/Trigonometric_integral
|
| 2165 |
+
|
| 2166 |
+
"""
|
| 2167 |
+
|
| 2168 |
+
_trigfunc = sinh
|
| 2169 |
+
_atzero = S.Zero
|
| 2170 |
+
|
| 2171 |
+
@classmethod
|
| 2172 |
+
def _atinf(cls):
|
| 2173 |
+
return S.Infinity
|
| 2174 |
+
|
| 2175 |
+
@classmethod
|
| 2176 |
+
def _atneginf(cls):
|
| 2177 |
+
return S.NegativeInfinity
|
| 2178 |
+
|
| 2179 |
+
@classmethod
|
| 2180 |
+
def _minusfactor(cls, z):
|
| 2181 |
+
return -Shi(z)
|
| 2182 |
+
|
| 2183 |
+
@classmethod
|
| 2184 |
+
def _Ifactor(cls, z, sign):
|
| 2185 |
+
return I*Si(z)*sign
|
| 2186 |
+
|
| 2187 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 2188 |
+
# XXX should we polarify z?
|
| 2189 |
+
return (E1(z) - E1(exp_polar(I*pi)*z))/2 - I*pi/2
|
| 2190 |
+
|
| 2191 |
+
def _eval_is_zero(self):
|
| 2192 |
+
z = self.args[0]
|
| 2193 |
+
if z.is_zero:
|
| 2194 |
+
return True
|
| 2195 |
+
|
| 2196 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 2197 |
+
arg = self.args[0].as_leading_term(x)
|
| 2198 |
+
arg0 = arg.subs(x, 0)
|
| 2199 |
+
|
| 2200 |
+
if arg0 is S.NaN:
|
| 2201 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 2202 |
+
if arg0.is_zero:
|
| 2203 |
+
return arg
|
| 2204 |
+
elif not arg0.is_infinite:
|
| 2205 |
+
return self.func(arg0)
|
| 2206 |
+
else:
|
| 2207 |
+
return self
|
| 2208 |
+
|
| 2209 |
+
|
| 2210 |
+
class Chi(TrigonometricIntegral):
|
| 2211 |
+
r"""
|
| 2212 |
+
Cosh integral.
|
| 2213 |
+
|
| 2214 |
+
Explanation
|
| 2215 |
+
===========
|
| 2216 |
+
|
| 2217 |
+
This function is defined for positive $x$ by
|
| 2218 |
+
|
| 2219 |
+
.. math:: \operatorname{Chi}(x) = \gamma + \log{x}
|
| 2220 |
+
+ \int_0^x \frac{\cosh{t} - 1}{t} \mathrm{d}t,
|
| 2221 |
+
|
| 2222 |
+
where $\gamma$ is the Euler-Mascheroni constant.
|
| 2223 |
+
|
| 2224 |
+
We have
|
| 2225 |
+
|
| 2226 |
+
.. math:: \operatorname{Chi}(z) = \operatorname{Ci}\left(e^{i \pi/2}z\right)
|
| 2227 |
+
- i\frac{\pi}{2},
|
| 2228 |
+
|
| 2229 |
+
which holds for all polar $z$ and thus provides an analytic
|
| 2230 |
+
continuation to the Riemann surface of the logarithm.
|
| 2231 |
+
By lifting to the principal branch we obtain an analytic function on the
|
| 2232 |
+
cut complex plane.
|
| 2233 |
+
|
| 2234 |
+
Examples
|
| 2235 |
+
========
|
| 2236 |
+
|
| 2237 |
+
>>> from sympy import Chi
|
| 2238 |
+
>>> from sympy.abc import z
|
| 2239 |
+
|
| 2240 |
+
The $\cosh$ integral is a primitive of $\cosh(z)/z$:
|
| 2241 |
+
|
| 2242 |
+
>>> Chi(z).diff(z)
|
| 2243 |
+
cosh(z)/z
|
| 2244 |
+
|
| 2245 |
+
It has a logarithmic branch point at the origin:
|
| 2246 |
+
|
| 2247 |
+
>>> from sympy import exp_polar, I, pi
|
| 2248 |
+
>>> Chi(z*exp_polar(2*I*pi))
|
| 2249 |
+
Chi(z) + 2*I*pi
|
| 2250 |
+
|
| 2251 |
+
The $\cosh$ integral behaves somewhat like ordinary $\cosh$ under
|
| 2252 |
+
multiplication by $i$:
|
| 2253 |
+
|
| 2254 |
+
>>> from sympy import polar_lift
|
| 2255 |
+
>>> Chi(polar_lift(I)*z)
|
| 2256 |
+
Ci(z) + I*pi/2
|
| 2257 |
+
>>> Chi(polar_lift(-1)*z)
|
| 2258 |
+
Chi(z) + I*pi
|
| 2259 |
+
|
| 2260 |
+
It can also be expressed in terms of exponential integrals:
|
| 2261 |
+
|
| 2262 |
+
>>> from sympy import expint
|
| 2263 |
+
>>> Chi(z).rewrite(expint)
|
| 2264 |
+
-expint(1, z)/2 - expint(1, z*exp_polar(I*pi))/2 - I*pi/2
|
| 2265 |
+
|
| 2266 |
+
See Also
|
| 2267 |
+
========
|
| 2268 |
+
|
| 2269 |
+
Si: Sine integral.
|
| 2270 |
+
Ci: Cosine integral.
|
| 2271 |
+
Shi: Hyperbolic sine integral.
|
| 2272 |
+
Ei: Exponential integral.
|
| 2273 |
+
expint: Generalised exponential integral.
|
| 2274 |
+
E1: Special case of the generalised exponential integral.
|
| 2275 |
+
li: Logarithmic integral.
|
| 2276 |
+
Li: Offset logarithmic integral.
|
| 2277 |
+
|
| 2278 |
+
References
|
| 2279 |
+
==========
|
| 2280 |
+
|
| 2281 |
+
.. [1] https://en.wikipedia.org/wiki/Trigonometric_integral
|
| 2282 |
+
|
| 2283 |
+
"""
|
| 2284 |
+
|
| 2285 |
+
_trigfunc = cosh
|
| 2286 |
+
_atzero = S.ComplexInfinity
|
| 2287 |
+
|
| 2288 |
+
@classmethod
|
| 2289 |
+
def _atinf(cls):
|
| 2290 |
+
return S.Infinity
|
| 2291 |
+
|
| 2292 |
+
@classmethod
|
| 2293 |
+
def _atneginf(cls):
|
| 2294 |
+
return S.Infinity
|
| 2295 |
+
|
| 2296 |
+
@classmethod
|
| 2297 |
+
def _minusfactor(cls, z):
|
| 2298 |
+
return Chi(z) + I*pi
|
| 2299 |
+
|
| 2300 |
+
@classmethod
|
| 2301 |
+
def _Ifactor(cls, z, sign):
|
| 2302 |
+
return Ci(z) + I*pi/2*sign
|
| 2303 |
+
|
| 2304 |
+
def _eval_rewrite_as_expint(self, z, **kwargs):
|
| 2305 |
+
return -I*pi/2 - (E1(z) + E1(exp_polar(I*pi)*z))/2
|
| 2306 |
+
|
| 2307 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 2308 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 2309 |
+
arg0 = arg.subs(x, 0)
|
| 2310 |
+
|
| 2311 |
+
if arg0 is S.NaN:
|
| 2312 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 2313 |
+
if arg0.is_zero:
|
| 2314 |
+
c, e = arg.as_coeff_exponent(x)
|
| 2315 |
+
logx = log(x) if logx is None else logx
|
| 2316 |
+
return log(c) + e*logx + EulerGamma
|
| 2317 |
+
elif arg0.is_finite:
|
| 2318 |
+
return self.func(arg0)
|
| 2319 |
+
else:
|
| 2320 |
+
return self
|
| 2321 |
+
|
| 2322 |
+
|
| 2323 |
+
###############################################################################
|
| 2324 |
+
#################### FRESNEL INTEGRALS ########################################
|
| 2325 |
+
###############################################################################
|
| 2326 |
+
|
| 2327 |
+
class FresnelIntegral(DefinedFunction):
|
| 2328 |
+
""" Base class for the Fresnel integrals."""
|
| 2329 |
+
|
| 2330 |
+
unbranched = True
|
| 2331 |
+
|
| 2332 |
+
@classmethod
|
| 2333 |
+
def eval(cls, z):
|
| 2334 |
+
# Values at positive infinities signs
|
| 2335 |
+
# if any were extracted automatically
|
| 2336 |
+
if z is S.Infinity:
|
| 2337 |
+
return S.Half
|
| 2338 |
+
|
| 2339 |
+
# Value at zero
|
| 2340 |
+
if z.is_zero:
|
| 2341 |
+
return S.Zero
|
| 2342 |
+
|
| 2343 |
+
# Try to pull out factors of -1 and I
|
| 2344 |
+
prefact = S.One
|
| 2345 |
+
newarg = z
|
| 2346 |
+
changed = False
|
| 2347 |
+
|
| 2348 |
+
nz = newarg.extract_multiplicatively(-1)
|
| 2349 |
+
if nz is not None:
|
| 2350 |
+
prefact = -prefact
|
| 2351 |
+
newarg = nz
|
| 2352 |
+
changed = True
|
| 2353 |
+
|
| 2354 |
+
nz = newarg.extract_multiplicatively(I)
|
| 2355 |
+
if nz is not None:
|
| 2356 |
+
prefact = cls._sign*I*prefact
|
| 2357 |
+
newarg = nz
|
| 2358 |
+
changed = True
|
| 2359 |
+
|
| 2360 |
+
if changed:
|
| 2361 |
+
return prefact*cls(newarg)
|
| 2362 |
+
|
| 2363 |
+
def fdiff(self, argindex=1):
|
| 2364 |
+
if argindex == 1:
|
| 2365 |
+
return self._trigfunc(S.Half*pi*self.args[0]**2)
|
| 2366 |
+
else:
|
| 2367 |
+
raise ArgumentIndexError(self, argindex)
|
| 2368 |
+
|
| 2369 |
+
def _eval_is_extended_real(self):
|
| 2370 |
+
return self.args[0].is_extended_real
|
| 2371 |
+
|
| 2372 |
+
_eval_is_finite = _eval_is_extended_real
|
| 2373 |
+
|
| 2374 |
+
def _eval_is_zero(self):
|
| 2375 |
+
return self.args[0].is_zero
|
| 2376 |
+
|
| 2377 |
+
def _eval_conjugate(self):
|
| 2378 |
+
return self.func(self.args[0].conjugate())
|
| 2379 |
+
|
| 2380 |
+
as_real_imag = real_to_real_as_real_imag
|
| 2381 |
+
|
| 2382 |
+
|
| 2383 |
+
class fresnels(FresnelIntegral):
|
| 2384 |
+
r"""
|
| 2385 |
+
Fresnel integral S.
|
| 2386 |
+
|
| 2387 |
+
Explanation
|
| 2388 |
+
===========
|
| 2389 |
+
|
| 2390 |
+
This function is defined by
|
| 2391 |
+
|
| 2392 |
+
.. math:: \operatorname{S}(z) = \int_0^z \sin{\frac{\pi}{2} t^2} \mathrm{d}t.
|
| 2393 |
+
|
| 2394 |
+
It is an entire function.
|
| 2395 |
+
|
| 2396 |
+
Examples
|
| 2397 |
+
========
|
| 2398 |
+
|
| 2399 |
+
>>> from sympy import I, oo, fresnels
|
| 2400 |
+
>>> from sympy.abc import z
|
| 2401 |
+
|
| 2402 |
+
Several special values are known:
|
| 2403 |
+
|
| 2404 |
+
>>> fresnels(0)
|
| 2405 |
+
0
|
| 2406 |
+
>>> fresnels(oo)
|
| 2407 |
+
1/2
|
| 2408 |
+
>>> fresnels(-oo)
|
| 2409 |
+
-1/2
|
| 2410 |
+
>>> fresnels(I*oo)
|
| 2411 |
+
-I/2
|
| 2412 |
+
>>> fresnels(-I*oo)
|
| 2413 |
+
I/2
|
| 2414 |
+
|
| 2415 |
+
In general one can pull out factors of -1 and $i$ from the argument:
|
| 2416 |
+
|
| 2417 |
+
>>> fresnels(-z)
|
| 2418 |
+
-fresnels(z)
|
| 2419 |
+
>>> fresnels(I*z)
|
| 2420 |
+
-I*fresnels(z)
|
| 2421 |
+
|
| 2422 |
+
The Fresnel S integral obeys the mirror symmetry
|
| 2423 |
+
$\overline{S(z)} = S(\bar{z})$:
|
| 2424 |
+
|
| 2425 |
+
>>> from sympy import conjugate
|
| 2426 |
+
>>> conjugate(fresnels(z))
|
| 2427 |
+
fresnels(conjugate(z))
|
| 2428 |
+
|
| 2429 |
+
Differentiation with respect to $z$ is supported:
|
| 2430 |
+
|
| 2431 |
+
>>> from sympy import diff
|
| 2432 |
+
>>> diff(fresnels(z), z)
|
| 2433 |
+
sin(pi*z**2/2)
|
| 2434 |
+
|
| 2435 |
+
Defining the Fresnel functions via an integral:
|
| 2436 |
+
|
| 2437 |
+
>>> from sympy import integrate, pi, sin, expand_func
|
| 2438 |
+
>>> integrate(sin(pi*z**2/2), z)
|
| 2439 |
+
3*fresnels(z)*gamma(3/4)/(4*gamma(7/4))
|
| 2440 |
+
>>> expand_func(integrate(sin(pi*z**2/2), z))
|
| 2441 |
+
fresnels(z)
|
| 2442 |
+
|
| 2443 |
+
We can numerically evaluate the Fresnel integral to arbitrary precision
|
| 2444 |
+
on the whole complex plane:
|
| 2445 |
+
|
| 2446 |
+
>>> fresnels(2).evalf(30)
|
| 2447 |
+
0.343415678363698242195300815958
|
| 2448 |
+
|
| 2449 |
+
>>> fresnels(-2*I).evalf(30)
|
| 2450 |
+
0.343415678363698242195300815958*I
|
| 2451 |
+
|
| 2452 |
+
See Also
|
| 2453 |
+
========
|
| 2454 |
+
|
| 2455 |
+
fresnelc: Fresnel cosine integral.
|
| 2456 |
+
|
| 2457 |
+
References
|
| 2458 |
+
==========
|
| 2459 |
+
|
| 2460 |
+
.. [1] https://en.wikipedia.org/wiki/Fresnel_integral
|
| 2461 |
+
.. [2] https://dlmf.nist.gov/7
|
| 2462 |
+
.. [3] https://mathworld.wolfram.com/FresnelIntegrals.html
|
| 2463 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/FresnelS
|
| 2464 |
+
.. [5] The converging factors for the fresnel integrals
|
| 2465 |
+
by John W. Wrench Jr. and Vicki Alley
|
| 2466 |
+
|
| 2467 |
+
"""
|
| 2468 |
+
_trigfunc = sin
|
| 2469 |
+
_sign = -S.One
|
| 2470 |
+
|
| 2471 |
+
@staticmethod
|
| 2472 |
+
@cacheit
|
| 2473 |
+
def taylor_term(n, x, *previous_terms):
|
| 2474 |
+
if n < 0:
|
| 2475 |
+
return S.Zero
|
| 2476 |
+
else:
|
| 2477 |
+
x = sympify(x)
|
| 2478 |
+
if len(previous_terms) > 1:
|
| 2479 |
+
p = previous_terms[-1]
|
| 2480 |
+
return (-pi**2*x**4*(4*n - 1)/(8*n*(2*n + 1)*(4*n + 3))) * p
|
| 2481 |
+
else:
|
| 2482 |
+
return x**3 * (-x**4)**n * (S(2)**(-2*n - 1)*pi**(2*n + 1)) / ((4*n + 3)*factorial(2*n + 1))
|
| 2483 |
+
|
| 2484 |
+
def _eval_rewrite_as_erf(self, z, **kwargs):
|
| 2485 |
+
return (S.One + I)/4 * (erf((S.One + I)/2*sqrt(pi)*z) - I*erf((S.One - I)/2*sqrt(pi)*z))
|
| 2486 |
+
|
| 2487 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 2488 |
+
return pi*z**3/6 * hyper([Rational(3, 4)], [Rational(3, 2), Rational(7, 4)], -pi**2*z**4/16)
|
| 2489 |
+
|
| 2490 |
+
def _eval_rewrite_as_meijerg(self, z, **kwargs):
|
| 2491 |
+
return (pi*z**Rational(9, 4) / (sqrt(2)*(z**2)**Rational(3, 4)*(-z)**Rational(3, 4))
|
| 2492 |
+
* meijerg([], [1], [Rational(3, 4)], [Rational(1, 4), 0], -pi**2*z**4/16))
|
| 2493 |
+
|
| 2494 |
+
def _eval_rewrite_as_Integral(self, z, **kwargs):
|
| 2495 |
+
from sympy.integrals.integrals import Integral
|
| 2496 |
+
t = Dummy(uniquely_named_symbol('t', [z]).name)
|
| 2497 |
+
return Integral(sin(pi*t**2/2), (t, 0, z))
|
| 2498 |
+
|
| 2499 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 2500 |
+
from sympy.series.order import Order
|
| 2501 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 2502 |
+
arg0 = arg.subs(x, 0)
|
| 2503 |
+
|
| 2504 |
+
if arg0 is S.ComplexInfinity:
|
| 2505 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 2506 |
+
if arg0.is_zero:
|
| 2507 |
+
return pi*arg**3/6
|
| 2508 |
+
elif arg0 in [S.Infinity, S.NegativeInfinity]:
|
| 2509 |
+
s = 1 if arg0 is S.Infinity else -1
|
| 2510 |
+
return s*S.Half + Order(x, x)
|
| 2511 |
+
else:
|
| 2512 |
+
return self.func(arg0)
|
| 2513 |
+
|
| 2514 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 2515 |
+
from sympy.series.order import Order
|
| 2516 |
+
point = args0[0]
|
| 2517 |
+
|
| 2518 |
+
# Expansion at oo and -oo
|
| 2519 |
+
if point in [S.Infinity, -S.Infinity]:
|
| 2520 |
+
z = self.args[0]
|
| 2521 |
+
|
| 2522 |
+
# expansion of S(x) = S1(x*sqrt(pi/2)), see reference[5] page 1-8
|
| 2523 |
+
# as only real infinities are dealt with, sin and cos are O(1)
|
| 2524 |
+
p = [S.NegativeOne**k * factorial(4*k + 1) /
|
| 2525 |
+
(2**(2*k + 2) * z**(4*k + 3) * 2**(2*k)*factorial(2*k))
|
| 2526 |
+
for k in range(0, n) if 4*k + 3 < n]
|
| 2527 |
+
q = [1/(2*z)] + [S.NegativeOne**k * factorial(4*k - 1) /
|
| 2528 |
+
(2**(2*k + 1) * z**(4*k + 1) * 2**(2*k - 1)*factorial(2*k - 1))
|
| 2529 |
+
for k in range(1, n) if 4*k + 1 < n]
|
| 2530 |
+
|
| 2531 |
+
p = [-sqrt(2/pi)*t for t in p]
|
| 2532 |
+
q = [-sqrt(2/pi)*t for t in q]
|
| 2533 |
+
s = 1 if point is S.Infinity else -1
|
| 2534 |
+
# The expansion at oo is 1/2 + some odd powers of z
|
| 2535 |
+
# To get the expansion at -oo, replace z by -z and flip the sign
|
| 2536 |
+
# The result -1/2 + the same odd powers of z as before.
|
| 2537 |
+
return s*S.Half + (sin(z**2)*Add(*p) + cos(z**2)*Add(*q)
|
| 2538 |
+
).subs(x, sqrt(2/pi)*x) + Order(1/z**n, x)
|
| 2539 |
+
|
| 2540 |
+
# All other points are not handled
|
| 2541 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 2542 |
+
|
| 2543 |
+
|
| 2544 |
+
class fresnelc(FresnelIntegral):
|
| 2545 |
+
r"""
|
| 2546 |
+
Fresnel integral C.
|
| 2547 |
+
|
| 2548 |
+
Explanation
|
| 2549 |
+
===========
|
| 2550 |
+
|
| 2551 |
+
This function is defined by
|
| 2552 |
+
|
| 2553 |
+
.. math:: \operatorname{C}(z) = \int_0^z \cos{\frac{\pi}{2} t^2} \mathrm{d}t.
|
| 2554 |
+
|
| 2555 |
+
It is an entire function.
|
| 2556 |
+
|
| 2557 |
+
Examples
|
| 2558 |
+
========
|
| 2559 |
+
|
| 2560 |
+
>>> from sympy import I, oo, fresnelc
|
| 2561 |
+
>>> from sympy.abc import z
|
| 2562 |
+
|
| 2563 |
+
Several special values are known:
|
| 2564 |
+
|
| 2565 |
+
>>> fresnelc(0)
|
| 2566 |
+
0
|
| 2567 |
+
>>> fresnelc(oo)
|
| 2568 |
+
1/2
|
| 2569 |
+
>>> fresnelc(-oo)
|
| 2570 |
+
-1/2
|
| 2571 |
+
>>> fresnelc(I*oo)
|
| 2572 |
+
I/2
|
| 2573 |
+
>>> fresnelc(-I*oo)
|
| 2574 |
+
-I/2
|
| 2575 |
+
|
| 2576 |
+
In general one can pull out factors of -1 and $i$ from the argument:
|
| 2577 |
+
|
| 2578 |
+
>>> fresnelc(-z)
|
| 2579 |
+
-fresnelc(z)
|
| 2580 |
+
>>> fresnelc(I*z)
|
| 2581 |
+
I*fresnelc(z)
|
| 2582 |
+
|
| 2583 |
+
The Fresnel C integral obeys the mirror symmetry
|
| 2584 |
+
$\overline{C(z)} = C(\bar{z})$:
|
| 2585 |
+
|
| 2586 |
+
>>> from sympy import conjugate
|
| 2587 |
+
>>> conjugate(fresnelc(z))
|
| 2588 |
+
fresnelc(conjugate(z))
|
| 2589 |
+
|
| 2590 |
+
Differentiation with respect to $z$ is supported:
|
| 2591 |
+
|
| 2592 |
+
>>> from sympy import diff
|
| 2593 |
+
>>> diff(fresnelc(z), z)
|
| 2594 |
+
cos(pi*z**2/2)
|
| 2595 |
+
|
| 2596 |
+
Defining the Fresnel functions via an integral:
|
| 2597 |
+
|
| 2598 |
+
>>> from sympy import integrate, pi, cos, expand_func
|
| 2599 |
+
>>> integrate(cos(pi*z**2/2), z)
|
| 2600 |
+
fresnelc(z)*gamma(1/4)/(4*gamma(5/4))
|
| 2601 |
+
>>> expand_func(integrate(cos(pi*z**2/2), z))
|
| 2602 |
+
fresnelc(z)
|
| 2603 |
+
|
| 2604 |
+
We can numerically evaluate the Fresnel integral to arbitrary precision
|
| 2605 |
+
on the whole complex plane:
|
| 2606 |
+
|
| 2607 |
+
>>> fresnelc(2).evalf(30)
|
| 2608 |
+
0.488253406075340754500223503357
|
| 2609 |
+
|
| 2610 |
+
>>> fresnelc(-2*I).evalf(30)
|
| 2611 |
+
-0.488253406075340754500223503357*I
|
| 2612 |
+
|
| 2613 |
+
See Also
|
| 2614 |
+
========
|
| 2615 |
+
|
| 2616 |
+
fresnels: Fresnel sine integral.
|
| 2617 |
+
|
| 2618 |
+
References
|
| 2619 |
+
==========
|
| 2620 |
+
|
| 2621 |
+
.. [1] https://en.wikipedia.org/wiki/Fresnel_integral
|
| 2622 |
+
.. [2] https://dlmf.nist.gov/7
|
| 2623 |
+
.. [3] https://mathworld.wolfram.com/FresnelIntegrals.html
|
| 2624 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/FresnelC
|
| 2625 |
+
.. [5] The converging factors for the fresnel integrals
|
| 2626 |
+
by John W. Wrench Jr. and Vicki Alley
|
| 2627 |
+
|
| 2628 |
+
"""
|
| 2629 |
+
_trigfunc = cos
|
| 2630 |
+
_sign = S.One
|
| 2631 |
+
|
| 2632 |
+
@staticmethod
|
| 2633 |
+
@cacheit
|
| 2634 |
+
def taylor_term(n, x, *previous_terms):
|
| 2635 |
+
if n < 0:
|
| 2636 |
+
return S.Zero
|
| 2637 |
+
else:
|
| 2638 |
+
x = sympify(x)
|
| 2639 |
+
if len(previous_terms) > 1:
|
| 2640 |
+
p = previous_terms[-1]
|
| 2641 |
+
return (-pi**2*x**4*(4*n - 3)/(8*n*(2*n - 1)*(4*n + 1))) * p
|
| 2642 |
+
else:
|
| 2643 |
+
return x * (-x**4)**n * (S(2)**(-2*n)*pi**(2*n)) / ((4*n + 1)*factorial(2*n))
|
| 2644 |
+
|
| 2645 |
+
def _eval_rewrite_as_erf(self, z, **kwargs):
|
| 2646 |
+
return (S.One - I)/4 * (erf((S.One + I)/2*sqrt(pi)*z) + I*erf((S.One - I)/2*sqrt(pi)*z))
|
| 2647 |
+
|
| 2648 |
+
def _eval_rewrite_as_hyper(self, z, **kwargs):
|
| 2649 |
+
return z * hyper([Rational(1, 4)], [S.Half, Rational(5, 4)], -pi**2*z**4/16)
|
| 2650 |
+
|
| 2651 |
+
def _eval_rewrite_as_meijerg(self, z, **kwargs):
|
| 2652 |
+
return (pi*z**Rational(3, 4) / (sqrt(2)*root(z**2, 4)*root(-z, 4))
|
| 2653 |
+
* meijerg([], [1], [Rational(1, 4)], [Rational(3, 4), 0], -pi**2*z**4/16))
|
| 2654 |
+
|
| 2655 |
+
def _eval_rewrite_as_Integral(self, z, **kwargs):
|
| 2656 |
+
from sympy.integrals.integrals import Integral
|
| 2657 |
+
t = Dummy(uniquely_named_symbol('t', [z]).name)
|
| 2658 |
+
return Integral(cos(pi*t**2/2), (t, 0, z))
|
| 2659 |
+
|
| 2660 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 2661 |
+
from sympy.series.order import Order
|
| 2662 |
+
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
|
| 2663 |
+
arg0 = arg.subs(x, 0)
|
| 2664 |
+
|
| 2665 |
+
if arg0 is S.ComplexInfinity:
|
| 2666 |
+
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 2667 |
+
if arg0.is_zero:
|
| 2668 |
+
return arg
|
| 2669 |
+
elif arg0 in [S.Infinity, S.NegativeInfinity]:
|
| 2670 |
+
s = 1 if arg0 is S.Infinity else -1
|
| 2671 |
+
return s*S.Half + Order(x, x)
|
| 2672 |
+
else:
|
| 2673 |
+
return self.func(arg0)
|
| 2674 |
+
|
| 2675 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 2676 |
+
from sympy.series.order import Order
|
| 2677 |
+
point = args0[0]
|
| 2678 |
+
|
| 2679 |
+
# Expansion at oo
|
| 2680 |
+
if point in [S.Infinity, -S.Infinity]:
|
| 2681 |
+
z = self.args[0]
|
| 2682 |
+
|
| 2683 |
+
# expansion of C(x) = C1(x*sqrt(pi/2)), see reference[5] page 1-8
|
| 2684 |
+
# as only real infinities are dealt with, sin and cos are O(1)
|
| 2685 |
+
p = [S.NegativeOne**k * factorial(4*k + 1) /
|
| 2686 |
+
(2**(2*k + 2) * z**(4*k + 3) * 2**(2*k)*factorial(2*k))
|
| 2687 |
+
for k in range(n) if 4*k + 3 < n]
|
| 2688 |
+
q = [1/(2*z)] + [S.NegativeOne**k * factorial(4*k - 1) /
|
| 2689 |
+
(2**(2*k + 1) * z**(4*k + 1) * 2**(2*k - 1)*factorial(2*k - 1))
|
| 2690 |
+
for k in range(1, n) if 4*k + 1 < n]
|
| 2691 |
+
|
| 2692 |
+
p = [-sqrt(2/pi)*t for t in p]
|
| 2693 |
+
q = [ sqrt(2/pi)*t for t in q]
|
| 2694 |
+
s = 1 if point is S.Infinity else -1
|
| 2695 |
+
# The expansion at oo is 1/2 + some odd powers of z
|
| 2696 |
+
# To get the expansion at -oo, replace z by -z and flip the sign
|
| 2697 |
+
# The result -1/2 + the same odd powers of z as before.
|
| 2698 |
+
return s*S.Half + (cos(z**2)*Add(*p) + sin(z**2)*Add(*q)
|
| 2699 |
+
).subs(x, sqrt(2/pi)*x) + Order(1/z**n, x)
|
| 2700 |
+
|
| 2701 |
+
# All other points are not handled
|
| 2702 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 2703 |
+
|
| 2704 |
+
|
| 2705 |
+
###############################################################################
|
| 2706 |
+
#################### HELPER FUNCTIONS #########################################
|
| 2707 |
+
###############################################################################
|
| 2708 |
+
|
| 2709 |
+
|
| 2710 |
+
class _erfs(DefinedFunction):
|
| 2711 |
+
"""
|
| 2712 |
+
Helper function to make the $\\mathrm{erf}(z)$ function
|
| 2713 |
+
tractable for the Gruntz algorithm.
|
| 2714 |
+
|
| 2715 |
+
"""
|
| 2716 |
+
@classmethod
|
| 2717 |
+
def eval(cls, arg):
|
| 2718 |
+
if arg.is_zero:
|
| 2719 |
+
return S.One
|
| 2720 |
+
|
| 2721 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 2722 |
+
from sympy.series.order import Order
|
| 2723 |
+
point = args0[0]
|
| 2724 |
+
|
| 2725 |
+
# Expansion at oo
|
| 2726 |
+
if point is S.Infinity:
|
| 2727 |
+
z = self.args[0]
|
| 2728 |
+
l = [1/sqrt(pi) * factorial(2*k)*(-S(
|
| 2729 |
+
4))**(-k)/factorial(k) * (1/z)**(2*k + 1) for k in range(n)]
|
| 2730 |
+
o = Order(1/z**(2*n + 1), x)
|
| 2731 |
+
# It is very inefficient to first add the order and then do the nseries
|
| 2732 |
+
return (Add(*l))._eval_nseries(x, n, logx) + o
|
| 2733 |
+
|
| 2734 |
+
# Expansion at I*oo
|
| 2735 |
+
t = point.extract_multiplicatively(I)
|
| 2736 |
+
if t is S.Infinity:
|
| 2737 |
+
z = self.args[0]
|
| 2738 |
+
# TODO: is the series really correct?
|
| 2739 |
+
l = [1/sqrt(pi) * factorial(2*k)*(-S(
|
| 2740 |
+
4))**(-k)/factorial(k) * (1/z)**(2*k + 1) for k in range(n)]
|
| 2741 |
+
o = Order(1/z**(2*n + 1), x)
|
| 2742 |
+
# It is very inefficient to first add the order and then do the nseries
|
| 2743 |
+
return (Add(*l))._eval_nseries(x, n, logx) + o
|
| 2744 |
+
|
| 2745 |
+
# All other points are not handled
|
| 2746 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 2747 |
+
|
| 2748 |
+
def fdiff(self, argindex=1):
|
| 2749 |
+
if argindex == 1:
|
| 2750 |
+
z = self.args[0]
|
| 2751 |
+
return -2/sqrt(pi) + 2*z*_erfs(z)
|
| 2752 |
+
else:
|
| 2753 |
+
raise ArgumentIndexError(self, argindex)
|
| 2754 |
+
|
| 2755 |
+
def _eval_rewrite_as_intractable(self, z, **kwargs):
|
| 2756 |
+
return (S.One - erf(z))*exp(z**2)
|
| 2757 |
+
|
| 2758 |
+
|
| 2759 |
+
class _eis(DefinedFunction):
|
| 2760 |
+
"""
|
| 2761 |
+
Helper function to make the $\\mathrm{Ei}(z)$ and $\\mathrm{li}(z)$
|
| 2762 |
+
functions tractable for the Gruntz algorithm.
|
| 2763 |
+
|
| 2764 |
+
"""
|
| 2765 |
+
|
| 2766 |
+
|
| 2767 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 2768 |
+
from sympy.series.order import Order
|
| 2769 |
+
if args0[0] not in (S.Infinity, S.NegativeInfinity):
|
| 2770 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 2771 |
+
|
| 2772 |
+
z = self.args[0]
|
| 2773 |
+
l = [factorial(k) * (1/z)**(k + 1) for k in range(n)]
|
| 2774 |
+
o = Order(1/z**(n + 1), x)
|
| 2775 |
+
# It is very inefficient to first add the order and then do the nseries
|
| 2776 |
+
return (Add(*l))._eval_nseries(x, n, logx) + o
|
| 2777 |
+
|
| 2778 |
+
|
| 2779 |
+
def fdiff(self, argindex=1):
|
| 2780 |
+
if argindex == 1:
|
| 2781 |
+
z = self.args[0]
|
| 2782 |
+
return S.One / z - _eis(z)
|
| 2783 |
+
else:
|
| 2784 |
+
raise ArgumentIndexError(self, argindex)
|
| 2785 |
+
|
| 2786 |
+
def _eval_rewrite_as_intractable(self, z, **kwargs):
|
| 2787 |
+
return exp(-z)*Ei(z)
|
| 2788 |
+
|
| 2789 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 2790 |
+
x0 = self.args[0].limit(x, 0)
|
| 2791 |
+
if x0.is_zero:
|
| 2792 |
+
f = self._eval_rewrite_as_intractable(*self.args)
|
| 2793 |
+
return f._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 2794 |
+
return super()._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 2795 |
+
|
| 2796 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 2797 |
+
x0 = self.args[0].limit(x, 0)
|
| 2798 |
+
if x0.is_zero:
|
| 2799 |
+
f = self._eval_rewrite_as_intractable(*self.args)
|
| 2800 |
+
return f._eval_nseries(x, n, logx)
|
| 2801 |
+
return super()._eval_nseries(x, n, logx)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/gamma_functions.py
ADDED
|
@@ -0,0 +1,1344 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from math import prod
|
| 2 |
+
|
| 3 |
+
from sympy.core import Add, S, Dummy, expand_func
|
| 4 |
+
from sympy.core.expr import Expr
|
| 5 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError, PoleError
|
| 6 |
+
from sympy.core.logic import fuzzy_and, fuzzy_not
|
| 7 |
+
from sympy.core.numbers import Rational, pi, oo, I
|
| 8 |
+
from sympy.core.power import Pow
|
| 9 |
+
from sympy.functions.special.zeta_functions import zeta
|
| 10 |
+
from sympy.functions.special.error_functions import erf, erfc, Ei
|
| 11 |
+
from sympy.functions.elementary.complexes import re, unpolarify
|
| 12 |
+
from sympy.functions.elementary.exponential import exp, log
|
| 13 |
+
from sympy.functions.elementary.integers import ceiling, floor
|
| 14 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 15 |
+
from sympy.functions.elementary.trigonometric import sin, cos, cot
|
| 16 |
+
from sympy.functions.combinatorial.numbers import bernoulli, harmonic
|
| 17 |
+
from sympy.functions.combinatorial.factorials import factorial, rf, RisingFactorial
|
| 18 |
+
from sympy.utilities.misc import as_int
|
| 19 |
+
|
| 20 |
+
from mpmath import mp, workprec
|
| 21 |
+
from mpmath.libmp.libmpf import prec_to_dps
|
| 22 |
+
|
| 23 |
+
def intlike(n):
|
| 24 |
+
try:
|
| 25 |
+
as_int(n, strict=False)
|
| 26 |
+
return True
|
| 27 |
+
except ValueError:
|
| 28 |
+
return False
|
| 29 |
+
|
| 30 |
+
###############################################################################
|
| 31 |
+
############################ COMPLETE GAMMA FUNCTION ##########################
|
| 32 |
+
###############################################################################
|
| 33 |
+
|
| 34 |
+
class gamma(DefinedFunction):
|
| 35 |
+
r"""
|
| 36 |
+
The gamma function
|
| 37 |
+
|
| 38 |
+
.. math::
|
| 39 |
+
\Gamma(x) := \int^{\infty}_{0} t^{x-1} e^{-t} \mathrm{d}t.
|
| 40 |
+
|
| 41 |
+
Explanation
|
| 42 |
+
===========
|
| 43 |
+
|
| 44 |
+
The ``gamma`` function implements the function which passes through the
|
| 45 |
+
values of the factorial function (i.e., $\Gamma(n) = (n - 1)!$ when n is
|
| 46 |
+
an integer). More generally, $\Gamma(z)$ is defined in the whole complex
|
| 47 |
+
plane except at the negative integers where there are simple poles.
|
| 48 |
+
|
| 49 |
+
Examples
|
| 50 |
+
========
|
| 51 |
+
|
| 52 |
+
>>> from sympy import S, I, pi, gamma
|
| 53 |
+
>>> from sympy.abc import x
|
| 54 |
+
|
| 55 |
+
Several special values are known:
|
| 56 |
+
|
| 57 |
+
>>> gamma(1)
|
| 58 |
+
1
|
| 59 |
+
>>> gamma(4)
|
| 60 |
+
6
|
| 61 |
+
>>> gamma(S(3)/2)
|
| 62 |
+
sqrt(pi)/2
|
| 63 |
+
|
| 64 |
+
The ``gamma`` function obeys the mirror symmetry:
|
| 65 |
+
|
| 66 |
+
>>> from sympy import conjugate
|
| 67 |
+
>>> conjugate(gamma(x))
|
| 68 |
+
gamma(conjugate(x))
|
| 69 |
+
|
| 70 |
+
Differentiation with respect to $x$ is supported:
|
| 71 |
+
|
| 72 |
+
>>> from sympy import diff
|
| 73 |
+
>>> diff(gamma(x), x)
|
| 74 |
+
gamma(x)*polygamma(0, x)
|
| 75 |
+
|
| 76 |
+
Series expansion is also supported:
|
| 77 |
+
|
| 78 |
+
>>> from sympy import series
|
| 79 |
+
>>> series(gamma(x), x, 0, 3)
|
| 80 |
+
1/x - EulerGamma + x*(EulerGamma**2/2 + pi**2/12) + x**2*(-EulerGamma*pi**2/12 - zeta(3)/3 - EulerGamma**3/6) + O(x**3)
|
| 81 |
+
|
| 82 |
+
We can numerically evaluate the ``gamma`` function to arbitrary precision
|
| 83 |
+
on the whole complex plane:
|
| 84 |
+
|
| 85 |
+
>>> gamma(pi).evalf(40)
|
| 86 |
+
2.288037795340032417959588909060233922890
|
| 87 |
+
>>> gamma(1+I).evalf(20)
|
| 88 |
+
0.49801566811835604271 - 0.15494982830181068512*I
|
| 89 |
+
|
| 90 |
+
See Also
|
| 91 |
+
========
|
| 92 |
+
|
| 93 |
+
lowergamma: Lower incomplete gamma function.
|
| 94 |
+
uppergamma: Upper incomplete gamma function.
|
| 95 |
+
polygamma: Polygamma function.
|
| 96 |
+
loggamma: Log Gamma function.
|
| 97 |
+
digamma: Digamma function.
|
| 98 |
+
trigamma: Trigamma function.
|
| 99 |
+
sympy.functions.special.beta_functions.beta: Euler Beta function.
|
| 100 |
+
|
| 101 |
+
References
|
| 102 |
+
==========
|
| 103 |
+
|
| 104 |
+
.. [1] https://en.wikipedia.org/wiki/Gamma_function
|
| 105 |
+
.. [2] https://dlmf.nist.gov/5
|
| 106 |
+
.. [3] https://mathworld.wolfram.com/GammaFunction.html
|
| 107 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/Gamma/
|
| 108 |
+
|
| 109 |
+
"""
|
| 110 |
+
|
| 111 |
+
unbranched = True
|
| 112 |
+
_singularities = (S.ComplexInfinity,)
|
| 113 |
+
|
| 114 |
+
def fdiff(self, argindex=1):
|
| 115 |
+
if argindex == 1:
|
| 116 |
+
return self.func(self.args[0])*polygamma(0, self.args[0])
|
| 117 |
+
else:
|
| 118 |
+
raise ArgumentIndexError(self, argindex)
|
| 119 |
+
|
| 120 |
+
@classmethod
|
| 121 |
+
def eval(cls, arg):
|
| 122 |
+
if arg.is_Number:
|
| 123 |
+
if arg is S.NaN:
|
| 124 |
+
return S.NaN
|
| 125 |
+
elif arg is oo:
|
| 126 |
+
return oo
|
| 127 |
+
elif intlike(arg):
|
| 128 |
+
if arg.is_positive:
|
| 129 |
+
return factorial(arg - 1)
|
| 130 |
+
else:
|
| 131 |
+
return S.ComplexInfinity
|
| 132 |
+
elif arg.is_Rational:
|
| 133 |
+
if arg.q == 2:
|
| 134 |
+
n = abs(arg.p) // arg.q
|
| 135 |
+
|
| 136 |
+
if arg.is_positive:
|
| 137 |
+
k, coeff = n, S.One
|
| 138 |
+
else:
|
| 139 |
+
n = k = n + 1
|
| 140 |
+
|
| 141 |
+
if n & 1 == 0:
|
| 142 |
+
coeff = S.One
|
| 143 |
+
else:
|
| 144 |
+
coeff = S.NegativeOne
|
| 145 |
+
|
| 146 |
+
coeff *= prod(range(3, 2*k, 2))
|
| 147 |
+
|
| 148 |
+
if arg.is_positive:
|
| 149 |
+
return coeff*sqrt(pi) / 2**n
|
| 150 |
+
else:
|
| 151 |
+
return 2**n*sqrt(pi) / coeff
|
| 152 |
+
|
| 153 |
+
def _eval_expand_func(self, **hints):
|
| 154 |
+
arg = self.args[0]
|
| 155 |
+
if arg.is_Rational:
|
| 156 |
+
if abs(arg.p) > arg.q:
|
| 157 |
+
x = Dummy('x')
|
| 158 |
+
n = arg.p // arg.q
|
| 159 |
+
p = arg.p - n*arg.q
|
| 160 |
+
return self.func(x + n)._eval_expand_func().subs(x, Rational(p, arg.q))
|
| 161 |
+
|
| 162 |
+
if arg.is_Add:
|
| 163 |
+
coeff, tail = arg.as_coeff_add()
|
| 164 |
+
if coeff and coeff.q != 1:
|
| 165 |
+
intpart = floor(coeff)
|
| 166 |
+
tail = (coeff - intpart,) + tail
|
| 167 |
+
coeff = intpart
|
| 168 |
+
tail = arg._new_rawargs(*tail, reeval=False)
|
| 169 |
+
return self.func(tail)*RisingFactorial(tail, coeff)
|
| 170 |
+
|
| 171 |
+
return self.func(*self.args)
|
| 172 |
+
|
| 173 |
+
def _eval_conjugate(self):
|
| 174 |
+
return self.func(self.args[0].conjugate())
|
| 175 |
+
|
| 176 |
+
def _eval_is_real(self):
|
| 177 |
+
x = self.args[0]
|
| 178 |
+
if x.is_nonpositive and x.is_integer:
|
| 179 |
+
return False
|
| 180 |
+
if intlike(x) and x <= 0:
|
| 181 |
+
return False
|
| 182 |
+
if x.is_positive or x.is_noninteger:
|
| 183 |
+
return True
|
| 184 |
+
|
| 185 |
+
def _eval_is_positive(self):
|
| 186 |
+
x = self.args[0]
|
| 187 |
+
if x.is_positive:
|
| 188 |
+
return True
|
| 189 |
+
elif x.is_noninteger:
|
| 190 |
+
return floor(x).is_even
|
| 191 |
+
|
| 192 |
+
def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs):
|
| 193 |
+
return exp(loggamma(z))
|
| 194 |
+
|
| 195 |
+
def _eval_rewrite_as_factorial(self, z, **kwargs):
|
| 196 |
+
return factorial(z - 1)
|
| 197 |
+
|
| 198 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 199 |
+
x0 = self.args[0].limit(x, 0)
|
| 200 |
+
if not (x0.is_Integer and x0 <= 0):
|
| 201 |
+
return super()._eval_nseries(x, n, logx)
|
| 202 |
+
t = self.args[0] - x0
|
| 203 |
+
return (self.func(t + 1)/rf(self.args[0], -x0 + 1))._eval_nseries(x, n, logx)
|
| 204 |
+
|
| 205 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 206 |
+
arg = self.args[0]
|
| 207 |
+
x0 = arg.subs(x, 0)
|
| 208 |
+
|
| 209 |
+
if x0.is_integer and x0.is_nonpositive:
|
| 210 |
+
n = -x0
|
| 211 |
+
res = S.NegativeOne**n/self.func(n + 1)
|
| 212 |
+
return res/(arg + n).as_leading_term(x)
|
| 213 |
+
elif not x0.is_infinite:
|
| 214 |
+
return self.func(x0)
|
| 215 |
+
raise PoleError()
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
###############################################################################
|
| 219 |
+
################## LOWER and UPPER INCOMPLETE GAMMA FUNCTIONS #################
|
| 220 |
+
###############################################################################
|
| 221 |
+
|
| 222 |
+
class lowergamma(DefinedFunction):
|
| 223 |
+
r"""
|
| 224 |
+
The lower incomplete gamma function.
|
| 225 |
+
|
| 226 |
+
Explanation
|
| 227 |
+
===========
|
| 228 |
+
|
| 229 |
+
It can be defined as the meromorphic continuation of
|
| 230 |
+
|
| 231 |
+
.. math::
|
| 232 |
+
\gamma(s, x) := \int_0^x t^{s-1} e^{-t} \mathrm{d}t = \Gamma(s) - \Gamma(s, x).
|
| 233 |
+
|
| 234 |
+
This can be shown to be the same as
|
| 235 |
+
|
| 236 |
+
.. math::
|
| 237 |
+
\gamma(s, x) = \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right),
|
| 238 |
+
|
| 239 |
+
where ${}_1F_1$ is the (confluent) hypergeometric function.
|
| 240 |
+
|
| 241 |
+
Examples
|
| 242 |
+
========
|
| 243 |
+
|
| 244 |
+
>>> from sympy import lowergamma, S
|
| 245 |
+
>>> from sympy.abc import s, x
|
| 246 |
+
>>> lowergamma(s, x)
|
| 247 |
+
lowergamma(s, x)
|
| 248 |
+
>>> lowergamma(3, x)
|
| 249 |
+
-2*(x**2/2 + x + 1)*exp(-x) + 2
|
| 250 |
+
>>> lowergamma(-S(1)/2, x)
|
| 251 |
+
-2*sqrt(pi)*erf(sqrt(x)) - 2*exp(-x)/sqrt(x)
|
| 252 |
+
|
| 253 |
+
See Also
|
| 254 |
+
========
|
| 255 |
+
|
| 256 |
+
gamma: Gamma function.
|
| 257 |
+
uppergamma: Upper incomplete gamma function.
|
| 258 |
+
polygamma: Polygamma function.
|
| 259 |
+
loggamma: Log Gamma function.
|
| 260 |
+
digamma: Digamma function.
|
| 261 |
+
trigamma: Trigamma function.
|
| 262 |
+
sympy.functions.special.beta_functions.beta: Euler Beta function.
|
| 263 |
+
|
| 264 |
+
References
|
| 265 |
+
==========
|
| 266 |
+
|
| 267 |
+
.. [1] https://en.wikipedia.org/wiki/Incomplete_gamma_function#Lower_incomplete_gamma_function
|
| 268 |
+
.. [2] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6,
|
| 269 |
+
Section 5, Handbook of Mathematical Functions with Formulas, Graphs,
|
| 270 |
+
and Mathematical Tables
|
| 271 |
+
.. [3] https://dlmf.nist.gov/8
|
| 272 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/Gamma2/
|
| 273 |
+
.. [5] https://functions.wolfram.com/GammaBetaErf/Gamma3/
|
| 274 |
+
|
| 275 |
+
"""
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def fdiff(self, argindex=2):
|
| 279 |
+
from sympy.functions.special.hyper import meijerg
|
| 280 |
+
if argindex == 2:
|
| 281 |
+
a, z = self.args
|
| 282 |
+
return exp(-unpolarify(z))*z**(a - 1)
|
| 283 |
+
elif argindex == 1:
|
| 284 |
+
a, z = self.args
|
| 285 |
+
return gamma(a)*digamma(a) - log(z)*uppergamma(a, z) \
|
| 286 |
+
- meijerg([], [1, 1], [0, 0, a], [], z)
|
| 287 |
+
|
| 288 |
+
else:
|
| 289 |
+
raise ArgumentIndexError(self, argindex)
|
| 290 |
+
|
| 291 |
+
@classmethod
|
| 292 |
+
def eval(cls, a, x):
|
| 293 |
+
# For lack of a better place, we use this one to extract branching
|
| 294 |
+
# information. The following can be
|
| 295 |
+
# found in the literature (c/f references given above), albeit scattered:
|
| 296 |
+
# 1) For fixed x != 0, lowergamma(s, x) is an entire function of s
|
| 297 |
+
# 2) For fixed positive integers s, lowergamma(s, x) is an entire
|
| 298 |
+
# function of x.
|
| 299 |
+
# 3) For fixed non-positive integers s,
|
| 300 |
+
# lowergamma(s, exp(I*2*pi*n)*x) =
|
| 301 |
+
# 2*pi*I*n*(-1)**(-s)/factorial(-s) + lowergamma(s, x)
|
| 302 |
+
# (this follows from lowergamma(s, x).diff(x) = x**(s-1)*exp(-x)).
|
| 303 |
+
# 4) For fixed non-integral s,
|
| 304 |
+
# lowergamma(s, x) = x**s*gamma(s)*lowergamma_unbranched(s, x),
|
| 305 |
+
# where lowergamma_unbranched(s, x) is an entire function (in fact
|
| 306 |
+
# of both s and x), i.e.
|
| 307 |
+
# lowergamma(s, exp(2*I*pi*n)*x) = exp(2*pi*I*n*a)*lowergamma(a, x)
|
| 308 |
+
if x is S.Zero:
|
| 309 |
+
return S.Zero
|
| 310 |
+
nx, n = x.extract_branch_factor()
|
| 311 |
+
if a.is_integer and a.is_positive:
|
| 312 |
+
nx = unpolarify(x)
|
| 313 |
+
if nx != x:
|
| 314 |
+
return lowergamma(a, nx)
|
| 315 |
+
elif a.is_integer and a.is_nonpositive:
|
| 316 |
+
if n != 0:
|
| 317 |
+
return 2*pi*I*n*S.NegativeOne**(-a)/factorial(-a) + lowergamma(a, nx)
|
| 318 |
+
elif n != 0:
|
| 319 |
+
return exp(2*pi*I*n*a)*lowergamma(a, nx)
|
| 320 |
+
|
| 321 |
+
# Special values.
|
| 322 |
+
if a.is_Number:
|
| 323 |
+
if a is S.One:
|
| 324 |
+
return S.One - exp(-x)
|
| 325 |
+
elif a is S.Half:
|
| 326 |
+
return sqrt(pi)*erf(sqrt(x))
|
| 327 |
+
elif a.is_Integer or (2*a).is_Integer:
|
| 328 |
+
b = a - 1
|
| 329 |
+
if b.is_positive:
|
| 330 |
+
if a.is_integer:
|
| 331 |
+
return factorial(b) - exp(-x) * factorial(b) * Add(*[x ** k / factorial(k) for k in range(a)])
|
| 332 |
+
else:
|
| 333 |
+
return gamma(a)*(lowergamma(S.Half, x)/sqrt(pi) - exp(-x)*Add(*[x**(k - S.Half)/gamma(S.Half + k) for k in range(1, a + S.Half)]))
|
| 334 |
+
|
| 335 |
+
if not a.is_Integer:
|
| 336 |
+
return S.NegativeOne**(S.Half - a)*pi*erf(sqrt(x))/gamma(1 - a) + exp(-x)*Add(*[x**(k + a - 1)*gamma(a)/gamma(a + k) for k in range(1, Rational(3, 2) - a)])
|
| 337 |
+
|
| 338 |
+
if x.is_zero:
|
| 339 |
+
return S.Zero
|
| 340 |
+
|
| 341 |
+
def _eval_evalf(self, prec):
|
| 342 |
+
if all(x.is_number for x in self.args):
|
| 343 |
+
a = self.args[0]._to_mpmath(prec)
|
| 344 |
+
z = self.args[1]._to_mpmath(prec)
|
| 345 |
+
with workprec(prec):
|
| 346 |
+
res = mp.gammainc(a, 0, z)
|
| 347 |
+
return Expr._from_mpmath(res, prec)
|
| 348 |
+
else:
|
| 349 |
+
return self
|
| 350 |
+
|
| 351 |
+
def _eval_conjugate(self):
|
| 352 |
+
x = self.args[1]
|
| 353 |
+
if x not in (S.Zero, S.NegativeInfinity):
|
| 354 |
+
return self.func(self.args[0].conjugate(), x.conjugate())
|
| 355 |
+
|
| 356 |
+
def _eval_is_meromorphic(self, x, a):
|
| 357 |
+
# By https://en.wikipedia.org/wiki/Incomplete_gamma_function#Holomorphic_extension,
|
| 358 |
+
# lowergamma(s, z) = z**s*gamma(s)*gammastar(s, z),
|
| 359 |
+
# where gammastar(s, z) is holomorphic for all s and z.
|
| 360 |
+
# Hence the singularities of lowergamma are z = 0 (branch
|
| 361 |
+
# point) and nonpositive integer values of s (poles of gamma(s)).
|
| 362 |
+
s, z = self.args
|
| 363 |
+
args_merom = fuzzy_and([z._eval_is_meromorphic(x, a),
|
| 364 |
+
s._eval_is_meromorphic(x, a)])
|
| 365 |
+
if not args_merom:
|
| 366 |
+
return args_merom
|
| 367 |
+
z0 = z.subs(x, a)
|
| 368 |
+
if s.is_integer:
|
| 369 |
+
return fuzzy_and([s.is_positive, z0.is_finite])
|
| 370 |
+
s0 = s.subs(x, a)
|
| 371 |
+
return fuzzy_and([s0.is_finite, z0.is_finite, fuzzy_not(z0.is_zero)])
|
| 372 |
+
|
| 373 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 374 |
+
from sympy.series.order import O
|
| 375 |
+
s, z = self.args
|
| 376 |
+
if args0[0] is oo and not z.has(x):
|
| 377 |
+
coeff = z**s*exp(-z)
|
| 378 |
+
sum_expr = sum(z**k/rf(s, k + 1) for k in range(n - 1))
|
| 379 |
+
o = O(z**s*s**(-n))
|
| 380 |
+
return coeff*sum_expr + o
|
| 381 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 382 |
+
|
| 383 |
+
def _eval_rewrite_as_uppergamma(self, s, x, **kwargs):
|
| 384 |
+
return gamma(s) - uppergamma(s, x)
|
| 385 |
+
|
| 386 |
+
def _eval_rewrite_as_expint(self, s, x, **kwargs):
|
| 387 |
+
from sympy.functions.special.error_functions import expint
|
| 388 |
+
if s.is_integer and s.is_nonpositive:
|
| 389 |
+
return self
|
| 390 |
+
return self.rewrite(uppergamma).rewrite(expint)
|
| 391 |
+
|
| 392 |
+
def _eval_is_zero(self):
|
| 393 |
+
x = self.args[1]
|
| 394 |
+
if x.is_zero:
|
| 395 |
+
return True
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
class uppergamma(DefinedFunction):
|
| 399 |
+
r"""
|
| 400 |
+
The upper incomplete gamma function.
|
| 401 |
+
|
| 402 |
+
Explanation
|
| 403 |
+
===========
|
| 404 |
+
|
| 405 |
+
It can be defined as the meromorphic continuation of
|
| 406 |
+
|
| 407 |
+
.. math::
|
| 408 |
+
\Gamma(s, x) := \int_x^\infty t^{s-1} e^{-t} \mathrm{d}t = \Gamma(s) - \gamma(s, x).
|
| 409 |
+
|
| 410 |
+
where $\gamma(s, x)$ is the lower incomplete gamma function,
|
| 411 |
+
:class:`lowergamma`. This can be shown to be the same as
|
| 412 |
+
|
| 413 |
+
.. math::
|
| 414 |
+
\Gamma(s, x) = \Gamma(s) - \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right),
|
| 415 |
+
|
| 416 |
+
where ${}_1F_1$ is the (confluent) hypergeometric function.
|
| 417 |
+
|
| 418 |
+
The upper incomplete gamma function is also essentially equivalent to the
|
| 419 |
+
generalized exponential integral:
|
| 420 |
+
|
| 421 |
+
.. math::
|
| 422 |
+
\operatorname{E}_{n}(x) = \int_{1}^{\infty}{\frac{e^{-xt}}{t^n} \, dt} = x^{n-1}\Gamma(1-n,x).
|
| 423 |
+
|
| 424 |
+
Examples
|
| 425 |
+
========
|
| 426 |
+
|
| 427 |
+
>>> from sympy import uppergamma, S
|
| 428 |
+
>>> from sympy.abc import s, x
|
| 429 |
+
>>> uppergamma(s, x)
|
| 430 |
+
uppergamma(s, x)
|
| 431 |
+
>>> uppergamma(3, x)
|
| 432 |
+
2*(x**2/2 + x + 1)*exp(-x)
|
| 433 |
+
>>> uppergamma(-S(1)/2, x)
|
| 434 |
+
-2*sqrt(pi)*erfc(sqrt(x)) + 2*exp(-x)/sqrt(x)
|
| 435 |
+
>>> uppergamma(-2, x)
|
| 436 |
+
expint(3, x)/x**2
|
| 437 |
+
|
| 438 |
+
See Also
|
| 439 |
+
========
|
| 440 |
+
|
| 441 |
+
gamma: Gamma function.
|
| 442 |
+
lowergamma: Lower incomplete gamma function.
|
| 443 |
+
polygamma: Polygamma function.
|
| 444 |
+
loggamma: Log Gamma function.
|
| 445 |
+
digamma: Digamma function.
|
| 446 |
+
trigamma: Trigamma function.
|
| 447 |
+
sympy.functions.special.beta_functions.beta: Euler Beta function.
|
| 448 |
+
|
| 449 |
+
References
|
| 450 |
+
==========
|
| 451 |
+
|
| 452 |
+
.. [1] https://en.wikipedia.org/wiki/Incomplete_gamma_function#Upper_incomplete_gamma_function
|
| 453 |
+
.. [2] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6,
|
| 454 |
+
Section 5, Handbook of Mathematical Functions with Formulas, Graphs,
|
| 455 |
+
and Mathematical Tables
|
| 456 |
+
.. [3] https://dlmf.nist.gov/8
|
| 457 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/Gamma2/
|
| 458 |
+
.. [5] https://functions.wolfram.com/GammaBetaErf/Gamma3/
|
| 459 |
+
.. [6] https://en.wikipedia.org/wiki/Exponential_integral#Relation_with_other_functions
|
| 460 |
+
|
| 461 |
+
"""
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
def fdiff(self, argindex=2):
|
| 465 |
+
from sympy.functions.special.hyper import meijerg
|
| 466 |
+
if argindex == 2:
|
| 467 |
+
a, z = self.args
|
| 468 |
+
return -exp(-unpolarify(z))*z**(a - 1)
|
| 469 |
+
elif argindex == 1:
|
| 470 |
+
a, z = self.args
|
| 471 |
+
return uppergamma(a, z)*log(z) + meijerg([], [1, 1], [0, 0, a], [], z)
|
| 472 |
+
else:
|
| 473 |
+
raise ArgumentIndexError(self, argindex)
|
| 474 |
+
|
| 475 |
+
def _eval_evalf(self, prec):
|
| 476 |
+
if all(x.is_number for x in self.args):
|
| 477 |
+
a = self.args[0]._to_mpmath(prec)
|
| 478 |
+
z = self.args[1]._to_mpmath(prec)
|
| 479 |
+
with workprec(prec):
|
| 480 |
+
res = mp.gammainc(a, z, mp.inf)
|
| 481 |
+
return Expr._from_mpmath(res, prec)
|
| 482 |
+
return self
|
| 483 |
+
|
| 484 |
+
@classmethod
|
| 485 |
+
def eval(cls, a, z):
|
| 486 |
+
from sympy.functions.special.error_functions import expint
|
| 487 |
+
if z.is_Number:
|
| 488 |
+
if z is S.NaN:
|
| 489 |
+
return S.NaN
|
| 490 |
+
elif z is oo:
|
| 491 |
+
return S.Zero
|
| 492 |
+
elif z.is_zero:
|
| 493 |
+
if re(a).is_positive:
|
| 494 |
+
return gamma(a)
|
| 495 |
+
|
| 496 |
+
# We extract branching information here. C/f lowergamma.
|
| 497 |
+
nx, n = z.extract_branch_factor()
|
| 498 |
+
if a.is_integer and a.is_positive:
|
| 499 |
+
nx = unpolarify(z)
|
| 500 |
+
if z != nx:
|
| 501 |
+
return uppergamma(a, nx)
|
| 502 |
+
elif a.is_integer and a.is_nonpositive:
|
| 503 |
+
if n != 0:
|
| 504 |
+
return -2*pi*I*n*S.NegativeOne**(-a)/factorial(-a) + uppergamma(a, nx)
|
| 505 |
+
elif n != 0:
|
| 506 |
+
return gamma(a)*(1 - exp(2*pi*I*n*a)) + exp(2*pi*I*n*a)*uppergamma(a, nx)
|
| 507 |
+
|
| 508 |
+
# Special values.
|
| 509 |
+
if a.is_Number:
|
| 510 |
+
if a is S.Zero and z.is_positive:
|
| 511 |
+
return -Ei(-z)
|
| 512 |
+
elif a is S.One:
|
| 513 |
+
return exp(-z)
|
| 514 |
+
elif a is S.Half:
|
| 515 |
+
return sqrt(pi)*erfc(sqrt(z))
|
| 516 |
+
elif a.is_Integer or (2*a).is_Integer:
|
| 517 |
+
b = a - 1
|
| 518 |
+
if b.is_positive:
|
| 519 |
+
if a.is_integer:
|
| 520 |
+
return exp(-z) * factorial(b) * Add(*[z**k / factorial(k)
|
| 521 |
+
for k in range(a)])
|
| 522 |
+
else:
|
| 523 |
+
return (gamma(a) * erfc(sqrt(z)) +
|
| 524 |
+
S.NegativeOne**(a - S(3)/2) * exp(-z) * sqrt(z)
|
| 525 |
+
* Add(*[gamma(-S.Half - k) * (-z)**k / gamma(1-a)
|
| 526 |
+
for k in range(a - S.Half)]))
|
| 527 |
+
elif b.is_Integer:
|
| 528 |
+
return expint(-b, z)*unpolarify(z)**(b + 1)
|
| 529 |
+
|
| 530 |
+
if not a.is_Integer:
|
| 531 |
+
return (S.NegativeOne**(S.Half - a) * pi*erfc(sqrt(z))/gamma(1-a)
|
| 532 |
+
- z**a * exp(-z) * Add(*[z**k * gamma(a) / gamma(a+k+1)
|
| 533 |
+
for k in range(S.Half - a)]))
|
| 534 |
+
|
| 535 |
+
if a.is_zero and z.is_positive:
|
| 536 |
+
return -Ei(-z)
|
| 537 |
+
|
| 538 |
+
if z.is_zero and re(a).is_positive:
|
| 539 |
+
return gamma(a)
|
| 540 |
+
|
| 541 |
+
def _eval_conjugate(self):
|
| 542 |
+
z = self.args[1]
|
| 543 |
+
if z not in (S.Zero, S.NegativeInfinity):
|
| 544 |
+
return self.func(self.args[0].conjugate(), z.conjugate())
|
| 545 |
+
|
| 546 |
+
def _eval_is_meromorphic(self, x, a):
|
| 547 |
+
return lowergamma._eval_is_meromorphic(self, x, a)
|
| 548 |
+
|
| 549 |
+
def _eval_rewrite_as_lowergamma(self, s, x, **kwargs):
|
| 550 |
+
return gamma(s) - lowergamma(s, x)
|
| 551 |
+
|
| 552 |
+
def _eval_rewrite_as_tractable(self, s, x, **kwargs):
|
| 553 |
+
return exp(loggamma(s)) - lowergamma(s, x)
|
| 554 |
+
|
| 555 |
+
def _eval_rewrite_as_expint(self, s, x, **kwargs):
|
| 556 |
+
from sympy.functions.special.error_functions import expint
|
| 557 |
+
return expint(1 - s, x)*x**s
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
###############################################################################
|
| 561 |
+
###################### POLYGAMMA and LOGGAMMA FUNCTIONS #######################
|
| 562 |
+
###############################################################################
|
| 563 |
+
|
| 564 |
+
class polygamma(DefinedFunction):
|
| 565 |
+
r"""
|
| 566 |
+
The function ``polygamma(n, z)`` returns ``log(gamma(z)).diff(n + 1)``.
|
| 567 |
+
|
| 568 |
+
Explanation
|
| 569 |
+
===========
|
| 570 |
+
|
| 571 |
+
It is a meromorphic function on $\mathbb{C}$ and defined as the $(n+1)$-th
|
| 572 |
+
derivative of the logarithm of the gamma function:
|
| 573 |
+
|
| 574 |
+
.. math::
|
| 575 |
+
\psi^{(n)} (z) := \frac{\mathrm{d}^{n+1}}{\mathrm{d} z^{n+1}} \log\Gamma(z).
|
| 576 |
+
|
| 577 |
+
For `n` not a nonnegative integer the generalization by Espinosa and Moll [5]_
|
| 578 |
+
is used:
|
| 579 |
+
|
| 580 |
+
.. math:: \psi(s,z) = \frac{\zeta'(s+1, z) + (\gamma + \psi(-s)) \zeta(s+1, z)}
|
| 581 |
+
{\Gamma(-s)}
|
| 582 |
+
|
| 583 |
+
Examples
|
| 584 |
+
========
|
| 585 |
+
|
| 586 |
+
Several special values are known:
|
| 587 |
+
|
| 588 |
+
>>> from sympy import S, polygamma
|
| 589 |
+
>>> polygamma(0, 1)
|
| 590 |
+
-EulerGamma
|
| 591 |
+
>>> polygamma(0, 1/S(2))
|
| 592 |
+
-2*log(2) - EulerGamma
|
| 593 |
+
>>> polygamma(0, 1/S(3))
|
| 594 |
+
-log(3) - sqrt(3)*pi/6 - EulerGamma - log(sqrt(3))
|
| 595 |
+
>>> polygamma(0, 1/S(4))
|
| 596 |
+
-pi/2 - log(4) - log(2) - EulerGamma
|
| 597 |
+
>>> polygamma(0, 2)
|
| 598 |
+
1 - EulerGamma
|
| 599 |
+
>>> polygamma(0, 23)
|
| 600 |
+
19093197/5173168 - EulerGamma
|
| 601 |
+
|
| 602 |
+
>>> from sympy import oo, I
|
| 603 |
+
>>> polygamma(0, oo)
|
| 604 |
+
oo
|
| 605 |
+
>>> polygamma(0, -oo)
|
| 606 |
+
oo
|
| 607 |
+
>>> polygamma(0, I*oo)
|
| 608 |
+
oo
|
| 609 |
+
>>> polygamma(0, -I*oo)
|
| 610 |
+
oo
|
| 611 |
+
|
| 612 |
+
Differentiation with respect to $x$ is supported:
|
| 613 |
+
|
| 614 |
+
>>> from sympy import Symbol, diff
|
| 615 |
+
>>> x = Symbol("x")
|
| 616 |
+
>>> diff(polygamma(0, x), x)
|
| 617 |
+
polygamma(1, x)
|
| 618 |
+
>>> diff(polygamma(0, x), x, 2)
|
| 619 |
+
polygamma(2, x)
|
| 620 |
+
>>> diff(polygamma(0, x), x, 3)
|
| 621 |
+
polygamma(3, x)
|
| 622 |
+
>>> diff(polygamma(1, x), x)
|
| 623 |
+
polygamma(2, x)
|
| 624 |
+
>>> diff(polygamma(1, x), x, 2)
|
| 625 |
+
polygamma(3, x)
|
| 626 |
+
>>> diff(polygamma(2, x), x)
|
| 627 |
+
polygamma(3, x)
|
| 628 |
+
>>> diff(polygamma(2, x), x, 2)
|
| 629 |
+
polygamma(4, x)
|
| 630 |
+
|
| 631 |
+
>>> n = Symbol("n")
|
| 632 |
+
>>> diff(polygamma(n, x), x)
|
| 633 |
+
polygamma(n + 1, x)
|
| 634 |
+
>>> diff(polygamma(n, x), x, 2)
|
| 635 |
+
polygamma(n + 2, x)
|
| 636 |
+
|
| 637 |
+
We can rewrite ``polygamma`` functions in terms of harmonic numbers:
|
| 638 |
+
|
| 639 |
+
>>> from sympy import harmonic
|
| 640 |
+
>>> polygamma(0, x).rewrite(harmonic)
|
| 641 |
+
harmonic(x - 1) - EulerGamma
|
| 642 |
+
>>> polygamma(2, x).rewrite(harmonic)
|
| 643 |
+
2*harmonic(x - 1, 3) - 2*zeta(3)
|
| 644 |
+
>>> ni = Symbol("n", integer=True)
|
| 645 |
+
>>> polygamma(ni, x).rewrite(harmonic)
|
| 646 |
+
(-1)**(n + 1)*(-harmonic(x - 1, n + 1) + zeta(n + 1))*factorial(n)
|
| 647 |
+
|
| 648 |
+
See Also
|
| 649 |
+
========
|
| 650 |
+
|
| 651 |
+
gamma: Gamma function.
|
| 652 |
+
lowergamma: Lower incomplete gamma function.
|
| 653 |
+
uppergamma: Upper incomplete gamma function.
|
| 654 |
+
loggamma: Log Gamma function.
|
| 655 |
+
digamma: Digamma function.
|
| 656 |
+
trigamma: Trigamma function.
|
| 657 |
+
sympy.functions.special.beta_functions.beta: Euler Beta function.
|
| 658 |
+
|
| 659 |
+
References
|
| 660 |
+
==========
|
| 661 |
+
|
| 662 |
+
.. [1] https://en.wikipedia.org/wiki/Polygamma_function
|
| 663 |
+
.. [2] https://mathworld.wolfram.com/PolygammaFunction.html
|
| 664 |
+
.. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma/
|
| 665 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/
|
| 666 |
+
.. [5] O. Espinosa and V. Moll, "A generalized polygamma function",
|
| 667 |
+
*Integral Transforms and Special Functions* (2004), 101-115.
|
| 668 |
+
|
| 669 |
+
"""
|
| 670 |
+
|
| 671 |
+
@classmethod
|
| 672 |
+
def eval(cls, n, z):
|
| 673 |
+
if n is S.NaN or z is S.NaN:
|
| 674 |
+
return S.NaN
|
| 675 |
+
elif z is oo:
|
| 676 |
+
return oo if n.is_zero else S.Zero
|
| 677 |
+
elif z.is_Integer and z.is_nonpositive:
|
| 678 |
+
return S.ComplexInfinity
|
| 679 |
+
elif n is S.NegativeOne:
|
| 680 |
+
return loggamma(z) - log(2*pi) / 2
|
| 681 |
+
elif n.is_zero:
|
| 682 |
+
if z is -oo or z.extract_multiplicatively(I) in (oo, -oo):
|
| 683 |
+
return oo
|
| 684 |
+
elif z.is_Integer:
|
| 685 |
+
return harmonic(z-1) - S.EulerGamma
|
| 686 |
+
elif z.is_Rational:
|
| 687 |
+
# TODO n == 1 also can do some rational z
|
| 688 |
+
p, q = z.as_numer_denom()
|
| 689 |
+
# only expand for small denominators to avoid creating long expressions
|
| 690 |
+
if q <= 6:
|
| 691 |
+
return expand_func(polygamma(S.Zero, z, evaluate=False))
|
| 692 |
+
elif n.is_integer and n.is_nonnegative:
|
| 693 |
+
nz = unpolarify(z)
|
| 694 |
+
if z != nz:
|
| 695 |
+
return polygamma(n, nz)
|
| 696 |
+
if z.is_Integer:
|
| 697 |
+
return S.NegativeOne**(n+1) * factorial(n) * zeta(n+1, z)
|
| 698 |
+
elif z is S.Half:
|
| 699 |
+
return S.NegativeOne**(n+1) * factorial(n) * (2**(n+1)-1) * zeta(n+1)
|
| 700 |
+
|
| 701 |
+
def _eval_is_real(self):
|
| 702 |
+
if self.args[0].is_positive and self.args[1].is_positive:
|
| 703 |
+
return True
|
| 704 |
+
|
| 705 |
+
def _eval_is_complex(self):
|
| 706 |
+
z = self.args[1]
|
| 707 |
+
is_negative_integer = fuzzy_and([z.is_negative, z.is_integer])
|
| 708 |
+
return fuzzy_and([z.is_complex, fuzzy_not(is_negative_integer)])
|
| 709 |
+
|
| 710 |
+
def _eval_is_positive(self):
|
| 711 |
+
n, z = self.args
|
| 712 |
+
if n.is_positive:
|
| 713 |
+
if n.is_odd and z.is_real:
|
| 714 |
+
return True
|
| 715 |
+
if n.is_even and z.is_positive:
|
| 716 |
+
return False
|
| 717 |
+
|
| 718 |
+
def _eval_is_negative(self):
|
| 719 |
+
n, z = self.args
|
| 720 |
+
if n.is_positive:
|
| 721 |
+
if n.is_even and z.is_positive:
|
| 722 |
+
return True
|
| 723 |
+
if n.is_odd and z.is_real:
|
| 724 |
+
return False
|
| 725 |
+
|
| 726 |
+
def _eval_expand_func(self, **hints):
|
| 727 |
+
n, z = self.args
|
| 728 |
+
|
| 729 |
+
if n.is_Integer and n.is_nonnegative:
|
| 730 |
+
if z.is_Add:
|
| 731 |
+
coeff = z.args[0]
|
| 732 |
+
if coeff.is_Integer:
|
| 733 |
+
e = -(n + 1)
|
| 734 |
+
if coeff > 0:
|
| 735 |
+
tail = Add(*[Pow(
|
| 736 |
+
z - i, e) for i in range(1, int(coeff) + 1)])
|
| 737 |
+
else:
|
| 738 |
+
tail = -Add(*[Pow(
|
| 739 |
+
z + i, e) for i in range(int(-coeff))])
|
| 740 |
+
return polygamma(n, z - coeff) + S.NegativeOne**n*factorial(n)*tail
|
| 741 |
+
|
| 742 |
+
elif z.is_Mul:
|
| 743 |
+
coeff, z = z.as_two_terms()
|
| 744 |
+
if coeff.is_Integer and coeff.is_positive:
|
| 745 |
+
tail = [polygamma(n, z + Rational(
|
| 746 |
+
i, coeff)) for i in range(int(coeff))]
|
| 747 |
+
if n == 0:
|
| 748 |
+
return Add(*tail)/coeff + log(coeff)
|
| 749 |
+
else:
|
| 750 |
+
return Add(*tail)/coeff**(n + 1)
|
| 751 |
+
z *= coeff
|
| 752 |
+
|
| 753 |
+
if n == 0 and z.is_Rational:
|
| 754 |
+
p, q = z.as_numer_denom()
|
| 755 |
+
|
| 756 |
+
# Reference:
|
| 757 |
+
# Values of the polygamma functions at rational arguments, J. Choi, 2007
|
| 758 |
+
part_1 = -S.EulerGamma - pi * cot(p * pi / q) / 2 - log(q) + Add(
|
| 759 |
+
*[cos(2 * k * pi * p / q) * log(2 * sin(k * pi / q)) for k in range(1, q)])
|
| 760 |
+
|
| 761 |
+
if z > 0:
|
| 762 |
+
n = floor(z)
|
| 763 |
+
z0 = z - n
|
| 764 |
+
return part_1 + Add(*[1 / (z0 + k) for k in range(n)])
|
| 765 |
+
elif z < 0:
|
| 766 |
+
n = floor(1 - z)
|
| 767 |
+
z0 = z + n
|
| 768 |
+
return part_1 - Add(*[1 / (z0 - 1 - k) for k in range(n)])
|
| 769 |
+
|
| 770 |
+
if n == -1:
|
| 771 |
+
return loggamma(z) - log(2*pi) / 2
|
| 772 |
+
if n.is_integer is False or n.is_nonnegative is False:
|
| 773 |
+
s = Dummy("s")
|
| 774 |
+
dzt = zeta(s, z).diff(s).subs(s, n+1)
|
| 775 |
+
return (dzt + (S.EulerGamma + digamma(-n)) * zeta(n+1, z)) / gamma(-n)
|
| 776 |
+
|
| 777 |
+
return polygamma(n, z)
|
| 778 |
+
|
| 779 |
+
def _eval_rewrite_as_zeta(self, n, z, **kwargs):
|
| 780 |
+
if n.is_integer and n.is_positive:
|
| 781 |
+
return S.NegativeOne**(n + 1)*factorial(n)*zeta(n + 1, z)
|
| 782 |
+
|
| 783 |
+
def _eval_rewrite_as_harmonic(self, n, z, **kwargs):
|
| 784 |
+
if n.is_integer:
|
| 785 |
+
if n.is_zero:
|
| 786 |
+
return harmonic(z - 1) - S.EulerGamma
|
| 787 |
+
else:
|
| 788 |
+
return S.NegativeOne**(n+1) * factorial(n) * (zeta(n+1) - harmonic(z-1, n+1))
|
| 789 |
+
|
| 790 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 791 |
+
from sympy.series.order import Order
|
| 792 |
+
n, z = [a.as_leading_term(x) for a in self.args]
|
| 793 |
+
o = Order(z, x)
|
| 794 |
+
if n == 0 and o.contains(1/x):
|
| 795 |
+
logx = log(x) if logx is None else logx
|
| 796 |
+
return o.getn() * logx
|
| 797 |
+
else:
|
| 798 |
+
return self.func(n, z)
|
| 799 |
+
|
| 800 |
+
def fdiff(self, argindex=2):
|
| 801 |
+
if argindex == 2:
|
| 802 |
+
n, z = self.args[:2]
|
| 803 |
+
return polygamma(n + 1, z)
|
| 804 |
+
else:
|
| 805 |
+
raise ArgumentIndexError(self, argindex)
|
| 806 |
+
|
| 807 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 808 |
+
from sympy.series.order import Order
|
| 809 |
+
if args0[1] != oo or not \
|
| 810 |
+
(self.args[0].is_Integer and self.args[0].is_nonnegative):
|
| 811 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 812 |
+
z = self.args[1]
|
| 813 |
+
N = self.args[0]
|
| 814 |
+
|
| 815 |
+
if N == 0:
|
| 816 |
+
# digamma function series
|
| 817 |
+
# Abramowitz & Stegun, p. 259, 6.3.18
|
| 818 |
+
r = log(z) - 1/(2*z)
|
| 819 |
+
o = None
|
| 820 |
+
if n < 2:
|
| 821 |
+
o = Order(1/z, x)
|
| 822 |
+
else:
|
| 823 |
+
m = ceiling((n + 1)//2)
|
| 824 |
+
l = [bernoulli(2*k) / (2*k*z**(2*k)) for k in range(1, m)]
|
| 825 |
+
r -= Add(*l)
|
| 826 |
+
o = Order(1/z**n, x)
|
| 827 |
+
return r._eval_nseries(x, n, logx) + o
|
| 828 |
+
else:
|
| 829 |
+
# proper polygamma function
|
| 830 |
+
# Abramowitz & Stegun, p. 260, 6.4.10
|
| 831 |
+
# We return terms to order higher than O(x**n) on purpose
|
| 832 |
+
# -- otherwise we would not be able to return any terms for
|
| 833 |
+
# quite a long time!
|
| 834 |
+
fac = gamma(N)
|
| 835 |
+
e0 = fac + N*fac/(2*z)
|
| 836 |
+
m = ceiling((n + 1)//2)
|
| 837 |
+
for k in range(1, m):
|
| 838 |
+
fac = fac*(2*k + N - 1)*(2*k + N - 2) / ((2*k)*(2*k - 1))
|
| 839 |
+
e0 += bernoulli(2*k)*fac/z**(2*k)
|
| 840 |
+
o = Order(1/z**(2*m), x)
|
| 841 |
+
if n == 0:
|
| 842 |
+
o = Order(1/z, x)
|
| 843 |
+
elif n == 1:
|
| 844 |
+
o = Order(1/z**2, x)
|
| 845 |
+
r = e0._eval_nseries(z, n, logx) + o
|
| 846 |
+
return (-1 * (-1/z)**N * r)._eval_nseries(x, n, logx)
|
| 847 |
+
|
| 848 |
+
def _eval_evalf(self, prec):
|
| 849 |
+
if not all(i.is_number for i in self.args):
|
| 850 |
+
return
|
| 851 |
+
s = self.args[0]._to_mpmath(prec+12)
|
| 852 |
+
z = self.args[1]._to_mpmath(prec+12)
|
| 853 |
+
if mp.isint(z) and z <= 0:
|
| 854 |
+
return S.ComplexInfinity
|
| 855 |
+
with workprec(prec+12):
|
| 856 |
+
if mp.isint(s) and s >= 0:
|
| 857 |
+
res = mp.polygamma(s, z)
|
| 858 |
+
else:
|
| 859 |
+
zt = mp.zeta(s+1, z)
|
| 860 |
+
dzt = mp.zeta(s+1, z, 1)
|
| 861 |
+
res = (dzt + (mp.euler + mp.digamma(-s)) * zt) * mp.rgamma(-s)
|
| 862 |
+
return Expr._from_mpmath(res, prec)
|
| 863 |
+
|
| 864 |
+
|
| 865 |
+
class loggamma(DefinedFunction):
|
| 866 |
+
r"""
|
| 867 |
+
The ``loggamma`` function implements the logarithm of the
|
| 868 |
+
gamma function (i.e., $\log\Gamma(x)$).
|
| 869 |
+
|
| 870 |
+
Examples
|
| 871 |
+
========
|
| 872 |
+
|
| 873 |
+
Several special values are known. For numerical integral
|
| 874 |
+
arguments we have:
|
| 875 |
+
|
| 876 |
+
>>> from sympy import loggamma
|
| 877 |
+
>>> loggamma(-2)
|
| 878 |
+
oo
|
| 879 |
+
>>> loggamma(0)
|
| 880 |
+
oo
|
| 881 |
+
>>> loggamma(1)
|
| 882 |
+
0
|
| 883 |
+
>>> loggamma(2)
|
| 884 |
+
0
|
| 885 |
+
>>> loggamma(3)
|
| 886 |
+
log(2)
|
| 887 |
+
|
| 888 |
+
And for symbolic values:
|
| 889 |
+
|
| 890 |
+
>>> from sympy import Symbol
|
| 891 |
+
>>> n = Symbol("n", integer=True, positive=True)
|
| 892 |
+
>>> loggamma(n)
|
| 893 |
+
log(gamma(n))
|
| 894 |
+
>>> loggamma(-n)
|
| 895 |
+
oo
|
| 896 |
+
|
| 897 |
+
For half-integral values:
|
| 898 |
+
|
| 899 |
+
>>> from sympy import S
|
| 900 |
+
>>> loggamma(S(5)/2)
|
| 901 |
+
log(3*sqrt(pi)/4)
|
| 902 |
+
>>> loggamma(n/2)
|
| 903 |
+
log(2**(1 - n)*sqrt(pi)*gamma(n)/gamma(n/2 + 1/2))
|
| 904 |
+
|
| 905 |
+
And general rational arguments:
|
| 906 |
+
|
| 907 |
+
>>> from sympy import expand_func
|
| 908 |
+
>>> L = loggamma(S(16)/3)
|
| 909 |
+
>>> expand_func(L).doit()
|
| 910 |
+
-5*log(3) + loggamma(1/3) + log(4) + log(7) + log(10) + log(13)
|
| 911 |
+
>>> L = loggamma(S(19)/4)
|
| 912 |
+
>>> expand_func(L).doit()
|
| 913 |
+
-4*log(4) + loggamma(3/4) + log(3) + log(7) + log(11) + log(15)
|
| 914 |
+
>>> L = loggamma(S(23)/7)
|
| 915 |
+
>>> expand_func(L).doit()
|
| 916 |
+
-3*log(7) + log(2) + loggamma(2/7) + log(9) + log(16)
|
| 917 |
+
|
| 918 |
+
The ``loggamma`` function has the following limits towards infinity:
|
| 919 |
+
|
| 920 |
+
>>> from sympy import oo
|
| 921 |
+
>>> loggamma(oo)
|
| 922 |
+
oo
|
| 923 |
+
>>> loggamma(-oo)
|
| 924 |
+
zoo
|
| 925 |
+
|
| 926 |
+
The ``loggamma`` function obeys the mirror symmetry
|
| 927 |
+
if $x \in \mathbb{C} \setminus \{-\infty, 0\}$:
|
| 928 |
+
|
| 929 |
+
>>> from sympy.abc import x
|
| 930 |
+
>>> from sympy import conjugate
|
| 931 |
+
>>> conjugate(loggamma(x))
|
| 932 |
+
loggamma(conjugate(x))
|
| 933 |
+
|
| 934 |
+
Differentiation with respect to $x$ is supported:
|
| 935 |
+
|
| 936 |
+
>>> from sympy import diff
|
| 937 |
+
>>> diff(loggamma(x), x)
|
| 938 |
+
polygamma(0, x)
|
| 939 |
+
|
| 940 |
+
Series expansion is also supported:
|
| 941 |
+
|
| 942 |
+
>>> from sympy import series
|
| 943 |
+
>>> series(loggamma(x), x, 0, 4).cancel()
|
| 944 |
+
-log(x) - EulerGamma*x + pi**2*x**2/12 - x**3*zeta(3)/3 + O(x**4)
|
| 945 |
+
|
| 946 |
+
We can numerically evaluate the ``loggamma`` function
|
| 947 |
+
to arbitrary precision on the whole complex plane:
|
| 948 |
+
|
| 949 |
+
>>> from sympy import I
|
| 950 |
+
>>> loggamma(5).evalf(30)
|
| 951 |
+
3.17805383034794561964694160130
|
| 952 |
+
>>> loggamma(I).evalf(20)
|
| 953 |
+
-0.65092319930185633889 - 1.8724366472624298171*I
|
| 954 |
+
|
| 955 |
+
See Also
|
| 956 |
+
========
|
| 957 |
+
|
| 958 |
+
gamma: Gamma function.
|
| 959 |
+
lowergamma: Lower incomplete gamma function.
|
| 960 |
+
uppergamma: Upper incomplete gamma function.
|
| 961 |
+
polygamma: Polygamma function.
|
| 962 |
+
digamma: Digamma function.
|
| 963 |
+
trigamma: Trigamma function.
|
| 964 |
+
sympy.functions.special.beta_functions.beta: Euler Beta function.
|
| 965 |
+
|
| 966 |
+
References
|
| 967 |
+
==========
|
| 968 |
+
|
| 969 |
+
.. [1] https://en.wikipedia.org/wiki/Gamma_function
|
| 970 |
+
.. [2] https://dlmf.nist.gov/5
|
| 971 |
+
.. [3] https://mathworld.wolfram.com/LogGammaFunction.html
|
| 972 |
+
.. [4] https://functions.wolfram.com/GammaBetaErf/LogGamma/
|
| 973 |
+
|
| 974 |
+
"""
|
| 975 |
+
@classmethod
|
| 976 |
+
def eval(cls, z):
|
| 977 |
+
if z.is_integer:
|
| 978 |
+
if z.is_nonpositive:
|
| 979 |
+
return oo
|
| 980 |
+
elif z.is_positive:
|
| 981 |
+
return log(gamma(z))
|
| 982 |
+
elif z.is_rational:
|
| 983 |
+
p, q = z.as_numer_denom()
|
| 984 |
+
# Half-integral values:
|
| 985 |
+
if p.is_positive and q == 2:
|
| 986 |
+
return log(sqrt(pi) * 2**(1 - p) * gamma(p) / gamma((p + 1)*S.Half))
|
| 987 |
+
|
| 988 |
+
if z is oo:
|
| 989 |
+
return oo
|
| 990 |
+
elif abs(z) is oo:
|
| 991 |
+
return S.ComplexInfinity
|
| 992 |
+
if z is S.NaN:
|
| 993 |
+
return S.NaN
|
| 994 |
+
|
| 995 |
+
def _eval_expand_func(self, **hints):
|
| 996 |
+
from sympy.concrete.summations import Sum
|
| 997 |
+
z = self.args[0]
|
| 998 |
+
|
| 999 |
+
if z.is_Rational:
|
| 1000 |
+
p, q = z.as_numer_denom()
|
| 1001 |
+
# General rational arguments (u + p/q)
|
| 1002 |
+
# Split z as n + p/q with p < q
|
| 1003 |
+
n = p // q
|
| 1004 |
+
p = p - n*q
|
| 1005 |
+
if p.is_positive and q.is_positive and p < q:
|
| 1006 |
+
k = Dummy("k")
|
| 1007 |
+
if n.is_positive:
|
| 1008 |
+
return loggamma(p / q) - n*log(q) + Sum(log((k - 1)*q + p), (k, 1, n))
|
| 1009 |
+
elif n.is_negative:
|
| 1010 |
+
return loggamma(p / q) - n*log(q) + pi*I*n - Sum(log(k*q - p), (k, 1, -n))
|
| 1011 |
+
elif n.is_zero:
|
| 1012 |
+
return loggamma(p / q)
|
| 1013 |
+
|
| 1014 |
+
return self
|
| 1015 |
+
|
| 1016 |
+
def _eval_nseries(self, x, n, logx=None, cdir=0):
|
| 1017 |
+
x0 = self.args[0].limit(x, 0)
|
| 1018 |
+
if x0.is_zero:
|
| 1019 |
+
f = self._eval_rewrite_as_intractable(*self.args)
|
| 1020 |
+
return f._eval_nseries(x, n, logx)
|
| 1021 |
+
return super()._eval_nseries(x, n, logx)
|
| 1022 |
+
|
| 1023 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 1024 |
+
from sympy.series.order import Order
|
| 1025 |
+
if args0[0] != oo:
|
| 1026 |
+
return super()._eval_aseries(n, args0, x, logx)
|
| 1027 |
+
z = self.args[0]
|
| 1028 |
+
r = log(z)*(z - S.Half) - z + log(2*pi)/2
|
| 1029 |
+
l = [bernoulli(2*k) / (2*k*(2*k - 1)*z**(2*k - 1)) for k in range(1, n)]
|
| 1030 |
+
o = None
|
| 1031 |
+
if n == 0:
|
| 1032 |
+
o = Order(1, x)
|
| 1033 |
+
else:
|
| 1034 |
+
o = Order(1/z**n, x)
|
| 1035 |
+
# It is very inefficient to first add the order and then do the nseries
|
| 1036 |
+
return (r + Add(*l))._eval_nseries(x, n, logx) + o
|
| 1037 |
+
|
| 1038 |
+
def _eval_rewrite_as_intractable(self, z, **kwargs):
|
| 1039 |
+
return log(gamma(z))
|
| 1040 |
+
|
| 1041 |
+
def _eval_is_real(self):
|
| 1042 |
+
z = self.args[0]
|
| 1043 |
+
if z.is_positive:
|
| 1044 |
+
return True
|
| 1045 |
+
elif z.is_nonpositive:
|
| 1046 |
+
return False
|
| 1047 |
+
|
| 1048 |
+
def _eval_conjugate(self):
|
| 1049 |
+
z = self.args[0]
|
| 1050 |
+
if z not in (S.Zero, S.NegativeInfinity):
|
| 1051 |
+
return self.func(z.conjugate())
|
| 1052 |
+
|
| 1053 |
+
def fdiff(self, argindex=1):
|
| 1054 |
+
if argindex == 1:
|
| 1055 |
+
return polygamma(0, self.args[0])
|
| 1056 |
+
else:
|
| 1057 |
+
raise ArgumentIndexError(self, argindex)
|
| 1058 |
+
|
| 1059 |
+
|
| 1060 |
+
class digamma(DefinedFunction):
|
| 1061 |
+
r"""
|
| 1062 |
+
The ``digamma`` function is the first derivative of the ``loggamma``
|
| 1063 |
+
function
|
| 1064 |
+
|
| 1065 |
+
.. math::
|
| 1066 |
+
\psi(x) := \frac{\mathrm{d}}{\mathrm{d} z} \log\Gamma(z)
|
| 1067 |
+
= \frac{\Gamma'(z)}{\Gamma(z) }.
|
| 1068 |
+
|
| 1069 |
+
In this case, ``digamma(z) = polygamma(0, z)``.
|
| 1070 |
+
|
| 1071 |
+
Examples
|
| 1072 |
+
========
|
| 1073 |
+
|
| 1074 |
+
>>> from sympy import digamma
|
| 1075 |
+
>>> digamma(0)
|
| 1076 |
+
zoo
|
| 1077 |
+
>>> from sympy import Symbol
|
| 1078 |
+
>>> z = Symbol('z')
|
| 1079 |
+
>>> digamma(z)
|
| 1080 |
+
polygamma(0, z)
|
| 1081 |
+
|
| 1082 |
+
To retain ``digamma`` as it is:
|
| 1083 |
+
|
| 1084 |
+
>>> digamma(0, evaluate=False)
|
| 1085 |
+
digamma(0)
|
| 1086 |
+
>>> digamma(z, evaluate=False)
|
| 1087 |
+
digamma(z)
|
| 1088 |
+
|
| 1089 |
+
See Also
|
| 1090 |
+
========
|
| 1091 |
+
|
| 1092 |
+
gamma: Gamma function.
|
| 1093 |
+
lowergamma: Lower incomplete gamma function.
|
| 1094 |
+
uppergamma: Upper incomplete gamma function.
|
| 1095 |
+
polygamma: Polygamma function.
|
| 1096 |
+
loggamma: Log Gamma function.
|
| 1097 |
+
trigamma: Trigamma function.
|
| 1098 |
+
sympy.functions.special.beta_functions.beta: Euler Beta function.
|
| 1099 |
+
|
| 1100 |
+
References
|
| 1101 |
+
==========
|
| 1102 |
+
|
| 1103 |
+
.. [1] https://en.wikipedia.org/wiki/Digamma_function
|
| 1104 |
+
.. [2] https://mathworld.wolfram.com/DigammaFunction.html
|
| 1105 |
+
.. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/
|
| 1106 |
+
|
| 1107 |
+
"""
|
| 1108 |
+
def _eval_evalf(self, prec):
|
| 1109 |
+
z = self.args[0]
|
| 1110 |
+
nprec = prec_to_dps(prec)
|
| 1111 |
+
return polygamma(0, z).evalf(n=nprec)
|
| 1112 |
+
|
| 1113 |
+
def fdiff(self, argindex=1):
|
| 1114 |
+
z = self.args[0]
|
| 1115 |
+
return polygamma(0, z).fdiff()
|
| 1116 |
+
|
| 1117 |
+
def _eval_is_real(self):
|
| 1118 |
+
z = self.args[0]
|
| 1119 |
+
return polygamma(0, z).is_real
|
| 1120 |
+
|
| 1121 |
+
def _eval_is_positive(self):
|
| 1122 |
+
z = self.args[0]
|
| 1123 |
+
return polygamma(0, z).is_positive
|
| 1124 |
+
|
| 1125 |
+
def _eval_is_negative(self):
|
| 1126 |
+
z = self.args[0]
|
| 1127 |
+
return polygamma(0, z).is_negative
|
| 1128 |
+
|
| 1129 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 1130 |
+
as_polygamma = self.rewrite(polygamma)
|
| 1131 |
+
args0 = [S.Zero,] + args0
|
| 1132 |
+
return as_polygamma._eval_aseries(n, args0, x, logx)
|
| 1133 |
+
|
| 1134 |
+
@classmethod
|
| 1135 |
+
def eval(cls, z):
|
| 1136 |
+
return polygamma(0, z)
|
| 1137 |
+
|
| 1138 |
+
def _eval_expand_func(self, **hints):
|
| 1139 |
+
z = self.args[0]
|
| 1140 |
+
return polygamma(0, z).expand(func=True)
|
| 1141 |
+
|
| 1142 |
+
def _eval_rewrite_as_harmonic(self, z, **kwargs):
|
| 1143 |
+
return harmonic(z - 1) - S.EulerGamma
|
| 1144 |
+
|
| 1145 |
+
def _eval_rewrite_as_polygamma(self, z, **kwargs):
|
| 1146 |
+
return polygamma(0, z)
|
| 1147 |
+
|
| 1148 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1149 |
+
z = self.args[0]
|
| 1150 |
+
return polygamma(0, z).as_leading_term(x)
|
| 1151 |
+
|
| 1152 |
+
|
| 1153 |
+
|
| 1154 |
+
class trigamma(DefinedFunction):
|
| 1155 |
+
r"""
|
| 1156 |
+
The ``trigamma`` function is the second derivative of the ``loggamma``
|
| 1157 |
+
function
|
| 1158 |
+
|
| 1159 |
+
.. math::
|
| 1160 |
+
\psi^{(1)}(z) := \frac{\mathrm{d}^{2}}{\mathrm{d} z^{2}} \log\Gamma(z).
|
| 1161 |
+
|
| 1162 |
+
In this case, ``trigamma(z) = polygamma(1, z)``.
|
| 1163 |
+
|
| 1164 |
+
Examples
|
| 1165 |
+
========
|
| 1166 |
+
|
| 1167 |
+
>>> from sympy import trigamma
|
| 1168 |
+
>>> trigamma(0)
|
| 1169 |
+
zoo
|
| 1170 |
+
>>> from sympy import Symbol
|
| 1171 |
+
>>> z = Symbol('z')
|
| 1172 |
+
>>> trigamma(z)
|
| 1173 |
+
polygamma(1, z)
|
| 1174 |
+
|
| 1175 |
+
To retain ``trigamma`` as it is:
|
| 1176 |
+
|
| 1177 |
+
>>> trigamma(0, evaluate=False)
|
| 1178 |
+
trigamma(0)
|
| 1179 |
+
>>> trigamma(z, evaluate=False)
|
| 1180 |
+
trigamma(z)
|
| 1181 |
+
|
| 1182 |
+
|
| 1183 |
+
See Also
|
| 1184 |
+
========
|
| 1185 |
+
|
| 1186 |
+
gamma: Gamma function.
|
| 1187 |
+
lowergamma: Lower incomplete gamma function.
|
| 1188 |
+
uppergamma: Upper incomplete gamma function.
|
| 1189 |
+
polygamma: Polygamma function.
|
| 1190 |
+
loggamma: Log Gamma function.
|
| 1191 |
+
digamma: Digamma function.
|
| 1192 |
+
sympy.functions.special.beta_functions.beta: Euler Beta function.
|
| 1193 |
+
|
| 1194 |
+
References
|
| 1195 |
+
==========
|
| 1196 |
+
|
| 1197 |
+
.. [1] https://en.wikipedia.org/wiki/Trigamma_function
|
| 1198 |
+
.. [2] https://mathworld.wolfram.com/TrigammaFunction.html
|
| 1199 |
+
.. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/
|
| 1200 |
+
|
| 1201 |
+
"""
|
| 1202 |
+
def _eval_evalf(self, prec):
|
| 1203 |
+
z = self.args[0]
|
| 1204 |
+
nprec = prec_to_dps(prec)
|
| 1205 |
+
return polygamma(1, z).evalf(n=nprec)
|
| 1206 |
+
|
| 1207 |
+
def fdiff(self, argindex=1):
|
| 1208 |
+
z = self.args[0]
|
| 1209 |
+
return polygamma(1, z).fdiff()
|
| 1210 |
+
|
| 1211 |
+
def _eval_is_real(self):
|
| 1212 |
+
z = self.args[0]
|
| 1213 |
+
return polygamma(1, z).is_real
|
| 1214 |
+
|
| 1215 |
+
def _eval_is_positive(self):
|
| 1216 |
+
z = self.args[0]
|
| 1217 |
+
return polygamma(1, z).is_positive
|
| 1218 |
+
|
| 1219 |
+
def _eval_is_negative(self):
|
| 1220 |
+
z = self.args[0]
|
| 1221 |
+
return polygamma(1, z).is_negative
|
| 1222 |
+
|
| 1223 |
+
def _eval_aseries(self, n, args0, x, logx):
|
| 1224 |
+
as_polygamma = self.rewrite(polygamma)
|
| 1225 |
+
args0 = [S.One,] + args0
|
| 1226 |
+
return as_polygamma._eval_aseries(n, args0, x, logx)
|
| 1227 |
+
|
| 1228 |
+
@classmethod
|
| 1229 |
+
def eval(cls, z):
|
| 1230 |
+
return polygamma(1, z)
|
| 1231 |
+
|
| 1232 |
+
def _eval_expand_func(self, **hints):
|
| 1233 |
+
z = self.args[0]
|
| 1234 |
+
return polygamma(1, z).expand(func=True)
|
| 1235 |
+
|
| 1236 |
+
def _eval_rewrite_as_zeta(self, z, **kwargs):
|
| 1237 |
+
return zeta(2, z)
|
| 1238 |
+
|
| 1239 |
+
def _eval_rewrite_as_polygamma(self, z, **kwargs):
|
| 1240 |
+
return polygamma(1, z)
|
| 1241 |
+
|
| 1242 |
+
def _eval_rewrite_as_harmonic(self, z, **kwargs):
|
| 1243 |
+
return -harmonic(z - 1, 2) + pi**2 / 6
|
| 1244 |
+
|
| 1245 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 1246 |
+
z = self.args[0]
|
| 1247 |
+
return polygamma(1, z).as_leading_term(x)
|
| 1248 |
+
|
| 1249 |
+
|
| 1250 |
+
###############################################################################
|
| 1251 |
+
##################### COMPLETE MULTIVARIATE GAMMA FUNCTION ####################
|
| 1252 |
+
###############################################################################
|
| 1253 |
+
|
| 1254 |
+
|
| 1255 |
+
class multigamma(DefinedFunction):
|
| 1256 |
+
r"""
|
| 1257 |
+
The multivariate gamma function is a generalization of the gamma function
|
| 1258 |
+
|
| 1259 |
+
.. math::
|
| 1260 |
+
\Gamma_p(z) = \pi^{p(p-1)/4}\prod_{k=1}^p \Gamma[z + (1 - k)/2].
|
| 1261 |
+
|
| 1262 |
+
In a special case, ``multigamma(x, 1) = gamma(x)``.
|
| 1263 |
+
|
| 1264 |
+
Examples
|
| 1265 |
+
========
|
| 1266 |
+
|
| 1267 |
+
>>> from sympy import S, multigamma
|
| 1268 |
+
>>> from sympy import Symbol
|
| 1269 |
+
>>> x = Symbol('x')
|
| 1270 |
+
>>> p = Symbol('p', positive=True, integer=True)
|
| 1271 |
+
|
| 1272 |
+
>>> multigamma(x, p)
|
| 1273 |
+
pi**(p*(p - 1)/4)*Product(gamma(-_k/2 + x + 1/2), (_k, 1, p))
|
| 1274 |
+
|
| 1275 |
+
Several special values are known:
|
| 1276 |
+
|
| 1277 |
+
>>> multigamma(1, 1)
|
| 1278 |
+
1
|
| 1279 |
+
>>> multigamma(4, 1)
|
| 1280 |
+
6
|
| 1281 |
+
>>> multigamma(S(3)/2, 1)
|
| 1282 |
+
sqrt(pi)/2
|
| 1283 |
+
|
| 1284 |
+
Writing ``multigamma`` in terms of the ``gamma`` function:
|
| 1285 |
+
|
| 1286 |
+
>>> multigamma(x, 1)
|
| 1287 |
+
gamma(x)
|
| 1288 |
+
|
| 1289 |
+
>>> multigamma(x, 2)
|
| 1290 |
+
sqrt(pi)*gamma(x)*gamma(x - 1/2)
|
| 1291 |
+
|
| 1292 |
+
>>> multigamma(x, 3)
|
| 1293 |
+
pi**(3/2)*gamma(x)*gamma(x - 1)*gamma(x - 1/2)
|
| 1294 |
+
|
| 1295 |
+
Parameters
|
| 1296 |
+
==========
|
| 1297 |
+
|
| 1298 |
+
p : order or dimension of the multivariate gamma function
|
| 1299 |
+
|
| 1300 |
+
See Also
|
| 1301 |
+
========
|
| 1302 |
+
|
| 1303 |
+
gamma, lowergamma, uppergamma, polygamma, loggamma, digamma, trigamma,
|
| 1304 |
+
sympy.functions.special.beta_functions.beta
|
| 1305 |
+
|
| 1306 |
+
References
|
| 1307 |
+
==========
|
| 1308 |
+
|
| 1309 |
+
.. [1] https://en.wikipedia.org/wiki/Multivariate_gamma_function
|
| 1310 |
+
|
| 1311 |
+
"""
|
| 1312 |
+
unbranched = True
|
| 1313 |
+
|
| 1314 |
+
def fdiff(self, argindex=2):
|
| 1315 |
+
from sympy.concrete.summations import Sum
|
| 1316 |
+
if argindex == 2:
|
| 1317 |
+
x, p = self.args
|
| 1318 |
+
k = Dummy("k")
|
| 1319 |
+
return self.func(x, p)*Sum(polygamma(0, x + (1 - k)/2), (k, 1, p))
|
| 1320 |
+
else:
|
| 1321 |
+
raise ArgumentIndexError(self, argindex)
|
| 1322 |
+
|
| 1323 |
+
@classmethod
|
| 1324 |
+
def eval(cls, x, p):
|
| 1325 |
+
from sympy.concrete.products import Product
|
| 1326 |
+
if p.is_positive is False or p.is_integer is False:
|
| 1327 |
+
raise ValueError('Order parameter p must be positive integer.')
|
| 1328 |
+
k = Dummy("k")
|
| 1329 |
+
return (pi**(p*(p - 1)/4)*Product(gamma(x + (1 - k)/2),
|
| 1330 |
+
(k, 1, p))).doit()
|
| 1331 |
+
|
| 1332 |
+
def _eval_conjugate(self):
|
| 1333 |
+
x, p = self.args
|
| 1334 |
+
return self.func(x.conjugate(), p)
|
| 1335 |
+
|
| 1336 |
+
def _eval_is_real(self):
|
| 1337 |
+
x, p = self.args
|
| 1338 |
+
y = 2*x
|
| 1339 |
+
if y.is_integer and (y <= (p - 1)) is True:
|
| 1340 |
+
return False
|
| 1341 |
+
if intlike(y) and (y <= (p - 1)):
|
| 1342 |
+
return False
|
| 1343 |
+
if y > (p - 1) or y.is_noninteger:
|
| 1344 |
+
return True
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/hyper.py
ADDED
|
@@ -0,0 +1,1185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Hypergeometric and Meijer G-functions"""
|
| 2 |
+
from collections import Counter
|
| 3 |
+
|
| 4 |
+
from sympy.core import S, Mod
|
| 5 |
+
from sympy.core.add import Add
|
| 6 |
+
from sympy.core.expr import Expr
|
| 7 |
+
from sympy.core.function import DefinedFunction, Derivative, ArgumentIndexError
|
| 8 |
+
|
| 9 |
+
from sympy.core.containers import Tuple
|
| 10 |
+
from sympy.core.mul import Mul
|
| 11 |
+
from sympy.core.numbers import I, pi, oo, zoo
|
| 12 |
+
from sympy.core.parameters import global_parameters
|
| 13 |
+
from sympy.core.relational import Ne
|
| 14 |
+
from sympy.core.sorting import default_sort_key
|
| 15 |
+
from sympy.core.symbol import Dummy
|
| 16 |
+
|
| 17 |
+
from sympy.external.gmpy import lcm
|
| 18 |
+
from sympy.functions import (sqrt, exp, log, sin, cos, asin, atan,
|
| 19 |
+
sinh, cosh, asinh, acosh, atanh, acoth)
|
| 20 |
+
from sympy.functions import factorial, RisingFactorial
|
| 21 |
+
from sympy.functions.elementary.complexes import Abs, re, unpolarify
|
| 22 |
+
from sympy.functions.elementary.exponential import exp_polar
|
| 23 |
+
from sympy.functions.elementary.integers import ceiling
|
| 24 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 25 |
+
from sympy.logic.boolalg import (And, Or)
|
| 26 |
+
from sympy import ordered
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class TupleArg(Tuple):
|
| 30 |
+
|
| 31 |
+
# This method is only needed because hyper._eval_as_leading_term falls back
|
| 32 |
+
# (via super()) on using Function._eval_as_leading_term, which in turn
|
| 33 |
+
# calls as_leading_term on the args of the hyper. Ideally hyper should just
|
| 34 |
+
# have an _eval_as_leading_term method that handles all cases and this
|
| 35 |
+
# method should be removed because leading terms of tuples don't make
|
| 36 |
+
# sense.
|
| 37 |
+
def as_leading_term(self, *x, logx=None, cdir=0):
|
| 38 |
+
return TupleArg(*[f.as_leading_term(*x, logx=logx, cdir=cdir) for f in self.args])
|
| 39 |
+
|
| 40 |
+
def limit(self, x, xlim, dir='+'):
|
| 41 |
+
""" Compute limit x->xlim.
|
| 42 |
+
"""
|
| 43 |
+
from sympy.series.limits import limit
|
| 44 |
+
return TupleArg(*[limit(f, x, xlim, dir) for f in self.args])
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
# TODO should __new__ accept **options?
|
| 48 |
+
# TODO should constructors should check if parameters are sensible?
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _prep_tuple(v):
|
| 52 |
+
"""
|
| 53 |
+
Turn an iterable argument *v* into a tuple and unpolarify, since both
|
| 54 |
+
hypergeometric and meijer g-functions are unbranched in their parameters.
|
| 55 |
+
|
| 56 |
+
Examples
|
| 57 |
+
========
|
| 58 |
+
|
| 59 |
+
>>> from sympy.functions.special.hyper import _prep_tuple
|
| 60 |
+
>>> _prep_tuple([1, 2, 3])
|
| 61 |
+
(1, 2, 3)
|
| 62 |
+
>>> _prep_tuple((4, 5))
|
| 63 |
+
(4, 5)
|
| 64 |
+
>>> _prep_tuple((7, 8, 9))
|
| 65 |
+
(7, 8, 9)
|
| 66 |
+
|
| 67 |
+
"""
|
| 68 |
+
return TupleArg(*[unpolarify(x) for x in v])
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class TupleParametersBase(DefinedFunction):
|
| 72 |
+
""" Base class that takes care of differentiation, when some of
|
| 73 |
+
the arguments are actually tuples. """
|
| 74 |
+
# This is not deduced automatically since there are Tuples as arguments.
|
| 75 |
+
is_commutative = True
|
| 76 |
+
|
| 77 |
+
def _eval_derivative(self, s):
|
| 78 |
+
try:
|
| 79 |
+
res = 0
|
| 80 |
+
if self.args[0].has(s) or self.args[1].has(s):
|
| 81 |
+
for i, p in enumerate(self._diffargs):
|
| 82 |
+
m = self._diffargs[i].diff(s)
|
| 83 |
+
if m != 0:
|
| 84 |
+
res += self.fdiff((1, i))*m
|
| 85 |
+
return res + self.fdiff(3)*self.args[2].diff(s)
|
| 86 |
+
except (ArgumentIndexError, NotImplementedError):
|
| 87 |
+
return Derivative(self, s)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class hyper(TupleParametersBase):
|
| 91 |
+
r"""
|
| 92 |
+
The generalized hypergeometric function is defined by a series where
|
| 93 |
+
the ratios of successive terms are a rational function of the summation
|
| 94 |
+
index. When convergent, it is continued analytically to the largest
|
| 95 |
+
possible domain.
|
| 96 |
+
|
| 97 |
+
Explanation
|
| 98 |
+
===========
|
| 99 |
+
|
| 100 |
+
The hypergeometric function depends on two vectors of parameters, called
|
| 101 |
+
the numerator parameters $a_p$, and the denominator parameters
|
| 102 |
+
$b_q$. It also has an argument $z$. The series definition is
|
| 103 |
+
|
| 104 |
+
.. math ::
|
| 105 |
+
{}_pF_q\left(\begin{matrix} a_1, \cdots, a_p \\ b_1, \cdots, b_q \end{matrix}
|
| 106 |
+
\middle| z \right)
|
| 107 |
+
= \sum_{n=0}^\infty \frac{(a_1)_n \cdots (a_p)_n}{(b_1)_n \cdots (b_q)_n}
|
| 108 |
+
\frac{z^n}{n!},
|
| 109 |
+
|
| 110 |
+
where $(a)_n = (a)(a+1)\cdots(a+n-1)$ denotes the rising factorial.
|
| 111 |
+
|
| 112 |
+
If one of the $b_q$ is a non-positive integer then the series is
|
| 113 |
+
undefined unless one of the $a_p$ is a larger (i.e., smaller in
|
| 114 |
+
magnitude) non-positive integer. If none of the $b_q$ is a
|
| 115 |
+
non-positive integer and one of the $a_p$ is a non-positive
|
| 116 |
+
integer, then the series reduces to a polynomial. To simplify the
|
| 117 |
+
following discussion, we assume that none of the $a_p$ or
|
| 118 |
+
$b_q$ is a non-positive integer. For more details, see the
|
| 119 |
+
references.
|
| 120 |
+
|
| 121 |
+
The series converges for all $z$ if $p \le q$, and thus
|
| 122 |
+
defines an entire single-valued function in this case. If $p =
|
| 123 |
+
q+1$ the series converges for $|z| < 1$, and can be continued
|
| 124 |
+
analytically into a half-plane. If $p > q+1$ the series is
|
| 125 |
+
divergent for all $z$.
|
| 126 |
+
|
| 127 |
+
Please note the hypergeometric function constructor currently does *not*
|
| 128 |
+
check if the parameters actually yield a well-defined function.
|
| 129 |
+
|
| 130 |
+
Examples
|
| 131 |
+
========
|
| 132 |
+
|
| 133 |
+
The parameters $a_p$ and $b_q$ can be passed as arbitrary
|
| 134 |
+
iterables, for example:
|
| 135 |
+
|
| 136 |
+
>>> from sympy import hyper
|
| 137 |
+
>>> from sympy.abc import x, n, a
|
| 138 |
+
>>> h = hyper((1, 2, 3), [3, 4], x); h
|
| 139 |
+
hyper((1, 2), (4,), x)
|
| 140 |
+
>>> hyper((3, 1, 2), [3, 4], x, evaluate=False) # don't remove duplicates
|
| 141 |
+
hyper((1, 2, 3), (3, 4), x)
|
| 142 |
+
|
| 143 |
+
There is also pretty printing (it looks better using Unicode):
|
| 144 |
+
|
| 145 |
+
>>> from sympy import pprint
|
| 146 |
+
>>> pprint(h, use_unicode=False)
|
| 147 |
+
_
|
| 148 |
+
|_ /1, 2 | \
|
| 149 |
+
| | | x|
|
| 150 |
+
2 1 \ 4 | /
|
| 151 |
+
|
| 152 |
+
The parameters must always be iterables, even if they are vectors of
|
| 153 |
+
length one or zero:
|
| 154 |
+
|
| 155 |
+
>>> hyper((1, ), [], x)
|
| 156 |
+
hyper((1,), (), x)
|
| 157 |
+
|
| 158 |
+
But of course they may be variables (but if they depend on $x$ then you
|
| 159 |
+
should not expect much implemented functionality):
|
| 160 |
+
|
| 161 |
+
>>> hyper((n, a), (n**2,), x)
|
| 162 |
+
hyper((a, n), (n**2,), x)
|
| 163 |
+
|
| 164 |
+
The hypergeometric function generalizes many named special functions.
|
| 165 |
+
The function ``hyperexpand()`` tries to express a hypergeometric function
|
| 166 |
+
using named special functions. For example:
|
| 167 |
+
|
| 168 |
+
>>> from sympy import hyperexpand
|
| 169 |
+
>>> hyperexpand(hyper([], [], x))
|
| 170 |
+
exp(x)
|
| 171 |
+
|
| 172 |
+
You can also use ``expand_func()``:
|
| 173 |
+
|
| 174 |
+
>>> from sympy import expand_func
|
| 175 |
+
>>> expand_func(x*hyper([1, 1], [2], -x))
|
| 176 |
+
log(x + 1)
|
| 177 |
+
|
| 178 |
+
More examples:
|
| 179 |
+
|
| 180 |
+
>>> from sympy import S
|
| 181 |
+
>>> hyperexpand(hyper([], [S(1)/2], -x**2/4))
|
| 182 |
+
cos(x)
|
| 183 |
+
>>> hyperexpand(x*hyper([S(1)/2, S(1)/2], [S(3)/2], x**2))
|
| 184 |
+
asin(x)
|
| 185 |
+
|
| 186 |
+
We can also sometimes ``hyperexpand()`` parametric functions:
|
| 187 |
+
|
| 188 |
+
>>> from sympy.abc import a
|
| 189 |
+
>>> hyperexpand(hyper([-a], [], x))
|
| 190 |
+
(1 - x)**a
|
| 191 |
+
|
| 192 |
+
See Also
|
| 193 |
+
========
|
| 194 |
+
|
| 195 |
+
sympy.simplify.hyperexpand
|
| 196 |
+
gamma
|
| 197 |
+
meijerg
|
| 198 |
+
|
| 199 |
+
References
|
| 200 |
+
==========
|
| 201 |
+
|
| 202 |
+
.. [1] Luke, Y. L. (1969), The Special Functions and Their Approximations,
|
| 203 |
+
Volume 1
|
| 204 |
+
.. [2] https://en.wikipedia.org/wiki/Generalized_hypergeometric_function
|
| 205 |
+
|
| 206 |
+
"""
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def __new__(cls, ap, bq, z, **kwargs):
|
| 210 |
+
# TODO should we check convergence conditions?
|
| 211 |
+
if kwargs.pop('evaluate', global_parameters.evaluate):
|
| 212 |
+
ca = Counter(Tuple(*ap))
|
| 213 |
+
cb = Counter(Tuple(*bq))
|
| 214 |
+
common = ca & cb
|
| 215 |
+
arg = ap, bq = [], []
|
| 216 |
+
for i, c in enumerate((ca, cb)):
|
| 217 |
+
c -= common
|
| 218 |
+
for k in ordered(c):
|
| 219 |
+
arg[i].extend([k]*c[k])
|
| 220 |
+
else:
|
| 221 |
+
ap = list(ordered(ap))
|
| 222 |
+
bq = list(ordered(bq))
|
| 223 |
+
return super().__new__(cls, _prep_tuple(ap), _prep_tuple(bq), z, **kwargs)
|
| 224 |
+
|
| 225 |
+
@classmethod
|
| 226 |
+
def eval(cls, ap, bq, z):
|
| 227 |
+
if len(ap) <= len(bq) or (len(ap) == len(bq) + 1 and (Abs(z) <= 1) == True):
|
| 228 |
+
nz = unpolarify(z)
|
| 229 |
+
if z != nz:
|
| 230 |
+
return hyper(ap, bq, nz)
|
| 231 |
+
|
| 232 |
+
def fdiff(self, argindex=3):
|
| 233 |
+
if argindex != 3:
|
| 234 |
+
raise ArgumentIndexError(self, argindex)
|
| 235 |
+
nap = Tuple(*[a + 1 for a in self.ap])
|
| 236 |
+
nbq = Tuple(*[b + 1 for b in self.bq])
|
| 237 |
+
fac = Mul(*self.ap)/Mul(*self.bq)
|
| 238 |
+
return fac*hyper(nap, nbq, self.argument)
|
| 239 |
+
|
| 240 |
+
def _eval_expand_func(self, **hints):
|
| 241 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 242 |
+
from sympy.simplify.hyperexpand import hyperexpand
|
| 243 |
+
if len(self.ap) == 2 and len(self.bq) == 1 and self.argument == 1:
|
| 244 |
+
a, b = self.ap
|
| 245 |
+
c = self.bq[0]
|
| 246 |
+
return gamma(c)*gamma(c - a - b)/gamma(c - a)/gamma(c - b)
|
| 247 |
+
return hyperexpand(self)
|
| 248 |
+
|
| 249 |
+
def _eval_rewrite_as_Sum(self, ap, bq, z, **kwargs):
|
| 250 |
+
from sympy.concrete.summations import Sum
|
| 251 |
+
n = Dummy("n", integer=True)
|
| 252 |
+
rfap = [RisingFactorial(a, n) for a in ap]
|
| 253 |
+
rfbq = [RisingFactorial(b, n) for b in bq]
|
| 254 |
+
coeff = Mul(*rfap) / Mul(*rfbq)
|
| 255 |
+
return Piecewise((Sum(coeff * z**n / factorial(n), (n, 0, oo)),
|
| 256 |
+
self.convergence_statement), (self, True))
|
| 257 |
+
|
| 258 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 259 |
+
arg = self.args[2]
|
| 260 |
+
x0 = arg.subs(x, 0)
|
| 261 |
+
if x0 is S.NaN:
|
| 262 |
+
x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 263 |
+
|
| 264 |
+
if x0 is S.Zero:
|
| 265 |
+
return S.One
|
| 266 |
+
return super()._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 267 |
+
|
| 268 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 269 |
+
|
| 270 |
+
from sympy.series.order import Order
|
| 271 |
+
|
| 272 |
+
arg = self.args[2]
|
| 273 |
+
x0 = arg.limit(x, 0)
|
| 274 |
+
ap = self.args[0]
|
| 275 |
+
bq = self.args[1]
|
| 276 |
+
|
| 277 |
+
if not (arg == x and x0 == 0):
|
| 278 |
+
# It would be better to do something with arg.nseries here, rather
|
| 279 |
+
# than falling back on Function._eval_nseries. The code below
|
| 280 |
+
# though is not sufficient if arg is something like x/(x+1).
|
| 281 |
+
from sympy.simplify.hyperexpand import hyperexpand
|
| 282 |
+
return hyperexpand(super()._eval_nseries(x, n, logx))
|
| 283 |
+
|
| 284 |
+
terms = []
|
| 285 |
+
|
| 286 |
+
for i in range(n):
|
| 287 |
+
num = Mul(*[RisingFactorial(a, i) for a in ap])
|
| 288 |
+
den = Mul(*[RisingFactorial(b, i) for b in bq])
|
| 289 |
+
terms.append(((num/den) * (arg**i)) / factorial(i))
|
| 290 |
+
|
| 291 |
+
return (Add(*terms) + Order(x**n,x))
|
| 292 |
+
|
| 293 |
+
@property
|
| 294 |
+
def argument(self):
|
| 295 |
+
""" Argument of the hypergeometric function. """
|
| 296 |
+
return self.args[2]
|
| 297 |
+
|
| 298 |
+
@property
|
| 299 |
+
def ap(self):
|
| 300 |
+
""" Numerator parameters of the hypergeometric function. """
|
| 301 |
+
return Tuple(*self.args[0])
|
| 302 |
+
|
| 303 |
+
@property
|
| 304 |
+
def bq(self):
|
| 305 |
+
""" Denominator parameters of the hypergeometric function. """
|
| 306 |
+
return Tuple(*self.args[1])
|
| 307 |
+
|
| 308 |
+
@property
|
| 309 |
+
def _diffargs(self):
|
| 310 |
+
return self.ap + self.bq
|
| 311 |
+
|
| 312 |
+
@property
|
| 313 |
+
def eta(self):
|
| 314 |
+
""" A quantity related to the convergence of the series. """
|
| 315 |
+
return sum(self.ap) - sum(self.bq)
|
| 316 |
+
|
| 317 |
+
@property
|
| 318 |
+
def radius_of_convergence(self):
|
| 319 |
+
"""
|
| 320 |
+
Compute the radius of convergence of the defining series.
|
| 321 |
+
|
| 322 |
+
Explanation
|
| 323 |
+
===========
|
| 324 |
+
|
| 325 |
+
Note that even if this is not ``oo``, the function may still be
|
| 326 |
+
evaluated outside of the radius of convergence by analytic
|
| 327 |
+
continuation. But if this is zero, then the function is not actually
|
| 328 |
+
defined anywhere else.
|
| 329 |
+
|
| 330 |
+
Examples
|
| 331 |
+
========
|
| 332 |
+
|
| 333 |
+
>>> from sympy import hyper
|
| 334 |
+
>>> from sympy.abc import z
|
| 335 |
+
>>> hyper((1, 2), [3], z).radius_of_convergence
|
| 336 |
+
1
|
| 337 |
+
>>> hyper((1, 2, 3), [4], z).radius_of_convergence
|
| 338 |
+
0
|
| 339 |
+
>>> hyper((1, 2), (3, 4), z).radius_of_convergence
|
| 340 |
+
oo
|
| 341 |
+
|
| 342 |
+
"""
|
| 343 |
+
if any(a.is_integer and (a <= 0) == True for a in self.ap + self.bq):
|
| 344 |
+
aints = [a for a in self.ap if a.is_Integer and (a <= 0) == True]
|
| 345 |
+
bints = [a for a in self.bq if a.is_Integer and (a <= 0) == True]
|
| 346 |
+
if len(aints) < len(bints):
|
| 347 |
+
return S.Zero
|
| 348 |
+
popped = False
|
| 349 |
+
for b in bints:
|
| 350 |
+
cancelled = False
|
| 351 |
+
while aints:
|
| 352 |
+
a = aints.pop()
|
| 353 |
+
if a >= b:
|
| 354 |
+
cancelled = True
|
| 355 |
+
break
|
| 356 |
+
popped = True
|
| 357 |
+
if not cancelled:
|
| 358 |
+
return S.Zero
|
| 359 |
+
if aints or popped:
|
| 360 |
+
# There are still non-positive numerator parameters.
|
| 361 |
+
# This is a polynomial.
|
| 362 |
+
return oo
|
| 363 |
+
if len(self.ap) == len(self.bq) + 1:
|
| 364 |
+
return S.One
|
| 365 |
+
elif len(self.ap) <= len(self.bq):
|
| 366 |
+
return oo
|
| 367 |
+
else:
|
| 368 |
+
return S.Zero
|
| 369 |
+
|
| 370 |
+
@property
|
| 371 |
+
def convergence_statement(self):
|
| 372 |
+
""" Return a condition on z under which the series converges. """
|
| 373 |
+
R = self.radius_of_convergence
|
| 374 |
+
if R == 0:
|
| 375 |
+
return False
|
| 376 |
+
if R == oo:
|
| 377 |
+
return True
|
| 378 |
+
# The special functions and their approximations, page 44
|
| 379 |
+
e = self.eta
|
| 380 |
+
z = self.argument
|
| 381 |
+
c1 = And(re(e) < 0, abs(z) <= 1)
|
| 382 |
+
c2 = And(0 <= re(e), re(e) < 1, abs(z) <= 1, Ne(z, 1))
|
| 383 |
+
c3 = And(re(e) >= 1, abs(z) < 1)
|
| 384 |
+
return Or(c1, c2, c3)
|
| 385 |
+
|
| 386 |
+
def _eval_simplify(self, **kwargs):
|
| 387 |
+
from sympy.simplify.hyperexpand import hyperexpand
|
| 388 |
+
return hyperexpand(self)
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
class meijerg(TupleParametersBase):
|
| 392 |
+
r"""
|
| 393 |
+
The Meijer G-function is defined by a Mellin-Barnes type integral that
|
| 394 |
+
resembles an inverse Mellin transform. It generalizes the hypergeometric
|
| 395 |
+
functions.
|
| 396 |
+
|
| 397 |
+
Explanation
|
| 398 |
+
===========
|
| 399 |
+
|
| 400 |
+
The Meijer G-function depends on four sets of parameters. There are
|
| 401 |
+
"*numerator parameters*"
|
| 402 |
+
$a_1, \ldots, a_n$ and $a_{n+1}, \ldots, a_p$, and there are
|
| 403 |
+
"*denominator parameters*"
|
| 404 |
+
$b_1, \ldots, b_m$ and $b_{m+1}, \ldots, b_q$.
|
| 405 |
+
Confusingly, it is traditionally denoted as follows (note the position
|
| 406 |
+
of $m$, $n$, $p$, $q$, and how they relate to the lengths of the four
|
| 407 |
+
parameter vectors):
|
| 408 |
+
|
| 409 |
+
.. math ::
|
| 410 |
+
G_{p,q}^{m,n} \left(\begin{matrix}a_1, \cdots, a_n & a_{n+1}, \cdots, a_p \\
|
| 411 |
+
b_1, \cdots, b_m & b_{m+1}, \cdots, b_q
|
| 412 |
+
\end{matrix} \middle| z \right).
|
| 413 |
+
|
| 414 |
+
However, in SymPy the four parameter vectors are always available
|
| 415 |
+
separately (see examples), so that there is no need to keep track of the
|
| 416 |
+
decorating sub- and super-scripts on the G symbol.
|
| 417 |
+
|
| 418 |
+
The G function is defined as the following integral:
|
| 419 |
+
|
| 420 |
+
.. math ::
|
| 421 |
+
\frac{1}{2 \pi i} \int_L \frac{\prod_{j=1}^m \Gamma(b_j - s)
|
| 422 |
+
\prod_{j=1}^n \Gamma(1 - a_j + s)}{\prod_{j=m+1}^q \Gamma(1- b_j +s)
|
| 423 |
+
\prod_{j=n+1}^p \Gamma(a_j - s)} z^s \mathrm{d}s,
|
| 424 |
+
|
| 425 |
+
where $\Gamma(z)$ is the gamma function. There are three possible
|
| 426 |
+
contours which we will not describe in detail here (see the references).
|
| 427 |
+
If the integral converges along more than one of them, the definitions
|
| 428 |
+
agree. The contours all separate the poles of $\Gamma(1-a_j+s)$
|
| 429 |
+
from the poles of $\Gamma(b_k-s)$, so in particular the G function
|
| 430 |
+
is undefined if $a_j - b_k \in \mathbb{Z}_{>0}$ for some
|
| 431 |
+
$j \le n$ and $k \le m$.
|
| 432 |
+
|
| 433 |
+
The conditions under which one of the contours yields a convergent integral
|
| 434 |
+
are complicated and we do not state them here, see the references.
|
| 435 |
+
|
| 436 |
+
Please note currently the Meijer G-function constructor does *not* check any
|
| 437 |
+
convergence conditions.
|
| 438 |
+
|
| 439 |
+
Examples
|
| 440 |
+
========
|
| 441 |
+
|
| 442 |
+
You can pass the parameters either as four separate vectors:
|
| 443 |
+
|
| 444 |
+
>>> from sympy import meijerg, Tuple, pprint
|
| 445 |
+
>>> from sympy.abc import x, a
|
| 446 |
+
>>> pprint(meijerg((1, 2), (a, 4), (5,), [], x), use_unicode=False)
|
| 447 |
+
__1, 2 /1, 2 4, a | \
|
| 448 |
+
/__ | | x|
|
| 449 |
+
\_|4, 1 \ 5 | /
|
| 450 |
+
|
| 451 |
+
Or as two nested vectors:
|
| 452 |
+
|
| 453 |
+
>>> pprint(meijerg([(1, 2), (3, 4)], ([5], Tuple()), x), use_unicode=False)
|
| 454 |
+
__1, 2 /1, 2 3, 4 | \
|
| 455 |
+
/__ | | x|
|
| 456 |
+
\_|4, 1 \ 5 | /
|
| 457 |
+
|
| 458 |
+
As with the hypergeometric function, the parameters may be passed as
|
| 459 |
+
arbitrary iterables. Vectors of length zero and one also have to be
|
| 460 |
+
passed as iterables. The parameters need not be constants, but if they
|
| 461 |
+
depend on the argument then not much implemented functionality should be
|
| 462 |
+
expected.
|
| 463 |
+
|
| 464 |
+
All the subvectors of parameters are available:
|
| 465 |
+
|
| 466 |
+
>>> from sympy import pprint
|
| 467 |
+
>>> g = meijerg([1], [2], [3], [4], x)
|
| 468 |
+
>>> pprint(g, use_unicode=False)
|
| 469 |
+
__1, 1 /1 2 | \
|
| 470 |
+
/__ | | x|
|
| 471 |
+
\_|2, 2 \3 4 | /
|
| 472 |
+
>>> g.an
|
| 473 |
+
(1,)
|
| 474 |
+
>>> g.ap
|
| 475 |
+
(1, 2)
|
| 476 |
+
>>> g.aother
|
| 477 |
+
(2,)
|
| 478 |
+
>>> g.bm
|
| 479 |
+
(3,)
|
| 480 |
+
>>> g.bq
|
| 481 |
+
(3, 4)
|
| 482 |
+
>>> g.bother
|
| 483 |
+
(4,)
|
| 484 |
+
|
| 485 |
+
The Meijer G-function generalizes the hypergeometric functions.
|
| 486 |
+
In some cases it can be expressed in terms of hypergeometric functions,
|
| 487 |
+
using Slater's theorem. For example:
|
| 488 |
+
|
| 489 |
+
>>> from sympy import hyperexpand
|
| 490 |
+
>>> from sympy.abc import a, b, c
|
| 491 |
+
>>> hyperexpand(meijerg([a], [], [c], [b], x), allow_hyper=True)
|
| 492 |
+
x**c*gamma(-a + c + 1)*hyper((-a + c + 1,),
|
| 493 |
+
(-b + c + 1,), -x)/gamma(-b + c + 1)
|
| 494 |
+
|
| 495 |
+
Thus the Meijer G-function also subsumes many named functions as special
|
| 496 |
+
cases. You can use ``expand_func()`` or ``hyperexpand()`` to (try to)
|
| 497 |
+
rewrite a Meijer G-function in terms of named special functions. For
|
| 498 |
+
example:
|
| 499 |
+
|
| 500 |
+
>>> from sympy import expand_func, S
|
| 501 |
+
>>> expand_func(meijerg([[],[]], [[0],[]], -x))
|
| 502 |
+
exp(x)
|
| 503 |
+
>>> hyperexpand(meijerg([[],[]], [[S(1)/2],[0]], (x/2)**2))
|
| 504 |
+
sin(x)/sqrt(pi)
|
| 505 |
+
|
| 506 |
+
See Also
|
| 507 |
+
========
|
| 508 |
+
|
| 509 |
+
hyper
|
| 510 |
+
sympy.simplify.hyperexpand
|
| 511 |
+
|
| 512 |
+
References
|
| 513 |
+
==========
|
| 514 |
+
|
| 515 |
+
.. [1] Luke, Y. L. (1969), The Special Functions and Their Approximations,
|
| 516 |
+
Volume 1
|
| 517 |
+
.. [2] https://en.wikipedia.org/wiki/Meijer_G-function
|
| 518 |
+
|
| 519 |
+
"""
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
def __new__(cls, *args, **kwargs):
|
| 523 |
+
if len(args) == 5:
|
| 524 |
+
args = [(args[0], args[1]), (args[2], args[3]), args[4]]
|
| 525 |
+
if len(args) != 3:
|
| 526 |
+
raise TypeError("args must be either as, as', bs, bs', z or "
|
| 527 |
+
"as, bs, z")
|
| 528 |
+
|
| 529 |
+
def tr(p):
|
| 530 |
+
if len(p) != 2:
|
| 531 |
+
raise TypeError("wrong argument")
|
| 532 |
+
p = [list(ordered(i)) for i in p]
|
| 533 |
+
return TupleArg(_prep_tuple(p[0]), _prep_tuple(p[1]))
|
| 534 |
+
|
| 535 |
+
arg0, arg1 = tr(args[0]), tr(args[1])
|
| 536 |
+
if Tuple(arg0, arg1).has(oo, zoo, -oo):
|
| 537 |
+
raise ValueError("G-function parameters must be finite")
|
| 538 |
+
if any((a - b).is_Integer and a - b > 0
|
| 539 |
+
for a in arg0[0] for b in arg1[0]):
|
| 540 |
+
raise ValueError("no parameter a1, ..., an may differ from "
|
| 541 |
+
"any b1, ..., bm by a positive integer")
|
| 542 |
+
|
| 543 |
+
# TODO should we check convergence conditions?
|
| 544 |
+
return super().__new__(cls, arg0, arg1, args[2], **kwargs)
|
| 545 |
+
|
| 546 |
+
def fdiff(self, argindex=3):
|
| 547 |
+
if argindex != 3:
|
| 548 |
+
return self._diff_wrt_parameter(argindex[1])
|
| 549 |
+
if len(self.an) >= 1:
|
| 550 |
+
a = list(self.an)
|
| 551 |
+
a[0] -= 1
|
| 552 |
+
G = meijerg(a, self.aother, self.bm, self.bother, self.argument)
|
| 553 |
+
return 1/self.argument * ((self.an[0] - 1)*self + G)
|
| 554 |
+
elif len(self.bm) >= 1:
|
| 555 |
+
b = list(self.bm)
|
| 556 |
+
b[0] += 1
|
| 557 |
+
G = meijerg(self.an, self.aother, b, self.bother, self.argument)
|
| 558 |
+
return 1/self.argument * (self.bm[0]*self - G)
|
| 559 |
+
else:
|
| 560 |
+
return S.Zero
|
| 561 |
+
|
| 562 |
+
def _diff_wrt_parameter(self, idx):
|
| 563 |
+
# Differentiation wrt a parameter can only be done in very special
|
| 564 |
+
# cases. In particular, if we want to differentiate with respect to
|
| 565 |
+
# `a`, all other gamma factors have to reduce to rational functions.
|
| 566 |
+
#
|
| 567 |
+
# Let MT denote mellin transform. Suppose T(-s) is the gamma factor
|
| 568 |
+
# appearing in the definition of G. Then
|
| 569 |
+
#
|
| 570 |
+
# MT(log(z)G(z)) = d/ds T(s) = d/da T(s) + ...
|
| 571 |
+
#
|
| 572 |
+
# Thus d/da G(z) = log(z)G(z) - ...
|
| 573 |
+
# The ... can be evaluated as a G function under the above conditions,
|
| 574 |
+
# the formula being most easily derived by using
|
| 575 |
+
#
|
| 576 |
+
# d Gamma(s + n) Gamma(s + n) / 1 1 1 \
|
| 577 |
+
# -- ------------ = ------------ | - + ---- + ... + --------- |
|
| 578 |
+
# ds Gamma(s) Gamma(s) \ s s + 1 s + n - 1 /
|
| 579 |
+
#
|
| 580 |
+
# which follows from the difference equation of the digamma function.
|
| 581 |
+
# (There is a similar equation for -n instead of +n).
|
| 582 |
+
|
| 583 |
+
# We first figure out how to pair the parameters.
|
| 584 |
+
an = list(self.an)
|
| 585 |
+
ap = list(self.aother)
|
| 586 |
+
bm = list(self.bm)
|
| 587 |
+
bq = list(self.bother)
|
| 588 |
+
if idx < len(an):
|
| 589 |
+
an.pop(idx)
|
| 590 |
+
else:
|
| 591 |
+
idx -= len(an)
|
| 592 |
+
if idx < len(ap):
|
| 593 |
+
ap.pop(idx)
|
| 594 |
+
else:
|
| 595 |
+
idx -= len(ap)
|
| 596 |
+
if idx < len(bm):
|
| 597 |
+
bm.pop(idx)
|
| 598 |
+
else:
|
| 599 |
+
bq.pop(idx - len(bm))
|
| 600 |
+
pairs1 = []
|
| 601 |
+
pairs2 = []
|
| 602 |
+
for l1, l2, pairs in [(an, bq, pairs1), (ap, bm, pairs2)]:
|
| 603 |
+
while l1:
|
| 604 |
+
x = l1.pop()
|
| 605 |
+
found = None
|
| 606 |
+
for i, y in enumerate(l2):
|
| 607 |
+
if not Mod((x - y).simplify(), 1):
|
| 608 |
+
found = i
|
| 609 |
+
break
|
| 610 |
+
if found is None:
|
| 611 |
+
raise NotImplementedError('Derivative not expressible '
|
| 612 |
+
'as G-function?')
|
| 613 |
+
y = l2[i]
|
| 614 |
+
l2.pop(i)
|
| 615 |
+
pairs.append((x, y))
|
| 616 |
+
|
| 617 |
+
# Now build the result.
|
| 618 |
+
res = log(self.argument)*self
|
| 619 |
+
|
| 620 |
+
for a, b in pairs1:
|
| 621 |
+
sign = 1
|
| 622 |
+
n = a - b
|
| 623 |
+
base = b
|
| 624 |
+
if n < 0:
|
| 625 |
+
sign = -1
|
| 626 |
+
n = b - a
|
| 627 |
+
base = a
|
| 628 |
+
for k in range(n):
|
| 629 |
+
res -= sign*meijerg(self.an + (base + k + 1,), self.aother,
|
| 630 |
+
self.bm, self.bother + (base + k + 0,),
|
| 631 |
+
self.argument)
|
| 632 |
+
|
| 633 |
+
for a, b in pairs2:
|
| 634 |
+
sign = 1
|
| 635 |
+
n = b - a
|
| 636 |
+
base = a
|
| 637 |
+
if n < 0:
|
| 638 |
+
sign = -1
|
| 639 |
+
n = a - b
|
| 640 |
+
base = b
|
| 641 |
+
for k in range(n):
|
| 642 |
+
res -= sign*meijerg(self.an, self.aother + (base + k + 1,),
|
| 643 |
+
self.bm + (base + k + 0,), self.bother,
|
| 644 |
+
self.argument)
|
| 645 |
+
|
| 646 |
+
return res
|
| 647 |
+
|
| 648 |
+
def get_period(self):
|
| 649 |
+
"""
|
| 650 |
+
Return a number $P$ such that $G(x*exp(I*P)) == G(x)$.
|
| 651 |
+
|
| 652 |
+
Examples
|
| 653 |
+
========
|
| 654 |
+
|
| 655 |
+
>>> from sympy import meijerg, pi, S
|
| 656 |
+
>>> from sympy.abc import z
|
| 657 |
+
|
| 658 |
+
>>> meijerg([1], [], [], [], z).get_period()
|
| 659 |
+
2*pi
|
| 660 |
+
>>> meijerg([pi], [], [], [], z).get_period()
|
| 661 |
+
oo
|
| 662 |
+
>>> meijerg([1, 2], [], [], [], z).get_period()
|
| 663 |
+
oo
|
| 664 |
+
>>> meijerg([1,1], [2], [1, S(1)/2, S(1)/3], [1], z).get_period()
|
| 665 |
+
12*pi
|
| 666 |
+
|
| 667 |
+
"""
|
| 668 |
+
# This follows from slater's theorem.
|
| 669 |
+
def compute(l):
|
| 670 |
+
# first check that no two differ by an integer
|
| 671 |
+
for i, b in enumerate(l):
|
| 672 |
+
if not b.is_Rational:
|
| 673 |
+
return oo
|
| 674 |
+
for j in range(i + 1, len(l)):
|
| 675 |
+
if not Mod((b - l[j]).simplify(), 1):
|
| 676 |
+
return oo
|
| 677 |
+
return lcm(*(x.q for x in l))
|
| 678 |
+
beta = compute(self.bm)
|
| 679 |
+
alpha = compute(self.an)
|
| 680 |
+
p, q = len(self.ap), len(self.bq)
|
| 681 |
+
if p == q:
|
| 682 |
+
if oo in (alpha, beta):
|
| 683 |
+
return oo
|
| 684 |
+
return 2*pi*lcm(alpha, beta)
|
| 685 |
+
elif p < q:
|
| 686 |
+
return 2*pi*beta
|
| 687 |
+
else:
|
| 688 |
+
return 2*pi*alpha
|
| 689 |
+
|
| 690 |
+
def _eval_expand_func(self, **hints):
|
| 691 |
+
from sympy.simplify.hyperexpand import hyperexpand
|
| 692 |
+
return hyperexpand(self)
|
| 693 |
+
|
| 694 |
+
def _eval_evalf(self, prec):
|
| 695 |
+
# The default code is insufficient for polar arguments.
|
| 696 |
+
# mpmath provides an optional argument "r", which evaluates
|
| 697 |
+
# G(z**(1/r)). I am not sure what its intended use is, but we hijack it
|
| 698 |
+
# here in the following way: to evaluate at a number z of |argument|
|
| 699 |
+
# less than (say) n*pi, we put r=1/n, compute z' = root(z, n)
|
| 700 |
+
# (carefully so as not to loose the branch information), and evaluate
|
| 701 |
+
# G(z'**(1/r)) = G(z'**n) = G(z).
|
| 702 |
+
import mpmath
|
| 703 |
+
znum = self.argument._eval_evalf(prec)
|
| 704 |
+
if znum.has(exp_polar):
|
| 705 |
+
znum, branch = znum.as_coeff_mul(exp_polar)
|
| 706 |
+
if len(branch) != 1:
|
| 707 |
+
return
|
| 708 |
+
branch = branch[0].args[0]/I
|
| 709 |
+
else:
|
| 710 |
+
branch = S.Zero
|
| 711 |
+
n = ceiling(abs(branch/pi)) + 1
|
| 712 |
+
znum = znum**(S.One/n)*exp(I*branch / n)
|
| 713 |
+
|
| 714 |
+
# Convert all args to mpf or mpc
|
| 715 |
+
try:
|
| 716 |
+
[z, r, ap, bq] = [arg._to_mpmath(prec)
|
| 717 |
+
for arg in [znum, 1/n, self.args[0], self.args[1]]]
|
| 718 |
+
except ValueError:
|
| 719 |
+
return
|
| 720 |
+
|
| 721 |
+
with mpmath.workprec(prec):
|
| 722 |
+
v = mpmath.meijerg(ap, bq, z, r)
|
| 723 |
+
|
| 724 |
+
return Expr._from_mpmath(v, prec)
|
| 725 |
+
|
| 726 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 727 |
+
from sympy.simplify.hyperexpand import hyperexpand
|
| 728 |
+
return hyperexpand(self).as_leading_term(x, logx=logx, cdir=cdir)
|
| 729 |
+
|
| 730 |
+
def integrand(self, s):
|
| 731 |
+
""" Get the defining integrand D(s). """
|
| 732 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 733 |
+
return self.argument**s \
|
| 734 |
+
* Mul(*(gamma(b - s) for b in self.bm)) \
|
| 735 |
+
* Mul(*(gamma(1 - a + s) for a in self.an)) \
|
| 736 |
+
/ Mul(*(gamma(1 - b + s) for b in self.bother)) \
|
| 737 |
+
/ Mul(*(gamma(a - s) for a in self.aother))
|
| 738 |
+
|
| 739 |
+
@property
|
| 740 |
+
def argument(self):
|
| 741 |
+
""" Argument of the Meijer G-function. """
|
| 742 |
+
return self.args[2]
|
| 743 |
+
|
| 744 |
+
@property
|
| 745 |
+
def an(self):
|
| 746 |
+
""" First set of numerator parameters. """
|
| 747 |
+
return Tuple(*self.args[0][0])
|
| 748 |
+
|
| 749 |
+
@property
|
| 750 |
+
def ap(self):
|
| 751 |
+
""" Combined numerator parameters. """
|
| 752 |
+
return Tuple(*(self.args[0][0] + self.args[0][1]))
|
| 753 |
+
|
| 754 |
+
@property
|
| 755 |
+
def aother(self):
|
| 756 |
+
""" Second set of numerator parameters. """
|
| 757 |
+
return Tuple(*self.args[0][1])
|
| 758 |
+
|
| 759 |
+
@property
|
| 760 |
+
def bm(self):
|
| 761 |
+
""" First set of denominator parameters. """
|
| 762 |
+
return Tuple(*self.args[1][0])
|
| 763 |
+
|
| 764 |
+
@property
|
| 765 |
+
def bq(self):
|
| 766 |
+
""" Combined denominator parameters. """
|
| 767 |
+
return Tuple(*(self.args[1][0] + self.args[1][1]))
|
| 768 |
+
|
| 769 |
+
@property
|
| 770 |
+
def bother(self):
|
| 771 |
+
""" Second set of denominator parameters. """
|
| 772 |
+
return Tuple(*self.args[1][1])
|
| 773 |
+
|
| 774 |
+
@property
|
| 775 |
+
def _diffargs(self):
|
| 776 |
+
return self.ap + self.bq
|
| 777 |
+
|
| 778 |
+
@property
|
| 779 |
+
def nu(self):
|
| 780 |
+
""" A quantity related to the convergence region of the integral,
|
| 781 |
+
c.f. references. """
|
| 782 |
+
return sum(self.bq) - sum(self.ap)
|
| 783 |
+
|
| 784 |
+
@property
|
| 785 |
+
def delta(self):
|
| 786 |
+
""" A quantity related to the convergence region of the integral,
|
| 787 |
+
c.f. references. """
|
| 788 |
+
return len(self.bm) + len(self.an) - S(len(self.ap) + len(self.bq))/2
|
| 789 |
+
|
| 790 |
+
@property
|
| 791 |
+
def is_number(self):
|
| 792 |
+
""" Returns true if expression has numeric data only. """
|
| 793 |
+
return not self.free_symbols
|
| 794 |
+
|
| 795 |
+
|
| 796 |
+
class HyperRep(DefinedFunction):
|
| 797 |
+
"""
|
| 798 |
+
A base class for "hyper representation functions".
|
| 799 |
+
|
| 800 |
+
This is used exclusively in ``hyperexpand()``, but fits more logically here.
|
| 801 |
+
|
| 802 |
+
pFq is branched at 1 if p == q+1. For use with slater-expansion, we want
|
| 803 |
+
define an "analytic continuation" to all polar numbers, which is
|
| 804 |
+
continuous on circles and on the ray t*exp_polar(I*pi). Moreover, we want
|
| 805 |
+
a "nice" expression for the various cases.
|
| 806 |
+
|
| 807 |
+
This base class contains the core logic, concrete derived classes only
|
| 808 |
+
supply the actual functions.
|
| 809 |
+
|
| 810 |
+
"""
|
| 811 |
+
|
| 812 |
+
|
| 813 |
+
@classmethod
|
| 814 |
+
def eval(cls, *args):
|
| 815 |
+
newargs = tuple(map(unpolarify, args[:-1])) + args[-1:]
|
| 816 |
+
if args != newargs:
|
| 817 |
+
return cls(*newargs)
|
| 818 |
+
|
| 819 |
+
@classmethod
|
| 820 |
+
def _expr_small(cls, x):
|
| 821 |
+
""" An expression for F(x) which holds for |x| < 1. """
|
| 822 |
+
raise NotImplementedError
|
| 823 |
+
|
| 824 |
+
@classmethod
|
| 825 |
+
def _expr_small_minus(cls, x):
|
| 826 |
+
""" An expression for F(-x) which holds for |x| < 1. """
|
| 827 |
+
raise NotImplementedError
|
| 828 |
+
|
| 829 |
+
@classmethod
|
| 830 |
+
def _expr_big(cls, x, n):
|
| 831 |
+
""" An expression for F(exp_polar(2*I*pi*n)*x), |x| > 1. """
|
| 832 |
+
raise NotImplementedError
|
| 833 |
+
|
| 834 |
+
@classmethod
|
| 835 |
+
def _expr_big_minus(cls, x, n):
|
| 836 |
+
""" An expression for F(exp_polar(2*I*pi*n + pi*I)*x), |x| > 1. """
|
| 837 |
+
raise NotImplementedError
|
| 838 |
+
|
| 839 |
+
def _eval_rewrite_as_nonrep(self, *args, **kwargs):
|
| 840 |
+
x, n = self.args[-1].extract_branch_factor(allow_half=True)
|
| 841 |
+
minus = False
|
| 842 |
+
newargs = self.args[:-1] + (x,)
|
| 843 |
+
if not n.is_Integer:
|
| 844 |
+
minus = True
|
| 845 |
+
n -= S.Half
|
| 846 |
+
newerargs = newargs + (n,)
|
| 847 |
+
if minus:
|
| 848 |
+
small = self._expr_small_minus(*newargs)
|
| 849 |
+
big = self._expr_big_minus(*newerargs)
|
| 850 |
+
else:
|
| 851 |
+
small = self._expr_small(*newargs)
|
| 852 |
+
big = self._expr_big(*newerargs)
|
| 853 |
+
|
| 854 |
+
if big == small:
|
| 855 |
+
return small
|
| 856 |
+
return Piecewise((big, abs(x) > 1), (small, True))
|
| 857 |
+
|
| 858 |
+
def _eval_rewrite_as_nonrepsmall(self, *args, **kwargs):
|
| 859 |
+
x, n = self.args[-1].extract_branch_factor(allow_half=True)
|
| 860 |
+
args = self.args[:-1] + (x,)
|
| 861 |
+
if not n.is_Integer:
|
| 862 |
+
return self._expr_small_minus(*args)
|
| 863 |
+
return self._expr_small(*args)
|
| 864 |
+
|
| 865 |
+
|
| 866 |
+
class HyperRep_power1(HyperRep):
|
| 867 |
+
""" Return a representative for hyper([-a], [], z) == (1 - z)**a. """
|
| 868 |
+
|
| 869 |
+
@classmethod
|
| 870 |
+
def _expr_small(cls, a, x):
|
| 871 |
+
return (1 - x)**a
|
| 872 |
+
|
| 873 |
+
@classmethod
|
| 874 |
+
def _expr_small_minus(cls, a, x):
|
| 875 |
+
return (1 + x)**a
|
| 876 |
+
|
| 877 |
+
@classmethod
|
| 878 |
+
def _expr_big(cls, a, x, n):
|
| 879 |
+
if a.is_integer:
|
| 880 |
+
return cls._expr_small(a, x)
|
| 881 |
+
return (x - 1)**a*exp((2*n - 1)*pi*I*a)
|
| 882 |
+
|
| 883 |
+
@classmethod
|
| 884 |
+
def _expr_big_minus(cls, a, x, n):
|
| 885 |
+
if a.is_integer:
|
| 886 |
+
return cls._expr_small_minus(a, x)
|
| 887 |
+
return (1 + x)**a*exp(2*n*pi*I*a)
|
| 888 |
+
|
| 889 |
+
|
| 890 |
+
class HyperRep_power2(HyperRep):
|
| 891 |
+
""" Return a representative for hyper([a, a - 1/2], [2*a], z). """
|
| 892 |
+
|
| 893 |
+
@classmethod
|
| 894 |
+
def _expr_small(cls, a, x):
|
| 895 |
+
return 2**(2*a - 1)*(1 + sqrt(1 - x))**(1 - 2*a)
|
| 896 |
+
|
| 897 |
+
@classmethod
|
| 898 |
+
def _expr_small_minus(cls, a, x):
|
| 899 |
+
return 2**(2*a - 1)*(1 + sqrt(1 + x))**(1 - 2*a)
|
| 900 |
+
|
| 901 |
+
@classmethod
|
| 902 |
+
def _expr_big(cls, a, x, n):
|
| 903 |
+
sgn = -1
|
| 904 |
+
if n.is_odd:
|
| 905 |
+
sgn = 1
|
| 906 |
+
n -= 1
|
| 907 |
+
return 2**(2*a - 1)*(1 + sgn*I*sqrt(x - 1))**(1 - 2*a) \
|
| 908 |
+
*exp(-2*n*pi*I*a)
|
| 909 |
+
|
| 910 |
+
@classmethod
|
| 911 |
+
def _expr_big_minus(cls, a, x, n):
|
| 912 |
+
sgn = 1
|
| 913 |
+
if n.is_odd:
|
| 914 |
+
sgn = -1
|
| 915 |
+
return sgn*2**(2*a - 1)*(sqrt(1 + x) + sgn)**(1 - 2*a)*exp(-2*pi*I*a*n)
|
| 916 |
+
|
| 917 |
+
|
| 918 |
+
class HyperRep_log1(HyperRep):
|
| 919 |
+
""" Represent -z*hyper([1, 1], [2], z) == log(1 - z). """
|
| 920 |
+
@classmethod
|
| 921 |
+
def _expr_small(cls, x):
|
| 922 |
+
return log(1 - x)
|
| 923 |
+
|
| 924 |
+
@classmethod
|
| 925 |
+
def _expr_small_minus(cls, x):
|
| 926 |
+
return log(1 + x)
|
| 927 |
+
|
| 928 |
+
@classmethod
|
| 929 |
+
def _expr_big(cls, x, n):
|
| 930 |
+
return log(x - 1) + (2*n - 1)*pi*I
|
| 931 |
+
|
| 932 |
+
@classmethod
|
| 933 |
+
def _expr_big_minus(cls, x, n):
|
| 934 |
+
return log(1 + x) + 2*n*pi*I
|
| 935 |
+
|
| 936 |
+
|
| 937 |
+
class HyperRep_atanh(HyperRep):
|
| 938 |
+
""" Represent hyper([1/2, 1], [3/2], z) == atanh(sqrt(z))/sqrt(z). """
|
| 939 |
+
@classmethod
|
| 940 |
+
def _expr_small(cls, x):
|
| 941 |
+
return atanh(sqrt(x))/sqrt(x)
|
| 942 |
+
|
| 943 |
+
def _expr_small_minus(cls, x):
|
| 944 |
+
return atan(sqrt(x))/sqrt(x)
|
| 945 |
+
|
| 946 |
+
def _expr_big(cls, x, n):
|
| 947 |
+
if n.is_even:
|
| 948 |
+
return (acoth(sqrt(x)) + I*pi/2)/sqrt(x)
|
| 949 |
+
else:
|
| 950 |
+
return (acoth(sqrt(x)) - I*pi/2)/sqrt(x)
|
| 951 |
+
|
| 952 |
+
def _expr_big_minus(cls, x, n):
|
| 953 |
+
if n.is_even:
|
| 954 |
+
return atan(sqrt(x))/sqrt(x)
|
| 955 |
+
else:
|
| 956 |
+
return (atan(sqrt(x)) - pi)/sqrt(x)
|
| 957 |
+
|
| 958 |
+
|
| 959 |
+
class HyperRep_asin1(HyperRep):
|
| 960 |
+
""" Represent hyper([1/2, 1/2], [3/2], z) == asin(sqrt(z))/sqrt(z). """
|
| 961 |
+
@classmethod
|
| 962 |
+
def _expr_small(cls, z):
|
| 963 |
+
return asin(sqrt(z))/sqrt(z)
|
| 964 |
+
|
| 965 |
+
@classmethod
|
| 966 |
+
def _expr_small_minus(cls, z):
|
| 967 |
+
return asinh(sqrt(z))/sqrt(z)
|
| 968 |
+
|
| 969 |
+
@classmethod
|
| 970 |
+
def _expr_big(cls, z, n):
|
| 971 |
+
return S.NegativeOne**n*((S.Half - n)*pi/sqrt(z) + I*acosh(sqrt(z))/sqrt(z))
|
| 972 |
+
|
| 973 |
+
@classmethod
|
| 974 |
+
def _expr_big_minus(cls, z, n):
|
| 975 |
+
return S.NegativeOne**n*(asinh(sqrt(z))/sqrt(z) + n*pi*I/sqrt(z))
|
| 976 |
+
|
| 977 |
+
|
| 978 |
+
class HyperRep_asin2(HyperRep):
|
| 979 |
+
""" Represent hyper([1, 1], [3/2], z) == asin(sqrt(z))/sqrt(z)/sqrt(1-z). """
|
| 980 |
+
# TODO this can be nicer
|
| 981 |
+
@classmethod
|
| 982 |
+
def _expr_small(cls, z):
|
| 983 |
+
return HyperRep_asin1._expr_small(z) \
|
| 984 |
+
/HyperRep_power1._expr_small(S.Half, z)
|
| 985 |
+
|
| 986 |
+
@classmethod
|
| 987 |
+
def _expr_small_minus(cls, z):
|
| 988 |
+
return HyperRep_asin1._expr_small_minus(z) \
|
| 989 |
+
/HyperRep_power1._expr_small_minus(S.Half, z)
|
| 990 |
+
|
| 991 |
+
@classmethod
|
| 992 |
+
def _expr_big(cls, z, n):
|
| 993 |
+
return HyperRep_asin1._expr_big(z, n) \
|
| 994 |
+
/HyperRep_power1._expr_big(S.Half, z, n)
|
| 995 |
+
|
| 996 |
+
@classmethod
|
| 997 |
+
def _expr_big_minus(cls, z, n):
|
| 998 |
+
return HyperRep_asin1._expr_big_minus(z, n) \
|
| 999 |
+
/HyperRep_power1._expr_big_minus(S.Half, z, n)
|
| 1000 |
+
|
| 1001 |
+
|
| 1002 |
+
class HyperRep_sqrts1(HyperRep):
|
| 1003 |
+
""" Return a representative for hyper([-a, 1/2 - a], [1/2], z). """
|
| 1004 |
+
|
| 1005 |
+
@classmethod
|
| 1006 |
+
def _expr_small(cls, a, z):
|
| 1007 |
+
return ((1 - sqrt(z))**(2*a) + (1 + sqrt(z))**(2*a))/2
|
| 1008 |
+
|
| 1009 |
+
@classmethod
|
| 1010 |
+
def _expr_small_minus(cls, a, z):
|
| 1011 |
+
return (1 + z)**a*cos(2*a*atan(sqrt(z)))
|
| 1012 |
+
|
| 1013 |
+
@classmethod
|
| 1014 |
+
def _expr_big(cls, a, z, n):
|
| 1015 |
+
if n.is_even:
|
| 1016 |
+
return ((sqrt(z) + 1)**(2*a)*exp(2*pi*I*n*a) +
|
| 1017 |
+
(sqrt(z) - 1)**(2*a)*exp(2*pi*I*(n - 1)*a))/2
|
| 1018 |
+
else:
|
| 1019 |
+
n -= 1
|
| 1020 |
+
return ((sqrt(z) - 1)**(2*a)*exp(2*pi*I*a*(n + 1)) +
|
| 1021 |
+
(sqrt(z) + 1)**(2*a)*exp(2*pi*I*a*n))/2
|
| 1022 |
+
|
| 1023 |
+
@classmethod
|
| 1024 |
+
def _expr_big_minus(cls, a, z, n):
|
| 1025 |
+
if n.is_even:
|
| 1026 |
+
return (1 + z)**a*exp(2*pi*I*n*a)*cos(2*a*atan(sqrt(z)))
|
| 1027 |
+
else:
|
| 1028 |
+
return (1 + z)**a*exp(2*pi*I*n*a)*cos(2*a*atan(sqrt(z)) - 2*pi*a)
|
| 1029 |
+
|
| 1030 |
+
|
| 1031 |
+
class HyperRep_sqrts2(HyperRep):
|
| 1032 |
+
""" Return a representative for
|
| 1033 |
+
sqrt(z)/2*[(1-sqrt(z))**2a - (1 + sqrt(z))**2a]
|
| 1034 |
+
== -2*z/(2*a+1) d/dz hyper([-a - 1/2, -a], [1/2], z)"""
|
| 1035 |
+
|
| 1036 |
+
@classmethod
|
| 1037 |
+
def _expr_small(cls, a, z):
|
| 1038 |
+
return sqrt(z)*((1 - sqrt(z))**(2*a) - (1 + sqrt(z))**(2*a))/2
|
| 1039 |
+
|
| 1040 |
+
@classmethod
|
| 1041 |
+
def _expr_small_minus(cls, a, z):
|
| 1042 |
+
return sqrt(z)*(1 + z)**a*sin(2*a*atan(sqrt(z)))
|
| 1043 |
+
|
| 1044 |
+
@classmethod
|
| 1045 |
+
def _expr_big(cls, a, z, n):
|
| 1046 |
+
if n.is_even:
|
| 1047 |
+
return sqrt(z)/2*((sqrt(z) - 1)**(2*a)*exp(2*pi*I*a*(n - 1)) -
|
| 1048 |
+
(sqrt(z) + 1)**(2*a)*exp(2*pi*I*a*n))
|
| 1049 |
+
else:
|
| 1050 |
+
n -= 1
|
| 1051 |
+
return sqrt(z)/2*((sqrt(z) - 1)**(2*a)*exp(2*pi*I*a*(n + 1)) -
|
| 1052 |
+
(sqrt(z) + 1)**(2*a)*exp(2*pi*I*a*n))
|
| 1053 |
+
|
| 1054 |
+
def _expr_big_minus(cls, a, z, n):
|
| 1055 |
+
if n.is_even:
|
| 1056 |
+
return (1 + z)**a*exp(2*pi*I*n*a)*sqrt(z)*sin(2*a*atan(sqrt(z)))
|
| 1057 |
+
else:
|
| 1058 |
+
return (1 + z)**a*exp(2*pi*I*n*a)*sqrt(z) \
|
| 1059 |
+
*sin(2*a*atan(sqrt(z)) - 2*pi*a)
|
| 1060 |
+
|
| 1061 |
+
|
| 1062 |
+
class HyperRep_log2(HyperRep):
|
| 1063 |
+
""" Represent log(1/2 + sqrt(1 - z)/2) == -z/4*hyper([3/2, 1, 1], [2, 2], z) """
|
| 1064 |
+
|
| 1065 |
+
@classmethod
|
| 1066 |
+
def _expr_small(cls, z):
|
| 1067 |
+
return log(S.Half + sqrt(1 - z)/2)
|
| 1068 |
+
|
| 1069 |
+
@classmethod
|
| 1070 |
+
def _expr_small_minus(cls, z):
|
| 1071 |
+
return log(S.Half + sqrt(1 + z)/2)
|
| 1072 |
+
|
| 1073 |
+
@classmethod
|
| 1074 |
+
def _expr_big(cls, z, n):
|
| 1075 |
+
if n.is_even:
|
| 1076 |
+
return (n - S.Half)*pi*I + log(sqrt(z)/2) + I*asin(1/sqrt(z))
|
| 1077 |
+
else:
|
| 1078 |
+
return (n - S.Half)*pi*I + log(sqrt(z)/2) - I*asin(1/sqrt(z))
|
| 1079 |
+
|
| 1080 |
+
def _expr_big_minus(cls, z, n):
|
| 1081 |
+
if n.is_even:
|
| 1082 |
+
return pi*I*n + log(S.Half + sqrt(1 + z)/2)
|
| 1083 |
+
else:
|
| 1084 |
+
return pi*I*n + log(sqrt(1 + z)/2 - S.Half)
|
| 1085 |
+
|
| 1086 |
+
|
| 1087 |
+
class HyperRep_cosasin(HyperRep):
|
| 1088 |
+
""" Represent hyper([a, -a], [1/2], z) == cos(2*a*asin(sqrt(z))). """
|
| 1089 |
+
# Note there are many alternative expressions, e.g. as powers of a sum of
|
| 1090 |
+
# square roots.
|
| 1091 |
+
|
| 1092 |
+
@classmethod
|
| 1093 |
+
def _expr_small(cls, a, z):
|
| 1094 |
+
return cos(2*a*asin(sqrt(z)))
|
| 1095 |
+
|
| 1096 |
+
@classmethod
|
| 1097 |
+
def _expr_small_minus(cls, a, z):
|
| 1098 |
+
return cosh(2*a*asinh(sqrt(z)))
|
| 1099 |
+
|
| 1100 |
+
@classmethod
|
| 1101 |
+
def _expr_big(cls, a, z, n):
|
| 1102 |
+
return cosh(2*a*acosh(sqrt(z)) + a*pi*I*(2*n - 1))
|
| 1103 |
+
|
| 1104 |
+
@classmethod
|
| 1105 |
+
def _expr_big_minus(cls, a, z, n):
|
| 1106 |
+
return cosh(2*a*asinh(sqrt(z)) + 2*a*pi*I*n)
|
| 1107 |
+
|
| 1108 |
+
|
| 1109 |
+
class HyperRep_sinasin(HyperRep):
|
| 1110 |
+
""" Represent 2*a*z*hyper([1 - a, 1 + a], [3/2], z)
|
| 1111 |
+
== sqrt(z)/sqrt(1-z)*sin(2*a*asin(sqrt(z))) """
|
| 1112 |
+
|
| 1113 |
+
@classmethod
|
| 1114 |
+
def _expr_small(cls, a, z):
|
| 1115 |
+
return sqrt(z)/sqrt(1 - z)*sin(2*a*asin(sqrt(z)))
|
| 1116 |
+
|
| 1117 |
+
@classmethod
|
| 1118 |
+
def _expr_small_minus(cls, a, z):
|
| 1119 |
+
return -sqrt(z)/sqrt(1 + z)*sinh(2*a*asinh(sqrt(z)))
|
| 1120 |
+
|
| 1121 |
+
@classmethod
|
| 1122 |
+
def _expr_big(cls, a, z, n):
|
| 1123 |
+
return -1/sqrt(1 - 1/z)*sinh(2*a*acosh(sqrt(z)) + a*pi*I*(2*n - 1))
|
| 1124 |
+
|
| 1125 |
+
@classmethod
|
| 1126 |
+
def _expr_big_minus(cls, a, z, n):
|
| 1127 |
+
return -1/sqrt(1 + 1/z)*sinh(2*a*asinh(sqrt(z)) + 2*a*pi*I*n)
|
| 1128 |
+
|
| 1129 |
+
class appellf1(DefinedFunction):
|
| 1130 |
+
r"""
|
| 1131 |
+
This is the Appell hypergeometric function of two variables as:
|
| 1132 |
+
|
| 1133 |
+
.. math ::
|
| 1134 |
+
F_1(a,b_1,b_2,c,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty}
|
| 1135 |
+
\frac{(a)_{m+n} (b_1)_m (b_2)_n}{(c)_{m+n}}
|
| 1136 |
+
\frac{x^m y^n}{m! n!}.
|
| 1137 |
+
|
| 1138 |
+
Examples
|
| 1139 |
+
========
|
| 1140 |
+
|
| 1141 |
+
>>> from sympy import appellf1, symbols
|
| 1142 |
+
>>> x, y, a, b1, b2, c = symbols('x y a b1 b2 c')
|
| 1143 |
+
>>> appellf1(2., 1., 6., 4., 5., 6.)
|
| 1144 |
+
0.0063339426292673
|
| 1145 |
+
>>> appellf1(12., 12., 6., 4., 0.5, 0.12)
|
| 1146 |
+
172870711.659936
|
| 1147 |
+
>>> appellf1(40, 2, 6, 4, 15, 60)
|
| 1148 |
+
appellf1(40, 2, 6, 4, 15, 60)
|
| 1149 |
+
>>> appellf1(20., 12., 10., 3., 0.5, 0.12)
|
| 1150 |
+
15605338197184.4
|
| 1151 |
+
>>> appellf1(40, 2, 6, 4, x, y)
|
| 1152 |
+
appellf1(40, 2, 6, 4, x, y)
|
| 1153 |
+
>>> appellf1(a, b1, b2, c, x, y)
|
| 1154 |
+
appellf1(a, b1, b2, c, x, y)
|
| 1155 |
+
|
| 1156 |
+
References
|
| 1157 |
+
==========
|
| 1158 |
+
|
| 1159 |
+
.. [1] https://en.wikipedia.org/wiki/Appell_series
|
| 1160 |
+
.. [2] https://functions.wolfram.com/HypergeometricFunctions/AppellF1/
|
| 1161 |
+
|
| 1162 |
+
"""
|
| 1163 |
+
|
| 1164 |
+
@classmethod
|
| 1165 |
+
def eval(cls, a, b1, b2, c, x, y):
|
| 1166 |
+
if default_sort_key(b1) > default_sort_key(b2):
|
| 1167 |
+
b1, b2 = b2, b1
|
| 1168 |
+
x, y = y, x
|
| 1169 |
+
return cls(a, b1, b2, c, x, y)
|
| 1170 |
+
elif b1 == b2 and default_sort_key(x) > default_sort_key(y):
|
| 1171 |
+
x, y = y, x
|
| 1172 |
+
return cls(a, b1, b2, c, x, y)
|
| 1173 |
+
if x == 0 and y == 0:
|
| 1174 |
+
return S.One
|
| 1175 |
+
|
| 1176 |
+
def fdiff(self, argindex=5):
|
| 1177 |
+
a, b1, b2, c, x, y = self.args
|
| 1178 |
+
if argindex == 5:
|
| 1179 |
+
return (a*b1/c)*appellf1(a + 1, b1 + 1, b2, c + 1, x, y)
|
| 1180 |
+
elif argindex == 6:
|
| 1181 |
+
return (a*b2/c)*appellf1(a + 1, b1, b2 + 1, c + 1, x, y)
|
| 1182 |
+
elif argindex in (1, 2, 3, 4):
|
| 1183 |
+
return Derivative(self, self.args[argindex-1])
|
| 1184 |
+
else:
|
| 1185 |
+
raise ArgumentIndexError(self, argindex)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/mathieu_functions.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" This module contains the Mathieu functions.
|
| 2 |
+
"""
|
| 3 |
+
|
| 4 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 5 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 6 |
+
from sympy.functions.elementary.trigonometric import sin, cos
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class MathieuBase(DefinedFunction):
|
| 10 |
+
"""
|
| 11 |
+
Abstract base class for Mathieu functions.
|
| 12 |
+
|
| 13 |
+
This class is meant to reduce code duplication.
|
| 14 |
+
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
unbranched = True
|
| 18 |
+
|
| 19 |
+
def _eval_conjugate(self):
|
| 20 |
+
a, q, z = self.args
|
| 21 |
+
return self.func(a.conjugate(), q.conjugate(), z.conjugate())
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class mathieus(MathieuBase):
|
| 25 |
+
r"""
|
| 26 |
+
The Mathieu Sine function $S(a,q,z)$.
|
| 27 |
+
|
| 28 |
+
Explanation
|
| 29 |
+
===========
|
| 30 |
+
|
| 31 |
+
This function is one solution of the Mathieu differential equation:
|
| 32 |
+
|
| 33 |
+
.. math ::
|
| 34 |
+
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
| 35 |
+
|
| 36 |
+
The other solution is the Mathieu Cosine function.
|
| 37 |
+
|
| 38 |
+
Examples
|
| 39 |
+
========
|
| 40 |
+
|
| 41 |
+
>>> from sympy import diff, mathieus
|
| 42 |
+
>>> from sympy.abc import a, q, z
|
| 43 |
+
|
| 44 |
+
>>> mathieus(a, q, z)
|
| 45 |
+
mathieus(a, q, z)
|
| 46 |
+
|
| 47 |
+
>>> mathieus(a, 0, z)
|
| 48 |
+
sin(sqrt(a)*z)
|
| 49 |
+
|
| 50 |
+
>>> diff(mathieus(a, q, z), z)
|
| 51 |
+
mathieusprime(a, q, z)
|
| 52 |
+
|
| 53 |
+
See Also
|
| 54 |
+
========
|
| 55 |
+
|
| 56 |
+
mathieuc: Mathieu cosine function.
|
| 57 |
+
mathieusprime: Derivative of Mathieu sine function.
|
| 58 |
+
mathieucprime: Derivative of Mathieu cosine function.
|
| 59 |
+
|
| 60 |
+
References
|
| 61 |
+
==========
|
| 62 |
+
|
| 63 |
+
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
| 64 |
+
.. [2] https://dlmf.nist.gov/28
|
| 65 |
+
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
| 66 |
+
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuS/
|
| 67 |
+
|
| 68 |
+
"""
|
| 69 |
+
|
| 70 |
+
def fdiff(self, argindex=1):
|
| 71 |
+
if argindex == 3:
|
| 72 |
+
a, q, z = self.args
|
| 73 |
+
return mathieusprime(a, q, z)
|
| 74 |
+
else:
|
| 75 |
+
raise ArgumentIndexError(self, argindex)
|
| 76 |
+
|
| 77 |
+
@classmethod
|
| 78 |
+
def eval(cls, a, q, z):
|
| 79 |
+
if q.is_Number and q.is_zero:
|
| 80 |
+
return sin(sqrt(a)*z)
|
| 81 |
+
# Try to pull out factors of -1
|
| 82 |
+
if z.could_extract_minus_sign():
|
| 83 |
+
return -cls(a, q, -z)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class mathieuc(MathieuBase):
|
| 87 |
+
r"""
|
| 88 |
+
The Mathieu Cosine function $C(a,q,z)$.
|
| 89 |
+
|
| 90 |
+
Explanation
|
| 91 |
+
===========
|
| 92 |
+
|
| 93 |
+
This function is one solution of the Mathieu differential equation:
|
| 94 |
+
|
| 95 |
+
.. math ::
|
| 96 |
+
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
| 97 |
+
|
| 98 |
+
The other solution is the Mathieu Sine function.
|
| 99 |
+
|
| 100 |
+
Examples
|
| 101 |
+
========
|
| 102 |
+
|
| 103 |
+
>>> from sympy import diff, mathieuc
|
| 104 |
+
>>> from sympy.abc import a, q, z
|
| 105 |
+
|
| 106 |
+
>>> mathieuc(a, q, z)
|
| 107 |
+
mathieuc(a, q, z)
|
| 108 |
+
|
| 109 |
+
>>> mathieuc(a, 0, z)
|
| 110 |
+
cos(sqrt(a)*z)
|
| 111 |
+
|
| 112 |
+
>>> diff(mathieuc(a, q, z), z)
|
| 113 |
+
mathieucprime(a, q, z)
|
| 114 |
+
|
| 115 |
+
See Also
|
| 116 |
+
========
|
| 117 |
+
|
| 118 |
+
mathieus: Mathieu sine function
|
| 119 |
+
mathieusprime: Derivative of Mathieu sine function
|
| 120 |
+
mathieucprime: Derivative of Mathieu cosine function
|
| 121 |
+
|
| 122 |
+
References
|
| 123 |
+
==========
|
| 124 |
+
|
| 125 |
+
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
| 126 |
+
.. [2] https://dlmf.nist.gov/28
|
| 127 |
+
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
| 128 |
+
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuC/
|
| 129 |
+
|
| 130 |
+
"""
|
| 131 |
+
|
| 132 |
+
def fdiff(self, argindex=1):
|
| 133 |
+
if argindex == 3:
|
| 134 |
+
a, q, z = self.args
|
| 135 |
+
return mathieucprime(a, q, z)
|
| 136 |
+
else:
|
| 137 |
+
raise ArgumentIndexError(self, argindex)
|
| 138 |
+
|
| 139 |
+
@classmethod
|
| 140 |
+
def eval(cls, a, q, z):
|
| 141 |
+
if q.is_Number and q.is_zero:
|
| 142 |
+
return cos(sqrt(a)*z)
|
| 143 |
+
# Try to pull out factors of -1
|
| 144 |
+
if z.could_extract_minus_sign():
|
| 145 |
+
return cls(a, q, -z)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
class mathieusprime(MathieuBase):
|
| 149 |
+
r"""
|
| 150 |
+
The derivative $S^{\prime}(a,q,z)$ of the Mathieu Sine function.
|
| 151 |
+
|
| 152 |
+
Explanation
|
| 153 |
+
===========
|
| 154 |
+
|
| 155 |
+
This function is one solution of the Mathieu differential equation:
|
| 156 |
+
|
| 157 |
+
.. math ::
|
| 158 |
+
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
| 159 |
+
|
| 160 |
+
The other solution is the Mathieu Cosine function.
|
| 161 |
+
|
| 162 |
+
Examples
|
| 163 |
+
========
|
| 164 |
+
|
| 165 |
+
>>> from sympy import diff, mathieusprime
|
| 166 |
+
>>> from sympy.abc import a, q, z
|
| 167 |
+
|
| 168 |
+
>>> mathieusprime(a, q, z)
|
| 169 |
+
mathieusprime(a, q, z)
|
| 170 |
+
|
| 171 |
+
>>> mathieusprime(a, 0, z)
|
| 172 |
+
sqrt(a)*cos(sqrt(a)*z)
|
| 173 |
+
|
| 174 |
+
>>> diff(mathieusprime(a, q, z), z)
|
| 175 |
+
(-a + 2*q*cos(2*z))*mathieus(a, q, z)
|
| 176 |
+
|
| 177 |
+
See Also
|
| 178 |
+
========
|
| 179 |
+
|
| 180 |
+
mathieus: Mathieu sine function
|
| 181 |
+
mathieuc: Mathieu cosine function
|
| 182 |
+
mathieucprime: Derivative of Mathieu cosine function
|
| 183 |
+
|
| 184 |
+
References
|
| 185 |
+
==========
|
| 186 |
+
|
| 187 |
+
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
| 188 |
+
.. [2] https://dlmf.nist.gov/28
|
| 189 |
+
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
| 190 |
+
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuSPrime/
|
| 191 |
+
|
| 192 |
+
"""
|
| 193 |
+
|
| 194 |
+
def fdiff(self, argindex=1):
|
| 195 |
+
if argindex == 3:
|
| 196 |
+
a, q, z = self.args
|
| 197 |
+
return (2*q*cos(2*z) - a)*mathieus(a, q, z)
|
| 198 |
+
else:
|
| 199 |
+
raise ArgumentIndexError(self, argindex)
|
| 200 |
+
|
| 201 |
+
@classmethod
|
| 202 |
+
def eval(cls, a, q, z):
|
| 203 |
+
if q.is_Number and q.is_zero:
|
| 204 |
+
return sqrt(a)*cos(sqrt(a)*z)
|
| 205 |
+
# Try to pull out factors of -1
|
| 206 |
+
if z.could_extract_minus_sign():
|
| 207 |
+
return cls(a, q, -z)
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
class mathieucprime(MathieuBase):
|
| 211 |
+
r"""
|
| 212 |
+
The derivative $C^{\prime}(a,q,z)$ of the Mathieu Cosine function.
|
| 213 |
+
|
| 214 |
+
Explanation
|
| 215 |
+
===========
|
| 216 |
+
|
| 217 |
+
This function is one solution of the Mathieu differential equation:
|
| 218 |
+
|
| 219 |
+
.. math ::
|
| 220 |
+
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
| 221 |
+
|
| 222 |
+
The other solution is the Mathieu Sine function.
|
| 223 |
+
|
| 224 |
+
Examples
|
| 225 |
+
========
|
| 226 |
+
|
| 227 |
+
>>> from sympy import diff, mathieucprime
|
| 228 |
+
>>> from sympy.abc import a, q, z
|
| 229 |
+
|
| 230 |
+
>>> mathieucprime(a, q, z)
|
| 231 |
+
mathieucprime(a, q, z)
|
| 232 |
+
|
| 233 |
+
>>> mathieucprime(a, 0, z)
|
| 234 |
+
-sqrt(a)*sin(sqrt(a)*z)
|
| 235 |
+
|
| 236 |
+
>>> diff(mathieucprime(a, q, z), z)
|
| 237 |
+
(-a + 2*q*cos(2*z))*mathieuc(a, q, z)
|
| 238 |
+
|
| 239 |
+
See Also
|
| 240 |
+
========
|
| 241 |
+
|
| 242 |
+
mathieus: Mathieu sine function
|
| 243 |
+
mathieuc: Mathieu cosine function
|
| 244 |
+
mathieusprime: Derivative of Mathieu sine function
|
| 245 |
+
|
| 246 |
+
References
|
| 247 |
+
==========
|
| 248 |
+
|
| 249 |
+
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
| 250 |
+
.. [2] https://dlmf.nist.gov/28
|
| 251 |
+
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
| 252 |
+
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuCPrime/
|
| 253 |
+
|
| 254 |
+
"""
|
| 255 |
+
|
| 256 |
+
def fdiff(self, argindex=1):
|
| 257 |
+
if argindex == 3:
|
| 258 |
+
a, q, z = self.args
|
| 259 |
+
return (2*q*cos(2*z) - a)*mathieuc(a, q, z)
|
| 260 |
+
else:
|
| 261 |
+
raise ArgumentIndexError(self, argindex)
|
| 262 |
+
|
| 263 |
+
@classmethod
|
| 264 |
+
def eval(cls, a, q, z):
|
| 265 |
+
if q.is_Number and q.is_zero:
|
| 266 |
+
return -sqrt(a)*sin(sqrt(a)*z)
|
| 267 |
+
# Try to pull out factors of -1
|
| 268 |
+
if z.could_extract_minus_sign():
|
| 269 |
+
return -cls(a, q, -z)
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/polynomials.py
ADDED
|
@@ -0,0 +1,1447 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module mainly implements special orthogonal polynomials.
|
| 3 |
+
|
| 4 |
+
See also functions.combinatorial.numbers which contains some
|
| 5 |
+
combinatorial polynomials.
|
| 6 |
+
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from sympy.core import Rational
|
| 10 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 11 |
+
from sympy.core.singleton import S
|
| 12 |
+
from sympy.core.symbol import Dummy
|
| 13 |
+
from sympy.functions.combinatorial.factorials import binomial, factorial, RisingFactorial
|
| 14 |
+
from sympy.functions.elementary.complexes import re
|
| 15 |
+
from sympy.functions.elementary.exponential import exp
|
| 16 |
+
from sympy.functions.elementary.integers import floor
|
| 17 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 18 |
+
from sympy.functions.elementary.trigonometric import cos, sec
|
| 19 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 20 |
+
from sympy.functions.special.hyper import hyper
|
| 21 |
+
from sympy.polys.orthopolys import (chebyshevt_poly, chebyshevu_poly,
|
| 22 |
+
gegenbauer_poly, hermite_poly, hermite_prob_poly,
|
| 23 |
+
jacobi_poly, laguerre_poly, legendre_poly)
|
| 24 |
+
|
| 25 |
+
_x = Dummy('x')
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class OrthogonalPolynomial(DefinedFunction):
|
| 29 |
+
"""Base class for orthogonal polynomials.
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
@classmethod
|
| 33 |
+
def _eval_at_order(cls, n, x):
|
| 34 |
+
if n.is_integer and n >= 0:
|
| 35 |
+
return cls._ortho_poly(int(n), _x).subs(_x, x)
|
| 36 |
+
|
| 37 |
+
def _eval_conjugate(self):
|
| 38 |
+
return self.func(self.args[0], self.args[1].conjugate())
|
| 39 |
+
|
| 40 |
+
#----------------------------------------------------------------------------
|
| 41 |
+
# Jacobi polynomials
|
| 42 |
+
#
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class jacobi(OrthogonalPolynomial):
|
| 46 |
+
r"""
|
| 47 |
+
Jacobi polynomial $P_n^{\left(\alpha, \beta\right)}(x)$.
|
| 48 |
+
|
| 49 |
+
Explanation
|
| 50 |
+
===========
|
| 51 |
+
|
| 52 |
+
``jacobi(n, alpha, beta, x)`` gives the $n$th Jacobi polynomial
|
| 53 |
+
in $x$, $P_n^{\left(\alpha, \beta\right)}(x)$.
|
| 54 |
+
|
| 55 |
+
The Jacobi polynomials are orthogonal on $[-1, 1]$ with respect
|
| 56 |
+
to the weight $\left(1-x\right)^\alpha \left(1+x\right)^\beta$.
|
| 57 |
+
|
| 58 |
+
Examples
|
| 59 |
+
========
|
| 60 |
+
|
| 61 |
+
>>> from sympy import jacobi, S, conjugate, diff
|
| 62 |
+
>>> from sympy.abc import a, b, n, x
|
| 63 |
+
|
| 64 |
+
>>> jacobi(0, a, b, x)
|
| 65 |
+
1
|
| 66 |
+
>>> jacobi(1, a, b, x)
|
| 67 |
+
a/2 - b/2 + x*(a/2 + b/2 + 1)
|
| 68 |
+
>>> jacobi(2, a, b, x)
|
| 69 |
+
a**2/8 - a*b/4 - a/8 + b**2/8 - b/8 + x**2*(a**2/8 + a*b/4 + 7*a/8 + b**2/8 + 7*b/8 + 3/2) + x*(a**2/4 + 3*a/4 - b**2/4 - 3*b/4) - 1/2
|
| 70 |
+
|
| 71 |
+
>>> jacobi(n, a, b, x)
|
| 72 |
+
jacobi(n, a, b, x)
|
| 73 |
+
|
| 74 |
+
>>> jacobi(n, a, a, x)
|
| 75 |
+
RisingFactorial(a + 1, n)*gegenbauer(n,
|
| 76 |
+
a + 1/2, x)/RisingFactorial(2*a + 1, n)
|
| 77 |
+
|
| 78 |
+
>>> jacobi(n, 0, 0, x)
|
| 79 |
+
legendre(n, x)
|
| 80 |
+
|
| 81 |
+
>>> jacobi(n, S(1)/2, S(1)/2, x)
|
| 82 |
+
RisingFactorial(3/2, n)*chebyshevu(n, x)/factorial(n + 1)
|
| 83 |
+
|
| 84 |
+
>>> jacobi(n, -S(1)/2, -S(1)/2, x)
|
| 85 |
+
RisingFactorial(1/2, n)*chebyshevt(n, x)/factorial(n)
|
| 86 |
+
|
| 87 |
+
>>> jacobi(n, a, b, -x)
|
| 88 |
+
(-1)**n*jacobi(n, b, a, x)
|
| 89 |
+
|
| 90 |
+
>>> jacobi(n, a, b, 0)
|
| 91 |
+
gamma(a + n + 1)*hyper((-n, -b - n), (a + 1,), -1)/(2**n*factorial(n)*gamma(a + 1))
|
| 92 |
+
>>> jacobi(n, a, b, 1)
|
| 93 |
+
RisingFactorial(a + 1, n)/factorial(n)
|
| 94 |
+
|
| 95 |
+
>>> conjugate(jacobi(n, a, b, x))
|
| 96 |
+
jacobi(n, conjugate(a), conjugate(b), conjugate(x))
|
| 97 |
+
|
| 98 |
+
>>> diff(jacobi(n,a,b,x), x)
|
| 99 |
+
(a/2 + b/2 + n/2 + 1/2)*jacobi(n - 1, a + 1, b + 1, x)
|
| 100 |
+
|
| 101 |
+
See Also
|
| 102 |
+
========
|
| 103 |
+
|
| 104 |
+
gegenbauer,
|
| 105 |
+
chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 106 |
+
legendre, assoc_legendre,
|
| 107 |
+
hermite, hermite_prob,
|
| 108 |
+
laguerre, assoc_laguerre,
|
| 109 |
+
sympy.polys.orthopolys.jacobi_poly,
|
| 110 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 111 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 112 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 113 |
+
sympy.polys.orthopolys.hermite_poly
|
| 114 |
+
sympy.polys.orthopolys.legendre_poly
|
| 115 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 116 |
+
|
| 117 |
+
References
|
| 118 |
+
==========
|
| 119 |
+
|
| 120 |
+
.. [1] https://en.wikipedia.org/wiki/Jacobi_polynomials
|
| 121 |
+
.. [2] https://mathworld.wolfram.com/JacobiPolynomial.html
|
| 122 |
+
.. [3] https://functions.wolfram.com/Polynomials/JacobiP/
|
| 123 |
+
|
| 124 |
+
"""
|
| 125 |
+
|
| 126 |
+
@classmethod
|
| 127 |
+
def eval(cls, n, a, b, x):
|
| 128 |
+
# Simplify to other polynomials
|
| 129 |
+
# P^{a, a}_n(x)
|
| 130 |
+
if a == b:
|
| 131 |
+
if a == Rational(-1, 2):
|
| 132 |
+
return RisingFactorial(S.Half, n) / factorial(n) * chebyshevt(n, x)
|
| 133 |
+
elif a.is_zero:
|
| 134 |
+
return legendre(n, x)
|
| 135 |
+
elif a == S.Half:
|
| 136 |
+
return RisingFactorial(3*S.Half, n) / factorial(n + 1) * chebyshevu(n, x)
|
| 137 |
+
else:
|
| 138 |
+
return RisingFactorial(a + 1, n) / RisingFactorial(2*a + 1, n) * gegenbauer(n, a + S.Half, x)
|
| 139 |
+
elif b == -a:
|
| 140 |
+
# P^{a, -a}_n(x)
|
| 141 |
+
return gamma(n + a + 1) / gamma(n + 1) * (1 + x)**(a/2) / (1 - x)**(a/2) * assoc_legendre(n, -a, x)
|
| 142 |
+
|
| 143 |
+
if not n.is_Number:
|
| 144 |
+
# Symbolic result P^{a,b}_n(x)
|
| 145 |
+
# P^{a,b}_n(-x) ---> (-1)**n * P^{b,a}_n(-x)
|
| 146 |
+
if x.could_extract_minus_sign():
|
| 147 |
+
return S.NegativeOne**n * jacobi(n, b, a, -x)
|
| 148 |
+
# We can evaluate for some special values of x
|
| 149 |
+
if x.is_zero:
|
| 150 |
+
return (2**(-n) * gamma(a + n + 1) / (gamma(a + 1) * factorial(n)) *
|
| 151 |
+
hyper([-b - n, -n], [a + 1], -1))
|
| 152 |
+
if x == S.One:
|
| 153 |
+
return RisingFactorial(a + 1, n) / factorial(n)
|
| 154 |
+
elif x is S.Infinity:
|
| 155 |
+
if n.is_positive:
|
| 156 |
+
# Make sure a+b+2*n \notin Z
|
| 157 |
+
if (a + b + 2*n).is_integer:
|
| 158 |
+
raise ValueError("Error. a + b + 2*n should not be an integer.")
|
| 159 |
+
return RisingFactorial(a + b + n + 1, n) * S.Infinity
|
| 160 |
+
else:
|
| 161 |
+
# n is a given fixed integer, evaluate into polynomial
|
| 162 |
+
return jacobi_poly(n, a, b, x)
|
| 163 |
+
|
| 164 |
+
def fdiff(self, argindex=4):
|
| 165 |
+
from sympy.concrete.summations import Sum
|
| 166 |
+
if argindex == 1:
|
| 167 |
+
# Diff wrt n
|
| 168 |
+
raise ArgumentIndexError(self, argindex)
|
| 169 |
+
elif argindex == 2:
|
| 170 |
+
# Diff wrt a
|
| 171 |
+
n, a, b, x = self.args
|
| 172 |
+
k = Dummy("k")
|
| 173 |
+
f1 = 1 / (a + b + n + k + 1)
|
| 174 |
+
f2 = ((a + b + 2*k + 1) * RisingFactorial(b + k + 1, n - k) /
|
| 175 |
+
((n - k) * RisingFactorial(a + b + k + 1, n - k)))
|
| 176 |
+
return Sum(f1 * (jacobi(n, a, b, x) + f2*jacobi(k, a, b, x)), (k, 0, n - 1))
|
| 177 |
+
elif argindex == 3:
|
| 178 |
+
# Diff wrt b
|
| 179 |
+
n, a, b, x = self.args
|
| 180 |
+
k = Dummy("k")
|
| 181 |
+
f1 = 1 / (a + b + n + k + 1)
|
| 182 |
+
f2 = (-1)**(n - k) * ((a + b + 2*k + 1) * RisingFactorial(a + k + 1, n - k) /
|
| 183 |
+
((n - k) * RisingFactorial(a + b + k + 1, n - k)))
|
| 184 |
+
return Sum(f1 * (jacobi(n, a, b, x) + f2*jacobi(k, a, b, x)), (k, 0, n - 1))
|
| 185 |
+
elif argindex == 4:
|
| 186 |
+
# Diff wrt x
|
| 187 |
+
n, a, b, x = self.args
|
| 188 |
+
return S.Half * (a + b + n + 1) * jacobi(n - 1, a + 1, b + 1, x)
|
| 189 |
+
else:
|
| 190 |
+
raise ArgumentIndexError(self, argindex)
|
| 191 |
+
|
| 192 |
+
def _eval_rewrite_as_Sum(self, n, a, b, x, **kwargs):
|
| 193 |
+
from sympy.concrete.summations import Sum
|
| 194 |
+
# Make sure n \in N
|
| 195 |
+
if n.is_negative or n.is_integer is False:
|
| 196 |
+
raise ValueError("Error: n should be a non-negative integer.")
|
| 197 |
+
k = Dummy("k")
|
| 198 |
+
kern = (RisingFactorial(-n, k) * RisingFactorial(a + b + n + 1, k) * RisingFactorial(a + k + 1, n - k) /
|
| 199 |
+
factorial(k) * ((1 - x)/2)**k)
|
| 200 |
+
return 1 / factorial(n) * Sum(kern, (k, 0, n))
|
| 201 |
+
|
| 202 |
+
def _eval_rewrite_as_polynomial(self, n, a, b, x, **kwargs):
|
| 203 |
+
# This function is just kept for backwards compatibility
|
| 204 |
+
# but should not be used
|
| 205 |
+
return self._eval_rewrite_as_Sum(n, a, b, x, **kwargs)
|
| 206 |
+
|
| 207 |
+
def _eval_conjugate(self):
|
| 208 |
+
n, a, b, x = self.args
|
| 209 |
+
return self.func(n, a.conjugate(), b.conjugate(), x.conjugate())
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def jacobi_normalized(n, a, b, x):
|
| 213 |
+
r"""
|
| 214 |
+
Jacobi polynomial $P_n^{\left(\alpha, \beta\right)}(x)$.
|
| 215 |
+
|
| 216 |
+
Explanation
|
| 217 |
+
===========
|
| 218 |
+
|
| 219 |
+
``jacobi_normalized(n, alpha, beta, x)`` gives the $n$th
|
| 220 |
+
Jacobi polynomial in $x$, $P_n^{\left(\alpha, \beta\right)}(x)$.
|
| 221 |
+
|
| 222 |
+
The Jacobi polynomials are orthogonal on $[-1, 1]$ with respect
|
| 223 |
+
to the weight $\left(1-x\right)^\alpha \left(1+x\right)^\beta$.
|
| 224 |
+
|
| 225 |
+
This functions returns the polynomials normilzed:
|
| 226 |
+
|
| 227 |
+
.. math::
|
| 228 |
+
|
| 229 |
+
\int_{-1}^{1}
|
| 230 |
+
P_m^{\left(\alpha, \beta\right)}(x)
|
| 231 |
+
P_n^{\left(\alpha, \beta\right)}(x)
|
| 232 |
+
(1-x)^{\alpha} (1+x)^{\beta} \mathrm{d}x
|
| 233 |
+
= \delta_{m,n}
|
| 234 |
+
|
| 235 |
+
Examples
|
| 236 |
+
========
|
| 237 |
+
|
| 238 |
+
>>> from sympy import jacobi_normalized
|
| 239 |
+
>>> from sympy.abc import n,a,b,x
|
| 240 |
+
|
| 241 |
+
>>> jacobi_normalized(n, a, b, x)
|
| 242 |
+
jacobi(n, a, b, x)/sqrt(2**(a + b + 1)*gamma(a + n + 1)*gamma(b + n + 1)/((a + b + 2*n + 1)*factorial(n)*gamma(a + b + n + 1)))
|
| 243 |
+
|
| 244 |
+
Parameters
|
| 245 |
+
==========
|
| 246 |
+
|
| 247 |
+
n : integer degree of polynomial
|
| 248 |
+
|
| 249 |
+
a : alpha value
|
| 250 |
+
|
| 251 |
+
b : beta value
|
| 252 |
+
|
| 253 |
+
x : symbol
|
| 254 |
+
|
| 255 |
+
See Also
|
| 256 |
+
========
|
| 257 |
+
|
| 258 |
+
gegenbauer,
|
| 259 |
+
chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 260 |
+
legendre, assoc_legendre,
|
| 261 |
+
hermite, hermite_prob,
|
| 262 |
+
laguerre, assoc_laguerre,
|
| 263 |
+
sympy.polys.orthopolys.jacobi_poly,
|
| 264 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 265 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 266 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 267 |
+
sympy.polys.orthopolys.hermite_poly
|
| 268 |
+
sympy.polys.orthopolys.legendre_poly
|
| 269 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 270 |
+
|
| 271 |
+
References
|
| 272 |
+
==========
|
| 273 |
+
|
| 274 |
+
.. [1] https://en.wikipedia.org/wiki/Jacobi_polynomials
|
| 275 |
+
.. [2] https://mathworld.wolfram.com/JacobiPolynomial.html
|
| 276 |
+
.. [3] https://functions.wolfram.com/Polynomials/JacobiP/
|
| 277 |
+
|
| 278 |
+
"""
|
| 279 |
+
nfactor = (S(2)**(a + b + 1) * (gamma(n + a + 1) * gamma(n + b + 1))
|
| 280 |
+
/ (2*n + a + b + 1) / (factorial(n) * gamma(n + a + b + 1)))
|
| 281 |
+
|
| 282 |
+
return jacobi(n, a, b, x) / sqrt(nfactor)
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
#----------------------------------------------------------------------------
|
| 286 |
+
# Gegenbauer polynomials
|
| 287 |
+
#
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class gegenbauer(OrthogonalPolynomial):
|
| 291 |
+
r"""
|
| 292 |
+
Gegenbauer polynomial $C_n^{\left(\alpha\right)}(x)$.
|
| 293 |
+
|
| 294 |
+
Explanation
|
| 295 |
+
===========
|
| 296 |
+
|
| 297 |
+
``gegenbauer(n, alpha, x)`` gives the $n$th Gegenbauer polynomial
|
| 298 |
+
in $x$, $C_n^{\left(\alpha\right)}(x)$.
|
| 299 |
+
|
| 300 |
+
The Gegenbauer polynomials are orthogonal on $[-1, 1]$ with
|
| 301 |
+
respect to the weight $\left(1-x^2\right)^{\alpha-\frac{1}{2}}$.
|
| 302 |
+
|
| 303 |
+
Examples
|
| 304 |
+
========
|
| 305 |
+
|
| 306 |
+
>>> from sympy import gegenbauer, conjugate, diff
|
| 307 |
+
>>> from sympy.abc import n,a,x
|
| 308 |
+
>>> gegenbauer(0, a, x)
|
| 309 |
+
1
|
| 310 |
+
>>> gegenbauer(1, a, x)
|
| 311 |
+
2*a*x
|
| 312 |
+
>>> gegenbauer(2, a, x)
|
| 313 |
+
-a + x**2*(2*a**2 + 2*a)
|
| 314 |
+
>>> gegenbauer(3, a, x)
|
| 315 |
+
x**3*(4*a**3/3 + 4*a**2 + 8*a/3) + x*(-2*a**2 - 2*a)
|
| 316 |
+
|
| 317 |
+
>>> gegenbauer(n, a, x)
|
| 318 |
+
gegenbauer(n, a, x)
|
| 319 |
+
>>> gegenbauer(n, a, -x)
|
| 320 |
+
(-1)**n*gegenbauer(n, a, x)
|
| 321 |
+
|
| 322 |
+
>>> gegenbauer(n, a, 0)
|
| 323 |
+
2**n*sqrt(pi)*gamma(a + n/2)/(gamma(a)*gamma(1/2 - n/2)*gamma(n + 1))
|
| 324 |
+
>>> gegenbauer(n, a, 1)
|
| 325 |
+
gamma(2*a + n)/(gamma(2*a)*gamma(n + 1))
|
| 326 |
+
|
| 327 |
+
>>> conjugate(gegenbauer(n, a, x))
|
| 328 |
+
gegenbauer(n, conjugate(a), conjugate(x))
|
| 329 |
+
|
| 330 |
+
>>> diff(gegenbauer(n, a, x), x)
|
| 331 |
+
2*a*gegenbauer(n - 1, a + 1, x)
|
| 332 |
+
|
| 333 |
+
See Also
|
| 334 |
+
========
|
| 335 |
+
|
| 336 |
+
jacobi,
|
| 337 |
+
chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 338 |
+
legendre, assoc_legendre,
|
| 339 |
+
hermite, hermite_prob,
|
| 340 |
+
laguerre, assoc_laguerre,
|
| 341 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 342 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 343 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 344 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 345 |
+
sympy.polys.orthopolys.hermite_poly
|
| 346 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 347 |
+
sympy.polys.orthopolys.legendre_poly
|
| 348 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 349 |
+
|
| 350 |
+
References
|
| 351 |
+
==========
|
| 352 |
+
|
| 353 |
+
.. [1] https://en.wikipedia.org/wiki/Gegenbauer_polynomials
|
| 354 |
+
.. [2] https://mathworld.wolfram.com/GegenbauerPolynomial.html
|
| 355 |
+
.. [3] https://functions.wolfram.com/Polynomials/GegenbauerC3/
|
| 356 |
+
|
| 357 |
+
"""
|
| 358 |
+
|
| 359 |
+
@classmethod
|
| 360 |
+
def eval(cls, n, a, x):
|
| 361 |
+
# For negative n the polynomials vanish
|
| 362 |
+
# See https://functions.wolfram.com/Polynomials/GegenbauerC3/03/01/03/0012/
|
| 363 |
+
if n.is_negative:
|
| 364 |
+
return S.Zero
|
| 365 |
+
|
| 366 |
+
# Some special values for fixed a
|
| 367 |
+
if a == S.Half:
|
| 368 |
+
return legendre(n, x)
|
| 369 |
+
elif a == S.One:
|
| 370 |
+
return chebyshevu(n, x)
|
| 371 |
+
elif a == S.NegativeOne:
|
| 372 |
+
return S.Zero
|
| 373 |
+
|
| 374 |
+
if not n.is_Number:
|
| 375 |
+
# Handle this before the general sign extraction rule
|
| 376 |
+
if x == S.NegativeOne:
|
| 377 |
+
if (re(a) > S.Half) == True:
|
| 378 |
+
return S.ComplexInfinity
|
| 379 |
+
else:
|
| 380 |
+
return (cos(S.Pi*(a+n)) * sec(S.Pi*a) * gamma(2*a+n) /
|
| 381 |
+
(gamma(2*a) * gamma(n+1)))
|
| 382 |
+
|
| 383 |
+
# Symbolic result C^a_n(x)
|
| 384 |
+
# C^a_n(-x) ---> (-1)**n * C^a_n(x)
|
| 385 |
+
if x.could_extract_minus_sign():
|
| 386 |
+
return S.NegativeOne**n * gegenbauer(n, a, -x)
|
| 387 |
+
# We can evaluate for some special values of x
|
| 388 |
+
if x.is_zero:
|
| 389 |
+
return (2**n * sqrt(S.Pi) * gamma(a + S.Half*n) /
|
| 390 |
+
(gamma((1 - n)/2) * gamma(n + 1) * gamma(a)) )
|
| 391 |
+
if x == S.One:
|
| 392 |
+
return gamma(2*a + n) / (gamma(2*a) * gamma(n + 1))
|
| 393 |
+
elif x is S.Infinity:
|
| 394 |
+
if n.is_positive:
|
| 395 |
+
return RisingFactorial(a, n) * S.Infinity
|
| 396 |
+
else:
|
| 397 |
+
# n is a given fixed integer, evaluate into polynomial
|
| 398 |
+
return gegenbauer_poly(n, a, x)
|
| 399 |
+
|
| 400 |
+
def fdiff(self, argindex=3):
|
| 401 |
+
from sympy.concrete.summations import Sum
|
| 402 |
+
if argindex == 1:
|
| 403 |
+
# Diff wrt n
|
| 404 |
+
raise ArgumentIndexError(self, argindex)
|
| 405 |
+
elif argindex == 2:
|
| 406 |
+
# Diff wrt a
|
| 407 |
+
n, a, x = self.args
|
| 408 |
+
k = Dummy("k")
|
| 409 |
+
factor1 = 2 * (1 + (-1)**(n - k)) * (k + a) / ((k +
|
| 410 |
+
n + 2*a) * (n - k))
|
| 411 |
+
factor2 = 2*(k + 1) / ((k + 2*a) * (2*k + 2*a + 1)) + \
|
| 412 |
+
2 / (k + n + 2*a)
|
| 413 |
+
kern = factor1*gegenbauer(k, a, x) + factor2*gegenbauer(n, a, x)
|
| 414 |
+
return Sum(kern, (k, 0, n - 1))
|
| 415 |
+
elif argindex == 3:
|
| 416 |
+
# Diff wrt x
|
| 417 |
+
n, a, x = self.args
|
| 418 |
+
return 2*a*gegenbauer(n - 1, a + 1, x)
|
| 419 |
+
else:
|
| 420 |
+
raise ArgumentIndexError(self, argindex)
|
| 421 |
+
|
| 422 |
+
def _eval_rewrite_as_Sum(self, n, a, x, **kwargs):
|
| 423 |
+
from sympy.concrete.summations import Sum
|
| 424 |
+
k = Dummy("k")
|
| 425 |
+
kern = ((-1)**k * RisingFactorial(a, n - k) * (2*x)**(n - 2*k) /
|
| 426 |
+
(factorial(k) * factorial(n - 2*k)))
|
| 427 |
+
return Sum(kern, (k, 0, floor(n/2)))
|
| 428 |
+
|
| 429 |
+
def _eval_rewrite_as_polynomial(self, n, a, x, **kwargs):
|
| 430 |
+
# This function is just kept for backwards compatibility
|
| 431 |
+
# but should not be used
|
| 432 |
+
return self._eval_rewrite_as_Sum(n, a, x, **kwargs)
|
| 433 |
+
|
| 434 |
+
def _eval_conjugate(self):
|
| 435 |
+
n, a, x = self.args
|
| 436 |
+
return self.func(n, a.conjugate(), x.conjugate())
|
| 437 |
+
|
| 438 |
+
#----------------------------------------------------------------------------
|
| 439 |
+
# Chebyshev polynomials of first and second kind
|
| 440 |
+
#
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
class chebyshevt(OrthogonalPolynomial):
|
| 444 |
+
r"""
|
| 445 |
+
Chebyshev polynomial of the first kind, $T_n(x)$.
|
| 446 |
+
|
| 447 |
+
Explanation
|
| 448 |
+
===========
|
| 449 |
+
|
| 450 |
+
``chebyshevt(n, x)`` gives the $n$th Chebyshev polynomial (of the first
|
| 451 |
+
kind) in $x$, $T_n(x)$.
|
| 452 |
+
|
| 453 |
+
The Chebyshev polynomials of the first kind are orthogonal on
|
| 454 |
+
$[-1, 1]$ with respect to the weight $\frac{1}{\sqrt{1-x^2}}$.
|
| 455 |
+
|
| 456 |
+
Examples
|
| 457 |
+
========
|
| 458 |
+
|
| 459 |
+
>>> from sympy import chebyshevt, diff
|
| 460 |
+
>>> from sympy.abc import n,x
|
| 461 |
+
>>> chebyshevt(0, x)
|
| 462 |
+
1
|
| 463 |
+
>>> chebyshevt(1, x)
|
| 464 |
+
x
|
| 465 |
+
>>> chebyshevt(2, x)
|
| 466 |
+
2*x**2 - 1
|
| 467 |
+
|
| 468 |
+
>>> chebyshevt(n, x)
|
| 469 |
+
chebyshevt(n, x)
|
| 470 |
+
>>> chebyshevt(n, -x)
|
| 471 |
+
(-1)**n*chebyshevt(n, x)
|
| 472 |
+
>>> chebyshevt(-n, x)
|
| 473 |
+
chebyshevt(n, x)
|
| 474 |
+
|
| 475 |
+
>>> chebyshevt(n, 0)
|
| 476 |
+
cos(pi*n/2)
|
| 477 |
+
>>> chebyshevt(n, -1)
|
| 478 |
+
(-1)**n
|
| 479 |
+
|
| 480 |
+
>>> diff(chebyshevt(n, x), x)
|
| 481 |
+
n*chebyshevu(n - 1, x)
|
| 482 |
+
|
| 483 |
+
See Also
|
| 484 |
+
========
|
| 485 |
+
|
| 486 |
+
jacobi, gegenbauer,
|
| 487 |
+
chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 488 |
+
legendre, assoc_legendre,
|
| 489 |
+
hermite, hermite_prob,
|
| 490 |
+
laguerre, assoc_laguerre,
|
| 491 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 492 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 493 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 494 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 495 |
+
sympy.polys.orthopolys.hermite_poly
|
| 496 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 497 |
+
sympy.polys.orthopolys.legendre_poly
|
| 498 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 499 |
+
|
| 500 |
+
References
|
| 501 |
+
==========
|
| 502 |
+
|
| 503 |
+
.. [1] https://en.wikipedia.org/wiki/Chebyshev_polynomial
|
| 504 |
+
.. [2] https://mathworld.wolfram.com/ChebyshevPolynomialoftheFirstKind.html
|
| 505 |
+
.. [3] https://mathworld.wolfram.com/ChebyshevPolynomialoftheSecondKind.html
|
| 506 |
+
.. [4] https://functions.wolfram.com/Polynomials/ChebyshevT/
|
| 507 |
+
.. [5] https://functions.wolfram.com/Polynomials/ChebyshevU/
|
| 508 |
+
|
| 509 |
+
"""
|
| 510 |
+
|
| 511 |
+
_ortho_poly = staticmethod(chebyshevt_poly)
|
| 512 |
+
|
| 513 |
+
@classmethod
|
| 514 |
+
def eval(cls, n, x):
|
| 515 |
+
if not n.is_Number:
|
| 516 |
+
# Symbolic result T_n(x)
|
| 517 |
+
# T_n(-x) ---> (-1)**n * T_n(x)
|
| 518 |
+
if x.could_extract_minus_sign():
|
| 519 |
+
return S.NegativeOne**n * chebyshevt(n, -x)
|
| 520 |
+
# T_{-n}(x) ---> T_n(x)
|
| 521 |
+
if n.could_extract_minus_sign():
|
| 522 |
+
return chebyshevt(-n, x)
|
| 523 |
+
# We can evaluate for some special values of x
|
| 524 |
+
if x.is_zero:
|
| 525 |
+
return cos(S.Half * S.Pi * n)
|
| 526 |
+
if x == S.One:
|
| 527 |
+
return S.One
|
| 528 |
+
elif x is S.Infinity:
|
| 529 |
+
return S.Infinity
|
| 530 |
+
else:
|
| 531 |
+
# n is a given fixed integer, evaluate into polynomial
|
| 532 |
+
if n.is_negative:
|
| 533 |
+
# T_{-n}(x) == T_n(x)
|
| 534 |
+
return cls._eval_at_order(-n, x)
|
| 535 |
+
else:
|
| 536 |
+
return cls._eval_at_order(n, x)
|
| 537 |
+
|
| 538 |
+
def fdiff(self, argindex=2):
|
| 539 |
+
if argindex == 1:
|
| 540 |
+
# Diff wrt n
|
| 541 |
+
raise ArgumentIndexError(self, argindex)
|
| 542 |
+
elif argindex == 2:
|
| 543 |
+
# Diff wrt x
|
| 544 |
+
n, x = self.args
|
| 545 |
+
return n * chebyshevu(n - 1, x)
|
| 546 |
+
else:
|
| 547 |
+
raise ArgumentIndexError(self, argindex)
|
| 548 |
+
|
| 549 |
+
def _eval_rewrite_as_Sum(self, n, x, **kwargs):
|
| 550 |
+
from sympy.concrete.summations import Sum
|
| 551 |
+
k = Dummy("k")
|
| 552 |
+
kern = binomial(n, 2*k) * (x**2 - 1)**k * x**(n - 2*k)
|
| 553 |
+
return Sum(kern, (k, 0, floor(n/2)))
|
| 554 |
+
|
| 555 |
+
def _eval_rewrite_as_polynomial(self, n, x, **kwargs):
|
| 556 |
+
# This function is just kept for backwards compatibility
|
| 557 |
+
# but should not be used
|
| 558 |
+
return self._eval_rewrite_as_Sum(n, x, **kwargs)
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
class chebyshevu(OrthogonalPolynomial):
|
| 562 |
+
r"""
|
| 563 |
+
Chebyshev polynomial of the second kind, $U_n(x)$.
|
| 564 |
+
|
| 565 |
+
Explanation
|
| 566 |
+
===========
|
| 567 |
+
|
| 568 |
+
``chebyshevu(n, x)`` gives the $n$th Chebyshev polynomial of the second
|
| 569 |
+
kind in x, $U_n(x)$.
|
| 570 |
+
|
| 571 |
+
The Chebyshev polynomials of the second kind are orthogonal on
|
| 572 |
+
$[-1, 1]$ with respect to the weight $\sqrt{1-x^2}$.
|
| 573 |
+
|
| 574 |
+
Examples
|
| 575 |
+
========
|
| 576 |
+
|
| 577 |
+
>>> from sympy import chebyshevu, diff
|
| 578 |
+
>>> from sympy.abc import n,x
|
| 579 |
+
>>> chebyshevu(0, x)
|
| 580 |
+
1
|
| 581 |
+
>>> chebyshevu(1, x)
|
| 582 |
+
2*x
|
| 583 |
+
>>> chebyshevu(2, x)
|
| 584 |
+
4*x**2 - 1
|
| 585 |
+
|
| 586 |
+
>>> chebyshevu(n, x)
|
| 587 |
+
chebyshevu(n, x)
|
| 588 |
+
>>> chebyshevu(n, -x)
|
| 589 |
+
(-1)**n*chebyshevu(n, x)
|
| 590 |
+
>>> chebyshevu(-n, x)
|
| 591 |
+
-chebyshevu(n - 2, x)
|
| 592 |
+
|
| 593 |
+
>>> chebyshevu(n, 0)
|
| 594 |
+
cos(pi*n/2)
|
| 595 |
+
>>> chebyshevu(n, 1)
|
| 596 |
+
n + 1
|
| 597 |
+
|
| 598 |
+
>>> diff(chebyshevu(n, x), x)
|
| 599 |
+
(-x*chebyshevu(n, x) + (n + 1)*chebyshevt(n + 1, x))/(x**2 - 1)
|
| 600 |
+
|
| 601 |
+
See Also
|
| 602 |
+
========
|
| 603 |
+
|
| 604 |
+
jacobi, gegenbauer,
|
| 605 |
+
chebyshevt, chebyshevt_root, chebyshevu_root,
|
| 606 |
+
legendre, assoc_legendre,
|
| 607 |
+
hermite, hermite_prob,
|
| 608 |
+
laguerre, assoc_laguerre,
|
| 609 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 610 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 611 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 612 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 613 |
+
sympy.polys.orthopolys.hermite_poly
|
| 614 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 615 |
+
sympy.polys.orthopolys.legendre_poly
|
| 616 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 617 |
+
|
| 618 |
+
References
|
| 619 |
+
==========
|
| 620 |
+
|
| 621 |
+
.. [1] https://en.wikipedia.org/wiki/Chebyshev_polynomial
|
| 622 |
+
.. [2] https://mathworld.wolfram.com/ChebyshevPolynomialoftheFirstKind.html
|
| 623 |
+
.. [3] https://mathworld.wolfram.com/ChebyshevPolynomialoftheSecondKind.html
|
| 624 |
+
.. [4] https://functions.wolfram.com/Polynomials/ChebyshevT/
|
| 625 |
+
.. [5] https://functions.wolfram.com/Polynomials/ChebyshevU/
|
| 626 |
+
|
| 627 |
+
"""
|
| 628 |
+
|
| 629 |
+
_ortho_poly = staticmethod(chebyshevu_poly)
|
| 630 |
+
|
| 631 |
+
@classmethod
|
| 632 |
+
def eval(cls, n, x):
|
| 633 |
+
if not n.is_Number:
|
| 634 |
+
# Symbolic result U_n(x)
|
| 635 |
+
# U_n(-x) ---> (-1)**n * U_n(x)
|
| 636 |
+
if x.could_extract_minus_sign():
|
| 637 |
+
return S.NegativeOne**n * chebyshevu(n, -x)
|
| 638 |
+
# U_{-n}(x) ---> -U_{n-2}(x)
|
| 639 |
+
if n.could_extract_minus_sign():
|
| 640 |
+
if n == S.NegativeOne:
|
| 641 |
+
# n can not be -1 here
|
| 642 |
+
return S.Zero
|
| 643 |
+
elif not (-n - 2).could_extract_minus_sign():
|
| 644 |
+
return -chebyshevu(-n - 2, x)
|
| 645 |
+
# We can evaluate for some special values of x
|
| 646 |
+
if x.is_zero:
|
| 647 |
+
return cos(S.Half * S.Pi * n)
|
| 648 |
+
if x == S.One:
|
| 649 |
+
return S.One + n
|
| 650 |
+
elif x is S.Infinity:
|
| 651 |
+
return S.Infinity
|
| 652 |
+
else:
|
| 653 |
+
# n is a given fixed integer, evaluate into polynomial
|
| 654 |
+
if n.is_negative:
|
| 655 |
+
# U_{-n}(x) ---> -U_{n-2}(x)
|
| 656 |
+
if n == S.NegativeOne:
|
| 657 |
+
return S.Zero
|
| 658 |
+
else:
|
| 659 |
+
return -cls._eval_at_order(-n - 2, x)
|
| 660 |
+
else:
|
| 661 |
+
return cls._eval_at_order(n, x)
|
| 662 |
+
|
| 663 |
+
def fdiff(self, argindex=2):
|
| 664 |
+
if argindex == 1:
|
| 665 |
+
# Diff wrt n
|
| 666 |
+
raise ArgumentIndexError(self, argindex)
|
| 667 |
+
elif argindex == 2:
|
| 668 |
+
# Diff wrt x
|
| 669 |
+
n, x = self.args
|
| 670 |
+
return ((n + 1) * chebyshevt(n + 1, x) - x * chebyshevu(n, x)) / (x**2 - 1)
|
| 671 |
+
else:
|
| 672 |
+
raise ArgumentIndexError(self, argindex)
|
| 673 |
+
|
| 674 |
+
def _eval_rewrite_as_Sum(self, n, x, **kwargs):
|
| 675 |
+
from sympy.concrete.summations import Sum
|
| 676 |
+
k = Dummy("k")
|
| 677 |
+
kern = S.NegativeOne**k * factorial(
|
| 678 |
+
n - k) * (2*x)**(n - 2*k) / (factorial(k) * factorial(n - 2*k))
|
| 679 |
+
return Sum(kern, (k, 0, floor(n/2)))
|
| 680 |
+
|
| 681 |
+
def _eval_rewrite_as_polynomial(self, n, x, **kwargs):
|
| 682 |
+
# This function is just kept for backwards compatibility
|
| 683 |
+
# but should not be used
|
| 684 |
+
return self._eval_rewrite_as_Sum(n, x, **kwargs)
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
class chebyshevt_root(DefinedFunction):
|
| 688 |
+
r"""
|
| 689 |
+
``chebyshev_root(n, k)`` returns the $k$th root (indexed from zero) of
|
| 690 |
+
the $n$th Chebyshev polynomial of the first kind; that is, if
|
| 691 |
+
$0 \le k < n$, ``chebyshevt(n, chebyshevt_root(n, k)) == 0``.
|
| 692 |
+
|
| 693 |
+
Examples
|
| 694 |
+
========
|
| 695 |
+
|
| 696 |
+
>>> from sympy import chebyshevt, chebyshevt_root
|
| 697 |
+
>>> chebyshevt_root(3, 2)
|
| 698 |
+
-sqrt(3)/2
|
| 699 |
+
>>> chebyshevt(3, chebyshevt_root(3, 2))
|
| 700 |
+
0
|
| 701 |
+
|
| 702 |
+
See Also
|
| 703 |
+
========
|
| 704 |
+
|
| 705 |
+
jacobi, gegenbauer,
|
| 706 |
+
chebyshevt, chebyshevu, chebyshevu_root,
|
| 707 |
+
legendre, assoc_legendre,
|
| 708 |
+
hermite, hermite_prob,
|
| 709 |
+
laguerre, assoc_laguerre,
|
| 710 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 711 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 712 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 713 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 714 |
+
sympy.polys.orthopolys.hermite_poly
|
| 715 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 716 |
+
sympy.polys.orthopolys.legendre_poly
|
| 717 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 718 |
+
"""
|
| 719 |
+
|
| 720 |
+
@classmethod
|
| 721 |
+
def eval(cls, n, k):
|
| 722 |
+
if not ((0 <= k) and (k < n)):
|
| 723 |
+
raise ValueError("must have 0 <= k < n, "
|
| 724 |
+
"got k = %s and n = %s" % (k, n))
|
| 725 |
+
return cos(S.Pi*(2*k + 1)/(2*n))
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
class chebyshevu_root(DefinedFunction):
|
| 729 |
+
r"""
|
| 730 |
+
``chebyshevu_root(n, k)`` returns the $k$th root (indexed from zero) of the
|
| 731 |
+
$n$th Chebyshev polynomial of the second kind; that is, if $0 \le k < n$,
|
| 732 |
+
``chebyshevu(n, chebyshevu_root(n, k)) == 0``.
|
| 733 |
+
|
| 734 |
+
Examples
|
| 735 |
+
========
|
| 736 |
+
|
| 737 |
+
>>> from sympy import chebyshevu, chebyshevu_root
|
| 738 |
+
>>> chebyshevu_root(3, 2)
|
| 739 |
+
-sqrt(2)/2
|
| 740 |
+
>>> chebyshevu(3, chebyshevu_root(3, 2))
|
| 741 |
+
0
|
| 742 |
+
|
| 743 |
+
See Also
|
| 744 |
+
========
|
| 745 |
+
|
| 746 |
+
chebyshevt, chebyshevt_root, chebyshevu,
|
| 747 |
+
legendre, assoc_legendre,
|
| 748 |
+
hermite, hermite_prob,
|
| 749 |
+
laguerre, assoc_laguerre,
|
| 750 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 751 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 752 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 753 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 754 |
+
sympy.polys.orthopolys.hermite_poly
|
| 755 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 756 |
+
sympy.polys.orthopolys.legendre_poly
|
| 757 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 758 |
+
"""
|
| 759 |
+
|
| 760 |
+
|
| 761 |
+
@classmethod
|
| 762 |
+
def eval(cls, n, k):
|
| 763 |
+
if not ((0 <= k) and (k < n)):
|
| 764 |
+
raise ValueError("must have 0 <= k < n, "
|
| 765 |
+
"got k = %s and n = %s" % (k, n))
|
| 766 |
+
return cos(S.Pi*(k + 1)/(n + 1))
|
| 767 |
+
|
| 768 |
+
#----------------------------------------------------------------------------
|
| 769 |
+
# Legendre polynomials and Associated Legendre polynomials
|
| 770 |
+
#
|
| 771 |
+
|
| 772 |
+
|
| 773 |
+
class legendre(OrthogonalPolynomial):
|
| 774 |
+
r"""
|
| 775 |
+
``legendre(n, x)`` gives the $n$th Legendre polynomial of $x$, $P_n(x)$
|
| 776 |
+
|
| 777 |
+
Explanation
|
| 778 |
+
===========
|
| 779 |
+
|
| 780 |
+
The Legendre polynomials are orthogonal on $[-1, 1]$ with respect to
|
| 781 |
+
the constant weight 1. They satisfy $P_n(1) = 1$ for all $n$; further,
|
| 782 |
+
$P_n$ is odd for odd $n$ and even for even $n$.
|
| 783 |
+
|
| 784 |
+
Examples
|
| 785 |
+
========
|
| 786 |
+
|
| 787 |
+
>>> from sympy import legendre, diff
|
| 788 |
+
>>> from sympy.abc import x, n
|
| 789 |
+
>>> legendre(0, x)
|
| 790 |
+
1
|
| 791 |
+
>>> legendre(1, x)
|
| 792 |
+
x
|
| 793 |
+
>>> legendre(2, x)
|
| 794 |
+
3*x**2/2 - 1/2
|
| 795 |
+
>>> legendre(n, x)
|
| 796 |
+
legendre(n, x)
|
| 797 |
+
>>> diff(legendre(n,x), x)
|
| 798 |
+
n*(x*legendre(n, x) - legendre(n - 1, x))/(x**2 - 1)
|
| 799 |
+
|
| 800 |
+
See Also
|
| 801 |
+
========
|
| 802 |
+
|
| 803 |
+
jacobi, gegenbauer,
|
| 804 |
+
chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 805 |
+
assoc_legendre,
|
| 806 |
+
hermite, hermite_prob,
|
| 807 |
+
laguerre, assoc_laguerre,
|
| 808 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 809 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 810 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 811 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 812 |
+
sympy.polys.orthopolys.hermite_poly
|
| 813 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 814 |
+
sympy.polys.orthopolys.legendre_poly
|
| 815 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 816 |
+
|
| 817 |
+
References
|
| 818 |
+
==========
|
| 819 |
+
|
| 820 |
+
.. [1] https://en.wikipedia.org/wiki/Legendre_polynomial
|
| 821 |
+
.. [2] https://mathworld.wolfram.com/LegendrePolynomial.html
|
| 822 |
+
.. [3] https://functions.wolfram.com/Polynomials/LegendreP/
|
| 823 |
+
.. [4] https://functions.wolfram.com/Polynomials/LegendreP2/
|
| 824 |
+
|
| 825 |
+
"""
|
| 826 |
+
|
| 827 |
+
_ortho_poly = staticmethod(legendre_poly)
|
| 828 |
+
|
| 829 |
+
@classmethod
|
| 830 |
+
def eval(cls, n, x):
|
| 831 |
+
if not n.is_Number:
|
| 832 |
+
# Symbolic result L_n(x)
|
| 833 |
+
# L_n(-x) ---> (-1)**n * L_n(x)
|
| 834 |
+
if x.could_extract_minus_sign():
|
| 835 |
+
return S.NegativeOne**n * legendre(n, -x)
|
| 836 |
+
# L_{-n}(x) ---> L_{n-1}(x)
|
| 837 |
+
if n.could_extract_minus_sign() and not(-n - 1).could_extract_minus_sign():
|
| 838 |
+
return legendre(-n - S.One, x)
|
| 839 |
+
# We can evaluate for some special values of x
|
| 840 |
+
if x.is_zero:
|
| 841 |
+
return sqrt(S.Pi)/(gamma(S.Half - n/2)*gamma(S.One + n/2))
|
| 842 |
+
elif x == S.One:
|
| 843 |
+
return S.One
|
| 844 |
+
elif x is S.Infinity:
|
| 845 |
+
return S.Infinity
|
| 846 |
+
else:
|
| 847 |
+
# n is a given fixed integer, evaluate into polynomial;
|
| 848 |
+
# L_{-n}(x) ---> L_{n-1}(x)
|
| 849 |
+
if n.is_negative:
|
| 850 |
+
n = -n - S.One
|
| 851 |
+
return cls._eval_at_order(n, x)
|
| 852 |
+
|
| 853 |
+
def fdiff(self, argindex=2):
|
| 854 |
+
if argindex == 1:
|
| 855 |
+
# Diff wrt n
|
| 856 |
+
raise ArgumentIndexError(self, argindex)
|
| 857 |
+
elif argindex == 2:
|
| 858 |
+
# Diff wrt x
|
| 859 |
+
# Find better formula, this is unsuitable for x = +/-1
|
| 860 |
+
# https://www.autodiff.org/ad16/Oral/Buecker_Legendre.pdf says
|
| 861 |
+
# at x = 1:
|
| 862 |
+
# n*(n + 1)/2 , m = 0
|
| 863 |
+
# oo , m = 1
|
| 864 |
+
# -(n-1)*n*(n+1)*(n+2)/4 , m = 2
|
| 865 |
+
# 0 , m = 3, 4, ..., n
|
| 866 |
+
#
|
| 867 |
+
# at x = -1
|
| 868 |
+
# (-1)**(n+1)*n*(n + 1)/2 , m = 0
|
| 869 |
+
# (-1)**n*oo , m = 1
|
| 870 |
+
# (-1)**n*(n-1)*n*(n+1)*(n+2)/4 , m = 2
|
| 871 |
+
# 0 , m = 3, 4, ..., n
|
| 872 |
+
n, x = self.args
|
| 873 |
+
return n/(x**2 - 1)*(x*legendre(n, x) - legendre(n - 1, x))
|
| 874 |
+
else:
|
| 875 |
+
raise ArgumentIndexError(self, argindex)
|
| 876 |
+
|
| 877 |
+
def _eval_rewrite_as_Sum(self, n, x, **kwargs):
|
| 878 |
+
from sympy.concrete.summations import Sum
|
| 879 |
+
k = Dummy("k")
|
| 880 |
+
kern = S.NegativeOne**k*binomial(n, k)**2*((1 + x)/2)**(n - k)*((1 - x)/2)**k
|
| 881 |
+
return Sum(kern, (k, 0, n))
|
| 882 |
+
|
| 883 |
+
def _eval_rewrite_as_polynomial(self, n, x, **kwargs):
|
| 884 |
+
# This function is just kept for backwards compatibility
|
| 885 |
+
# but should not be used
|
| 886 |
+
return self._eval_rewrite_as_Sum(n, x, **kwargs)
|
| 887 |
+
|
| 888 |
+
|
| 889 |
+
class assoc_legendre(DefinedFunction):
|
| 890 |
+
r"""
|
| 891 |
+
``assoc_legendre(n, m, x)`` gives $P_n^m(x)$, where $n$ and $m$ are
|
| 892 |
+
the degree and order or an expression which is related to the nth
|
| 893 |
+
order Legendre polynomial, $P_n(x)$ in the following manner:
|
| 894 |
+
|
| 895 |
+
.. math::
|
| 896 |
+
P_n^m(x) = (-1)^m (1 - x^2)^{\frac{m}{2}}
|
| 897 |
+
\frac{\mathrm{d}^m P_n(x)}{\mathrm{d} x^m}
|
| 898 |
+
|
| 899 |
+
Explanation
|
| 900 |
+
===========
|
| 901 |
+
|
| 902 |
+
Associated Legendre polynomials are orthogonal on $[-1, 1]$ with:
|
| 903 |
+
|
| 904 |
+
- weight $= 1$ for the same $m$ and different $n$.
|
| 905 |
+
- weight $= \frac{1}{1-x^2}$ for the same $n$ and different $m$.
|
| 906 |
+
|
| 907 |
+
Examples
|
| 908 |
+
========
|
| 909 |
+
|
| 910 |
+
>>> from sympy import assoc_legendre
|
| 911 |
+
>>> from sympy.abc import x, m, n
|
| 912 |
+
>>> assoc_legendre(0,0, x)
|
| 913 |
+
1
|
| 914 |
+
>>> assoc_legendre(1,0, x)
|
| 915 |
+
x
|
| 916 |
+
>>> assoc_legendre(1,1, x)
|
| 917 |
+
-sqrt(1 - x**2)
|
| 918 |
+
>>> assoc_legendre(n,m,x)
|
| 919 |
+
assoc_legendre(n, m, x)
|
| 920 |
+
|
| 921 |
+
See Also
|
| 922 |
+
========
|
| 923 |
+
|
| 924 |
+
jacobi, gegenbauer,
|
| 925 |
+
chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 926 |
+
legendre,
|
| 927 |
+
hermite, hermite_prob,
|
| 928 |
+
laguerre, assoc_laguerre,
|
| 929 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 930 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 931 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 932 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 933 |
+
sympy.polys.orthopolys.hermite_poly
|
| 934 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 935 |
+
sympy.polys.orthopolys.legendre_poly
|
| 936 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 937 |
+
|
| 938 |
+
References
|
| 939 |
+
==========
|
| 940 |
+
|
| 941 |
+
.. [1] https://en.wikipedia.org/wiki/Associated_Legendre_polynomials
|
| 942 |
+
.. [2] https://mathworld.wolfram.com/LegendrePolynomial.html
|
| 943 |
+
.. [3] https://functions.wolfram.com/Polynomials/LegendreP/
|
| 944 |
+
.. [4] https://functions.wolfram.com/Polynomials/LegendreP2/
|
| 945 |
+
|
| 946 |
+
"""
|
| 947 |
+
|
| 948 |
+
@classmethod
|
| 949 |
+
def _eval_at_order(cls, n, m):
|
| 950 |
+
P = legendre_poly(n, _x, polys=True).diff((_x, m))
|
| 951 |
+
return S.NegativeOne**m * (1 - _x**2)**Rational(m, 2) * P.as_expr()
|
| 952 |
+
|
| 953 |
+
@classmethod
|
| 954 |
+
def eval(cls, n, m, x):
|
| 955 |
+
if m.could_extract_minus_sign():
|
| 956 |
+
# P^{-m}_n ---> F * P^m_n
|
| 957 |
+
return S.NegativeOne**(-m) * (factorial(m + n)/factorial(n - m)) * assoc_legendre(n, -m, x)
|
| 958 |
+
if m == 0:
|
| 959 |
+
# P^0_n ---> L_n
|
| 960 |
+
return legendre(n, x)
|
| 961 |
+
if x == 0:
|
| 962 |
+
return 2**m*sqrt(S.Pi) / (gamma((1 - m - n)/2)*gamma(1 - (m - n)/2))
|
| 963 |
+
if n.is_Number and m.is_Number and n.is_integer and m.is_integer:
|
| 964 |
+
if n.is_negative:
|
| 965 |
+
raise ValueError("%s : 1st index must be nonnegative integer (got %r)" % (cls, n))
|
| 966 |
+
if abs(m) > n:
|
| 967 |
+
raise ValueError("%s : abs('2nd index') must be <= '1st index' (got %r, %r)" % (cls, n, m))
|
| 968 |
+
return cls._eval_at_order(int(n), abs(int(m))).subs(_x, x)
|
| 969 |
+
|
| 970 |
+
def fdiff(self, argindex=3):
|
| 971 |
+
if argindex == 1:
|
| 972 |
+
# Diff wrt n
|
| 973 |
+
raise ArgumentIndexError(self, argindex)
|
| 974 |
+
elif argindex == 2:
|
| 975 |
+
# Diff wrt m
|
| 976 |
+
raise ArgumentIndexError(self, argindex)
|
| 977 |
+
elif argindex == 3:
|
| 978 |
+
# Diff wrt x
|
| 979 |
+
# Find better formula, this is unsuitable for x = 1
|
| 980 |
+
n, m, x = self.args
|
| 981 |
+
return 1/(x**2 - 1)*(x*n*assoc_legendre(n, m, x) - (m + n)*assoc_legendre(n - 1, m, x))
|
| 982 |
+
else:
|
| 983 |
+
raise ArgumentIndexError(self, argindex)
|
| 984 |
+
|
| 985 |
+
def _eval_rewrite_as_Sum(self, n, m, x, **kwargs):
|
| 986 |
+
from sympy.concrete.summations import Sum
|
| 987 |
+
k = Dummy("k")
|
| 988 |
+
kern = factorial(2*n - 2*k)/(2**n*factorial(n - k)*factorial(
|
| 989 |
+
k)*factorial(n - 2*k - m))*S.NegativeOne**k*x**(n - m - 2*k)
|
| 990 |
+
return (1 - x**2)**(m/2) * Sum(kern, (k, 0, floor((n - m)*S.Half)))
|
| 991 |
+
|
| 992 |
+
def _eval_rewrite_as_polynomial(self, n, m, x, **kwargs):
|
| 993 |
+
# This function is just kept for backwards compatibility
|
| 994 |
+
# but should not be used
|
| 995 |
+
return self._eval_rewrite_as_Sum(n, m, x, **kwargs)
|
| 996 |
+
|
| 997 |
+
def _eval_conjugate(self):
|
| 998 |
+
n, m, x = self.args
|
| 999 |
+
return self.func(n, m.conjugate(), x.conjugate())
|
| 1000 |
+
|
| 1001 |
+
#----------------------------------------------------------------------------
|
| 1002 |
+
# Hermite polynomials
|
| 1003 |
+
#
|
| 1004 |
+
|
| 1005 |
+
|
| 1006 |
+
class hermite(OrthogonalPolynomial):
|
| 1007 |
+
r"""
|
| 1008 |
+
``hermite(n, x)`` gives the $n$th Hermite polynomial in $x$, $H_n(x)$.
|
| 1009 |
+
|
| 1010 |
+
Explanation
|
| 1011 |
+
===========
|
| 1012 |
+
|
| 1013 |
+
The Hermite polynomials are orthogonal on $(-\infty, \infty)$
|
| 1014 |
+
with respect to the weight $\exp\left(-x^2\right)$.
|
| 1015 |
+
|
| 1016 |
+
Examples
|
| 1017 |
+
========
|
| 1018 |
+
|
| 1019 |
+
>>> from sympy import hermite, diff
|
| 1020 |
+
>>> from sympy.abc import x, n
|
| 1021 |
+
>>> hermite(0, x)
|
| 1022 |
+
1
|
| 1023 |
+
>>> hermite(1, x)
|
| 1024 |
+
2*x
|
| 1025 |
+
>>> hermite(2, x)
|
| 1026 |
+
4*x**2 - 2
|
| 1027 |
+
>>> hermite(n, x)
|
| 1028 |
+
hermite(n, x)
|
| 1029 |
+
>>> diff(hermite(n,x), x)
|
| 1030 |
+
2*n*hermite(n - 1, x)
|
| 1031 |
+
>>> hermite(n, -x)
|
| 1032 |
+
(-1)**n*hermite(n, x)
|
| 1033 |
+
|
| 1034 |
+
See Also
|
| 1035 |
+
========
|
| 1036 |
+
|
| 1037 |
+
jacobi, gegenbauer,
|
| 1038 |
+
chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 1039 |
+
legendre, assoc_legendre,
|
| 1040 |
+
hermite_prob,
|
| 1041 |
+
laguerre, assoc_laguerre,
|
| 1042 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 1043 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 1044 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 1045 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 1046 |
+
sympy.polys.orthopolys.hermite_poly
|
| 1047 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 1048 |
+
sympy.polys.orthopolys.legendre_poly
|
| 1049 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 1050 |
+
|
| 1051 |
+
References
|
| 1052 |
+
==========
|
| 1053 |
+
|
| 1054 |
+
.. [1] https://en.wikipedia.org/wiki/Hermite_polynomial
|
| 1055 |
+
.. [2] https://mathworld.wolfram.com/HermitePolynomial.html
|
| 1056 |
+
.. [3] https://functions.wolfram.com/Polynomials/HermiteH/
|
| 1057 |
+
|
| 1058 |
+
"""
|
| 1059 |
+
|
| 1060 |
+
_ortho_poly = staticmethod(hermite_poly)
|
| 1061 |
+
|
| 1062 |
+
@classmethod
|
| 1063 |
+
def eval(cls, n, x):
|
| 1064 |
+
if not n.is_Number:
|
| 1065 |
+
# Symbolic result H_n(x)
|
| 1066 |
+
# H_n(-x) ---> (-1)**n * H_n(x)
|
| 1067 |
+
if x.could_extract_minus_sign():
|
| 1068 |
+
return S.NegativeOne**n * hermite(n, -x)
|
| 1069 |
+
# We can evaluate for some special values of x
|
| 1070 |
+
if x.is_zero:
|
| 1071 |
+
return 2**n * sqrt(S.Pi) / gamma((S.One - n)/2)
|
| 1072 |
+
elif x is S.Infinity:
|
| 1073 |
+
return S.Infinity
|
| 1074 |
+
else:
|
| 1075 |
+
# n is a given fixed integer, evaluate into polynomial
|
| 1076 |
+
if n.is_negative:
|
| 1077 |
+
raise ValueError(
|
| 1078 |
+
"The index n must be nonnegative integer (got %r)" % n)
|
| 1079 |
+
else:
|
| 1080 |
+
return cls._eval_at_order(n, x)
|
| 1081 |
+
|
| 1082 |
+
def fdiff(self, argindex=2):
|
| 1083 |
+
if argindex == 1:
|
| 1084 |
+
# Diff wrt n
|
| 1085 |
+
raise ArgumentIndexError(self, argindex)
|
| 1086 |
+
elif argindex == 2:
|
| 1087 |
+
# Diff wrt x
|
| 1088 |
+
n, x = self.args
|
| 1089 |
+
return 2*n*hermite(n - 1, x)
|
| 1090 |
+
else:
|
| 1091 |
+
raise ArgumentIndexError(self, argindex)
|
| 1092 |
+
|
| 1093 |
+
def _eval_rewrite_as_Sum(self, n, x, **kwargs):
|
| 1094 |
+
from sympy.concrete.summations import Sum
|
| 1095 |
+
k = Dummy("k")
|
| 1096 |
+
kern = S.NegativeOne**k / (factorial(k)*factorial(n - 2*k)) * (2*x)**(n - 2*k)
|
| 1097 |
+
return factorial(n)*Sum(kern, (k, 0, floor(n/2)))
|
| 1098 |
+
|
| 1099 |
+
def _eval_rewrite_as_polynomial(self, n, x, **kwargs):
|
| 1100 |
+
# This function is just kept for backwards compatibility
|
| 1101 |
+
# but should not be used
|
| 1102 |
+
return self._eval_rewrite_as_Sum(n, x, **kwargs)
|
| 1103 |
+
|
| 1104 |
+
def _eval_rewrite_as_hermite_prob(self, n, x, **kwargs):
|
| 1105 |
+
return sqrt(2)**n * hermite_prob(n, x*sqrt(2))
|
| 1106 |
+
|
| 1107 |
+
|
| 1108 |
+
class hermite_prob(OrthogonalPolynomial):
|
| 1109 |
+
r"""
|
| 1110 |
+
``hermite_prob(n, x)`` gives the $n$th probabilist's Hermite polynomial
|
| 1111 |
+
in $x$, $He_n(x)$.
|
| 1112 |
+
|
| 1113 |
+
Explanation
|
| 1114 |
+
===========
|
| 1115 |
+
|
| 1116 |
+
The probabilist's Hermite polynomials are orthogonal on $(-\infty, \infty)$
|
| 1117 |
+
with respect to the weight $\exp\left(-\frac{x^2}{2}\right)$. They are monic
|
| 1118 |
+
polynomials, related to the plain Hermite polynomials (:py:class:`~.hermite`) by
|
| 1119 |
+
|
| 1120 |
+
.. math :: He_n(x) = 2^{-n/2} H_n(x/\sqrt{2})
|
| 1121 |
+
|
| 1122 |
+
Examples
|
| 1123 |
+
========
|
| 1124 |
+
|
| 1125 |
+
>>> from sympy import hermite_prob, diff, I
|
| 1126 |
+
>>> from sympy.abc import x, n
|
| 1127 |
+
>>> hermite_prob(1, x)
|
| 1128 |
+
x
|
| 1129 |
+
>>> hermite_prob(5, x)
|
| 1130 |
+
x**5 - 10*x**3 + 15*x
|
| 1131 |
+
>>> diff(hermite_prob(n,x), x)
|
| 1132 |
+
n*hermite_prob(n - 1, x)
|
| 1133 |
+
>>> hermite_prob(n, -x)
|
| 1134 |
+
(-1)**n*hermite_prob(n, x)
|
| 1135 |
+
|
| 1136 |
+
The sum of absolute values of coefficients of $He_n(x)$ is the number of
|
| 1137 |
+
matchings in the complete graph $K_n$ or telephone number, A000085 in the OEIS:
|
| 1138 |
+
|
| 1139 |
+
>>> [hermite_prob(n,I) / I**n for n in range(11)]
|
| 1140 |
+
[1, 1, 2, 4, 10, 26, 76, 232, 764, 2620, 9496]
|
| 1141 |
+
|
| 1142 |
+
See Also
|
| 1143 |
+
========
|
| 1144 |
+
|
| 1145 |
+
jacobi, gegenbauer,
|
| 1146 |
+
chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 1147 |
+
legendre, assoc_legendre,
|
| 1148 |
+
hermite,
|
| 1149 |
+
laguerre, assoc_laguerre,
|
| 1150 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 1151 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 1152 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 1153 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 1154 |
+
sympy.polys.orthopolys.hermite_poly
|
| 1155 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 1156 |
+
sympy.polys.orthopolys.legendre_poly
|
| 1157 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 1158 |
+
|
| 1159 |
+
References
|
| 1160 |
+
==========
|
| 1161 |
+
|
| 1162 |
+
.. [1] https://en.wikipedia.org/wiki/Hermite_polynomial
|
| 1163 |
+
.. [2] https://mathworld.wolfram.com/HermitePolynomial.html
|
| 1164 |
+
"""
|
| 1165 |
+
|
| 1166 |
+
_ortho_poly = staticmethod(hermite_prob_poly)
|
| 1167 |
+
|
| 1168 |
+
@classmethod
|
| 1169 |
+
def eval(cls, n, x):
|
| 1170 |
+
if not n.is_Number:
|
| 1171 |
+
if x.could_extract_minus_sign():
|
| 1172 |
+
return S.NegativeOne**n * hermite_prob(n, -x)
|
| 1173 |
+
if x.is_zero:
|
| 1174 |
+
return sqrt(S.Pi) / gamma((S.One-n) / 2)
|
| 1175 |
+
elif x is S.Infinity:
|
| 1176 |
+
return S.Infinity
|
| 1177 |
+
else:
|
| 1178 |
+
if n.is_negative:
|
| 1179 |
+
ValueError("n must be a nonnegative integer, not %r" % n)
|
| 1180 |
+
else:
|
| 1181 |
+
return cls._eval_at_order(n, x)
|
| 1182 |
+
|
| 1183 |
+
def fdiff(self, argindex=2):
|
| 1184 |
+
if argindex == 2:
|
| 1185 |
+
n, x = self.args
|
| 1186 |
+
return n*hermite_prob(n-1, x)
|
| 1187 |
+
else:
|
| 1188 |
+
raise ArgumentIndexError(self, argindex)
|
| 1189 |
+
|
| 1190 |
+
def _eval_rewrite_as_Sum(self, n, x, **kwargs):
|
| 1191 |
+
from sympy.concrete.summations import Sum
|
| 1192 |
+
k = Dummy("k")
|
| 1193 |
+
kern = (-S.Half)**k * x**(n-2*k) / (factorial(k) * factorial(n-2*k))
|
| 1194 |
+
return factorial(n)*Sum(kern, (k, 0, floor(n/2)))
|
| 1195 |
+
|
| 1196 |
+
def _eval_rewrite_as_polynomial(self, n, x, **kwargs):
|
| 1197 |
+
# This function is just kept for backwards compatibility
|
| 1198 |
+
# but should not be used
|
| 1199 |
+
return self._eval_rewrite_as_Sum(n, x, **kwargs)
|
| 1200 |
+
|
| 1201 |
+
def _eval_rewrite_as_hermite(self, n, x, **kwargs):
|
| 1202 |
+
return sqrt(2)**(-n) * hermite(n, x/sqrt(2))
|
| 1203 |
+
|
| 1204 |
+
|
| 1205 |
+
#----------------------------------------------------------------------------
|
| 1206 |
+
# Laguerre polynomials
|
| 1207 |
+
#
|
| 1208 |
+
|
| 1209 |
+
|
| 1210 |
+
class laguerre(OrthogonalPolynomial):
|
| 1211 |
+
r"""
|
| 1212 |
+
Returns the $n$th Laguerre polynomial in $x$, $L_n(x)$.
|
| 1213 |
+
|
| 1214 |
+
Examples
|
| 1215 |
+
========
|
| 1216 |
+
|
| 1217 |
+
>>> from sympy import laguerre, diff
|
| 1218 |
+
>>> from sympy.abc import x, n
|
| 1219 |
+
>>> laguerre(0, x)
|
| 1220 |
+
1
|
| 1221 |
+
>>> laguerre(1, x)
|
| 1222 |
+
1 - x
|
| 1223 |
+
>>> laguerre(2, x)
|
| 1224 |
+
x**2/2 - 2*x + 1
|
| 1225 |
+
>>> laguerre(3, x)
|
| 1226 |
+
-x**3/6 + 3*x**2/2 - 3*x + 1
|
| 1227 |
+
|
| 1228 |
+
>>> laguerre(n, x)
|
| 1229 |
+
laguerre(n, x)
|
| 1230 |
+
|
| 1231 |
+
>>> diff(laguerre(n, x), x)
|
| 1232 |
+
-assoc_laguerre(n - 1, 1, x)
|
| 1233 |
+
|
| 1234 |
+
Parameters
|
| 1235 |
+
==========
|
| 1236 |
+
|
| 1237 |
+
n : int
|
| 1238 |
+
Degree of Laguerre polynomial. Must be `n \ge 0`.
|
| 1239 |
+
|
| 1240 |
+
See Also
|
| 1241 |
+
========
|
| 1242 |
+
|
| 1243 |
+
jacobi, gegenbauer,
|
| 1244 |
+
chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 1245 |
+
legendre, assoc_legendre,
|
| 1246 |
+
hermite, hermite_prob,
|
| 1247 |
+
assoc_laguerre,
|
| 1248 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 1249 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 1250 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 1251 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 1252 |
+
sympy.polys.orthopolys.hermite_poly
|
| 1253 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 1254 |
+
sympy.polys.orthopolys.legendre_poly
|
| 1255 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 1256 |
+
|
| 1257 |
+
References
|
| 1258 |
+
==========
|
| 1259 |
+
|
| 1260 |
+
.. [1] https://en.wikipedia.org/wiki/Laguerre_polynomial
|
| 1261 |
+
.. [2] https://mathworld.wolfram.com/LaguerrePolynomial.html
|
| 1262 |
+
.. [3] https://functions.wolfram.com/Polynomials/LaguerreL/
|
| 1263 |
+
.. [4] https://functions.wolfram.com/Polynomials/LaguerreL3/
|
| 1264 |
+
|
| 1265 |
+
"""
|
| 1266 |
+
|
| 1267 |
+
_ortho_poly = staticmethod(laguerre_poly)
|
| 1268 |
+
|
| 1269 |
+
@classmethod
|
| 1270 |
+
def eval(cls, n, x):
|
| 1271 |
+
if n.is_integer is False:
|
| 1272 |
+
raise ValueError("Error: n should be an integer.")
|
| 1273 |
+
if not n.is_Number:
|
| 1274 |
+
# Symbolic result L_n(x)
|
| 1275 |
+
# L_{n}(-x) ---> exp(-x) * L_{-n-1}(x)
|
| 1276 |
+
# L_{-n}(x) ---> exp(x) * L_{n-1}(-x)
|
| 1277 |
+
if n.could_extract_minus_sign() and not(-n - 1).could_extract_minus_sign():
|
| 1278 |
+
return exp(x)*laguerre(-n - 1, -x)
|
| 1279 |
+
# We can evaluate for some special values of x
|
| 1280 |
+
if x.is_zero:
|
| 1281 |
+
return S.One
|
| 1282 |
+
elif x is S.NegativeInfinity:
|
| 1283 |
+
return S.Infinity
|
| 1284 |
+
elif x is S.Infinity:
|
| 1285 |
+
return S.NegativeOne**n * S.Infinity
|
| 1286 |
+
else:
|
| 1287 |
+
if n.is_negative:
|
| 1288 |
+
return exp(x)*laguerre(-n - 1, -x)
|
| 1289 |
+
else:
|
| 1290 |
+
return cls._eval_at_order(n, x)
|
| 1291 |
+
|
| 1292 |
+
def fdiff(self, argindex=2):
|
| 1293 |
+
if argindex == 1:
|
| 1294 |
+
# Diff wrt n
|
| 1295 |
+
raise ArgumentIndexError(self, argindex)
|
| 1296 |
+
elif argindex == 2:
|
| 1297 |
+
# Diff wrt x
|
| 1298 |
+
n, x = self.args
|
| 1299 |
+
return -assoc_laguerre(n - 1, 1, x)
|
| 1300 |
+
else:
|
| 1301 |
+
raise ArgumentIndexError(self, argindex)
|
| 1302 |
+
|
| 1303 |
+
def _eval_rewrite_as_Sum(self, n, x, **kwargs):
|
| 1304 |
+
from sympy.concrete.summations import Sum
|
| 1305 |
+
# Make sure n \in N_0
|
| 1306 |
+
if n.is_negative:
|
| 1307 |
+
return exp(x) * self._eval_rewrite_as_Sum(-n - 1, -x, **kwargs)
|
| 1308 |
+
if n.is_integer is False:
|
| 1309 |
+
raise ValueError("Error: n should be an integer.")
|
| 1310 |
+
k = Dummy("k")
|
| 1311 |
+
kern = RisingFactorial(-n, k) / factorial(k)**2 * x**k
|
| 1312 |
+
return Sum(kern, (k, 0, n))
|
| 1313 |
+
|
| 1314 |
+
def _eval_rewrite_as_polynomial(self, n, x, **kwargs):
|
| 1315 |
+
# This function is just kept for backwards compatibility
|
| 1316 |
+
# but should not be used
|
| 1317 |
+
return self._eval_rewrite_as_Sum(n, x, **kwargs)
|
| 1318 |
+
|
| 1319 |
+
|
| 1320 |
+
class assoc_laguerre(OrthogonalPolynomial):
|
| 1321 |
+
r"""
|
| 1322 |
+
Returns the $n$th generalized Laguerre polynomial in $x$, $L_n(x)$.
|
| 1323 |
+
|
| 1324 |
+
Examples
|
| 1325 |
+
========
|
| 1326 |
+
|
| 1327 |
+
>>> from sympy import assoc_laguerre, diff
|
| 1328 |
+
>>> from sympy.abc import x, n, a
|
| 1329 |
+
>>> assoc_laguerre(0, a, x)
|
| 1330 |
+
1
|
| 1331 |
+
>>> assoc_laguerre(1, a, x)
|
| 1332 |
+
a - x + 1
|
| 1333 |
+
>>> assoc_laguerre(2, a, x)
|
| 1334 |
+
a**2/2 + 3*a/2 + x**2/2 + x*(-a - 2) + 1
|
| 1335 |
+
>>> assoc_laguerre(3, a, x)
|
| 1336 |
+
a**3/6 + a**2 + 11*a/6 - x**3/6 + x**2*(a/2 + 3/2) +
|
| 1337 |
+
x*(-a**2/2 - 5*a/2 - 3) + 1
|
| 1338 |
+
|
| 1339 |
+
>>> assoc_laguerre(n, a, 0)
|
| 1340 |
+
binomial(a + n, a)
|
| 1341 |
+
|
| 1342 |
+
>>> assoc_laguerre(n, a, x)
|
| 1343 |
+
assoc_laguerre(n, a, x)
|
| 1344 |
+
|
| 1345 |
+
>>> assoc_laguerre(n, 0, x)
|
| 1346 |
+
laguerre(n, x)
|
| 1347 |
+
|
| 1348 |
+
>>> diff(assoc_laguerre(n, a, x), x)
|
| 1349 |
+
-assoc_laguerre(n - 1, a + 1, x)
|
| 1350 |
+
|
| 1351 |
+
>>> diff(assoc_laguerre(n, a, x), a)
|
| 1352 |
+
Sum(assoc_laguerre(_k, a, x)/(-a + n), (_k, 0, n - 1))
|
| 1353 |
+
|
| 1354 |
+
Parameters
|
| 1355 |
+
==========
|
| 1356 |
+
|
| 1357 |
+
n : int
|
| 1358 |
+
Degree of Laguerre polynomial. Must be `n \ge 0`.
|
| 1359 |
+
|
| 1360 |
+
alpha : Expr
|
| 1361 |
+
Arbitrary expression. For ``alpha=0`` regular Laguerre
|
| 1362 |
+
polynomials will be generated.
|
| 1363 |
+
|
| 1364 |
+
See Also
|
| 1365 |
+
========
|
| 1366 |
+
|
| 1367 |
+
jacobi, gegenbauer,
|
| 1368 |
+
chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root,
|
| 1369 |
+
legendre, assoc_legendre,
|
| 1370 |
+
hermite, hermite_prob,
|
| 1371 |
+
laguerre,
|
| 1372 |
+
sympy.polys.orthopolys.jacobi_poly
|
| 1373 |
+
sympy.polys.orthopolys.gegenbauer_poly
|
| 1374 |
+
sympy.polys.orthopolys.chebyshevt_poly
|
| 1375 |
+
sympy.polys.orthopolys.chebyshevu_poly
|
| 1376 |
+
sympy.polys.orthopolys.hermite_poly
|
| 1377 |
+
sympy.polys.orthopolys.hermite_prob_poly
|
| 1378 |
+
sympy.polys.orthopolys.legendre_poly
|
| 1379 |
+
sympy.polys.orthopolys.laguerre_poly
|
| 1380 |
+
|
| 1381 |
+
References
|
| 1382 |
+
==========
|
| 1383 |
+
|
| 1384 |
+
.. [1] https://en.wikipedia.org/wiki/Laguerre_polynomial#Generalized_Laguerre_polynomials
|
| 1385 |
+
.. [2] https://mathworld.wolfram.com/AssociatedLaguerrePolynomial.html
|
| 1386 |
+
.. [3] https://functions.wolfram.com/Polynomials/LaguerreL/
|
| 1387 |
+
.. [4] https://functions.wolfram.com/Polynomials/LaguerreL3/
|
| 1388 |
+
|
| 1389 |
+
"""
|
| 1390 |
+
|
| 1391 |
+
@classmethod
|
| 1392 |
+
def eval(cls, n, alpha, x):
|
| 1393 |
+
# L_{n}^{0}(x) ---> L_{n}(x)
|
| 1394 |
+
if alpha.is_zero:
|
| 1395 |
+
return laguerre(n, x)
|
| 1396 |
+
|
| 1397 |
+
if not n.is_Number:
|
| 1398 |
+
# We can evaluate for some special values of x
|
| 1399 |
+
if x.is_zero:
|
| 1400 |
+
return binomial(n + alpha, alpha)
|
| 1401 |
+
elif x is S.Infinity and n > 0:
|
| 1402 |
+
return S.NegativeOne**n * S.Infinity
|
| 1403 |
+
elif x is S.NegativeInfinity and n > 0:
|
| 1404 |
+
return S.Infinity
|
| 1405 |
+
else:
|
| 1406 |
+
# n is a given fixed integer, evaluate into polynomial
|
| 1407 |
+
if n.is_negative:
|
| 1408 |
+
raise ValueError(
|
| 1409 |
+
"The index n must be nonnegative integer (got %r)" % n)
|
| 1410 |
+
else:
|
| 1411 |
+
return laguerre_poly(n, x, alpha)
|
| 1412 |
+
|
| 1413 |
+
def fdiff(self, argindex=3):
|
| 1414 |
+
from sympy.concrete.summations import Sum
|
| 1415 |
+
if argindex == 1:
|
| 1416 |
+
# Diff wrt n
|
| 1417 |
+
raise ArgumentIndexError(self, argindex)
|
| 1418 |
+
elif argindex == 2:
|
| 1419 |
+
# Diff wrt alpha
|
| 1420 |
+
n, alpha, x = self.args
|
| 1421 |
+
k = Dummy("k")
|
| 1422 |
+
return Sum(assoc_laguerre(k, alpha, x) / (n - alpha), (k, 0, n - 1))
|
| 1423 |
+
elif argindex == 3:
|
| 1424 |
+
# Diff wrt x
|
| 1425 |
+
n, alpha, x = self.args
|
| 1426 |
+
return -assoc_laguerre(n - 1, alpha + 1, x)
|
| 1427 |
+
else:
|
| 1428 |
+
raise ArgumentIndexError(self, argindex)
|
| 1429 |
+
|
| 1430 |
+
def _eval_rewrite_as_Sum(self, n, alpha, x, **kwargs):
|
| 1431 |
+
from sympy.concrete.summations import Sum
|
| 1432 |
+
# Make sure n \in N_0
|
| 1433 |
+
if n.is_negative or n.is_integer is False:
|
| 1434 |
+
raise ValueError("Error: n should be a non-negative integer.")
|
| 1435 |
+
k = Dummy("k")
|
| 1436 |
+
kern = RisingFactorial(
|
| 1437 |
+
-n, k) / (gamma(k + alpha + 1) * factorial(k)) * x**k
|
| 1438 |
+
return gamma(n + alpha + 1) / factorial(n) * Sum(kern, (k, 0, n))
|
| 1439 |
+
|
| 1440 |
+
def _eval_rewrite_as_polynomial(self, n, alpha, x, **kwargs):
|
| 1441 |
+
# This function is just kept for backwards compatibility
|
| 1442 |
+
# but should not be used
|
| 1443 |
+
return self._eval_rewrite_as_Sum(n, alpha, x, **kwargs)
|
| 1444 |
+
|
| 1445 |
+
def _eval_conjugate(self):
|
| 1446 |
+
n, alpha, x = self.args
|
| 1447 |
+
return self.func(n, alpha.conjugate(), x.conjugate())
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/singularity_functions.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S, oo, diff
|
| 2 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 3 |
+
from sympy.core.logic import fuzzy_not
|
| 4 |
+
from sympy.core.relational import Eq
|
| 5 |
+
from sympy.functions.elementary.complexes import im
|
| 6 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 7 |
+
from sympy.functions.special.delta_functions import Heaviside
|
| 8 |
+
|
| 9 |
+
###############################################################################
|
| 10 |
+
############################# SINGULARITY FUNCTION ############################
|
| 11 |
+
###############################################################################
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class SingularityFunction(DefinedFunction):
|
| 15 |
+
r"""
|
| 16 |
+
Singularity functions are a class of discontinuous functions.
|
| 17 |
+
|
| 18 |
+
Explanation
|
| 19 |
+
===========
|
| 20 |
+
|
| 21 |
+
Singularity functions take a variable, an offset, and an exponent as
|
| 22 |
+
arguments. These functions are represented using Macaulay brackets as:
|
| 23 |
+
|
| 24 |
+
SingularityFunction(x, a, n) := <x - a>^n
|
| 25 |
+
|
| 26 |
+
The singularity function will automatically evaluate to
|
| 27 |
+
``Derivative(DiracDelta(x - a), x, -n - 1)`` if ``n < 0``
|
| 28 |
+
and ``(x - a)**n*Heaviside(x - a, 1)`` if ``n >= 0``.
|
| 29 |
+
|
| 30 |
+
Examples
|
| 31 |
+
========
|
| 32 |
+
|
| 33 |
+
>>> from sympy import SingularityFunction, diff, Piecewise, DiracDelta, Heaviside, Symbol
|
| 34 |
+
>>> from sympy.abc import x, a, n
|
| 35 |
+
>>> SingularityFunction(x, a, n)
|
| 36 |
+
SingularityFunction(x, a, n)
|
| 37 |
+
>>> y = Symbol('y', positive=True)
|
| 38 |
+
>>> n = Symbol('n', nonnegative=True)
|
| 39 |
+
>>> SingularityFunction(y, -10, n)
|
| 40 |
+
(y + 10)**n
|
| 41 |
+
>>> y = Symbol('y', negative=True)
|
| 42 |
+
>>> SingularityFunction(y, 10, n)
|
| 43 |
+
0
|
| 44 |
+
>>> SingularityFunction(x, 4, -1).subs(x, 4)
|
| 45 |
+
oo
|
| 46 |
+
>>> SingularityFunction(x, 10, -2).subs(x, 10)
|
| 47 |
+
oo
|
| 48 |
+
>>> SingularityFunction(4, 1, 5)
|
| 49 |
+
243
|
| 50 |
+
>>> diff(SingularityFunction(x, 1, 5) + SingularityFunction(x, 1, 4), x)
|
| 51 |
+
4*SingularityFunction(x, 1, 3) + 5*SingularityFunction(x, 1, 4)
|
| 52 |
+
>>> diff(SingularityFunction(x, 4, 0), x, 2)
|
| 53 |
+
SingularityFunction(x, 4, -2)
|
| 54 |
+
>>> SingularityFunction(x, 4, 5).rewrite(Piecewise)
|
| 55 |
+
Piecewise(((x - 4)**5, x >= 4), (0, True))
|
| 56 |
+
>>> expr = SingularityFunction(x, a, n)
|
| 57 |
+
>>> y = Symbol('y', positive=True)
|
| 58 |
+
>>> n = Symbol('n', nonnegative=True)
|
| 59 |
+
>>> expr.subs({x: y, a: -10, n: n})
|
| 60 |
+
(y + 10)**n
|
| 61 |
+
|
| 62 |
+
The methods ``rewrite(DiracDelta)``, ``rewrite(Heaviside)``, and
|
| 63 |
+
``rewrite('HeavisideDiracDelta')`` returns the same output. One can use any
|
| 64 |
+
of these methods according to their choice.
|
| 65 |
+
|
| 66 |
+
>>> expr = SingularityFunction(x, 4, 5) + SingularityFunction(x, -3, -1) - SingularityFunction(x, 0, -2)
|
| 67 |
+
>>> expr.rewrite(Heaviside)
|
| 68 |
+
(x - 4)**5*Heaviside(x - 4, 1) + DiracDelta(x + 3) - DiracDelta(x, 1)
|
| 69 |
+
>>> expr.rewrite(DiracDelta)
|
| 70 |
+
(x - 4)**5*Heaviside(x - 4, 1) + DiracDelta(x + 3) - DiracDelta(x, 1)
|
| 71 |
+
>>> expr.rewrite('HeavisideDiracDelta')
|
| 72 |
+
(x - 4)**5*Heaviside(x - 4, 1) + DiracDelta(x + 3) - DiracDelta(x, 1)
|
| 73 |
+
|
| 74 |
+
See Also
|
| 75 |
+
========
|
| 76 |
+
|
| 77 |
+
DiracDelta, Heaviside
|
| 78 |
+
|
| 79 |
+
References
|
| 80 |
+
==========
|
| 81 |
+
|
| 82 |
+
.. [1] https://en.wikipedia.org/wiki/Singularity_function
|
| 83 |
+
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
is_real = True
|
| 87 |
+
|
| 88 |
+
def fdiff(self, argindex=1):
|
| 89 |
+
"""
|
| 90 |
+
Returns the first derivative of a DiracDelta Function.
|
| 91 |
+
|
| 92 |
+
Explanation
|
| 93 |
+
===========
|
| 94 |
+
|
| 95 |
+
The difference between ``diff()`` and ``fdiff()`` is: ``diff()`` is the
|
| 96 |
+
user-level function and ``fdiff()`` is an object method. ``fdiff()`` is
|
| 97 |
+
a convenience method available in the ``Function`` class. It returns
|
| 98 |
+
the derivative of the function without considering the chain rule.
|
| 99 |
+
``diff(function, x)`` calls ``Function._eval_derivative`` which in turn
|
| 100 |
+
calls ``fdiff()`` internally to compute the derivative of the function.
|
| 101 |
+
|
| 102 |
+
"""
|
| 103 |
+
|
| 104 |
+
if argindex == 1:
|
| 105 |
+
x, a, n = self.args
|
| 106 |
+
if n in (S.Zero, S.NegativeOne, S(-2), S(-3)):
|
| 107 |
+
return self.func(x, a, n-1)
|
| 108 |
+
elif n.is_positive:
|
| 109 |
+
return n*self.func(x, a, n-1)
|
| 110 |
+
else:
|
| 111 |
+
raise ArgumentIndexError(self, argindex)
|
| 112 |
+
|
| 113 |
+
@classmethod
|
| 114 |
+
def eval(cls, variable, offset, exponent):
|
| 115 |
+
"""
|
| 116 |
+
Returns a simplified form or a value of Singularity Function depending
|
| 117 |
+
on the argument passed by the object.
|
| 118 |
+
|
| 119 |
+
Explanation
|
| 120 |
+
===========
|
| 121 |
+
|
| 122 |
+
The ``eval()`` method is automatically called when the
|
| 123 |
+
``SingularityFunction`` class is about to be instantiated and it
|
| 124 |
+
returns either some simplified instance or the unevaluated instance
|
| 125 |
+
depending on the argument passed. In other words, ``eval()`` method is
|
| 126 |
+
not needed to be called explicitly, it is being called and evaluated
|
| 127 |
+
once the object is called.
|
| 128 |
+
|
| 129 |
+
Examples
|
| 130 |
+
========
|
| 131 |
+
|
| 132 |
+
>>> from sympy import SingularityFunction, Symbol, nan
|
| 133 |
+
>>> from sympy.abc import x, a, n
|
| 134 |
+
>>> SingularityFunction(x, a, n)
|
| 135 |
+
SingularityFunction(x, a, n)
|
| 136 |
+
>>> SingularityFunction(5, 3, 2)
|
| 137 |
+
4
|
| 138 |
+
>>> SingularityFunction(x, a, nan)
|
| 139 |
+
nan
|
| 140 |
+
>>> SingularityFunction(x, 3, 0).subs(x, 3)
|
| 141 |
+
1
|
| 142 |
+
>>> SingularityFunction(4, 1, 5)
|
| 143 |
+
243
|
| 144 |
+
>>> x = Symbol('x', positive = True)
|
| 145 |
+
>>> a = Symbol('a', negative = True)
|
| 146 |
+
>>> n = Symbol('n', nonnegative = True)
|
| 147 |
+
>>> SingularityFunction(x, a, n)
|
| 148 |
+
(-a + x)**n
|
| 149 |
+
>>> x = Symbol('x', negative = True)
|
| 150 |
+
>>> a = Symbol('a', positive = True)
|
| 151 |
+
>>> SingularityFunction(x, a, n)
|
| 152 |
+
0
|
| 153 |
+
|
| 154 |
+
"""
|
| 155 |
+
|
| 156 |
+
x = variable
|
| 157 |
+
a = offset
|
| 158 |
+
n = exponent
|
| 159 |
+
shift = (x - a)
|
| 160 |
+
|
| 161 |
+
if fuzzy_not(im(shift).is_zero):
|
| 162 |
+
raise ValueError("Singularity Functions are defined only for Real Numbers.")
|
| 163 |
+
if fuzzy_not(im(n).is_zero):
|
| 164 |
+
raise ValueError("Singularity Functions are not defined for imaginary exponents.")
|
| 165 |
+
if shift is S.NaN or n is S.NaN:
|
| 166 |
+
return S.NaN
|
| 167 |
+
if (n + 4).is_negative:
|
| 168 |
+
raise ValueError("Singularity Functions are not defined for exponents less than -4.")
|
| 169 |
+
if shift.is_extended_negative:
|
| 170 |
+
return S.Zero
|
| 171 |
+
if n.is_nonnegative:
|
| 172 |
+
if shift.is_zero: # use literal 0 in case of Symbol('z', zero=True)
|
| 173 |
+
return S.Zero**n
|
| 174 |
+
if shift.is_extended_nonnegative:
|
| 175 |
+
return shift**n
|
| 176 |
+
if n in (S.NegativeOne, -2, -3, -4):
|
| 177 |
+
if shift.is_negative or shift.is_extended_positive:
|
| 178 |
+
return S.Zero
|
| 179 |
+
if shift.is_zero:
|
| 180 |
+
return oo
|
| 181 |
+
|
| 182 |
+
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
| 183 |
+
'''
|
| 184 |
+
Converts a Singularity Function expression into its Piecewise form.
|
| 185 |
+
|
| 186 |
+
'''
|
| 187 |
+
x, a, n = self.args
|
| 188 |
+
|
| 189 |
+
if n in (S.NegativeOne, S(-2), S(-3), S(-4)):
|
| 190 |
+
return Piecewise((oo, Eq(x - a, 0)), (0, True))
|
| 191 |
+
elif n.is_nonnegative:
|
| 192 |
+
return Piecewise(((x - a)**n, x - a >= 0), (0, True))
|
| 193 |
+
|
| 194 |
+
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
| 195 |
+
'''
|
| 196 |
+
Rewrites a Singularity Function expression using Heavisides and DiracDeltas.
|
| 197 |
+
|
| 198 |
+
'''
|
| 199 |
+
x, a, n = self.args
|
| 200 |
+
|
| 201 |
+
if n == -4:
|
| 202 |
+
return diff(Heaviside(x - a), x.free_symbols.pop(), 4)
|
| 203 |
+
if n == -3:
|
| 204 |
+
return diff(Heaviside(x - a), x.free_symbols.pop(), 3)
|
| 205 |
+
if n == -2:
|
| 206 |
+
return diff(Heaviside(x - a), x.free_symbols.pop(), 2)
|
| 207 |
+
if n == -1:
|
| 208 |
+
return diff(Heaviside(x - a), x.free_symbols.pop(), 1)
|
| 209 |
+
if n.is_nonnegative:
|
| 210 |
+
return (x - a)**n*Heaviside(x - a, 1)
|
| 211 |
+
|
| 212 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 213 |
+
z, a, n = self.args
|
| 214 |
+
shift = (z - a).subs(x, 0)
|
| 215 |
+
if n < 0:
|
| 216 |
+
return S.Zero
|
| 217 |
+
elif n.is_zero and shift.is_zero:
|
| 218 |
+
return S.Zero if cdir == -1 else S.One
|
| 219 |
+
elif shift.is_positive:
|
| 220 |
+
return shift**n
|
| 221 |
+
return S.Zero
|
| 222 |
+
|
| 223 |
+
def _eval_nseries(self, x, n, logx=None, cdir=0):
|
| 224 |
+
z, a, n = self.args
|
| 225 |
+
shift = (z - a).subs(x, 0)
|
| 226 |
+
if n < 0:
|
| 227 |
+
return S.Zero
|
| 228 |
+
elif n.is_zero and shift.is_zero:
|
| 229 |
+
return S.Zero if cdir == -1 else S.One
|
| 230 |
+
elif shift.is_positive:
|
| 231 |
+
return ((z - a)**n)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
| 232 |
+
return S.Zero
|
| 233 |
+
|
| 234 |
+
_eval_rewrite_as_DiracDelta = _eval_rewrite_as_Heaviside
|
| 235 |
+
_eval_rewrite_as_HeavisideDiracDelta = _eval_rewrite_as_Heaviside
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/spherical_harmonics.py
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.expr import Expr
|
| 2 |
+
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
| 3 |
+
from sympy.core.numbers import I, pi
|
| 4 |
+
from sympy.core.singleton import S
|
| 5 |
+
from sympy.core.symbol import Dummy
|
| 6 |
+
from sympy.functions import assoc_legendre
|
| 7 |
+
from sympy.functions.combinatorial.factorials import factorial
|
| 8 |
+
from sympy.functions.elementary.complexes import Abs, conjugate
|
| 9 |
+
from sympy.functions.elementary.exponential import exp
|
| 10 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 11 |
+
from sympy.functions.elementary.trigonometric import sin, cos, cot
|
| 12 |
+
|
| 13 |
+
_x = Dummy("x")
|
| 14 |
+
|
| 15 |
+
class Ynm(DefinedFunction):
|
| 16 |
+
r"""
|
| 17 |
+
Spherical harmonics defined as
|
| 18 |
+
|
| 19 |
+
.. math::
|
| 20 |
+
Y_n^m(\theta, \varphi) := \sqrt{\frac{(2n+1)(n-m)!}{4\pi(n+m)!}}
|
| 21 |
+
\exp(i m \varphi)
|
| 22 |
+
\mathrm{P}_n^m\left(\cos(\theta)\right)
|
| 23 |
+
|
| 24 |
+
Explanation
|
| 25 |
+
===========
|
| 26 |
+
|
| 27 |
+
``Ynm()`` gives the spherical harmonic function of order $n$ and $m$
|
| 28 |
+
in $\theta$ and $\varphi$, $Y_n^m(\theta, \varphi)$. The four
|
| 29 |
+
parameters are as follows: $n \geq 0$ an integer and $m$ an integer
|
| 30 |
+
such that $-n \leq m \leq n$ holds. The two angles are real-valued
|
| 31 |
+
with $\theta \in [0, \pi]$ and $\varphi \in [0, 2\pi]$.
|
| 32 |
+
|
| 33 |
+
Examples
|
| 34 |
+
========
|
| 35 |
+
|
| 36 |
+
>>> from sympy import Ynm, Symbol, simplify
|
| 37 |
+
>>> from sympy.abc import n,m
|
| 38 |
+
>>> theta = Symbol("theta")
|
| 39 |
+
>>> phi = Symbol("phi")
|
| 40 |
+
|
| 41 |
+
>>> Ynm(n, m, theta, phi)
|
| 42 |
+
Ynm(n, m, theta, phi)
|
| 43 |
+
|
| 44 |
+
Several symmetries are known, for the order:
|
| 45 |
+
|
| 46 |
+
>>> Ynm(n, -m, theta, phi)
|
| 47 |
+
(-1)**m*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
| 48 |
+
|
| 49 |
+
As well as for the angles:
|
| 50 |
+
|
| 51 |
+
>>> Ynm(n, m, -theta, phi)
|
| 52 |
+
Ynm(n, m, theta, phi)
|
| 53 |
+
|
| 54 |
+
>>> Ynm(n, m, theta, -phi)
|
| 55 |
+
exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
| 56 |
+
|
| 57 |
+
For specific integers $n$ and $m$ we can evaluate the harmonics
|
| 58 |
+
to more useful expressions:
|
| 59 |
+
|
| 60 |
+
>>> simplify(Ynm(0, 0, theta, phi).expand(func=True))
|
| 61 |
+
1/(2*sqrt(pi))
|
| 62 |
+
|
| 63 |
+
>>> simplify(Ynm(1, -1, theta, phi).expand(func=True))
|
| 64 |
+
sqrt(6)*exp(-I*phi)*sin(theta)/(4*sqrt(pi))
|
| 65 |
+
|
| 66 |
+
>>> simplify(Ynm(1, 0, theta, phi).expand(func=True))
|
| 67 |
+
sqrt(3)*cos(theta)/(2*sqrt(pi))
|
| 68 |
+
|
| 69 |
+
>>> simplify(Ynm(1, 1, theta, phi).expand(func=True))
|
| 70 |
+
-sqrt(6)*exp(I*phi)*sin(theta)/(4*sqrt(pi))
|
| 71 |
+
|
| 72 |
+
>>> simplify(Ynm(2, -2, theta, phi).expand(func=True))
|
| 73 |
+
sqrt(30)*exp(-2*I*phi)*sin(theta)**2/(8*sqrt(pi))
|
| 74 |
+
|
| 75 |
+
>>> simplify(Ynm(2, -1, theta, phi).expand(func=True))
|
| 76 |
+
sqrt(30)*exp(-I*phi)*sin(2*theta)/(8*sqrt(pi))
|
| 77 |
+
|
| 78 |
+
>>> simplify(Ynm(2, 0, theta, phi).expand(func=True))
|
| 79 |
+
sqrt(5)*(3*cos(theta)**2 - 1)/(4*sqrt(pi))
|
| 80 |
+
|
| 81 |
+
>>> simplify(Ynm(2, 1, theta, phi).expand(func=True))
|
| 82 |
+
-sqrt(30)*exp(I*phi)*sin(2*theta)/(8*sqrt(pi))
|
| 83 |
+
|
| 84 |
+
>>> simplify(Ynm(2, 2, theta, phi).expand(func=True))
|
| 85 |
+
sqrt(30)*exp(2*I*phi)*sin(theta)**2/(8*sqrt(pi))
|
| 86 |
+
|
| 87 |
+
We can differentiate the functions with respect
|
| 88 |
+
to both angles:
|
| 89 |
+
|
| 90 |
+
>>> from sympy import Ynm, Symbol, diff
|
| 91 |
+
>>> from sympy.abc import n,m
|
| 92 |
+
>>> theta = Symbol("theta")
|
| 93 |
+
>>> phi = Symbol("phi")
|
| 94 |
+
|
| 95 |
+
>>> diff(Ynm(n, m, theta, phi), theta)
|
| 96 |
+
m*cot(theta)*Ynm(n, m, theta, phi) + sqrt((-m + n)*(m + n + 1))*exp(-I*phi)*Ynm(n, m + 1, theta, phi)
|
| 97 |
+
|
| 98 |
+
>>> diff(Ynm(n, m, theta, phi), phi)
|
| 99 |
+
I*m*Ynm(n, m, theta, phi)
|
| 100 |
+
|
| 101 |
+
Further we can compute the complex conjugation:
|
| 102 |
+
|
| 103 |
+
>>> from sympy import Ynm, Symbol, conjugate
|
| 104 |
+
>>> from sympy.abc import n,m
|
| 105 |
+
>>> theta = Symbol("theta")
|
| 106 |
+
>>> phi = Symbol("phi")
|
| 107 |
+
|
| 108 |
+
>>> conjugate(Ynm(n, m, theta, phi))
|
| 109 |
+
(-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
| 110 |
+
|
| 111 |
+
To get back the well known expressions in spherical
|
| 112 |
+
coordinates, we use full expansion:
|
| 113 |
+
|
| 114 |
+
>>> from sympy import Ynm, Symbol, expand_func
|
| 115 |
+
>>> from sympy.abc import n,m
|
| 116 |
+
>>> theta = Symbol("theta")
|
| 117 |
+
>>> phi = Symbol("phi")
|
| 118 |
+
|
| 119 |
+
>>> expand_func(Ynm(n, m, theta, phi))
|
| 120 |
+
sqrt((2*n + 1)*factorial(-m + n)/factorial(m + n))*exp(I*m*phi)*assoc_legendre(n, m, cos(theta))/(2*sqrt(pi))
|
| 121 |
+
|
| 122 |
+
See Also
|
| 123 |
+
========
|
| 124 |
+
|
| 125 |
+
Ynm_c, Znm
|
| 126 |
+
|
| 127 |
+
References
|
| 128 |
+
==========
|
| 129 |
+
|
| 130 |
+
.. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
|
| 131 |
+
.. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
|
| 132 |
+
.. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
|
| 133 |
+
.. [4] https://dlmf.nist.gov/14.30
|
| 134 |
+
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
@classmethod
|
| 138 |
+
def eval(cls, n, m, theta, phi):
|
| 139 |
+
# Handle negative index m and arguments theta, phi
|
| 140 |
+
if m.could_extract_minus_sign():
|
| 141 |
+
m = -m
|
| 142 |
+
return S.NegativeOne**m * exp(-2*I*m*phi) * Ynm(n, m, theta, phi)
|
| 143 |
+
if theta.could_extract_minus_sign():
|
| 144 |
+
theta = -theta
|
| 145 |
+
return Ynm(n, m, theta, phi)
|
| 146 |
+
if phi.could_extract_minus_sign():
|
| 147 |
+
phi = -phi
|
| 148 |
+
return exp(-2*I*m*phi) * Ynm(n, m, theta, phi)
|
| 149 |
+
|
| 150 |
+
# TODO Add more simplififcation here
|
| 151 |
+
|
| 152 |
+
def _eval_expand_func(self, **hints):
|
| 153 |
+
n, m, theta, phi = self.args
|
| 154 |
+
rv = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
|
| 155 |
+
exp(I*m*phi) * assoc_legendre(n, m, cos(theta)))
|
| 156 |
+
# We can do this because of the range of theta
|
| 157 |
+
return rv.subs(sqrt(-cos(theta)**2 + 1), sin(theta))
|
| 158 |
+
|
| 159 |
+
def fdiff(self, argindex=4):
|
| 160 |
+
if argindex == 1:
|
| 161 |
+
# Diff wrt n
|
| 162 |
+
raise ArgumentIndexError(self, argindex)
|
| 163 |
+
elif argindex == 2:
|
| 164 |
+
# Diff wrt m
|
| 165 |
+
raise ArgumentIndexError(self, argindex)
|
| 166 |
+
elif argindex == 3:
|
| 167 |
+
# Diff wrt theta
|
| 168 |
+
n, m, theta, phi = self.args
|
| 169 |
+
return (m * cot(theta) * Ynm(n, m, theta, phi) +
|
| 170 |
+
sqrt((n - m)*(n + m + 1)) * exp(-I*phi) * Ynm(n, m + 1, theta, phi))
|
| 171 |
+
elif argindex == 4:
|
| 172 |
+
# Diff wrt phi
|
| 173 |
+
n, m, theta, phi = self.args
|
| 174 |
+
return I * m * Ynm(n, m, theta, phi)
|
| 175 |
+
else:
|
| 176 |
+
raise ArgumentIndexError(self, argindex)
|
| 177 |
+
|
| 178 |
+
def _eval_rewrite_as_polynomial(self, n, m, theta, phi, **kwargs):
|
| 179 |
+
# TODO: Make sure n \in N
|
| 180 |
+
# TODO: Assert |m| <= n ortherwise we should return 0
|
| 181 |
+
return self.expand(func=True)
|
| 182 |
+
|
| 183 |
+
def _eval_rewrite_as_sin(self, n, m, theta, phi, **kwargs):
|
| 184 |
+
return self.rewrite(cos)
|
| 185 |
+
|
| 186 |
+
def _eval_rewrite_as_cos(self, n, m, theta, phi, **kwargs):
|
| 187 |
+
# This method can be expensive due to extensive use of simplification!
|
| 188 |
+
from sympy.simplify import simplify, trigsimp
|
| 189 |
+
# TODO: Make sure n \in N
|
| 190 |
+
# TODO: Assert |m| <= n ortherwise we should return 0
|
| 191 |
+
term = simplify(self.expand(func=True))
|
| 192 |
+
# We can do this because of the range of theta
|
| 193 |
+
term = term.xreplace({Abs(sin(theta)):sin(theta)})
|
| 194 |
+
return simplify(trigsimp(term))
|
| 195 |
+
|
| 196 |
+
def _eval_conjugate(self):
|
| 197 |
+
# TODO: Make sure theta \in R and phi \in R
|
| 198 |
+
n, m, theta, phi = self.args
|
| 199 |
+
return S.NegativeOne**m * self.func(n, -m, theta, phi)
|
| 200 |
+
|
| 201 |
+
def as_real_imag(self, deep=True, **hints):
|
| 202 |
+
# TODO: Handle deep and hints
|
| 203 |
+
n, m, theta, phi = self.args
|
| 204 |
+
re = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
|
| 205 |
+
cos(m*phi) * assoc_legendre(n, m, cos(theta)))
|
| 206 |
+
im = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
|
| 207 |
+
sin(m*phi) * assoc_legendre(n, m, cos(theta)))
|
| 208 |
+
return (re, im)
|
| 209 |
+
|
| 210 |
+
def _eval_evalf(self, prec):
|
| 211 |
+
# Note: works without this function by just calling
|
| 212 |
+
# mpmath for Legendre polynomials. But using
|
| 213 |
+
# the dedicated function directly is cleaner.
|
| 214 |
+
from mpmath import mp, workprec
|
| 215 |
+
n = self.args[0]._to_mpmath(prec)
|
| 216 |
+
m = self.args[1]._to_mpmath(prec)
|
| 217 |
+
theta = self.args[2]._to_mpmath(prec)
|
| 218 |
+
phi = self.args[3]._to_mpmath(prec)
|
| 219 |
+
with workprec(prec):
|
| 220 |
+
res = mp.spherharm(n, m, theta, phi)
|
| 221 |
+
return Expr._from_mpmath(res, prec)
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
def Ynm_c(n, m, theta, phi):
|
| 225 |
+
r"""
|
| 226 |
+
Conjugate spherical harmonics defined as
|
| 227 |
+
|
| 228 |
+
.. math::
|
| 229 |
+
\overline{Y_n^m(\theta, \varphi)} := (-1)^m Y_n^{-m}(\theta, \varphi).
|
| 230 |
+
|
| 231 |
+
Examples
|
| 232 |
+
========
|
| 233 |
+
|
| 234 |
+
>>> from sympy import Ynm_c, Symbol, simplify
|
| 235 |
+
>>> from sympy.abc import n,m
|
| 236 |
+
>>> theta = Symbol("theta")
|
| 237 |
+
>>> phi = Symbol("phi")
|
| 238 |
+
>>> Ynm_c(n, m, theta, phi)
|
| 239 |
+
(-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
| 240 |
+
>>> Ynm_c(n, m, -theta, phi)
|
| 241 |
+
(-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
| 242 |
+
|
| 243 |
+
For specific integers $n$ and $m$ we can evaluate the harmonics
|
| 244 |
+
to more useful expressions:
|
| 245 |
+
|
| 246 |
+
>>> simplify(Ynm_c(0, 0, theta, phi).expand(func=True))
|
| 247 |
+
1/(2*sqrt(pi))
|
| 248 |
+
>>> simplify(Ynm_c(1, -1, theta, phi).expand(func=True))
|
| 249 |
+
sqrt(6)*exp(I*(-phi + 2*conjugate(phi)))*sin(theta)/(4*sqrt(pi))
|
| 250 |
+
|
| 251 |
+
See Also
|
| 252 |
+
========
|
| 253 |
+
|
| 254 |
+
Ynm, Znm
|
| 255 |
+
|
| 256 |
+
References
|
| 257 |
+
==========
|
| 258 |
+
|
| 259 |
+
.. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
|
| 260 |
+
.. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
|
| 261 |
+
.. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
|
| 262 |
+
|
| 263 |
+
"""
|
| 264 |
+
return conjugate(Ynm(n, m, theta, phi))
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
class Znm(DefinedFunction):
|
| 268 |
+
r"""
|
| 269 |
+
Real spherical harmonics defined as
|
| 270 |
+
|
| 271 |
+
.. math::
|
| 272 |
+
|
| 273 |
+
Z_n^m(\theta, \varphi) :=
|
| 274 |
+
\begin{cases}
|
| 275 |
+
\frac{Y_n^m(\theta, \varphi) + \overline{Y_n^m(\theta, \varphi)}}{\sqrt{2}} &\quad m > 0 \\
|
| 276 |
+
Y_n^m(\theta, \varphi) &\quad m = 0 \\
|
| 277 |
+
\frac{Y_n^m(\theta, \varphi) - \overline{Y_n^m(\theta, \varphi)}}{i \sqrt{2}} &\quad m < 0 \\
|
| 278 |
+
\end{cases}
|
| 279 |
+
|
| 280 |
+
which gives in simplified form
|
| 281 |
+
|
| 282 |
+
.. math::
|
| 283 |
+
|
| 284 |
+
Z_n^m(\theta, \varphi) =
|
| 285 |
+
\begin{cases}
|
| 286 |
+
\frac{Y_n^m(\theta, \varphi) + (-1)^m Y_n^{-m}(\theta, \varphi)}{\sqrt{2}} &\quad m > 0 \\
|
| 287 |
+
Y_n^m(\theta, \varphi) &\quad m = 0 \\
|
| 288 |
+
\frac{Y_n^m(\theta, \varphi) - (-1)^m Y_n^{-m}(\theta, \varphi)}{i \sqrt{2}} &\quad m < 0 \\
|
| 289 |
+
\end{cases}
|
| 290 |
+
|
| 291 |
+
Examples
|
| 292 |
+
========
|
| 293 |
+
|
| 294 |
+
>>> from sympy import Znm, Symbol, simplify
|
| 295 |
+
>>> from sympy.abc import n, m
|
| 296 |
+
>>> theta = Symbol("theta")
|
| 297 |
+
>>> phi = Symbol("phi")
|
| 298 |
+
>>> Znm(n, m, theta, phi)
|
| 299 |
+
Znm(n, m, theta, phi)
|
| 300 |
+
|
| 301 |
+
For specific integers n and m we can evaluate the harmonics
|
| 302 |
+
to more useful expressions:
|
| 303 |
+
|
| 304 |
+
>>> simplify(Znm(0, 0, theta, phi).expand(func=True))
|
| 305 |
+
1/(2*sqrt(pi))
|
| 306 |
+
>>> simplify(Znm(1, 1, theta, phi).expand(func=True))
|
| 307 |
+
-sqrt(3)*sin(theta)*cos(phi)/(2*sqrt(pi))
|
| 308 |
+
>>> simplify(Znm(2, 1, theta, phi).expand(func=True))
|
| 309 |
+
-sqrt(15)*sin(2*theta)*cos(phi)/(4*sqrt(pi))
|
| 310 |
+
|
| 311 |
+
See Also
|
| 312 |
+
========
|
| 313 |
+
|
| 314 |
+
Ynm, Ynm_c
|
| 315 |
+
|
| 316 |
+
References
|
| 317 |
+
==========
|
| 318 |
+
|
| 319 |
+
.. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
|
| 320 |
+
.. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
|
| 321 |
+
.. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
|
| 322 |
+
|
| 323 |
+
"""
|
| 324 |
+
|
| 325 |
+
@classmethod
|
| 326 |
+
def eval(cls, n, m, theta, phi):
|
| 327 |
+
if m.is_positive:
|
| 328 |
+
zz = (Ynm(n, m, theta, phi) + Ynm_c(n, m, theta, phi)) / sqrt(2)
|
| 329 |
+
return zz
|
| 330 |
+
elif m.is_zero:
|
| 331 |
+
return Ynm(n, m, theta, phi)
|
| 332 |
+
elif m.is_negative:
|
| 333 |
+
zz = (Ynm(n, m, theta, phi) - Ynm_c(n, m, theta, phi)) / (sqrt(2)*I)
|
| 334 |
+
return zz
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/tensor_functions.py
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from math import prod
|
| 2 |
+
|
| 3 |
+
from sympy.core import S, Integer
|
| 4 |
+
from sympy.core.function import DefinedFunction
|
| 5 |
+
from sympy.core.logic import fuzzy_not
|
| 6 |
+
from sympy.core.relational import Ne
|
| 7 |
+
from sympy.core.sorting import default_sort_key
|
| 8 |
+
from sympy.external.gmpy import SYMPY_INTS
|
| 9 |
+
from sympy.functions.combinatorial.factorials import factorial
|
| 10 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 11 |
+
from sympy.utilities.iterables import has_dups
|
| 12 |
+
|
| 13 |
+
###############################################################################
|
| 14 |
+
###################### Kronecker Delta, Levi-Civita etc. ######################
|
| 15 |
+
###############################################################################
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def Eijk(*args, **kwargs):
|
| 19 |
+
"""
|
| 20 |
+
Represent the Levi-Civita symbol.
|
| 21 |
+
|
| 22 |
+
This is a compatibility wrapper to ``LeviCivita()``.
|
| 23 |
+
|
| 24 |
+
See Also
|
| 25 |
+
========
|
| 26 |
+
|
| 27 |
+
LeviCivita
|
| 28 |
+
|
| 29 |
+
"""
|
| 30 |
+
return LeviCivita(*args, **kwargs)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def eval_levicivita(*args):
|
| 34 |
+
"""Evaluate Levi-Civita symbol."""
|
| 35 |
+
n = len(args)
|
| 36 |
+
return prod(
|
| 37 |
+
prod(args[j] - args[i] for j in range(i + 1, n))
|
| 38 |
+
/ factorial(i) for i in range(n))
|
| 39 |
+
# converting factorial(i) to int is slightly faster
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class LeviCivita(DefinedFunction):
|
| 43 |
+
"""
|
| 44 |
+
Represent the Levi-Civita symbol.
|
| 45 |
+
|
| 46 |
+
Explanation
|
| 47 |
+
===========
|
| 48 |
+
|
| 49 |
+
For even permutations of indices it returns 1, for odd permutations -1, and
|
| 50 |
+
for everything else (a repeated index) it returns 0.
|
| 51 |
+
|
| 52 |
+
Thus it represents an alternating pseudotensor.
|
| 53 |
+
|
| 54 |
+
Examples
|
| 55 |
+
========
|
| 56 |
+
|
| 57 |
+
>>> from sympy import LeviCivita
|
| 58 |
+
>>> from sympy.abc import i, j, k
|
| 59 |
+
>>> LeviCivita(1, 2, 3)
|
| 60 |
+
1
|
| 61 |
+
>>> LeviCivita(1, 3, 2)
|
| 62 |
+
-1
|
| 63 |
+
>>> LeviCivita(1, 2, 2)
|
| 64 |
+
0
|
| 65 |
+
>>> LeviCivita(i, j, k)
|
| 66 |
+
LeviCivita(i, j, k)
|
| 67 |
+
>>> LeviCivita(i, j, i)
|
| 68 |
+
0
|
| 69 |
+
|
| 70 |
+
See Also
|
| 71 |
+
========
|
| 72 |
+
|
| 73 |
+
Eijk
|
| 74 |
+
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
is_integer = True
|
| 78 |
+
|
| 79 |
+
@classmethod
|
| 80 |
+
def eval(cls, *args):
|
| 81 |
+
if all(isinstance(a, (SYMPY_INTS, Integer)) for a in args):
|
| 82 |
+
return eval_levicivita(*args)
|
| 83 |
+
if has_dups(args):
|
| 84 |
+
return S.Zero
|
| 85 |
+
|
| 86 |
+
def doit(self, **hints):
|
| 87 |
+
return eval_levicivita(*self.args)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class KroneckerDelta(DefinedFunction):
|
| 91 |
+
"""
|
| 92 |
+
The discrete, or Kronecker, delta function.
|
| 93 |
+
|
| 94 |
+
Explanation
|
| 95 |
+
===========
|
| 96 |
+
|
| 97 |
+
A function that takes in two integers $i$ and $j$. It returns $0$ if $i$
|
| 98 |
+
and $j$ are not equal, or it returns $1$ if $i$ and $j$ are equal.
|
| 99 |
+
|
| 100 |
+
Examples
|
| 101 |
+
========
|
| 102 |
+
|
| 103 |
+
An example with integer indices:
|
| 104 |
+
|
| 105 |
+
>>> from sympy import KroneckerDelta
|
| 106 |
+
>>> KroneckerDelta(1, 2)
|
| 107 |
+
0
|
| 108 |
+
>>> KroneckerDelta(3, 3)
|
| 109 |
+
1
|
| 110 |
+
|
| 111 |
+
Symbolic indices:
|
| 112 |
+
|
| 113 |
+
>>> from sympy.abc import i, j, k
|
| 114 |
+
>>> KroneckerDelta(i, j)
|
| 115 |
+
KroneckerDelta(i, j)
|
| 116 |
+
>>> KroneckerDelta(i, i)
|
| 117 |
+
1
|
| 118 |
+
>>> KroneckerDelta(i, i + 1)
|
| 119 |
+
0
|
| 120 |
+
>>> KroneckerDelta(i, i + 1 + k)
|
| 121 |
+
KroneckerDelta(i, i + k + 1)
|
| 122 |
+
|
| 123 |
+
Parameters
|
| 124 |
+
==========
|
| 125 |
+
|
| 126 |
+
i : Number, Symbol
|
| 127 |
+
The first index of the delta function.
|
| 128 |
+
j : Number, Symbol
|
| 129 |
+
The second index of the delta function.
|
| 130 |
+
|
| 131 |
+
See Also
|
| 132 |
+
========
|
| 133 |
+
|
| 134 |
+
eval
|
| 135 |
+
DiracDelta
|
| 136 |
+
|
| 137 |
+
References
|
| 138 |
+
==========
|
| 139 |
+
|
| 140 |
+
.. [1] https://en.wikipedia.org/wiki/Kronecker_delta
|
| 141 |
+
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
is_integer = True
|
| 145 |
+
|
| 146 |
+
@classmethod
|
| 147 |
+
def eval(cls, i, j, delta_range=None):
|
| 148 |
+
"""
|
| 149 |
+
Evaluates the discrete delta function.
|
| 150 |
+
|
| 151 |
+
Examples
|
| 152 |
+
========
|
| 153 |
+
|
| 154 |
+
>>> from sympy import KroneckerDelta
|
| 155 |
+
>>> from sympy.abc import i, j, k
|
| 156 |
+
|
| 157 |
+
>>> KroneckerDelta(i, j)
|
| 158 |
+
KroneckerDelta(i, j)
|
| 159 |
+
>>> KroneckerDelta(i, i)
|
| 160 |
+
1
|
| 161 |
+
>>> KroneckerDelta(i, i + 1)
|
| 162 |
+
0
|
| 163 |
+
>>> KroneckerDelta(i, i + 1 + k)
|
| 164 |
+
KroneckerDelta(i, i + k + 1)
|
| 165 |
+
|
| 166 |
+
# indirect doctest
|
| 167 |
+
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
if delta_range is not None:
|
| 171 |
+
dinf, dsup = delta_range
|
| 172 |
+
if (dinf - i > 0) == True:
|
| 173 |
+
return S.Zero
|
| 174 |
+
if (dinf - j > 0) == True:
|
| 175 |
+
return S.Zero
|
| 176 |
+
if (dsup - i < 0) == True:
|
| 177 |
+
return S.Zero
|
| 178 |
+
if (dsup - j < 0) == True:
|
| 179 |
+
return S.Zero
|
| 180 |
+
|
| 181 |
+
diff = i - j
|
| 182 |
+
if diff.is_zero:
|
| 183 |
+
return S.One
|
| 184 |
+
elif fuzzy_not(diff.is_zero):
|
| 185 |
+
return S.Zero
|
| 186 |
+
|
| 187 |
+
if i.assumptions0.get("below_fermi") and \
|
| 188 |
+
j.assumptions0.get("above_fermi"):
|
| 189 |
+
return S.Zero
|
| 190 |
+
if j.assumptions0.get("below_fermi") and \
|
| 191 |
+
i.assumptions0.get("above_fermi"):
|
| 192 |
+
return S.Zero
|
| 193 |
+
# to make KroneckerDelta canonical
|
| 194 |
+
# following lines will check if inputs are in order
|
| 195 |
+
# if not, will return KroneckerDelta with correct order
|
| 196 |
+
if default_sort_key(j) < default_sort_key(i):
|
| 197 |
+
if delta_range:
|
| 198 |
+
return cls(j, i, delta_range)
|
| 199 |
+
else:
|
| 200 |
+
return cls(j, i)
|
| 201 |
+
|
| 202 |
+
@property
|
| 203 |
+
def delta_range(self):
|
| 204 |
+
if len(self.args) > 2:
|
| 205 |
+
return self.args[2]
|
| 206 |
+
|
| 207 |
+
def _eval_power(self, expt):
|
| 208 |
+
if expt.is_positive:
|
| 209 |
+
return self
|
| 210 |
+
if expt.is_negative and expt is not S.NegativeOne:
|
| 211 |
+
return 1/self
|
| 212 |
+
|
| 213 |
+
@property
|
| 214 |
+
def is_above_fermi(self):
|
| 215 |
+
"""
|
| 216 |
+
True if Delta can be non-zero above fermi.
|
| 217 |
+
|
| 218 |
+
Examples
|
| 219 |
+
========
|
| 220 |
+
|
| 221 |
+
>>> from sympy import KroneckerDelta, Symbol
|
| 222 |
+
>>> a = Symbol('a', above_fermi=True)
|
| 223 |
+
>>> i = Symbol('i', below_fermi=True)
|
| 224 |
+
>>> p = Symbol('p')
|
| 225 |
+
>>> q = Symbol('q')
|
| 226 |
+
>>> KroneckerDelta(p, a).is_above_fermi
|
| 227 |
+
True
|
| 228 |
+
>>> KroneckerDelta(p, i).is_above_fermi
|
| 229 |
+
False
|
| 230 |
+
>>> KroneckerDelta(p, q).is_above_fermi
|
| 231 |
+
True
|
| 232 |
+
|
| 233 |
+
See Also
|
| 234 |
+
========
|
| 235 |
+
|
| 236 |
+
is_below_fermi, is_only_below_fermi, is_only_above_fermi
|
| 237 |
+
|
| 238 |
+
"""
|
| 239 |
+
if self.args[0].assumptions0.get("below_fermi"):
|
| 240 |
+
return False
|
| 241 |
+
if self.args[1].assumptions0.get("below_fermi"):
|
| 242 |
+
return False
|
| 243 |
+
return True
|
| 244 |
+
|
| 245 |
+
@property
|
| 246 |
+
def is_below_fermi(self):
|
| 247 |
+
"""
|
| 248 |
+
True if Delta can be non-zero below fermi.
|
| 249 |
+
|
| 250 |
+
Examples
|
| 251 |
+
========
|
| 252 |
+
|
| 253 |
+
>>> from sympy import KroneckerDelta, Symbol
|
| 254 |
+
>>> a = Symbol('a', above_fermi=True)
|
| 255 |
+
>>> i = Symbol('i', below_fermi=True)
|
| 256 |
+
>>> p = Symbol('p')
|
| 257 |
+
>>> q = Symbol('q')
|
| 258 |
+
>>> KroneckerDelta(p, a).is_below_fermi
|
| 259 |
+
False
|
| 260 |
+
>>> KroneckerDelta(p, i).is_below_fermi
|
| 261 |
+
True
|
| 262 |
+
>>> KroneckerDelta(p, q).is_below_fermi
|
| 263 |
+
True
|
| 264 |
+
|
| 265 |
+
See Also
|
| 266 |
+
========
|
| 267 |
+
|
| 268 |
+
is_above_fermi, is_only_above_fermi, is_only_below_fermi
|
| 269 |
+
|
| 270 |
+
"""
|
| 271 |
+
if self.args[0].assumptions0.get("above_fermi"):
|
| 272 |
+
return False
|
| 273 |
+
if self.args[1].assumptions0.get("above_fermi"):
|
| 274 |
+
return False
|
| 275 |
+
return True
|
| 276 |
+
|
| 277 |
+
@property
|
| 278 |
+
def is_only_above_fermi(self):
|
| 279 |
+
"""
|
| 280 |
+
True if Delta is restricted to above fermi.
|
| 281 |
+
|
| 282 |
+
Examples
|
| 283 |
+
========
|
| 284 |
+
|
| 285 |
+
>>> from sympy import KroneckerDelta, Symbol
|
| 286 |
+
>>> a = Symbol('a', above_fermi=True)
|
| 287 |
+
>>> i = Symbol('i', below_fermi=True)
|
| 288 |
+
>>> p = Symbol('p')
|
| 289 |
+
>>> q = Symbol('q')
|
| 290 |
+
>>> KroneckerDelta(p, a).is_only_above_fermi
|
| 291 |
+
True
|
| 292 |
+
>>> KroneckerDelta(p, q).is_only_above_fermi
|
| 293 |
+
False
|
| 294 |
+
>>> KroneckerDelta(p, i).is_only_above_fermi
|
| 295 |
+
False
|
| 296 |
+
|
| 297 |
+
See Also
|
| 298 |
+
========
|
| 299 |
+
|
| 300 |
+
is_above_fermi, is_below_fermi, is_only_below_fermi
|
| 301 |
+
|
| 302 |
+
"""
|
| 303 |
+
return ( self.args[0].assumptions0.get("above_fermi")
|
| 304 |
+
or
|
| 305 |
+
self.args[1].assumptions0.get("above_fermi")
|
| 306 |
+
) or False
|
| 307 |
+
|
| 308 |
+
@property
|
| 309 |
+
def is_only_below_fermi(self):
|
| 310 |
+
"""
|
| 311 |
+
True if Delta is restricted to below fermi.
|
| 312 |
+
|
| 313 |
+
Examples
|
| 314 |
+
========
|
| 315 |
+
|
| 316 |
+
>>> from sympy import KroneckerDelta, Symbol
|
| 317 |
+
>>> a = Symbol('a', above_fermi=True)
|
| 318 |
+
>>> i = Symbol('i', below_fermi=True)
|
| 319 |
+
>>> p = Symbol('p')
|
| 320 |
+
>>> q = Symbol('q')
|
| 321 |
+
>>> KroneckerDelta(p, i).is_only_below_fermi
|
| 322 |
+
True
|
| 323 |
+
>>> KroneckerDelta(p, q).is_only_below_fermi
|
| 324 |
+
False
|
| 325 |
+
>>> KroneckerDelta(p, a).is_only_below_fermi
|
| 326 |
+
False
|
| 327 |
+
|
| 328 |
+
See Also
|
| 329 |
+
========
|
| 330 |
+
|
| 331 |
+
is_above_fermi, is_below_fermi, is_only_above_fermi
|
| 332 |
+
|
| 333 |
+
"""
|
| 334 |
+
return ( self.args[0].assumptions0.get("below_fermi")
|
| 335 |
+
or
|
| 336 |
+
self.args[1].assumptions0.get("below_fermi")
|
| 337 |
+
) or False
|
| 338 |
+
|
| 339 |
+
@property
|
| 340 |
+
def indices_contain_equal_information(self):
|
| 341 |
+
"""
|
| 342 |
+
Returns True if indices are either both above or below fermi.
|
| 343 |
+
|
| 344 |
+
Examples
|
| 345 |
+
========
|
| 346 |
+
|
| 347 |
+
>>> from sympy import KroneckerDelta, Symbol
|
| 348 |
+
>>> a = Symbol('a', above_fermi=True)
|
| 349 |
+
>>> i = Symbol('i', below_fermi=True)
|
| 350 |
+
>>> p = Symbol('p')
|
| 351 |
+
>>> q = Symbol('q')
|
| 352 |
+
>>> KroneckerDelta(p, q).indices_contain_equal_information
|
| 353 |
+
True
|
| 354 |
+
>>> KroneckerDelta(p, q+1).indices_contain_equal_information
|
| 355 |
+
True
|
| 356 |
+
>>> KroneckerDelta(i, p).indices_contain_equal_information
|
| 357 |
+
False
|
| 358 |
+
|
| 359 |
+
"""
|
| 360 |
+
if (self.args[0].assumptions0.get("below_fermi") and
|
| 361 |
+
self.args[1].assumptions0.get("below_fermi")):
|
| 362 |
+
return True
|
| 363 |
+
if (self.args[0].assumptions0.get("above_fermi")
|
| 364 |
+
and self.args[1].assumptions0.get("above_fermi")):
|
| 365 |
+
return True
|
| 366 |
+
|
| 367 |
+
# if both indices are general we are True, else false
|
| 368 |
+
return self.is_below_fermi and self.is_above_fermi
|
| 369 |
+
|
| 370 |
+
@property
|
| 371 |
+
def preferred_index(self):
|
| 372 |
+
"""
|
| 373 |
+
Returns the index which is preferred to keep in the final expression.
|
| 374 |
+
|
| 375 |
+
Explanation
|
| 376 |
+
===========
|
| 377 |
+
|
| 378 |
+
The preferred index is the index with more information regarding fermi
|
| 379 |
+
level. If indices contain the same information, 'a' is preferred before
|
| 380 |
+
'b'.
|
| 381 |
+
|
| 382 |
+
Examples
|
| 383 |
+
========
|
| 384 |
+
|
| 385 |
+
>>> from sympy import KroneckerDelta, Symbol
|
| 386 |
+
>>> a = Symbol('a', above_fermi=True)
|
| 387 |
+
>>> i = Symbol('i', below_fermi=True)
|
| 388 |
+
>>> j = Symbol('j', below_fermi=True)
|
| 389 |
+
>>> p = Symbol('p')
|
| 390 |
+
>>> KroneckerDelta(p, i).preferred_index
|
| 391 |
+
i
|
| 392 |
+
>>> KroneckerDelta(p, a).preferred_index
|
| 393 |
+
a
|
| 394 |
+
>>> KroneckerDelta(i, j).preferred_index
|
| 395 |
+
i
|
| 396 |
+
|
| 397 |
+
See Also
|
| 398 |
+
========
|
| 399 |
+
|
| 400 |
+
killable_index
|
| 401 |
+
|
| 402 |
+
"""
|
| 403 |
+
if self._get_preferred_index():
|
| 404 |
+
return self.args[1]
|
| 405 |
+
else:
|
| 406 |
+
return self.args[0]
|
| 407 |
+
|
| 408 |
+
@property
|
| 409 |
+
def killable_index(self):
|
| 410 |
+
"""
|
| 411 |
+
Returns the index which is preferred to substitute in the final
|
| 412 |
+
expression.
|
| 413 |
+
|
| 414 |
+
Explanation
|
| 415 |
+
===========
|
| 416 |
+
|
| 417 |
+
The index to substitute is the index with less information regarding
|
| 418 |
+
fermi level. If indices contain the same information, 'a' is preferred
|
| 419 |
+
before 'b'.
|
| 420 |
+
|
| 421 |
+
Examples
|
| 422 |
+
========
|
| 423 |
+
|
| 424 |
+
>>> from sympy import KroneckerDelta, Symbol
|
| 425 |
+
>>> a = Symbol('a', above_fermi=True)
|
| 426 |
+
>>> i = Symbol('i', below_fermi=True)
|
| 427 |
+
>>> j = Symbol('j', below_fermi=True)
|
| 428 |
+
>>> p = Symbol('p')
|
| 429 |
+
>>> KroneckerDelta(p, i).killable_index
|
| 430 |
+
p
|
| 431 |
+
>>> KroneckerDelta(p, a).killable_index
|
| 432 |
+
p
|
| 433 |
+
>>> KroneckerDelta(i, j).killable_index
|
| 434 |
+
j
|
| 435 |
+
|
| 436 |
+
See Also
|
| 437 |
+
========
|
| 438 |
+
|
| 439 |
+
preferred_index
|
| 440 |
+
|
| 441 |
+
"""
|
| 442 |
+
if self._get_preferred_index():
|
| 443 |
+
return self.args[0]
|
| 444 |
+
else:
|
| 445 |
+
return self.args[1]
|
| 446 |
+
|
| 447 |
+
def _get_preferred_index(self):
|
| 448 |
+
"""
|
| 449 |
+
Returns the index which is preferred to keep in the final expression.
|
| 450 |
+
|
| 451 |
+
The preferred index is the index with more information regarding fermi
|
| 452 |
+
level. If indices contain the same information, index 0 is returned.
|
| 453 |
+
|
| 454 |
+
"""
|
| 455 |
+
if not self.is_above_fermi:
|
| 456 |
+
if self.args[0].assumptions0.get("below_fermi"):
|
| 457 |
+
return 0
|
| 458 |
+
else:
|
| 459 |
+
return 1
|
| 460 |
+
elif not self.is_below_fermi:
|
| 461 |
+
if self.args[0].assumptions0.get("above_fermi"):
|
| 462 |
+
return 0
|
| 463 |
+
else:
|
| 464 |
+
return 1
|
| 465 |
+
else:
|
| 466 |
+
return 0
|
| 467 |
+
|
| 468 |
+
@property
|
| 469 |
+
def indices(self):
|
| 470 |
+
return self.args[0:2]
|
| 471 |
+
|
| 472 |
+
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
| 473 |
+
i, j = args
|
| 474 |
+
return Piecewise((0, Ne(i, j)), (1, True))
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/functions/special/zeta_functions.py
ADDED
|
@@ -0,0 +1,786 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Riemann zeta and related function. """
|
| 2 |
+
|
| 3 |
+
from sympy.core.add import Add
|
| 4 |
+
from sympy.core.cache import cacheit
|
| 5 |
+
from sympy.core.function import ArgumentIndexError, expand_mul, DefinedFunction
|
| 6 |
+
from sympy.core.logic import fuzzy_not
|
| 7 |
+
from sympy.core.numbers import pi, I, Integer
|
| 8 |
+
from sympy.core.relational import Eq
|
| 9 |
+
from sympy.core.singleton import S
|
| 10 |
+
from sympy.core.symbol import Dummy
|
| 11 |
+
from sympy.core.sympify import sympify
|
| 12 |
+
from sympy.functions.combinatorial.numbers import bernoulli, factorial, genocchi, harmonic
|
| 13 |
+
from sympy.functions.elementary.complexes import re, unpolarify, Abs, polar_lift
|
| 14 |
+
from sympy.functions.elementary.exponential import log, exp_polar, exp
|
| 15 |
+
from sympy.functions.elementary.integers import ceiling, floor
|
| 16 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 17 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 18 |
+
from sympy.polys.polytools import Poly
|
| 19 |
+
|
| 20 |
+
###############################################################################
|
| 21 |
+
###################### LERCH TRANSCENDENT #####################################
|
| 22 |
+
###############################################################################
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class lerchphi(DefinedFunction):
|
| 26 |
+
r"""
|
| 27 |
+
Lerch transcendent (Lerch phi function).
|
| 28 |
+
|
| 29 |
+
Explanation
|
| 30 |
+
===========
|
| 31 |
+
|
| 32 |
+
For $\operatorname{Re}(a) > 0$, $|z| < 1$ and $s \in \mathbb{C}$, the
|
| 33 |
+
Lerch transcendent is defined as
|
| 34 |
+
|
| 35 |
+
.. math :: \Phi(z, s, a) = \sum_{n=0}^\infty \frac{z^n}{(n + a)^s},
|
| 36 |
+
|
| 37 |
+
where the standard branch of the argument is used for $n + a$,
|
| 38 |
+
and by analytic continuation for other values of the parameters.
|
| 39 |
+
|
| 40 |
+
A commonly used related function is the Lerch zeta function, defined by
|
| 41 |
+
|
| 42 |
+
.. math:: L(q, s, a) = \Phi(e^{2\pi i q}, s, a).
|
| 43 |
+
|
| 44 |
+
**Analytic Continuation and Branching Behavior**
|
| 45 |
+
|
| 46 |
+
It can be shown that
|
| 47 |
+
|
| 48 |
+
.. math:: \Phi(z, s, a) = z\Phi(z, s, a+1) + a^{-s}.
|
| 49 |
+
|
| 50 |
+
This provides the analytic continuation to $\operatorname{Re}(a) \le 0$.
|
| 51 |
+
|
| 52 |
+
Assume now $\operatorname{Re}(a) > 0$. The integral representation
|
| 53 |
+
|
| 54 |
+
.. math:: \Phi_0(z, s, a) = \int_0^\infty \frac{t^{s-1} e^{-at}}{1 - ze^{-t}}
|
| 55 |
+
\frac{\mathrm{d}t}{\Gamma(s)}
|
| 56 |
+
|
| 57 |
+
provides an analytic continuation to $\mathbb{C} - [1, \infty)$.
|
| 58 |
+
Finally, for $x \in (1, \infty)$ we find
|
| 59 |
+
|
| 60 |
+
.. math:: \lim_{\epsilon \to 0^+} \Phi_0(x + i\epsilon, s, a)
|
| 61 |
+
-\lim_{\epsilon \to 0^+} \Phi_0(x - i\epsilon, s, a)
|
| 62 |
+
= \frac{2\pi i \log^{s-1}{x}}{x^a \Gamma(s)},
|
| 63 |
+
|
| 64 |
+
using the standard branch for both $\log{x}$ and
|
| 65 |
+
$\log{\log{x}}$ (a branch of $\log{\log{x}}$ is needed to
|
| 66 |
+
evaluate $\log{x}^{s-1}$).
|
| 67 |
+
This concludes the analytic continuation. The Lerch transcendent is thus
|
| 68 |
+
branched at $z \in \{0, 1, \infty\}$ and
|
| 69 |
+
$a \in \mathbb{Z}_{\le 0}$. For fixed $z, a$ outside these
|
| 70 |
+
branch points, it is an entire function of $s$.
|
| 71 |
+
|
| 72 |
+
Examples
|
| 73 |
+
========
|
| 74 |
+
|
| 75 |
+
The Lerch transcendent is a fairly general function, for this reason it does
|
| 76 |
+
not automatically evaluate to simpler functions. Use ``expand_func()`` to
|
| 77 |
+
achieve this.
|
| 78 |
+
|
| 79 |
+
If $z=1$, the Lerch transcendent reduces to the Hurwitz zeta function:
|
| 80 |
+
|
| 81 |
+
>>> from sympy import lerchphi, expand_func
|
| 82 |
+
>>> from sympy.abc import z, s, a
|
| 83 |
+
>>> expand_func(lerchphi(1, s, a))
|
| 84 |
+
zeta(s, a)
|
| 85 |
+
|
| 86 |
+
More generally, if $z$ is a root of unity, the Lerch transcendent
|
| 87 |
+
reduces to a sum of Hurwitz zeta functions:
|
| 88 |
+
|
| 89 |
+
>>> expand_func(lerchphi(-1, s, a))
|
| 90 |
+
zeta(s, a/2)/2**s - zeta(s, a/2 + 1/2)/2**s
|
| 91 |
+
|
| 92 |
+
If $a=1$, the Lerch transcendent reduces to the polylogarithm:
|
| 93 |
+
|
| 94 |
+
>>> expand_func(lerchphi(z, s, 1))
|
| 95 |
+
polylog(s, z)/z
|
| 96 |
+
|
| 97 |
+
More generally, if $a$ is rational, the Lerch transcendent reduces
|
| 98 |
+
to a sum of polylogarithms:
|
| 99 |
+
|
| 100 |
+
>>> from sympy import S
|
| 101 |
+
>>> expand_func(lerchphi(z, s, S(1)/2))
|
| 102 |
+
2**(s - 1)*(polylog(s, sqrt(z))/sqrt(z) -
|
| 103 |
+
polylog(s, sqrt(z)*exp_polar(I*pi))/sqrt(z))
|
| 104 |
+
>>> expand_func(lerchphi(z, s, S(3)/2))
|
| 105 |
+
-2**s/z + 2**(s - 1)*(polylog(s, sqrt(z))/sqrt(z) -
|
| 106 |
+
polylog(s, sqrt(z)*exp_polar(I*pi))/sqrt(z))/z
|
| 107 |
+
|
| 108 |
+
The derivatives with respect to $z$ and $a$ can be computed in
|
| 109 |
+
closed form:
|
| 110 |
+
|
| 111 |
+
>>> lerchphi(z, s, a).diff(z)
|
| 112 |
+
(-a*lerchphi(z, s, a) + lerchphi(z, s - 1, a))/z
|
| 113 |
+
>>> lerchphi(z, s, a).diff(a)
|
| 114 |
+
-s*lerchphi(z, s + 1, a)
|
| 115 |
+
|
| 116 |
+
See Also
|
| 117 |
+
========
|
| 118 |
+
|
| 119 |
+
polylog, zeta
|
| 120 |
+
|
| 121 |
+
References
|
| 122 |
+
==========
|
| 123 |
+
|
| 124 |
+
.. [1] Bateman, H.; Erdelyi, A. (1953), Higher Transcendental Functions,
|
| 125 |
+
Vol. I, New York: McGraw-Hill. Section 1.11.
|
| 126 |
+
.. [2] https://dlmf.nist.gov/25.14
|
| 127 |
+
.. [3] https://en.wikipedia.org/wiki/Lerch_transcendent
|
| 128 |
+
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
def _eval_expand_func(self, **hints):
|
| 132 |
+
z, s, a = self.args
|
| 133 |
+
if z == 1:
|
| 134 |
+
return zeta(s, a)
|
| 135 |
+
if s.is_Integer and s <= 0:
|
| 136 |
+
t = Dummy('t')
|
| 137 |
+
p = Poly((t + a)**(-s), t)
|
| 138 |
+
start = 1/(1 - t)
|
| 139 |
+
res = S.Zero
|
| 140 |
+
for c in reversed(p.all_coeffs()):
|
| 141 |
+
res += c*start
|
| 142 |
+
start = t*start.diff(t)
|
| 143 |
+
return res.subs(t, z)
|
| 144 |
+
|
| 145 |
+
if a.is_Rational:
|
| 146 |
+
# See section 18 of
|
| 147 |
+
# Kelly B. Roach. Hypergeometric Function Representations.
|
| 148 |
+
# In: Proceedings of the 1997 International Symposium on Symbolic and
|
| 149 |
+
# Algebraic Computation, pages 205-211, New York, 1997. ACM.
|
| 150 |
+
# TODO should something be polarified here?
|
| 151 |
+
add = S.Zero
|
| 152 |
+
mul = S.One
|
| 153 |
+
# First reduce a to the interaval (0, 1]
|
| 154 |
+
if a > 1:
|
| 155 |
+
n = floor(a)
|
| 156 |
+
if n == a:
|
| 157 |
+
n -= 1
|
| 158 |
+
a -= n
|
| 159 |
+
mul = z**(-n)
|
| 160 |
+
add = Add(*[-z**(k - n)/(a + k)**s for k in range(n)])
|
| 161 |
+
elif a <= 0:
|
| 162 |
+
n = floor(-a) + 1
|
| 163 |
+
a += n
|
| 164 |
+
mul = z**n
|
| 165 |
+
add = Add(*[z**(n - 1 - k)/(a - k - 1)**s for k in range(n)])
|
| 166 |
+
|
| 167 |
+
m, n = S([a.p, a.q])
|
| 168 |
+
zet = exp_polar(2*pi*I/n)
|
| 169 |
+
root = z**(1/n)
|
| 170 |
+
up_zet = unpolarify(zet)
|
| 171 |
+
addargs = []
|
| 172 |
+
for k in range(n):
|
| 173 |
+
p = polylog(s, zet**k*root)
|
| 174 |
+
if isinstance(p, polylog):
|
| 175 |
+
p = p._eval_expand_func(**hints)
|
| 176 |
+
addargs.append(p/(up_zet**k*root)**m)
|
| 177 |
+
return add + mul*n**(s - 1)*Add(*addargs)
|
| 178 |
+
|
| 179 |
+
# TODO use minpoly instead of ad-hoc methods when issue 5888 is fixed
|
| 180 |
+
if isinstance(z, exp) and (z.args[0]/(pi*I)).is_Rational or z in [-1, I, -I]:
|
| 181 |
+
# TODO reference?
|
| 182 |
+
if z == -1:
|
| 183 |
+
p, q = S([1, 2])
|
| 184 |
+
elif z == I:
|
| 185 |
+
p, q = S([1, 4])
|
| 186 |
+
elif z == -I:
|
| 187 |
+
p, q = S([-1, 4])
|
| 188 |
+
else:
|
| 189 |
+
arg = z.args[0]/(2*pi*I)
|
| 190 |
+
p, q = S([arg.p, arg.q])
|
| 191 |
+
return Add(*[exp(2*pi*I*k*p/q)/q**s*zeta(s, (k + a)/q)
|
| 192 |
+
for k in range(q)])
|
| 193 |
+
|
| 194 |
+
return lerchphi(z, s, a)
|
| 195 |
+
|
| 196 |
+
def fdiff(self, argindex=1):
|
| 197 |
+
z, s, a = self.args
|
| 198 |
+
if argindex == 3:
|
| 199 |
+
return -s*lerchphi(z, s + 1, a)
|
| 200 |
+
elif argindex == 1:
|
| 201 |
+
return (lerchphi(z, s - 1, a) - a*lerchphi(z, s, a))/z
|
| 202 |
+
else:
|
| 203 |
+
raise ArgumentIndexError
|
| 204 |
+
|
| 205 |
+
def _eval_rewrite_helper(self, target):
|
| 206 |
+
res = self._eval_expand_func()
|
| 207 |
+
if res.has(target):
|
| 208 |
+
return res
|
| 209 |
+
else:
|
| 210 |
+
return self
|
| 211 |
+
|
| 212 |
+
def _eval_rewrite_as_zeta(self, z, s, a, **kwargs):
|
| 213 |
+
return self._eval_rewrite_helper(zeta)
|
| 214 |
+
|
| 215 |
+
def _eval_rewrite_as_polylog(self, z, s, a, **kwargs):
|
| 216 |
+
return self._eval_rewrite_helper(polylog)
|
| 217 |
+
|
| 218 |
+
###############################################################################
|
| 219 |
+
###################### POLYLOGARITHM ##########################################
|
| 220 |
+
###############################################################################
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
class polylog(DefinedFunction):
|
| 224 |
+
r"""
|
| 225 |
+
Polylogarithm function.
|
| 226 |
+
|
| 227 |
+
Explanation
|
| 228 |
+
===========
|
| 229 |
+
|
| 230 |
+
For $|z| < 1$ and $s \in \mathbb{C}$, the polylogarithm is
|
| 231 |
+
defined by
|
| 232 |
+
|
| 233 |
+
.. math:: \operatorname{Li}_s(z) = \sum_{n=1}^\infty \frac{z^n}{n^s},
|
| 234 |
+
|
| 235 |
+
where the standard branch of the argument is used for $n$. It admits
|
| 236 |
+
an analytic continuation which is branched at $z=1$ (notably not on the
|
| 237 |
+
sheet of initial definition), $z=0$ and $z=\infty$.
|
| 238 |
+
|
| 239 |
+
The name polylogarithm comes from the fact that for $s=1$, the
|
| 240 |
+
polylogarithm is related to the ordinary logarithm (see examples), and that
|
| 241 |
+
|
| 242 |
+
.. math:: \operatorname{Li}_{s+1}(z) =
|
| 243 |
+
\int_0^z \frac{\operatorname{Li}_s(t)}{t} \mathrm{d}t.
|
| 244 |
+
|
| 245 |
+
The polylogarithm is a special case of the Lerch transcendent:
|
| 246 |
+
|
| 247 |
+
.. math:: \operatorname{Li}_{s}(z) = z \Phi(z, s, 1).
|
| 248 |
+
|
| 249 |
+
Examples
|
| 250 |
+
========
|
| 251 |
+
|
| 252 |
+
For $z \in \{0, 1, -1\}$, the polylogarithm is automatically expressed
|
| 253 |
+
using other functions:
|
| 254 |
+
|
| 255 |
+
>>> from sympy import polylog
|
| 256 |
+
>>> from sympy.abc import s
|
| 257 |
+
>>> polylog(s, 0)
|
| 258 |
+
0
|
| 259 |
+
>>> polylog(s, 1)
|
| 260 |
+
zeta(s)
|
| 261 |
+
>>> polylog(s, -1)
|
| 262 |
+
-dirichlet_eta(s)
|
| 263 |
+
|
| 264 |
+
If $s$ is a negative integer, $0$ or $1$, the polylogarithm can be
|
| 265 |
+
expressed using elementary functions. This can be done using
|
| 266 |
+
``expand_func()``:
|
| 267 |
+
|
| 268 |
+
>>> from sympy import expand_func
|
| 269 |
+
>>> from sympy.abc import z
|
| 270 |
+
>>> expand_func(polylog(1, z))
|
| 271 |
+
-log(1 - z)
|
| 272 |
+
>>> expand_func(polylog(0, z))
|
| 273 |
+
z/(1 - z)
|
| 274 |
+
|
| 275 |
+
The derivative with respect to $z$ can be computed in closed form:
|
| 276 |
+
|
| 277 |
+
>>> polylog(s, z).diff(z)
|
| 278 |
+
polylog(s - 1, z)/z
|
| 279 |
+
|
| 280 |
+
The polylogarithm can be expressed in terms of the lerch transcendent:
|
| 281 |
+
|
| 282 |
+
>>> from sympy import lerchphi
|
| 283 |
+
>>> polylog(s, z).rewrite(lerchphi)
|
| 284 |
+
z*lerchphi(z, s, 1)
|
| 285 |
+
|
| 286 |
+
See Also
|
| 287 |
+
========
|
| 288 |
+
|
| 289 |
+
zeta, lerchphi
|
| 290 |
+
|
| 291 |
+
"""
|
| 292 |
+
|
| 293 |
+
@classmethod
|
| 294 |
+
def eval(cls, s, z):
|
| 295 |
+
if z.is_number:
|
| 296 |
+
if z is S.One:
|
| 297 |
+
return zeta(s)
|
| 298 |
+
elif z is S.NegativeOne:
|
| 299 |
+
return -dirichlet_eta(s)
|
| 300 |
+
elif z is S.Zero:
|
| 301 |
+
return S.Zero
|
| 302 |
+
elif s == 2:
|
| 303 |
+
dilogtable = _dilogtable()
|
| 304 |
+
if z in dilogtable:
|
| 305 |
+
return dilogtable[z]
|
| 306 |
+
|
| 307 |
+
if z.is_zero:
|
| 308 |
+
return S.Zero
|
| 309 |
+
|
| 310 |
+
# Make an effort to determine if z is 1 to avoid replacing into
|
| 311 |
+
# expression with singularity
|
| 312 |
+
zone = z.equals(S.One)
|
| 313 |
+
|
| 314 |
+
if zone:
|
| 315 |
+
return zeta(s)
|
| 316 |
+
elif zone is False:
|
| 317 |
+
# For s = 0 or -1 use explicit formulas to evaluate, but
|
| 318 |
+
# automatically expanding polylog(1, z) to -log(1-z) seems
|
| 319 |
+
# undesirable for summation methods based on hypergeometric
|
| 320 |
+
# functions
|
| 321 |
+
if s is S.Zero:
|
| 322 |
+
return z/(1 - z)
|
| 323 |
+
elif s is S.NegativeOne:
|
| 324 |
+
return z/(1 - z)**2
|
| 325 |
+
if s.is_zero:
|
| 326 |
+
return z/(1 - z)
|
| 327 |
+
|
| 328 |
+
# polylog is branched, but not over the unit disk
|
| 329 |
+
if z.has(exp_polar, polar_lift) and (zone or (Abs(z) <= S.One) == True):
|
| 330 |
+
return cls(s, unpolarify(z))
|
| 331 |
+
|
| 332 |
+
def fdiff(self, argindex=1):
|
| 333 |
+
s, z = self.args
|
| 334 |
+
if argindex == 2:
|
| 335 |
+
return polylog(s - 1, z)/z
|
| 336 |
+
raise ArgumentIndexError
|
| 337 |
+
|
| 338 |
+
def _eval_rewrite_as_lerchphi(self, s, z, **kwargs):
|
| 339 |
+
return z*lerchphi(z, s, 1)
|
| 340 |
+
|
| 341 |
+
def _eval_expand_func(self, **hints):
|
| 342 |
+
s, z = self.args
|
| 343 |
+
if s == 1:
|
| 344 |
+
return -log(1 - z)
|
| 345 |
+
if s.is_Integer and s <= 0:
|
| 346 |
+
u = Dummy('u')
|
| 347 |
+
start = u/(1 - u)
|
| 348 |
+
for _ in range(-s):
|
| 349 |
+
start = u*start.diff(u)
|
| 350 |
+
return expand_mul(start).subs(u, z)
|
| 351 |
+
return polylog(s, z)
|
| 352 |
+
|
| 353 |
+
def _eval_is_zero(self):
|
| 354 |
+
z = self.args[1]
|
| 355 |
+
if z.is_zero:
|
| 356 |
+
return True
|
| 357 |
+
|
| 358 |
+
def _eval_nseries(self, x, n, logx, cdir=0):
|
| 359 |
+
from sympy.series.order import Order
|
| 360 |
+
nu, z = self.args
|
| 361 |
+
|
| 362 |
+
z0 = z.subs(x, 0)
|
| 363 |
+
if z0 is S.NaN:
|
| 364 |
+
z0 = z.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
| 365 |
+
|
| 366 |
+
if z0.is_zero:
|
| 367 |
+
# In case of powers less than 1, number of terms need to be computed
|
| 368 |
+
# separately to avoid repeated callings of _eval_nseries with wrong n
|
| 369 |
+
try:
|
| 370 |
+
_, exp = z.leadterm(x)
|
| 371 |
+
except (ValueError, NotImplementedError):
|
| 372 |
+
return self
|
| 373 |
+
|
| 374 |
+
if exp.is_positive:
|
| 375 |
+
newn = ceiling(n/exp)
|
| 376 |
+
o = Order(x**n, x)
|
| 377 |
+
r = z._eval_nseries(x, n, logx, cdir).removeO()
|
| 378 |
+
if r is S.Zero:
|
| 379 |
+
return o
|
| 380 |
+
|
| 381 |
+
term = r
|
| 382 |
+
s = [term]
|
| 383 |
+
for k in range(2, newn):
|
| 384 |
+
term *= r
|
| 385 |
+
s.append(term/k**nu)
|
| 386 |
+
return Add(*s) + o
|
| 387 |
+
|
| 388 |
+
return super(polylog, self)._eval_nseries(x, n, logx, cdir)
|
| 389 |
+
|
| 390 |
+
###############################################################################
|
| 391 |
+
###################### HURWITZ GENERALIZED ZETA FUNCTION ######################
|
| 392 |
+
###############################################################################
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
class zeta(DefinedFunction):
|
| 396 |
+
r"""
|
| 397 |
+
Hurwitz zeta function (or Riemann zeta function).
|
| 398 |
+
|
| 399 |
+
Explanation
|
| 400 |
+
===========
|
| 401 |
+
|
| 402 |
+
For $\operatorname{Re}(a) > 0$ and $\operatorname{Re}(s) > 1$, this
|
| 403 |
+
function is defined as
|
| 404 |
+
|
| 405 |
+
.. math:: \zeta(s, a) = \sum_{n=0}^\infty \frac{1}{(n + a)^s},
|
| 406 |
+
|
| 407 |
+
where the standard choice of argument for $n + a$ is used. For fixed
|
| 408 |
+
$a$ not a nonpositive integer the Hurwitz zeta function admits a
|
| 409 |
+
meromorphic continuation to all of $\mathbb{C}$; it is an unbranched
|
| 410 |
+
function with a simple pole at $s = 1$.
|
| 411 |
+
|
| 412 |
+
The Hurwitz zeta function is a special case of the Lerch transcendent:
|
| 413 |
+
|
| 414 |
+
.. math:: \zeta(s, a) = \Phi(1, s, a).
|
| 415 |
+
|
| 416 |
+
This formula defines an analytic continuation for all possible values of
|
| 417 |
+
$s$ and $a$ (also $\operatorname{Re}(a) < 0$), see the documentation of
|
| 418 |
+
:class:`lerchphi` for a description of the branching behavior.
|
| 419 |
+
|
| 420 |
+
If no value is passed for $a$ a default value of $a = 1$ is assumed,
|
| 421 |
+
yielding the Riemann zeta function.
|
| 422 |
+
|
| 423 |
+
Examples
|
| 424 |
+
========
|
| 425 |
+
|
| 426 |
+
For $a = 1$ the Hurwitz zeta function reduces to the famous Riemann
|
| 427 |
+
zeta function:
|
| 428 |
+
|
| 429 |
+
.. math:: \zeta(s, 1) = \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s}.
|
| 430 |
+
|
| 431 |
+
>>> from sympy import zeta
|
| 432 |
+
>>> from sympy.abc import s
|
| 433 |
+
>>> zeta(s, 1)
|
| 434 |
+
zeta(s)
|
| 435 |
+
>>> zeta(s)
|
| 436 |
+
zeta(s)
|
| 437 |
+
|
| 438 |
+
The Riemann zeta function can also be expressed using the Dirichlet eta
|
| 439 |
+
function:
|
| 440 |
+
|
| 441 |
+
>>> from sympy import dirichlet_eta
|
| 442 |
+
>>> zeta(s).rewrite(dirichlet_eta)
|
| 443 |
+
dirichlet_eta(s)/(1 - 2**(1 - s))
|
| 444 |
+
|
| 445 |
+
The Riemann zeta function at nonnegative even and negative integer
|
| 446 |
+
values is related to the Bernoulli numbers and polynomials:
|
| 447 |
+
|
| 448 |
+
>>> zeta(2)
|
| 449 |
+
pi**2/6
|
| 450 |
+
>>> zeta(4)
|
| 451 |
+
pi**4/90
|
| 452 |
+
>>> zeta(0)
|
| 453 |
+
-1/2
|
| 454 |
+
>>> zeta(-1)
|
| 455 |
+
-1/12
|
| 456 |
+
>>> zeta(-4)
|
| 457 |
+
0
|
| 458 |
+
|
| 459 |
+
The specific formulae are:
|
| 460 |
+
|
| 461 |
+
.. math:: \zeta(2n) = -\frac{(2\pi i)^{2n} B_{2n}}{2(2n)!}
|
| 462 |
+
.. math:: \zeta(-n,a) = -\frac{B_{n+1}(a)}{n+1}
|
| 463 |
+
|
| 464 |
+
No closed-form expressions are known at positive odd integers, but
|
| 465 |
+
numerical evaluation is possible:
|
| 466 |
+
|
| 467 |
+
>>> zeta(3).n()
|
| 468 |
+
1.20205690315959
|
| 469 |
+
|
| 470 |
+
The derivative of $\zeta(s, a)$ with respect to $a$ can be computed:
|
| 471 |
+
|
| 472 |
+
>>> from sympy.abc import a
|
| 473 |
+
>>> zeta(s, a).diff(a)
|
| 474 |
+
-s*zeta(s + 1, a)
|
| 475 |
+
|
| 476 |
+
However the derivative with respect to $s$ has no useful closed form
|
| 477 |
+
expression:
|
| 478 |
+
|
| 479 |
+
>>> zeta(s, a).diff(s)
|
| 480 |
+
Derivative(zeta(s, a), s)
|
| 481 |
+
|
| 482 |
+
The Hurwitz zeta function can be expressed in terms of the Lerch
|
| 483 |
+
transcendent, :class:`~.lerchphi`:
|
| 484 |
+
|
| 485 |
+
>>> from sympy import lerchphi
|
| 486 |
+
>>> zeta(s, a).rewrite(lerchphi)
|
| 487 |
+
lerchphi(1, s, a)
|
| 488 |
+
|
| 489 |
+
See Also
|
| 490 |
+
========
|
| 491 |
+
|
| 492 |
+
dirichlet_eta, lerchphi, polylog
|
| 493 |
+
|
| 494 |
+
References
|
| 495 |
+
==========
|
| 496 |
+
|
| 497 |
+
.. [1] https://dlmf.nist.gov/25.11
|
| 498 |
+
.. [2] https://en.wikipedia.org/wiki/Hurwitz_zeta_function
|
| 499 |
+
|
| 500 |
+
"""
|
| 501 |
+
|
| 502 |
+
@classmethod
|
| 503 |
+
def eval(cls, s, a=None):
|
| 504 |
+
if a is S.One:
|
| 505 |
+
return cls(s)
|
| 506 |
+
elif s is S.NaN or a is S.NaN:
|
| 507 |
+
return S.NaN
|
| 508 |
+
elif s is S.One:
|
| 509 |
+
return S.ComplexInfinity
|
| 510 |
+
elif s is S.Infinity:
|
| 511 |
+
return S.One
|
| 512 |
+
elif a is S.Infinity:
|
| 513 |
+
return S.Zero
|
| 514 |
+
|
| 515 |
+
sint = s.is_Integer
|
| 516 |
+
if a is None:
|
| 517 |
+
a = S.One
|
| 518 |
+
if sint and s.is_nonpositive:
|
| 519 |
+
return bernoulli(1-s, a) / (s-1)
|
| 520 |
+
elif a is S.One:
|
| 521 |
+
if sint and s.is_even:
|
| 522 |
+
return -(2*pi*I)**s * bernoulli(s) / (2*factorial(s))
|
| 523 |
+
elif sint and a.is_Integer and a.is_positive:
|
| 524 |
+
return cls(s) - harmonic(a-1, s)
|
| 525 |
+
elif a.is_Integer and a.is_nonpositive and \
|
| 526 |
+
(s.is_integer is False or s.is_nonpositive is False):
|
| 527 |
+
return S.NaN
|
| 528 |
+
|
| 529 |
+
def _eval_rewrite_as_bernoulli(self, s, a=1, **kwargs):
|
| 530 |
+
if a == 1 and s.is_integer and s.is_nonnegative and s.is_even:
|
| 531 |
+
return -(2*pi*I)**s * bernoulli(s) / (2*factorial(s))
|
| 532 |
+
return bernoulli(1-s, a) / (s-1)
|
| 533 |
+
|
| 534 |
+
def _eval_rewrite_as_dirichlet_eta(self, s, a=1, **kwargs):
|
| 535 |
+
if a != 1:
|
| 536 |
+
return self
|
| 537 |
+
s = self.args[0]
|
| 538 |
+
return dirichlet_eta(s)/(1 - 2**(1 - s))
|
| 539 |
+
|
| 540 |
+
def _eval_rewrite_as_lerchphi(self, s, a=1, **kwargs):
|
| 541 |
+
return lerchphi(1, s, a)
|
| 542 |
+
|
| 543 |
+
def _eval_is_finite(self):
|
| 544 |
+
return fuzzy_not((self.args[0] - 1).is_zero)
|
| 545 |
+
|
| 546 |
+
def _eval_expand_func(self, **hints):
|
| 547 |
+
s = self.args[0]
|
| 548 |
+
a = self.args[1] if len(self.args) > 1 else S.One
|
| 549 |
+
if a.is_integer:
|
| 550 |
+
if a.is_positive:
|
| 551 |
+
return zeta(s) - harmonic(a-1, s)
|
| 552 |
+
if a.is_nonpositive and (s.is_integer is False or
|
| 553 |
+
s.is_nonpositive is False):
|
| 554 |
+
return S.NaN
|
| 555 |
+
return self
|
| 556 |
+
|
| 557 |
+
def fdiff(self, argindex=1):
|
| 558 |
+
if len(self.args) == 2:
|
| 559 |
+
s, a = self.args
|
| 560 |
+
else:
|
| 561 |
+
s, a = self.args + (1,)
|
| 562 |
+
if argindex == 2:
|
| 563 |
+
return -s*zeta(s + 1, a)
|
| 564 |
+
else:
|
| 565 |
+
raise ArgumentIndexError
|
| 566 |
+
|
| 567 |
+
def _eval_as_leading_term(self, x, logx, cdir):
|
| 568 |
+
if len(self.args) == 2:
|
| 569 |
+
s, a = self.args
|
| 570 |
+
else:
|
| 571 |
+
s, a = self.args + (S.One,)
|
| 572 |
+
|
| 573 |
+
try:
|
| 574 |
+
c, e = a.leadterm(x)
|
| 575 |
+
except NotImplementedError:
|
| 576 |
+
return self
|
| 577 |
+
|
| 578 |
+
if e.is_negative and not s.is_positive:
|
| 579 |
+
raise NotImplementedError
|
| 580 |
+
|
| 581 |
+
return super(zeta, self)._eval_as_leading_term(x, logx=logx, cdir=cdir)
|
| 582 |
+
|
| 583 |
+
|
| 584 |
+
class dirichlet_eta(DefinedFunction):
|
| 585 |
+
r"""
|
| 586 |
+
Dirichlet eta function.
|
| 587 |
+
|
| 588 |
+
Explanation
|
| 589 |
+
===========
|
| 590 |
+
|
| 591 |
+
For $\operatorname{Re}(s) > 0$ and $0 < x \le 1$, this function is defined as
|
| 592 |
+
|
| 593 |
+
.. math:: \eta(s, a) = \sum_{n=0}^\infty \frac{(-1)^n}{(n+a)^s}.
|
| 594 |
+
|
| 595 |
+
It admits a unique analytic continuation to all of $\mathbb{C}$ for any
|
| 596 |
+
fixed $a$ not a nonpositive integer. It is an entire, unbranched function.
|
| 597 |
+
|
| 598 |
+
It can be expressed using the Hurwitz zeta function as
|
| 599 |
+
|
| 600 |
+
.. math:: \eta(s, a) = \zeta(s,a) - 2^{1-s} \zeta\left(s, \frac{a+1}{2}\right)
|
| 601 |
+
|
| 602 |
+
and using the generalized Genocchi function as
|
| 603 |
+
|
| 604 |
+
.. math:: \eta(s, a) = \frac{G(1-s, a)}{2(s-1)}.
|
| 605 |
+
|
| 606 |
+
In both cases the limiting value of $\log2 - \psi(a) + \psi\left(\frac{a+1}{2}\right)$
|
| 607 |
+
is used when $s = 1$.
|
| 608 |
+
|
| 609 |
+
Examples
|
| 610 |
+
========
|
| 611 |
+
|
| 612 |
+
>>> from sympy import dirichlet_eta, zeta
|
| 613 |
+
>>> from sympy.abc import s
|
| 614 |
+
>>> dirichlet_eta(s).rewrite(zeta)
|
| 615 |
+
Piecewise((log(2), Eq(s, 1)), ((1 - 2**(1 - s))*zeta(s), True))
|
| 616 |
+
|
| 617 |
+
See Also
|
| 618 |
+
========
|
| 619 |
+
|
| 620 |
+
zeta
|
| 621 |
+
|
| 622 |
+
References
|
| 623 |
+
==========
|
| 624 |
+
|
| 625 |
+
.. [1] https://en.wikipedia.org/wiki/Dirichlet_eta_function
|
| 626 |
+
.. [2] Peter Luschny, "An introduction to the Bernoulli function",
|
| 627 |
+
https://arxiv.org/abs/2009.06743
|
| 628 |
+
|
| 629 |
+
"""
|
| 630 |
+
|
| 631 |
+
@classmethod
|
| 632 |
+
def eval(cls, s, a=None):
|
| 633 |
+
if a is S.One:
|
| 634 |
+
return cls(s)
|
| 635 |
+
if a is None:
|
| 636 |
+
if s == 1:
|
| 637 |
+
return log(2)
|
| 638 |
+
z = zeta(s)
|
| 639 |
+
if not z.has(zeta):
|
| 640 |
+
return (1 - 2**(1-s)) * z
|
| 641 |
+
return
|
| 642 |
+
elif s == 1:
|
| 643 |
+
from sympy.functions.special.gamma_functions import digamma
|
| 644 |
+
return log(2) - digamma(a) + digamma((a+1)/2)
|
| 645 |
+
z1 = zeta(s, a)
|
| 646 |
+
z2 = zeta(s, (a+1)/2)
|
| 647 |
+
if not z1.has(zeta) and not z2.has(zeta):
|
| 648 |
+
return z1 - 2**(1-s) * z2
|
| 649 |
+
|
| 650 |
+
def _eval_rewrite_as_zeta(self, s, a=1, **kwargs):
|
| 651 |
+
from sympy.functions.special.gamma_functions import digamma
|
| 652 |
+
if a == 1:
|
| 653 |
+
return Piecewise((log(2), Eq(s, 1)), ((1 - 2**(1-s)) * zeta(s), True))
|
| 654 |
+
return Piecewise((log(2) - digamma(a) + digamma((a+1)/2), Eq(s, 1)),
|
| 655 |
+
(zeta(s, a) - 2**(1-s) * zeta(s, (a+1)/2), True))
|
| 656 |
+
|
| 657 |
+
def _eval_rewrite_as_genocchi(self, s, a=S.One, **kwargs):
|
| 658 |
+
from sympy.functions.special.gamma_functions import digamma
|
| 659 |
+
return Piecewise((log(2) - digamma(a) + digamma((a+1)/2), Eq(s, 1)),
|
| 660 |
+
(genocchi(1-s, a) / (2 * (s-1)), True))
|
| 661 |
+
|
| 662 |
+
def _eval_evalf(self, prec):
|
| 663 |
+
if all(i.is_number for i in self.args):
|
| 664 |
+
return self.rewrite(zeta)._eval_evalf(prec)
|
| 665 |
+
|
| 666 |
+
|
| 667 |
+
class riemann_xi(DefinedFunction):
|
| 668 |
+
r"""
|
| 669 |
+
Riemann Xi function.
|
| 670 |
+
|
| 671 |
+
Examples
|
| 672 |
+
========
|
| 673 |
+
|
| 674 |
+
The Riemann Xi function is closely related to the Riemann zeta function.
|
| 675 |
+
The zeros of Riemann Xi function are precisely the non-trivial zeros
|
| 676 |
+
of the zeta function.
|
| 677 |
+
|
| 678 |
+
>>> from sympy import riemann_xi, zeta
|
| 679 |
+
>>> from sympy.abc import s
|
| 680 |
+
>>> riemann_xi(s).rewrite(zeta)
|
| 681 |
+
s*(s - 1)*gamma(s/2)*zeta(s)/(2*pi**(s/2))
|
| 682 |
+
|
| 683 |
+
References
|
| 684 |
+
==========
|
| 685 |
+
|
| 686 |
+
.. [1] https://en.wikipedia.org/wiki/Riemann_Xi_function
|
| 687 |
+
|
| 688 |
+
"""
|
| 689 |
+
|
| 690 |
+
|
| 691 |
+
@classmethod
|
| 692 |
+
def eval(cls, s):
|
| 693 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 694 |
+
z = zeta(s)
|
| 695 |
+
if s in (S.Zero, S.One):
|
| 696 |
+
return S.Half
|
| 697 |
+
|
| 698 |
+
if not isinstance(z, zeta):
|
| 699 |
+
return s*(s - 1)*gamma(s/2)*z/(2*pi**(s/2))
|
| 700 |
+
|
| 701 |
+
def _eval_rewrite_as_zeta(self, s, **kwargs):
|
| 702 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 703 |
+
return s*(s - 1)*gamma(s/2)*zeta(s)/(2*pi**(s/2))
|
| 704 |
+
|
| 705 |
+
|
| 706 |
+
class stieltjes(DefinedFunction):
|
| 707 |
+
r"""
|
| 708 |
+
Represents Stieltjes constants, $\gamma_{k}$ that occur in
|
| 709 |
+
Laurent Series expansion of the Riemann zeta function.
|
| 710 |
+
|
| 711 |
+
Examples
|
| 712 |
+
========
|
| 713 |
+
|
| 714 |
+
>>> from sympy import stieltjes
|
| 715 |
+
>>> from sympy.abc import n, m
|
| 716 |
+
>>> stieltjes(n)
|
| 717 |
+
stieltjes(n)
|
| 718 |
+
|
| 719 |
+
The zero'th stieltjes constant:
|
| 720 |
+
|
| 721 |
+
>>> stieltjes(0)
|
| 722 |
+
EulerGamma
|
| 723 |
+
>>> stieltjes(0, 1)
|
| 724 |
+
EulerGamma
|
| 725 |
+
|
| 726 |
+
For generalized stieltjes constants:
|
| 727 |
+
|
| 728 |
+
>>> stieltjes(n, m)
|
| 729 |
+
stieltjes(n, m)
|
| 730 |
+
|
| 731 |
+
Constants are only defined for integers >= 0:
|
| 732 |
+
|
| 733 |
+
>>> stieltjes(-1)
|
| 734 |
+
zoo
|
| 735 |
+
|
| 736 |
+
References
|
| 737 |
+
==========
|
| 738 |
+
|
| 739 |
+
.. [1] https://en.wikipedia.org/wiki/Stieltjes_constants
|
| 740 |
+
|
| 741 |
+
"""
|
| 742 |
+
|
| 743 |
+
@classmethod
|
| 744 |
+
def eval(cls, n, a=None):
|
| 745 |
+
if a is not None:
|
| 746 |
+
a = sympify(a)
|
| 747 |
+
if a is S.NaN:
|
| 748 |
+
return S.NaN
|
| 749 |
+
if a.is_Integer and a.is_nonpositive:
|
| 750 |
+
return S.ComplexInfinity
|
| 751 |
+
|
| 752 |
+
if n.is_Number:
|
| 753 |
+
if n is S.NaN:
|
| 754 |
+
return S.NaN
|
| 755 |
+
elif n < 0:
|
| 756 |
+
return S.ComplexInfinity
|
| 757 |
+
elif not n.is_Integer:
|
| 758 |
+
return S.ComplexInfinity
|
| 759 |
+
elif n is S.Zero and a in [None, 1]:
|
| 760 |
+
return S.EulerGamma
|
| 761 |
+
|
| 762 |
+
if n.is_extended_negative:
|
| 763 |
+
return S.ComplexInfinity
|
| 764 |
+
|
| 765 |
+
if n.is_zero and a in [None, 1]:
|
| 766 |
+
return S.EulerGamma
|
| 767 |
+
|
| 768 |
+
if n.is_integer == False:
|
| 769 |
+
return S.ComplexInfinity
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
@cacheit
|
| 773 |
+
def _dilogtable():
|
| 774 |
+
return {
|
| 775 |
+
S.Half: pi**2/12 - log(2)**2/2,
|
| 776 |
+
Integer(2) : pi**2/4 - I*pi*log(2),
|
| 777 |
+
-(sqrt(5) - 1)/2 : -pi**2/15 + log((sqrt(5)-1)/2)**2/2,
|
| 778 |
+
-(sqrt(5) + 1)/2 : -pi**2/10 - log((sqrt(5)+1)/2)**2,
|
| 779 |
+
(3 - sqrt(5))/2 : pi**2/15 - log((sqrt(5)-1)/2)**2,
|
| 780 |
+
(sqrt(5) - 1)/2 : pi**2/10 - log((sqrt(5)-1)/2)**2,
|
| 781 |
+
I : I*S.Catalan - pi**2/48,
|
| 782 |
+
-I : -I*S.Catalan - pi**2/48,
|
| 783 |
+
1 - I : pi**2/16 - I*S.Catalan - pi*I/4*log(2),
|
| 784 |
+
1 + I : pi**2/16 + I*S.Catalan + pi*I/4*log(2),
|
| 785 |
+
(1 - I)/2 : -log(2)**2/8 + pi*I*log(2)/8 + 5*pi**2/96 - I*S.Catalan
|
| 786 |
+
}
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (643 Bytes). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/experimental_lambdify.cpython-312.pyc
ADDED
|
Binary file (20.7 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/plot.cpython-312.pyc
ADDED
|
Binary file (44 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/plot_implicit.cpython-312.pyc
ADDED
|
Binary file (8.52 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/plotgrid.cpython-312.pyc
ADDED
|
Binary file (8.37 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/textplot.cpython-312.pyc
ADDED
|
Binary file (7.09 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (14.4 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/backends/textbackend/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.backends.textbackend.text import TextBackend
|
| 2 |
+
|
| 3 |
+
__all__ = ["TextBackend"]
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/backends/textbackend/text.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sympy.plotting.backends.base_backend as base_backend
|
| 2 |
+
from sympy.plotting.series import LineOver1DRangeSeries
|
| 3 |
+
from sympy.plotting.textplot import textplot
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class TextBackend(base_backend.Plot):
|
| 7 |
+
def __init__(self, *args, **kwargs):
|
| 8 |
+
super().__init__(*args, **kwargs)
|
| 9 |
+
|
| 10 |
+
def show(self):
|
| 11 |
+
if not base_backend._show:
|
| 12 |
+
return
|
| 13 |
+
if len(self._series) != 1:
|
| 14 |
+
raise ValueError(
|
| 15 |
+
'The TextBackend supports only one graph per Plot.')
|
| 16 |
+
elif not isinstance(self._series[0], LineOver1DRangeSeries):
|
| 17 |
+
raise ValueError(
|
| 18 |
+
'The TextBackend supports only expressions over a 1D range')
|
| 19 |
+
else:
|
| 20 |
+
ser = self._series[0]
|
| 21 |
+
textplot(ser.expr, ser.start, ser.end)
|
| 22 |
+
|
| 23 |
+
def close(self):
|
| 24 |
+
pass
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/intervalmath/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .interval_arithmetic import interval
|
| 2 |
+
from .lib_interval import (Abs, exp, log, log10, sin, cos, tan, sqrt,
|
| 3 |
+
imin, imax, sinh, cosh, tanh, acosh, asinh, atanh,
|
| 4 |
+
asin, acos, atan, ceil, floor, And, Or)
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
'interval',
|
| 8 |
+
|
| 9 |
+
'Abs', 'exp', 'log', 'log10', 'sin', 'cos', 'tan', 'sqrt', 'imin', 'imax',
|
| 10 |
+
'sinh', 'cosh', 'tanh', 'acosh', 'asinh', 'atanh', 'asin', 'acos', 'atan',
|
| 11 |
+
'ceil', 'floor', 'And', 'Or',
|
| 12 |
+
]
|
URSA/.venv_ursa/lib/python3.12/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Interval Arithmetic for plotting.
|
| 3 |
+
This module does not implement interval arithmetic accurately and
|
| 4 |
+
hence cannot be used for purposes other than plotting. If you want
|
| 5 |
+
to use interval arithmetic, use mpmath's interval arithmetic.
|
| 6 |
+
|
| 7 |
+
The module implements interval arithmetic using numpy and
|
| 8 |
+
python floating points. The rounding up and down is not handled
|
| 9 |
+
and hence this is not an accurate implementation of interval
|
| 10 |
+
arithmetic.
|
| 11 |
+
|
| 12 |
+
The module uses numpy for speed which cannot be achieved with mpmath.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
# Q: Why use numpy? Why not simply use mpmath's interval arithmetic?
|
| 16 |
+
# A: mpmath's interval arithmetic simulates a floating point unit
|
| 17 |
+
# and hence is slow, while numpy evaluations are orders of magnitude
|
| 18 |
+
# faster.
|
| 19 |
+
|
| 20 |
+
# Q: Why create a separate class for intervals? Why not use SymPy's
|
| 21 |
+
# Interval Sets?
|
| 22 |
+
# A: The functionalities that will be required for plotting is quite
|
| 23 |
+
# different from what Interval Sets implement.
|
| 24 |
+
|
| 25 |
+
# Q: Why is rounding up and down according to IEEE754 not handled?
|
| 26 |
+
# A: It is not possible to do it in both numpy and python. An external
|
| 27 |
+
# library has to used, which defeats the whole purpose i.e., speed. Also
|
| 28 |
+
# rounding is handled for very few functions in those libraries.
|
| 29 |
+
|
| 30 |
+
# Q Will my plots be affected?
|
| 31 |
+
# A It will not affect most of the plots. The interval arithmetic
|
| 32 |
+
# module based suffers the same problems as that of floating point
|
| 33 |
+
# arithmetic.
|
| 34 |
+
|
| 35 |
+
from sympy.core.numbers import int_valued
|
| 36 |
+
from sympy.core.logic import fuzzy_and
|
| 37 |
+
from sympy.simplify.simplify import nsimplify
|
| 38 |
+
|
| 39 |
+
from .interval_membership import intervalMembership
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class interval:
|
| 43 |
+
""" Represents an interval containing floating points as start and
|
| 44 |
+
end of the interval
|
| 45 |
+
The is_valid variable tracks whether the interval obtained as the
|
| 46 |
+
result of the function is in the domain and is continuous.
|
| 47 |
+
- True: Represents the interval result of a function is continuous and
|
| 48 |
+
in the domain of the function.
|
| 49 |
+
- False: The interval argument of the function was not in the domain of
|
| 50 |
+
the function, hence the is_valid of the result interval is False
|
| 51 |
+
- None: The function was not continuous over the interval or
|
| 52 |
+
the function's argument interval is partly in the domain of the
|
| 53 |
+
function
|
| 54 |
+
|
| 55 |
+
A comparison between an interval and a real number, or a
|
| 56 |
+
comparison between two intervals may return ``intervalMembership``
|
| 57 |
+
of two 3-valued logic values.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
def __init__(self, *args, is_valid=True, **kwargs):
|
| 61 |
+
self.is_valid = is_valid
|
| 62 |
+
if len(args) == 1:
|
| 63 |
+
if isinstance(args[0], interval):
|
| 64 |
+
self.start, self.end = args[0].start, args[0].end
|
| 65 |
+
else:
|
| 66 |
+
self.start = float(args[0])
|
| 67 |
+
self.end = float(args[0])
|
| 68 |
+
elif len(args) == 2:
|
| 69 |
+
if args[0] < args[1]:
|
| 70 |
+
self.start = float(args[0])
|
| 71 |
+
self.end = float(args[1])
|
| 72 |
+
else:
|
| 73 |
+
self.start = float(args[1])
|
| 74 |
+
self.end = float(args[0])
|
| 75 |
+
|
| 76 |
+
else:
|
| 77 |
+
raise ValueError("interval takes a maximum of two float values "
|
| 78 |
+
"as arguments")
|
| 79 |
+
|
| 80 |
+
@property
|
| 81 |
+
def mid(self):
|
| 82 |
+
return (self.start + self.end) / 2.0
|
| 83 |
+
|
| 84 |
+
@property
|
| 85 |
+
def width(self):
|
| 86 |
+
return self.end - self.start
|
| 87 |
+
|
| 88 |
+
def __repr__(self):
|
| 89 |
+
return "interval(%f, %f)" % (self.start, self.end)
|
| 90 |
+
|
| 91 |
+
def __str__(self):
|
| 92 |
+
return "[%f, %f]" % (self.start, self.end)
|
| 93 |
+
|
| 94 |
+
def __lt__(self, other):
|
| 95 |
+
if isinstance(other, (int, float)):
|
| 96 |
+
if self.end < other:
|
| 97 |
+
return intervalMembership(True, self.is_valid)
|
| 98 |
+
elif self.start > other:
|
| 99 |
+
return intervalMembership(False, self.is_valid)
|
| 100 |
+
else:
|
| 101 |
+
return intervalMembership(None, self.is_valid)
|
| 102 |
+
|
| 103 |
+
elif isinstance(other, interval):
|
| 104 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 105 |
+
if self.end < other. start:
|
| 106 |
+
return intervalMembership(True, valid)
|
| 107 |
+
if self.start > other.end:
|
| 108 |
+
return intervalMembership(False, valid)
|
| 109 |
+
return intervalMembership(None, valid)
|
| 110 |
+
else:
|
| 111 |
+
return NotImplemented
|
| 112 |
+
|
| 113 |
+
def __gt__(self, other):
|
| 114 |
+
if isinstance(other, (int, float)):
|
| 115 |
+
if self.start > other:
|
| 116 |
+
return intervalMembership(True, self.is_valid)
|
| 117 |
+
elif self.end < other:
|
| 118 |
+
return intervalMembership(False, self.is_valid)
|
| 119 |
+
else:
|
| 120 |
+
return intervalMembership(None, self.is_valid)
|
| 121 |
+
elif isinstance(other, interval):
|
| 122 |
+
return other.__lt__(self)
|
| 123 |
+
else:
|
| 124 |
+
return NotImplemented
|
| 125 |
+
|
| 126 |
+
def __eq__(self, other):
|
| 127 |
+
if isinstance(other, (int, float)):
|
| 128 |
+
if self.start == other and self.end == other:
|
| 129 |
+
return intervalMembership(True, self.is_valid)
|
| 130 |
+
if other in self:
|
| 131 |
+
return intervalMembership(None, self.is_valid)
|
| 132 |
+
else:
|
| 133 |
+
return intervalMembership(False, self.is_valid)
|
| 134 |
+
|
| 135 |
+
if isinstance(other, interval):
|
| 136 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 137 |
+
if self.start == other.start and self.end == other.end:
|
| 138 |
+
return intervalMembership(True, valid)
|
| 139 |
+
elif self.__lt__(other)[0] is not None:
|
| 140 |
+
return intervalMembership(False, valid)
|
| 141 |
+
else:
|
| 142 |
+
return intervalMembership(None, valid)
|
| 143 |
+
else:
|
| 144 |
+
return NotImplemented
|
| 145 |
+
|
| 146 |
+
def __ne__(self, other):
|
| 147 |
+
if isinstance(other, (int, float)):
|
| 148 |
+
if self.start == other and self.end == other:
|
| 149 |
+
return intervalMembership(False, self.is_valid)
|
| 150 |
+
if other in self:
|
| 151 |
+
return intervalMembership(None, self.is_valid)
|
| 152 |
+
else:
|
| 153 |
+
return intervalMembership(True, self.is_valid)
|
| 154 |
+
|
| 155 |
+
if isinstance(other, interval):
|
| 156 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 157 |
+
if self.start == other.start and self.end == other.end:
|
| 158 |
+
return intervalMembership(False, valid)
|
| 159 |
+
if not self.__lt__(other)[0] is None:
|
| 160 |
+
return intervalMembership(True, valid)
|
| 161 |
+
return intervalMembership(None, valid)
|
| 162 |
+
else:
|
| 163 |
+
return NotImplemented
|
| 164 |
+
|
| 165 |
+
def __le__(self, other):
|
| 166 |
+
if isinstance(other, (int, float)):
|
| 167 |
+
if self.end <= other:
|
| 168 |
+
return intervalMembership(True, self.is_valid)
|
| 169 |
+
if self.start > other:
|
| 170 |
+
return intervalMembership(False, self.is_valid)
|
| 171 |
+
else:
|
| 172 |
+
return intervalMembership(None, self.is_valid)
|
| 173 |
+
|
| 174 |
+
if isinstance(other, interval):
|
| 175 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 176 |
+
if self.end <= other.start:
|
| 177 |
+
return intervalMembership(True, valid)
|
| 178 |
+
if self.start > other.end:
|
| 179 |
+
return intervalMembership(False, valid)
|
| 180 |
+
return intervalMembership(None, valid)
|
| 181 |
+
else:
|
| 182 |
+
return NotImplemented
|
| 183 |
+
|
| 184 |
+
def __ge__(self, other):
|
| 185 |
+
if isinstance(other, (int, float)):
|
| 186 |
+
if self.start >= other:
|
| 187 |
+
return intervalMembership(True, self.is_valid)
|
| 188 |
+
elif self.end < other:
|
| 189 |
+
return intervalMembership(False, self.is_valid)
|
| 190 |
+
else:
|
| 191 |
+
return intervalMembership(None, self.is_valid)
|
| 192 |
+
elif isinstance(other, interval):
|
| 193 |
+
return other.__le__(self)
|
| 194 |
+
|
| 195 |
+
def __add__(self, other):
|
| 196 |
+
if isinstance(other, (int, float)):
|
| 197 |
+
if self.is_valid:
|
| 198 |
+
return interval(self.start + other, self.end + other)
|
| 199 |
+
else:
|
| 200 |
+
start = self.start + other
|
| 201 |
+
end = self.end + other
|
| 202 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 203 |
+
|
| 204 |
+
elif isinstance(other, interval):
|
| 205 |
+
start = self.start + other.start
|
| 206 |
+
end = self.end + other.end
|
| 207 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 208 |
+
return interval(start, end, is_valid=valid)
|
| 209 |
+
else:
|
| 210 |
+
return NotImplemented
|
| 211 |
+
|
| 212 |
+
__radd__ = __add__
|
| 213 |
+
|
| 214 |
+
def __sub__(self, other):
|
| 215 |
+
if isinstance(other, (int, float)):
|
| 216 |
+
start = self.start - other
|
| 217 |
+
end = self.end - other
|
| 218 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 219 |
+
|
| 220 |
+
elif isinstance(other, interval):
|
| 221 |
+
start = self.start - other.end
|
| 222 |
+
end = self.end - other.start
|
| 223 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 224 |
+
return interval(start, end, is_valid=valid)
|
| 225 |
+
else:
|
| 226 |
+
return NotImplemented
|
| 227 |
+
|
| 228 |
+
def __rsub__(self, other):
|
| 229 |
+
if isinstance(other, (int, float)):
|
| 230 |
+
start = other - self.end
|
| 231 |
+
end = other - self.start
|
| 232 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 233 |
+
elif isinstance(other, interval):
|
| 234 |
+
return other.__sub__(self)
|
| 235 |
+
else:
|
| 236 |
+
return NotImplemented
|
| 237 |
+
|
| 238 |
+
def __neg__(self):
|
| 239 |
+
if self.is_valid:
|
| 240 |
+
return interval(-self.end, -self.start)
|
| 241 |
+
else:
|
| 242 |
+
return interval(-self.end, -self.start, is_valid=self.is_valid)
|
| 243 |
+
|
| 244 |
+
def __mul__(self, other):
|
| 245 |
+
if isinstance(other, interval):
|
| 246 |
+
if self.is_valid is False or other.is_valid is False:
|
| 247 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 248 |
+
elif self.is_valid is None or other.is_valid is None:
|
| 249 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 250 |
+
else:
|
| 251 |
+
inters = []
|
| 252 |
+
inters.append(self.start * other.start)
|
| 253 |
+
inters.append(self.end * other.start)
|
| 254 |
+
inters.append(self.start * other.end)
|
| 255 |
+
inters.append(self.end * other.end)
|
| 256 |
+
start = min(inters)
|
| 257 |
+
end = max(inters)
|
| 258 |
+
return interval(start, end)
|
| 259 |
+
elif isinstance(other, (int, float)):
|
| 260 |
+
return interval(self.start*other, self.end*other, is_valid=self.is_valid)
|
| 261 |
+
else:
|
| 262 |
+
return NotImplemented
|
| 263 |
+
|
| 264 |
+
__rmul__ = __mul__
|
| 265 |
+
|
| 266 |
+
def __contains__(self, other):
|
| 267 |
+
if isinstance(other, (int, float)):
|
| 268 |
+
return self.start <= other and self.end >= other
|
| 269 |
+
else:
|
| 270 |
+
return self.start <= other.start and other.end <= self.end
|
| 271 |
+
|
| 272 |
+
def __rtruediv__(self, other):
|
| 273 |
+
if isinstance(other, (int, float)):
|
| 274 |
+
other = interval(other)
|
| 275 |
+
return other.__truediv__(self)
|
| 276 |
+
elif isinstance(other, interval):
|
| 277 |
+
return other.__truediv__(self)
|
| 278 |
+
else:
|
| 279 |
+
return NotImplemented
|
| 280 |
+
|
| 281 |
+
def __truediv__(self, other):
|
| 282 |
+
# Both None and False are handled
|
| 283 |
+
if not self.is_valid:
|
| 284 |
+
# Don't divide as the value is not valid
|
| 285 |
+
return interval(-float('inf'), float('inf'), is_valid=self.is_valid)
|
| 286 |
+
if isinstance(other, (int, float)):
|
| 287 |
+
if other == 0:
|
| 288 |
+
# Divide by zero encountered. valid nowhere
|
| 289 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 290 |
+
else:
|
| 291 |
+
return interval(self.start / other, self.end / other)
|
| 292 |
+
|
| 293 |
+
elif isinstance(other, interval):
|
| 294 |
+
if other.is_valid is False or self.is_valid is False:
|
| 295 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 296 |
+
elif other.is_valid is None or self.is_valid is None:
|
| 297 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 298 |
+
else:
|
| 299 |
+
# denominator contains both signs, i.e. being divided by zero
|
| 300 |
+
# return the whole real line with is_valid = None
|
| 301 |
+
if 0 in other:
|
| 302 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 303 |
+
|
| 304 |
+
# denominator negative
|
| 305 |
+
this = self
|
| 306 |
+
if other.end < 0:
|
| 307 |
+
this = -this
|
| 308 |
+
other = -other
|
| 309 |
+
|
| 310 |
+
# denominator positive
|
| 311 |
+
inters = []
|
| 312 |
+
inters.append(this.start / other.start)
|
| 313 |
+
inters.append(this.end / other.start)
|
| 314 |
+
inters.append(this.start / other.end)
|
| 315 |
+
inters.append(this.end / other.end)
|
| 316 |
+
start = max(inters)
|
| 317 |
+
end = min(inters)
|
| 318 |
+
return interval(start, end)
|
| 319 |
+
else:
|
| 320 |
+
return NotImplemented
|
| 321 |
+
|
| 322 |
+
def __pow__(self, other):
|
| 323 |
+
# Implements only power to an integer.
|
| 324 |
+
from .lib_interval import exp, log
|
| 325 |
+
if not self.is_valid:
|
| 326 |
+
return self
|
| 327 |
+
if isinstance(other, interval):
|
| 328 |
+
return exp(other * log(self))
|
| 329 |
+
elif isinstance(other, (float, int)):
|
| 330 |
+
if other < 0:
|
| 331 |
+
return 1 / self.__pow__(abs(other))
|
| 332 |
+
else:
|
| 333 |
+
if int_valued(other):
|
| 334 |
+
return _pow_int(self, other)
|
| 335 |
+
else:
|
| 336 |
+
return _pow_float(self, other)
|
| 337 |
+
else:
|
| 338 |
+
return NotImplemented
|
| 339 |
+
|
| 340 |
+
def __rpow__(self, other):
|
| 341 |
+
if isinstance(other, (float, int)):
|
| 342 |
+
if not self.is_valid:
|
| 343 |
+
#Don't do anything
|
| 344 |
+
return self
|
| 345 |
+
elif other < 0:
|
| 346 |
+
if self.width > 0:
|
| 347 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 348 |
+
else:
|
| 349 |
+
power_rational = nsimplify(self.start)
|
| 350 |
+
num, denom = power_rational.as_numer_denom()
|
| 351 |
+
if denom % 2 == 0:
|
| 352 |
+
return interval(-float('inf'), float('inf'),
|
| 353 |
+
is_valid=False)
|
| 354 |
+
else:
|
| 355 |
+
start = -abs(other)**self.start
|
| 356 |
+
end = start
|
| 357 |
+
return interval(start, end)
|
| 358 |
+
else:
|
| 359 |
+
return interval(other**self.start, other**self.end)
|
| 360 |
+
elif isinstance(other, interval):
|
| 361 |
+
return other.__pow__(self)
|
| 362 |
+
else:
|
| 363 |
+
return NotImplemented
|
| 364 |
+
|
| 365 |
+
def __hash__(self):
|
| 366 |
+
return hash((self.is_valid, self.start, self.end))
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
def _pow_float(inter, power):
|
| 370 |
+
"""Evaluates an interval raised to a floating point."""
|
| 371 |
+
power_rational = nsimplify(power)
|
| 372 |
+
num, denom = power_rational.as_numer_denom()
|
| 373 |
+
if num % 2 == 0:
|
| 374 |
+
start = abs(inter.start)**power
|
| 375 |
+
end = abs(inter.end)**power
|
| 376 |
+
if start < 0:
|
| 377 |
+
ret = interval(0, max(start, end))
|
| 378 |
+
else:
|
| 379 |
+
ret = interval(start, end)
|
| 380 |
+
return ret
|
| 381 |
+
elif denom % 2 == 0:
|
| 382 |
+
if inter.end < 0:
|
| 383 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 384 |
+
elif inter.start < 0:
|
| 385 |
+
return interval(0, inter.end**power, is_valid=None)
|
| 386 |
+
else:
|
| 387 |
+
return interval(inter.start**power, inter.end**power)
|
| 388 |
+
else:
|
| 389 |
+
if inter.start < 0:
|
| 390 |
+
start = -abs(inter.start)**power
|
| 391 |
+
else:
|
| 392 |
+
start = inter.start**power
|
| 393 |
+
|
| 394 |
+
if inter.end < 0:
|
| 395 |
+
end = -abs(inter.end)**power
|
| 396 |
+
else:
|
| 397 |
+
end = inter.end**power
|
| 398 |
+
|
| 399 |
+
return interval(start, end, is_valid=inter.is_valid)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
def _pow_int(inter, power):
|
| 403 |
+
"""Evaluates an interval raised to an integer power"""
|
| 404 |
+
power = int(power)
|
| 405 |
+
if power & 1:
|
| 406 |
+
return interval(inter.start**power, inter.end**power)
|
| 407 |
+
else:
|
| 408 |
+
if inter.start < 0 and inter.end > 0:
|
| 409 |
+
start = 0
|
| 410 |
+
end = max(inter.start**power, inter.end**power)
|
| 411 |
+
return interval(start, end)
|
| 412 |
+
else:
|
| 413 |
+
return interval(inter.start**power, inter.end**power)
|