Compare commits
231 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fa2b9c793 | |||
| d3812b1194 | |||
| d8c4597a7d | |||
| f3751eb87f | |||
| bcc67d3e55 | |||
| 27a1fe7f6d | |||
| ffcaef924a | |||
| bc96a88926 | |||
| 0d2af59d5d | |||
| 70b722800f | |||
| d271b8312a | |||
| 49c7eeb43c | |||
| bf6459d6be | |||
| f72b86ca17 | |||
| 0abd5de3f0 | |||
| 017cc473fb | |||
| bfaf6a7a31 | |||
| 24538751aa | |||
| 4a3af8ef2b | |||
| e5142170db | |||
| fb69943530 | |||
| c90e242741 | |||
| 255d98ff98 | |||
| da14fb3a42 | |||
| 4f58dcd947 | |||
| 3e73240699 | |||
| 460c289f90 | |||
| 1f3e9bf593 | |||
| 534dd479f0 | |||
| 3481440ce5 | |||
| 28e36ca652 | |||
| b9fd43cc42 | |||
| 8d0190ad6d | |||
| afdb228884 | |||
| 2de2d509ec | |||
| 55aefa6f82 | |||
| 7eb6f23063 | |||
| 65dca57ca7 | |||
| 6309ea0550 | |||
| d30d632c39 | |||
| d8c9fd2ea3 | |||
| 101a8379a8 | |||
| ead1a61d09 | |||
| 9e85fabc1d | |||
| 6d356df80f | |||
| b747a30db1 | |||
| 6f2a553551 | |||
| 52c1546a0c | |||
| 573549a39f | |||
| ed63e50a8a | |||
| a2333847a8 | |||
| c328efcd37 | |||
| 86978167e1 | |||
| 8d21910bba | |||
| 76d91a4fa5 | |||
| cc198d4ecd | |||
| 5688722737 | |||
| 40cf9ba085 | |||
| b20d7c530a | |||
| f12743f60f | |||
| c44efa2ca2 | |||
| 6f1fbd275d | |||
| aa205a2821 | |||
| a686b903ae | |||
| 2a2de4e7ca | |||
| 8e31d10120 | |||
| 90e1cff769 | |||
| ba2a0da3a1 | |||
| c4f9187805 | |||
| 390972e250 | |||
| 1626936cca | |||
| 1aff951d93 | |||
| 92f61841bf | |||
| bf99017677 | |||
| 5a4116a13e | |||
| b5e38772b1 | |||
| 085f181126 | |||
| 60cf796714 | |||
| 3f3d3ec9a3 | |||
| 1d89343973 | |||
| 9e890f67cd | |||
| 1cfd36f49e | |||
| 0fe9033f18 | |||
| c8a17b495d | |||
| 3e6e7967ad | |||
| 5e683847c7 | |||
| 6dfd359ed6 | |||
| 7a1aaff95a | |||
| dca2abd6ca | |||
| a8a614c5f0 | |||
| e002cb4488 | |||
| b62c4173d1 | |||
| 12e7e74251 | |||
| 11a5afe7b6 | |||
| a2e0351345 | |||
| 29c496bf14 | |||
| e45f046a02 | |||
| d742930573 | |||
| 9b71ea0595 | |||
| f736ab6096 | |||
| 41243c5438 | |||
| b854af8d3e | |||
| 90684c4405 | |||
| 3d2aa3ea17 | |||
| 97a091279d | |||
| 6d10972d64 | |||
| e3dbcd0dd5 | |||
| f25a56f132 | |||
| 429c1d5ec2 | |||
| bce4dfb5b4 | |||
| 44ad9908cc | |||
| e10416f997 | |||
| fcb99d97a4 | |||
| 35a1e33f02 | |||
| 16d5fb332c | |||
| db21709e9a | |||
| 52e0daaaeb | |||
| 44859bf6cd | |||
| 34691c49f8 | |||
| f7579c0a0d | |||
| 939efcdd88 | |||
| aa5cd5a9bf | |||
| 0e1bd9496d | |||
| d1e52ed28d | |||
| b06b1f51ce | |||
| e90a592c7a | |||
| 1eb70ed3d8 | |||
| 41981f868a | |||
| 7d00f0aaa5 | |||
| a5edf2e3e5 | |||
| a1a6db90f2 | |||
| cd8722ecbb | |||
| b5a075fa53 | |||
| 87cfbc42a8 | |||
| fbf61435bf | |||
| 5393b8d55e | |||
| d84dc68b9d | |||
| eefbdcb6ad | |||
| 3a09694b91 | |||
| 63304aee63 | |||
| 8a965fe43b | |||
| 6e3d54ef7e | |||
| 4118cfd0db | |||
| 3cbeab5ca3 | |||
| f9822f0954 | |||
| 09178c95b3 | |||
| 527d9d228c | |||
| 7b40a40cc6 | |||
| f8a7f426e4 | |||
| f98636edf6 | |||
| 73ce23d164 | |||
| 5d48d2abd9 | |||
| 1108318e0f | |||
| 45f5adbb37 | |||
| 893bb431aa | |||
| 8ffd139b16 | |||
| c9c3946a9b | |||
| 4b84174154 | |||
| 3952417cd6 | |||
| 22bbe9ad4c | |||
| 718a67cb8e | |||
| 6665df00dd | |||
| 801a2e6955 | |||
| 3a454f6643 | |||
| ca0f67d9ab | |||
| 82e0a49593 | |||
| 81204d1105 | |||
| 754c073e7d | |||
| 3ba4132a93 | |||
| 485fe73586 | |||
| 60de9789bf | |||
| 6bc4fe86f3 | |||
| cd25f9b536 | |||
| 8f3e715b1e | |||
| b7728d8dbd | |||
| 9677a2a939 | |||
| 2f6a5d52f3 | |||
| 0669eb773f | |||
| e33d20e34b | |||
| 163096b16f | |||
| cf2ca665b4 | |||
| 14802e5d83 | |||
| a0a3e580f3 | |||
| 3e345d62e1 | |||
| fee75dd985 | |||
| d74dff7e48 | |||
| ed57d4034d | |||
| f3e9cf0706 | |||
| 8a01b18638 | |||
| acd4b569d4 | |||
| 2933f42ad5 | |||
| 711d3c3681 | |||
| 7cf16b7828 | |||
| 34cc1f8147 | |||
| 1e0dc7b307 | |||
| c7128a6ac5 | |||
| 81034e8648 | |||
| fa0da05a63 | |||
| 319703eafd | |||
| 951a7303db | |||
| aed6aecdcd | |||
| a9f1273e68 | |||
| d7b9ddb23c | |||
| 3c8119eeb2 | |||
| 7c3252b14c | |||
| e95bf2a22a | |||
| 5348705040 | |||
| b6b535bc72 | |||
| 454ee97a83 | |||
| e631d38cd5 | |||
| 0b9622a737 | |||
| 932ed68c1f | |||
| 4478a0e33d | |||
| 3d84b6f851 | |||
| 612a34fbca | |||
| dcd282eb77 | |||
| c5437d880b | |||
| c4176127b0 | |||
| 3af3a60584 | |||
| ccb21aa6b4 | |||
| c8e1d276c7 | |||
| 69f9e55489 | |||
| 34f6e5700a | |||
| d94ac5eeb3 | |||
| a1addfbc59 | |||
| 90ccd8c40e | |||
| dc207275a8 | |||
| 2aad193ce0 | |||
| 1dfa0143f9 | |||
| eefdb4d0c4 | |||
| e8aa7a0bd9 |
@@ -62,6 +62,43 @@ All work should be submitted via `Pull Requests (PR)`_.
|
||||
and check that it looks as expected.
|
||||
|
||||
|
||||
AI Generated Code and Communication Policy
|
||||
==========================================
|
||||
|
||||
The person submitting an issue or PR is responsible for its content, regardless
|
||||
of whether AI tools were used in its creation. Generative AI tools can produce
|
||||
output quickly, but discretion, good judgment, and critical thinking are the
|
||||
foundation of all good contributions.
|
||||
|
||||
You must understand and explain the code you submit as well as the existing
|
||||
related code. It is not acceptable to submit a patch that you cannot
|
||||
understand and explain yourself. In explaining your contribution, do not use
|
||||
AI to automatically generate descriptions, as AI rarely communicates such
|
||||
information correctly and concisely.
|
||||
|
||||
Disclosure
|
||||
----------
|
||||
|
||||
If you substantially make use of AI to assist in the development of your patch,
|
||||
you must disclose how it was used and what code in the patch is AI generated.
|
||||
Pull request without such disclosure may be rejected.
|
||||
|
||||
Code Quality
|
||||
------------
|
||||
|
||||
Code generated by AI is very often of low quality. Contributors are expected
|
||||
to submit code that meets our standards (see above). We will reject pull
|
||||
requests that we deem being "AI slop". Do not waste developers time by
|
||||
submitting code that is fully or mostly generated by AI.
|
||||
|
||||
Communication
|
||||
-------------
|
||||
|
||||
When interacting in communication among developers (email list, discussions,
|
||||
issues, pull requests, etc) do not use AI to speak for you, other than for
|
||||
translation or grammar editing.
|
||||
|
||||
|
||||
.. _GitHub issues: https://github.com/mpmath/mpmath/issues
|
||||
.. _Pull Requests (PR): https://github.com/mpmath/mpmath/pulls
|
||||
.. _PEP 8: https://www.python.org/dev/peps/pep-0008/
|
||||
|
||||
@@ -4,3 +4,8 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
rebase-strategy: "disabled"
|
||||
groups:
|
||||
actions-deps:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
@@ -8,12 +8,12 @@ jobs:
|
||||
env:
|
||||
PYTEST_ADDOPTS: --cov mpmath --cov-append -n auto
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.13"
|
||||
python-version: "3.x"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade setuptools pip
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
coverage html
|
||||
diff-cover coverage.xml --fail-under=100 \
|
||||
--compare-branch=origin/master
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverage
|
||||
path: |
|
||||
|
||||
@@ -4,10 +4,10 @@ jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install libs
|
||||
@@ -20,11 +20,13 @@ jobs:
|
||||
pip install --upgrade .[docs]
|
||||
- name: Building docs
|
||||
run: |
|
||||
alias sphinx-build='sphinx-build --color -W --keep-going'
|
||||
sphinx-build -b html docs build/sphinx/html
|
||||
sphinx-build -b latex docs build/sphinx/latex
|
||||
sphinx-build --color -W --keep-going -b html docs build/sphinx/html
|
||||
sphinx-build --color -W --keep-going -b latex docs build/sphinx/latex
|
||||
make -C build/sphinx/latex all-pdf
|
||||
- uses: actions/upload-artifact@v6
|
||||
env:
|
||||
NO_COLOR: 1 # workaround for sphinx-contrib/autoprogram#76
|
||||
COLUMNS: 80 # also enable line wrapping for argparse
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: docs
|
||||
path: |
|
||||
|
||||
@@ -4,10 +4,10 @@ jobs:
|
||||
linter:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- run: pip install --upgrade .[develop]
|
||||
|
||||
@@ -5,15 +5,15 @@ jobs:
|
||||
name: Build distributions
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- run: pip install build
|
||||
- run: python -m build
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: build
|
||||
path: dist/
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- build
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/download-artifact@v7
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: build
|
||||
path: dist/
|
||||
|
||||
@@ -5,6 +5,9 @@ on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 2'
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
linter:
|
||||
uses: ./.github/workflows/linter.yml
|
||||
@@ -12,38 +15,47 @@ jobs:
|
||||
uses: ./.github/workflows/coverage.yml
|
||||
docs:
|
||||
uses: ./.github/workflows/docs.yml
|
||||
frozen-version:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Run frozen version test
|
||||
run: ./mpmath/tests/test_version_frozen.sh
|
||||
tests:
|
||||
needs:
|
||||
- linter
|
||||
- coverage
|
||||
- frozen-version
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [3.9, '3.10', 3.11, 3.12, 3.13, 3.14, 3.13t, 3.14t]
|
||||
python-version: ['3.10', 3.11, 3.12, 3.13, 3.14, 3.14t, 3.15, 3.15t]
|
||||
nogmpy: [false]
|
||||
purepy: [false]
|
||||
include:
|
||||
- python-version: 3.14
|
||||
- python-version: "3.x"
|
||||
nogmpy: true
|
||||
- python-version: 3.14
|
||||
- python-version: "3.x"
|
||||
purepy: true
|
||||
- python-version: pypy3.11
|
||||
purepy: true
|
||||
env:
|
||||
PYTEST_ADDOPTS: -n auto
|
||||
PYTEST_ADDOPTS: -n auto --durations=20
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
allow-prereleases: true
|
||||
- name: Install gmpy2 deps
|
||||
if: ${{ startsWith(matrix.python-version, '3.14') }}
|
||||
run: sudo apt install libmpc-dev
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade setuptools pip
|
||||
|
||||
@@ -33,6 +33,9 @@ my/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Generated by setuptools_scm
|
||||
mpmath/_version.py
|
||||
|
||||
# Tox files
|
||||
.tox/
|
||||
|
||||
|
||||
@@ -1,8 +1,129 @@
|
||||
--1.4.0--
|
||||
--1.5.0--
|
||||
Released TBD
|
||||
|
||||
Features:
|
||||
|
||||
* Support special numbers in mpf_frexp() like math.frexp(),
|
||||
see #1081 (Sergey B Kirpichev)
|
||||
* Add ModAB rootfinding algorithm, see #1093 (Ayush Baranwal)
|
||||
* Add Brent root-finding algorithm, see #1103 (Ayush Baranwal)
|
||||
* Correct integral path of the lerchphi() to use Laplace transform
|
||||
integral, see #1109 (Sergey B Kirpichev)
|
||||
* Add Weierstrass elliptic functions, see #1113, #1117,
|
||||
#1141, #1146 and #1157 (Graham Hesketh)
|
||||
* Implement the modified spherical bessel functions spherical_in()
|
||||
and spherical_kn(), see #1121 (Warren Weckesser)
|
||||
* Add ulp(), see #1144 (Sergey B Kirpichev)
|
||||
* Use round_nearest in repr/str and as default mpf's rounding mode,
|
||||
see #1153 (Sergey B Kirpichev)
|
||||
* Add shortest_str context option to enable using shortest decimal
|
||||
representations in str/repr/format output, see #1115 (Sergey B Kirpichev)
|
||||
* Add fft()/invfft() functions for DFT calculation, see #1152 (Ayush Baranwal)
|
||||
|
||||
Compatibility:
|
||||
|
||||
* Drop support for CPython 3.9, see #1058 (Sergey B Kirpichev)
|
||||
* Remove deprecated math2 and rational modules, see #1057 (Sergey B Kirpichev)
|
||||
* Remove deprecated mp.mpnumeric alias, see #1057 (Sergey B Kirpichev)
|
||||
* Remove deprecated bitcount(), fp.is_special() and to/from_pickable()
|
||||
functions, see #1057 (Sergey B Kirpichev)
|
||||
* Drop DeprecationWarning for force_type kwarg for matrix(),
|
||||
see #1057 (Sergey B Kirpichev)
|
||||
* Use signed=True per default in to_man_exp(), see #1057 (Sergey B Kirpichev)
|
||||
* Use asc=True per default for polynomial functions, see
|
||||
#1057 (Sergey B Kirpichev)
|
||||
* Restrict libmp exports to public API, see #1089 (Sergey B Kirpichev)
|
||||
* Use explicit kwargs in public API, where possible,
|
||||
see #1127 (Sergey B Kirpichev)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fix test_hexadecimal_with_libc_bulk(), see #1049 (Doug Torrance)
|
||||
* Keep available deprecated aliases for mpc/mpf_log() (Sergey B Kirpichev)
|
||||
* Use version_file option of setuptools-scm to keep version info, see #1048
|
||||
(Sergey B Kirpichev)
|
||||
* Add workaround for test on s390x, see #1061 (Sergey B Kirpichev)
|
||||
* Fix signature of root(), see #1072 (Sergey B Kirpichev)
|
||||
* Speedup removal trailing zeros in _normalize/from_man_exp(), see #1074
|
||||
(Fredrik Johansson and Sergey B Kirpichev)
|
||||
* Improve documentation about rounding in the mp context,
|
||||
see #1079 (Sergey B Kirpichev)
|
||||
* Correct to_float() conversion for double-rounding cases (e.g. subnormals),
|
||||
see #1082 (Sergey B Kirpichev)
|
||||
* Fix qr_solve() failure on well-conditioned matrices with zero pivot, see
|
||||
#1083 (Jam Balaya)
|
||||
* Clarify to_float() docstring, see #1087 (Sergey B Kirpichev)
|
||||
* Add extra precision for summation in mpf_hypot(), see
|
||||
#1088 (Sergey B Kirpichev)
|
||||
* Fix typo and function names for sin/cospi(), see #1091 (Sergey B Kirpichev)
|
||||
* Raise ValueError when same sign at interval boundaries in bisection
|
||||
rootfinding algorithm, see #1092 (Ayush Baranwal)
|
||||
* Correct interval update for Ridder's method, see #1096 (Sergey B Kirpichev)
|
||||
* Set dynamic maxsteps value for the bisect method, see
|
||||
#1096 (Sergey B Kirpichev)
|
||||
* Implement direct series for lerchphi() base case with |z| < 1,
|
||||
see #1100 (Sergey B Kirpichev)
|
||||
* Correct exception message for THETA_Q_LIM, see #1102
|
||||
(Sergey B Kirpichev and Jam Balaya)
|
||||
* Fix gegenbauer() failing to converge for odd integer n at z=0,
|
||||
see #1101 (Vincent Gao)
|
||||
* Reorganize fixed-precision computations for theta3 to avoid
|
||||
severe cancellation, see #1107 (Sergey B Kirpichev)
|
||||
* Fix fp.hypsum() to exit if ZeroDivisionError occurs and t==0,
|
||||
see #1112 (Sergey B Kirpichev)
|
||||
* Fix chebyfit IndexError when N <= 0, see #1114 (Vincent Gao)
|
||||
* Use generic modular transformations to compute jtheta() with
|
||||
|q| ~ 1, see #1111 (Sergey B Kirpichev, Jam Balaya)
|
||||
* Fix repr_dps() to produce a correct estimate for prec!=53,
|
||||
see #1118 (Sergey B Kirpichev)
|
||||
* Use quasi-periodicity property (DLMF, §20.2(ii)) to compute
|
||||
jtheta(), *_theta2/3a() helpers now not needed, see #1120
|
||||
(Sergey B Kirpichev)
|
||||
* Close leftover figure in plot/cplot/splot on error,
|
||||
see #1123 (Apoorv Darshan)
|
||||
* Better document None return value of the pslq(), see
|
||||
#1134 (Sergey B Kirpichev)
|
||||
* Fix last-digit rounding of the "f" format type, see #1132 (Vincent Gao)
|
||||
* Use CoW pattern to manage constant_memo() cache, see
|
||||
#1138 (Sergey B Kirpichev)
|
||||
* Widen to_str's digit window so str/nstr rounds near-boundary values
|
||||
correctly, see #1139 (Vincent Gao)
|
||||
* Correct definitions for spherical Bessel functions, see #1143
|
||||
(Sergey B Kirpichev)
|
||||
* Added 2 new expceptions to Rosser's Rule, se #1148 (catalin-hanga)
|
||||
* Avoid spurious overflow in fp gammaprod, see #1150 (Sanjay Santhanam)
|
||||
* Document pretty_dps context's option, see #1153 (Sergey B Kirpichev)
|
||||
* Fix cplot points rounding, see #1155 (alexfyp)
|
||||
|
||||
Maintenance:
|
||||
|
||||
* Add bash script to test package version in a frozen application version
|
||||
and a separate CI job to run it, see #1055 (flurin4)
|
||||
* Revert "Add backport action", see #1063 (Sergey B Kirpichev)
|
||||
* Test on CPython 3.15, see #1071, #1106 and #1110 (Sergey B Kirpichev)
|
||||
* Add AI-related policy, see #1098 (Sergey B Kirpichev)
|
||||
* Fix test_sn_cn_dn_identities(), see #1106 (Sergey B Kirpichev)
|
||||
* Fix test_compatibility(): avoid using of private numpy
|
||||
API (Sergey B Kirpichev)
|
||||
|
||||
|
||||
--1.4.1--
|
||||
Released March 15, 2026
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fix test_hexadecimal_with_libc_bulk(), see #1049 (Doug Torrance)
|
||||
* Keep available deprecated aliases for mpc/mpf_log() (Sergey B Kirpichev)
|
||||
* Use version_file option of setuptools-scm to keep version info, see #1048
|
||||
(Sergey B Kirpichev)
|
||||
* Add workaround for test on s390x, see #1061 (Sergey B Kirpichev)
|
||||
|
||||
|
||||
--1.4.0--
|
||||
Released February 23, 2026
|
||||
|
||||
Features:
|
||||
|
||||
* Support underscores as digit separators per PEP 515, see #661 (Sergey B
|
||||
Kirpichev)
|
||||
* Add rationals converter for mpf's, see #666 (Sergey B Kirpichev)
|
||||
@@ -12,7 +133,7 @@ Features:
|
||||
Kirpichev)
|
||||
* Support randmatrix() for mp.iv and mp contexts, see #527 (Maximilian
|
||||
Gaukler)
|
||||
* Added rand() function for matrices, see #610 (Jan-Philipp Hoffmann)
|
||||
* Added rank() function for matrices, see #610 (Jan-Philipp Hoffmann)
|
||||
* Add plus flag to select the B_1 sign convention for bernoulli/bernfrac, see
|
||||
#724 (Jeremy Tan Jie Rui, Sergey B Kirpichev)
|
||||
* Add mpf.as_integer_ratio() method, support construction of mpf from Decimal
|
||||
@@ -58,14 +179,14 @@ Features:
|
||||
#936 (Sergey B Kirpichev)
|
||||
* Use PyREPL, as fallback (no IPython), see #941 (Sergey B Kirpichev)
|
||||
* Add exp2() and log2(), see #948 (Sergey B Kirpichev)
|
||||
* Add isspecial() method for contexts, see #949 (Sergey B Kirpichev)
|
||||
* Support rounding property for the mp context, see #963 (Sergey B Kirpichev)
|
||||
* Add Fox H-function with rational A/B parameters (foxh()), see #982 (Hongren Zheng)
|
||||
* Provide experimental support for free-threading builds, see #993 (Sergey B Kirpichev)
|
||||
|
||||
Compatibility:
|
||||
|
||||
* Drop Python 2 support, see #629 (Fangchen Li)
|
||||
* Drop support for Python versions < 3.8, see #675 (Sergey B Kirpichev)
|
||||
* Drop support for Python versions < 3.9, see #675 and #911 (Sergey B Kirpichev)
|
||||
* Drop private mpq class, use Rational's, provided by backend, see #691 and
|
||||
#769 (Sergey B Kirpichev)
|
||||
* Drop to_pickable()/from_pickable() helpers, see #667 and #769 (Sergey B
|
||||
@@ -78,14 +199,13 @@ Compatibility:
|
||||
* Deprecate current (descending) order of coefficients in polyval(), etc, see
|
||||
#779, #844 and #845 (Sergey B Kirpichev, Warren Weckesser)
|
||||
* Deprecate mpmath.math2, see #769 (Sergey B Kirpichev)
|
||||
* Drop support for CPython 3.8, see #911 (Sergey B Kirpichev)
|
||||
* Deprecate isnormal() method of contexts, see #949 (Sergey B Kirpichev)
|
||||
* Importing from the mpmath.libmp submodules is deprecated, use instead ``from
|
||||
mpmath.libmp import foo``, see
|
||||
issue https://github.com/mpmath/mpmath/issues/704#issuecomment-2953536980
|
||||
for available functions (Sergey B Kirpichev)
|
||||
* Deprecate bitcount function, see #721 and #955 (Sergey B Kirpichev)
|
||||
* Deprecate mpf/mpc_log, see #989 (Sergey B Kirpichev)
|
||||
* Deprecate fp.is_special(), see #1042 (Sergey B Kirpichev)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
@@ -146,8 +266,8 @@ Bug fixes:
|
||||
* Special case in ctx.hypsum for infinite z, see #902 (Sergey B Kirpichev)
|
||||
* Raise an exception if iv's comparison can't be decided, see #903 (Sergey B
|
||||
Kirpichev)
|
||||
* Add special case for ±inf in polylog_continuation(), see #904 (Sergey B
|
||||
Kirpichev)
|
||||
* Add special case for ±inf in polylog_continuation(), see #904, #1034
|
||||
and #1037 (Sergey B Kirpichev, Colin B. Macdonald)
|
||||
* Increase working precision in polylog_general() for negative s, see #898
|
||||
(Sergey B Kirpichev)
|
||||
* Correct case for integer n in besselj/besseli, see #909 (Sergey B Kirpichev)
|
||||
@@ -155,7 +275,7 @@ Bug fixes:
|
||||
* Ensure mpf_bernoulli() returns normalized answer, see #939 (Sergey B
|
||||
Kirpichev)
|
||||
* Use mpf_log1p in acos_asin() helper (implementing Hull et al algorithm), see
|
||||
#948 (Sergey B Kirpichev)
|
||||
#948 and #1036 (Sergey B Kirpichev)
|
||||
* Fix kwargs passing in the nstr() for mpc, see #964 (David Walker)
|
||||
* Fix exception type for int(inf), see #966 (Sergey B Kirpichev)
|
||||
* Ensure exp, sin, tan, etc have a correct __name__ attribute, see #997
|
||||
@@ -163,6 +283,12 @@ Bug fixes:
|
||||
* Matrix raise ValueError in case of negative dimensions, see #1004 (Ayush
|
||||
Baranwal)
|
||||
* Support lists in sinm() and cosm(), see #1003 (Ayush Baranwal)
|
||||
* Properly handle nan's elliprj(), see #1038 (Sergey B Kirpichev)
|
||||
* Raise ValueError for logm(0), see #1017 (Ayush Baranwal)
|
||||
* Fix erf(z) with re(z) of large magnitude, see #1039 (Sergey B Kirpichev)
|
||||
* Return nan's for polylog(s, nan) or polylog(s, nan+nanj),
|
||||
see #1041 (Sergey B Kirpichev)
|
||||
* Fix fp.isnormal() for subnormals, see #1042 (Sergey B Kirpichev)
|
||||
|
||||
Maintenance:
|
||||
|
||||
@@ -194,10 +320,11 @@ Maintenance:
|
||||
* Simplify ctx_mp_python.py, see #806 (Sergey B Kirpichev)
|
||||
* Update gmpy2 deps, see #808 and #813 (Sergey B Kirpichev)
|
||||
* Enable testing on 3.14, see #851 (Sergey B Kirpichev)
|
||||
* Refactor Github Actions, see 905 (Sergey B Kirpichev)
|
||||
* Refactor Github Actions, see #905 (Sergey B Kirpichev)
|
||||
* Build and publish wheel, see #913 (David Hotham)
|
||||
* Use the intended setuptools_scm integration pattern, see #940 (Ronny
|
||||
Pfannschmidt)
|
||||
* Add backport action, see #1042 (Sergey B Kirpichev)
|
||||
|
||||
See the release milestone (1.4) for a complete list of issues and pull requests
|
||||
involved in this release.
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
@manual{mpmath,
|
||||
key = {mpmath},
|
||||
author = {The mpmath development team},
|
||||
title = {mpmath: a {P}ython library for arbitrary-precision floating-point arithmetic (version 1.3.0)},
|
||||
title = {mpmath: a {P}ython library for arbitrary-precision floating-point arithmetic (version 1.4.0)},
|
||||
note = {{\tt https://mpmath.org/}},
|
||||
year = {2023},
|
||||
year = {2026},
|
||||
}
|
||||
|
||||
+7
-2
@@ -85,6 +85,8 @@ Credit also goes to:
|
||||
|
||||
Release history:
|
||||
|
||||
* Version 1.4.1 released on March 15, 2026
|
||||
* Version 1.4.0 released on February 23, 2026
|
||||
* Version 1.3.0 released on March 7, 2023
|
||||
* Version 1.2.1 released on February 9, 2021
|
||||
* Version 1.2.0 released on February 1, 2021
|
||||
@@ -113,8 +115,8 @@ Release history:
|
||||
1. Download & installation
|
||||
--------------------------
|
||||
|
||||
Mpmath requires Python 3.9 or later versions. It has been tested with CPython
|
||||
3.9 through 3.14 and for PyPy 3.11.
|
||||
Mpmath requires Python 3.10 or later versions. It has been tested with CPython
|
||||
3.10 through 3.15 and for PyPy 3.11.
|
||||
|
||||
The latest release of mpmath can be downloaded from the mpmath
|
||||
website and from https://github.com/mpmath/mpmath/releases
|
||||
@@ -185,3 +187,6 @@ to the `mpmath mailinglist <https://groups.google.com/g/mpmath>`_.
|
||||
|
||||
You can also report bugs and send patches to the mpmath issue tracker,
|
||||
https://github.com/mpmath/mpmath/issues
|
||||
|
||||
See also our `contributing guidelines
|
||||
<https://github.com/mpmath/mpmath/blob/master/.github/CONTRIBUTING.rst>`_.
|
||||
|
||||
+16
-12
@@ -1,4 +1,3 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
@@ -6,10 +5,6 @@ import pytest
|
||||
import mpmath
|
||||
|
||||
|
||||
collect_ignore = ['mpmath/__init__.py',
|
||||
'mpmath/rational.py', 'mpmath/math2.py']
|
||||
|
||||
|
||||
def pytest_report_header(config):
|
||||
print("mpmath backend: %s" % mpmath.libmp.backend.BACKEND)
|
||||
print("mpmath mp class: %s" % repr(mpmath.mp))
|
||||
@@ -20,13 +15,22 @@ def pytest_report_header(config):
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line('markers', 'slow: marks tests as slow')
|
||||
|
||||
if "no:hypothesispytest" not in config.getoption("-p"):
|
||||
from hypothesis import settings
|
||||
|
||||
default = settings.get_profile("default")
|
||||
settings.register_profile("default",
|
||||
settings(default, max_examples=1000))
|
||||
ci = settings.get_profile("ci")
|
||||
settings.register_profile("ci", settings(ci, max_examples=10000))
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_mp_globals():
|
||||
from mpmath import mp, iv
|
||||
mp.prec = sys.float_info.mant_dig
|
||||
mp.pretty = False
|
||||
mp.rounding = 'n'
|
||||
mp.pretty_dps = "str"
|
||||
iv.prec = mp.prec
|
||||
iv.pretty = False
|
||||
mpmath.mp.prec = sys.float_info.mant_dig
|
||||
mpmath.mp.pretty = False
|
||||
mpmath.mp.rounding = 'n'
|
||||
mpmath.mp.pretty_dps = "str"
|
||||
mpmath.mp.shortest_str = False
|
||||
mpmath.iv.prec = mpmath.mp.prec
|
||||
mpmath.iv.pretty = False
|
||||
|
||||
+5
-3
@@ -8,9 +8,11 @@ Run with:
|
||||
python manydigits.py
|
||||
|
||||
"""
|
||||
from mpmath import (mp, sin, tan, cos, sqrt, e, pi, exp, atanh, mpf, tanh,
|
||||
zeta, catalan, findroot, quadts, atan, asin, asinh)
|
||||
from mpmath.libmp import to_fixed, bin_to_radix
|
||||
from mpmath import (asin, asinh, atan, atanh, catalan, cos, e, exp, findroot,
|
||||
mp, mpf, pi, quadts, sin, sqrt, tan, tanh, zeta)
|
||||
from mpmath.libmp.libintmath import bin_to_radix
|
||||
from mpmath.libmp.libmpf import to_fixed
|
||||
|
||||
|
||||
dps = 100
|
||||
mp.dps = dps + 10
|
||||
|
||||
+4
-2
@@ -5,11 +5,13 @@ Calculate digits of pi. This module can be run interactively with
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import math
|
||||
import sys
|
||||
from time import perf_counter
|
||||
|
||||
from mpmath.libmp import bin_to_radix, numeral, pi_fixed
|
||||
from mpmath.libmp.libelefun import pi_fixed
|
||||
from mpmath.libmp.libintmath import bin_to_radix, numeral
|
||||
|
||||
|
||||
def display_fraction(digits, skip=0, colwidth=10, columns=5):
|
||||
perline = colwidth * columns
|
||||
|
||||
+35
-5
@@ -82,6 +82,8 @@ Mpmath uses a global working precision; it does not keep track of the precision
|
||||
mp.dps = 15 [default: 15]
|
||||
mp.rounding = 'n' [default: 'n']
|
||||
mp.trap_complex = False [default: False]
|
||||
mp.pretty_dps = 'str' [default: 'str']
|
||||
mp.shortest_str = False [default: False]
|
||||
|
||||
The term **prec** denotes the binary precision (measured in bits) while **dps** (short for *decimal places*) is the decimal precision. Binary and decimal precision are related roughly according to the formula ``prec = 3.33*dps``. For example, it takes a precision of roughly 333 bits to hold an approximation of pi that is accurate to 100 decimal places (actually slightly more than 333 bits is used).
|
||||
|
||||
@@ -97,10 +99,10 @@ When the precision has been set, all ``mpf`` operations are carried out at that
|
||||
|
||||
>>> mp.dps = 50
|
||||
>>> mpf(1) / 6
|
||||
mpf('0.16666666666666666666666666666666666666666666666666656')
|
||||
mpf('0.1666666666666666666666666666666666666666666666666666')
|
||||
>>> mp.dps = 25
|
||||
>>> mpf(2) ** mpf('0.5')
|
||||
mpf('1.414213562373095048801688713')
|
||||
mpf('1.41421356237309504880168871')
|
||||
|
||||
The precision of complex arithmetic is also controlled by the ``mp`` object:
|
||||
|
||||
@@ -123,7 +125,7 @@ The (binary) exponent is stored exactly and is independent of the precision.
|
||||
|
||||
The ``rounding`` property control default rounding mode for the context:
|
||||
|
||||
>>> mp.rounding # round to nearest
|
||||
>>> mp.rounding # round to nearest is the default
|
||||
'n'
|
||||
>>> sin(1)
|
||||
mpf('0.8414709848078965')
|
||||
@@ -233,10 +235,38 @@ Setting the ``mp.pretty`` option will use the ``str()``-style output for ``repr(
|
||||
>>> mpf(0.6)
|
||||
mpf('0.59999999999999998')
|
||||
|
||||
To use enough digits to be able recreate value exactly, set ``mp.pretty_dps``
|
||||
To use enough digits to be able recreate value exactly, enable
|
||||
``mp.shortest_str`` option. With this, repr/str and the new-style string
|
||||
formatting *without format specifier* will use *minimal* number of decimal
|
||||
digits that will preserve value on string input, like repr for CPython's
|
||||
builtin floats:
|
||||
|
||||
>>> mp.shortest_str = True
|
||||
>>> mp.pretty = True
|
||||
>>> mpf(10.9) == mpf("10.9")
|
||||
True
|
||||
>>> mpf(10.9)
|
||||
10.9
|
||||
>>> f"{_}"
|
||||
'10.9'
|
||||
>>> mp.pretty = False
|
||||
>>> mpf(10.9)
|
||||
mpf('10.9')
|
||||
>>> mp.shortest_str = False
|
||||
|
||||
Alternatively, set ``mp.pretty_dps``
|
||||
to ``"repr"`` (default value is ``"str"``). Same option is used to control
|
||||
default number of digits in the new-style string formatting *without format
|
||||
specifier*, i.e. `format(exp(mpf(1)))`.
|
||||
specifier*, i.e. ``format(exp(mpf(1)))``.
|
||||
|
||||
>>> mp.pretty = True
|
||||
>>> mpf(0.1)
|
||||
0.1
|
||||
>>> mp.pretty_dps = "repr"
|
||||
>>> mpf(0.1)
|
||||
0.10000000000000001
|
||||
>>> mp.pretty_dps = "str"
|
||||
>>> mp.pretty = False
|
||||
|
||||
The number of digits with which numbers are printed by default is determined by
|
||||
the working precision. To specify the number of digits to show without
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Fast Fourier Transform
|
||||
----------------------------------
|
||||
|
||||
FFT
|
||||
...
|
||||
|
||||
.. autofunction:: mpmath.fft
|
||||
|
||||
|
||||
Inverse FFT
|
||||
...........
|
||||
|
||||
.. autofunction:: mpmath.invfft
|
||||
@@ -12,3 +12,4 @@ Numerical calculus
|
||||
odes
|
||||
approximation
|
||||
inverselaplace
|
||||
fft
|
||||
|
||||
@@ -55,7 +55,7 @@ using the existing interface):
|
||||
>>> ft = lambda t: exp(-t) - exp(-1000*t)
|
||||
>>> fpvec = [fp(p) for p in myTalbot.p]
|
||||
>>> ft(t)-myTalbot.calc_time_domain_solution(fpvec,t,manual_prec=True)
|
||||
mpf('1.928300179528890061756872185e-21')
|
||||
mpf('1.92830017952889006175687218e-21')
|
||||
|
||||
This manual approach is also useful to look at the Laplace parameter,
|
||||
order, or working precision which were computed.
|
||||
|
||||
@@ -4,7 +4,7 @@ Root-finding and optimization
|
||||
Root-finding (``findroot``)
|
||||
...........................
|
||||
|
||||
.. autofunction:: mpmath.findroot(f, x0, solver=Secant, tol=None, verbose=False, verify=True, **kwargs)
|
||||
.. autofunction:: mpmath.findroot
|
||||
|
||||
Solvers
|
||||
^^^^^^^
|
||||
@@ -21,3 +21,5 @@ Solvers
|
||||
.. autoclass:: mpmath.calculus.optimization.Ridder
|
||||
.. autoclass:: mpmath.calculus.optimization.ANewton
|
||||
.. autoclass:: mpmath.calculus.optimization.MDNewton
|
||||
.. autoclass:: mpmath.calculus.optimization.ModAB
|
||||
.. autoclass:: mpmath.calculus.optimization.Brent
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ nitpicky = True
|
||||
|
||||
# Project information.
|
||||
project = mpmath.__name__
|
||||
copyright = '2007-2027, Fredrik Johansson and mpmath developers'
|
||||
copyright = '2007-2026, Fredrik Johansson and mpmath developers'
|
||||
release = version = mpmath.__version__
|
||||
|
||||
# Define how the current time is formatted using time.strftime().
|
||||
|
||||
@@ -38,6 +38,8 @@ Spherical Bessel functions
|
||||
|
||||
.. autofunction:: mpmath.spherical_jn
|
||||
.. autofunction:: mpmath.spherical_yn
|
||||
.. autofunction:: mpmath.spherical_in
|
||||
.. autofunction:: mpmath.spherical_kn
|
||||
|
||||
|
||||
Kelvin functions
|
||||
@@ -92,9 +94,9 @@ Coulomb wave functions
|
||||
Confluent U and Whittaker functions
|
||||
...................................
|
||||
|
||||
.. autofunction:: mpmath.hyperu(a, b, z)
|
||||
.. autofunction:: mpmath.whitm(k,m,z)
|
||||
.. autofunction:: mpmath.whitw(k,m,z)
|
||||
.. autofunction:: mpmath.hyperu
|
||||
.. autofunction:: mpmath.whitm
|
||||
.. autofunction:: mpmath.whitw
|
||||
|
||||
|
||||
Parabolic cylinder functions
|
||||
|
||||
@@ -22,11 +22,11 @@ function::
|
||||
>>> pi
|
||||
<pi: 3.14159~>
|
||||
>>> 2*pi
|
||||
mpf('6.283185307179586476925286766559005768394338')
|
||||
mpf('6.28318530717958647692528676655900576839434')
|
||||
>>> +pi
|
||||
mpf('3.141592653589793238462643383279502884197169')
|
||||
mpf('3.14159265358979323846264338327950288419717')
|
||||
>>> pi()
|
||||
mpf('3.141592653589793238462643383279502884197169')
|
||||
mpf('3.14159265358979323846264338327950288419717')
|
||||
|
||||
The predefined objects ``j`` (imaginary unit), ``inf`` (positive infinity) and
|
||||
``nan`` (not-a-number) are shortcuts to ``mpc`` and ``mpf`` instances with
|
||||
|
||||
@@ -13,6 +13,8 @@ Elliptic arguments
|
||||
.. autofunction:: mpmath.mfrom
|
||||
.. autofunction:: mpmath.kfrom
|
||||
.. autofunction:: mpmath.taufrom
|
||||
.. autofunction:: mpmath.g2g3from
|
||||
.. autofunction:: mpmath.omega1omega2from
|
||||
|
||||
|
||||
Legendre elliptic integrals
|
||||
@@ -46,8 +48,19 @@ Jacobi elliptic functions
|
||||
.. autofunction:: mpmath.ellipfun
|
||||
|
||||
|
||||
Weierstrass elliptic functions
|
||||
..............................
|
||||
|
||||
.. autofunction:: mpmath.weierp
|
||||
.. autofunction:: mpmath.weierpprime
|
||||
.. autofunction:: mpmath.weiersigma
|
||||
.. autofunction:: mpmath.weierzeta
|
||||
.. autofunction:: mpmath.weierpinv
|
||||
|
||||
|
||||
Modular functions
|
||||
.................
|
||||
|
||||
.. autofunction:: mpmath.eta
|
||||
.. autofunction:: mpmath.kleinj
|
||||
.. autofunction:: mpmath.kleinjinv
|
||||
|
||||
@@ -19,8 +19,8 @@ Exponentiation
|
||||
.. autofunction:: mpmath.power
|
||||
.. autofunction:: mpmath.expj
|
||||
.. autofunction:: mpmath.expjpi
|
||||
.. autofunction:: mpmath.expm1(x)
|
||||
.. autofunction:: mpmath.powm1(x, y)
|
||||
.. autofunction:: mpmath.expm1
|
||||
.. autofunction:: mpmath.powm1
|
||||
|
||||
|
||||
Logarithms
|
||||
@@ -30,7 +30,7 @@ Logarithms
|
||||
.. autofunction:: mpmath.ln
|
||||
.. autofunction:: mpmath.log2
|
||||
.. autofunction:: mpmath.log10
|
||||
.. autofunction:: mpmath.log1p(x)
|
||||
.. autofunction:: mpmath.log1p
|
||||
|
||||
|
||||
Lambert W function
|
||||
|
||||
+7
-4
@@ -173,10 +173,13 @@ Properties of numbers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: mpmath.nint_distance
|
||||
|
||||
.. :func:`~mpmath.absmin`
|
||||
.. ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. .. autofunction:: mpmath.absmin(x)
|
||||
.. .. autofunction:: mpmath.absmax(x)
|
||||
:func:`~mpmath.absmin`
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: mpmath.absmin
|
||||
|
||||
:func:`~mpmath.absmax`
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: mpmath.absmax
|
||||
|
||||
Number generation
|
||||
-----------------
|
||||
|
||||
+3
-3
@@ -426,9 +426,9 @@ Examples::
|
||||
>>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]])
|
||||
>>> Q, R = mp.schur(A)
|
||||
>>> mp.nprint(R, 3)
|
||||
[2.0 0.417 -2.53]
|
||||
[0.0 4.0 -4.74]
|
||||
[0.0 0.0 9.0]
|
||||
[2.0 0.417 2.53]
|
||||
[0.0 4.0 4.74]
|
||||
[0.0 0.0 9.0]
|
||||
>>> print(mp.chop(A - Q * R * Q.transpose_conj()))
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
|
||||
+138
-52
@@ -5,123 +5,209 @@ The following is a non-comprehensive list of works used in the development of mp
|
||||
or cited for examples or mathematical definitions used in this documentation.
|
||||
References not listed here can be found in the source code.
|
||||
|
||||
.. [AbramowitzStegun] M Abramowitz & I Stegun. *Handbook of Mathematical Functions, 9th Ed.*, Tenth Printing, December 1972, with corrections (electronic copy: http://people.math.sfu.ca/~cbm/aands/)
|
||||
.. [AbramowitzStegun] M Abramowitz & I Stegun. *Handbook of Mathematical
|
||||
Functions, 9th Ed.*, Tenth Printing, December 1972,
|
||||
with corrections (electronic copy:
|
||||
http://people.math.sfu.ca/~cbm/aands/)
|
||||
|
||||
.. [Abate] Abate, J., P. Valko (2004). Multi-precision Laplace transform inversion. *International Journal for Numerical Methods in Engineering* 60:979-993, http://dx.doi.org/10.1002/nme.995
|
||||
.. [Abate] Abate, J., P. Valko (2004). Multi-precision Laplace transform
|
||||
inversion. *International Journal for Numerical Methods
|
||||
in Engineering* 60:979-993, http://dx.doi.org/10.1002/nme.995
|
||||
|
||||
.. [Ainsworth] O. R. Ainsworth & L. W. Howell, "An integral representation of the generalized Euler-Mascheroni constants", NASA Technical Paper 2456 (1985), http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19850014994_1985014994.pdf
|
||||
.. [Ainsworth] O. R. Ainsworth & L. W. Howell, "An integral representation
|
||||
of the generalized Euler-Mascheroni constants", NASA
|
||||
Technical Paper 2456 (1985),
|
||||
http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19850014994_1985014994.pdf
|
||||
|
||||
.. [Bailey] D H Bailey. "Tanh-Sinh High-Precision Quadrature", http://crd.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf
|
||||
.. [Bailey] D H Bailey. "Tanh-Sinh High-Precision Quadrature",
|
||||
http://crd.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf
|
||||
|
||||
.. [Bellman] Bellman, R., R.E. Kalaba, J.A. Lockett (1966). *Numerical inversion of the Laplace transform: Applications to Biology, Economics, Engineering, and Physics*. Elsevier.
|
||||
.. [Bellman] Bellman, R., R.E. Kalaba, J.A. Lockett (1966). *Numerical
|
||||
inversion of the Laplace transform: Applications to Biology,
|
||||
Economics, Engineering, and Physics*. Elsevier.
|
||||
|
||||
.. [BenderOrszag] C M Bender & S A Orszag. *Advanced Mathematical Methods for
|
||||
Scientists and Engineers*, Springer 1999
|
||||
Scientists and Engineers*, Springer 1999
|
||||
|
||||
.. [Bernoulli] The Bernoulli Number Page: http://www.bernoulli.org/
|
||||
|
||||
.. [BorweinBailey] J Borwein, D H Bailey & R Girgensohn. *Experimentation in Mathematics - Computational Paths to Discovery*, A K Peters, 2003
|
||||
.. [BorweinBailey] J Borwein, D H Bailey & R Girgensohn. *Experimentation in
|
||||
Mathematics - Computational Paths to Discovery*,
|
||||
A K Peters, 2003
|
||||
|
||||
.. [BorweinBorwein] J Borwein & P B Borwein. *Pi and the AGM: A Study in Analytic Number Theory and Computational Complexity*, Wiley 1987
|
||||
.. [BorweinBorwein] J Borwein & P B Borwein. *Pi and the AGM: A Study in
|
||||
Analytic Number Theory and Computational Complexity*,
|
||||
Wiley 1987
|
||||
|
||||
.. [BorweinTanhSinh] Borwein, Jonathan Michael and Lingyun Ye. “Quadratic Convergence of the Tanh-sinh Quadrature Rule.” (2006). https://web.archive.org/web/20080221230631/http://users.cs.dal.ca/~jborwein/tanh-sinh.pdf
|
||||
.. [BorweinTanhSinh] Borwein, Jonathan Michael and Lingyun Ye. “Quadratic
|
||||
Convergence of the Tanh-sinh Quadrature Rule.” (2006).
|
||||
https://web.archive.org/web/20080221230631/http://users.cs.dal.ca/~jborwein/tanh-sinh.pdf
|
||||
|
||||
.. [BorweinZeta] P Borwein. "An Efficient Algorithm for the Riemann Zeta Function", http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf
|
||||
.. [BorweinZeta] P Borwein. "An Efficient Algorithm for the Riemann Zeta
|
||||
Function", http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf
|
||||
|
||||
.. [Brent79] R. P. Brent, On the Zeros of the Riemann Zeta Function in the Critical Strip, Math. Comp. 33 (1979) 1361--1372
|
||||
.. [Brent79] R. P. Brent, On the Zeros of the Riemann Zeta Function in the
|
||||
Critical Strip, Math. Comp. 33 (1979) 1361--1372
|
||||
|
||||
.. [Brent86] R. P. Brent, J. van de Lune, H. J. J. te Riele, D. T. Winter, 'On the Zeros of the Riemann Zeta Function in the Critical Strip. II', Math. Comp. 39 (1982) 681--688.
|
||||
.. [Brent86] R. P. Brent, J. van de Lune, H. J. J. te Riele, D. T. Winter,
|
||||
'On the Zeros of the Riemann Zeta Function in the Critical
|
||||
Strip. II', Math. Comp. 39 (1982) 681--688.
|
||||
|
||||
.. [Buhring] Wolfgang Buhring, "Generalized Hypergeometric Functions at Unit Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992), pp.145-153
|
||||
.. [Buhring] Wolfgang Buhring, "Generalized Hypergeometric Functions at Unit
|
||||
Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992),
|
||||
pp.145-153
|
||||
|
||||
.. [CabralRosetti] L G Cabral-Rosetti & M A Sanchis-Lozano. "Appell Functions and the Scalar One-Loop Three-point Integrals in Feynman Diagrams". http://arxiv.org/abs/hep-ph/0206081
|
||||
.. [CabralRosetti] L G Cabral-Rosetti & M A Sanchis-Lozano. "Appell Functions
|
||||
and the Scalar One-Loop Three-point Integrals in Feynman
|
||||
Diagrams". http://arxiv.org/abs/hep-ph/0206081
|
||||
|
||||
.. [Carlson] B C Carlson. "Numerical computation of real or complex elliptic integrals". http://arxiv.org/abs/math/9409227v1
|
||||
.. [Carlson] B C Carlson. "Numerical computation of real or complex elliptic
|
||||
integrals". http://arxiv.org/abs/math/9409227v1
|
||||
|
||||
.. [Coffey] M. W. Coffey, "The Stieltjes constants, their relation to the `\eta_j` coefficients, and representation of the Hurwitz zeta function", arXiv:0706.0343v1 http://arxiv.org/abs/0706.0343
|
||||
.. [Coffey] M. W. Coffey, "The Stieltjes constants, their relation to the
|
||||
`\eta_j` coefficients, and representation of the Hurwitz zeta
|
||||
function", arXiv:0706.0343v1 http://arxiv.org/abs/0706.0343
|
||||
|
||||
.. [Cohen] Cohen, A.M. (2007). Numerical Methods for Laplace Transform Inversion, Springer.
|
||||
.. [Cohen] Cohen, A.M. (2007). Numerical Methods for Laplace Transform
|
||||
Inversion, Springer.
|
||||
|
||||
.. [Corless] R M Corless et al. "On the Lambert W function", Adv. Comp. Math. 5 (1996) 329-359. http://www.apmaths.uwo.ca/~djeffrey/Offprints/W-adv-cm.pdf
|
||||
.. [Corless] R M Corless et al. "On the Lambert W function", Adv. Comp.
|
||||
Math. 5 (1996) 329-359.
|
||||
http://www.apmaths.uwo.ca/~djeffrey/Offprints/W-adv-cm.pdf
|
||||
|
||||
.. [Crandall] Richard Crandall, "Note on fast polylogarithm computation" http://www.reed.edu/physics/faculty/crandall/papers/Polylog.pdf
|
||||
.. [Crandall] Richard Crandall, "Note on fast polylogarithm computation"
|
||||
http://www.reed.edu/physics/faculty/crandall/papers/Polylog.pdf
|
||||
|
||||
.. [Davies] Davies, B. (2005). *Integral Transforms and their Applications*, Third Edition. Springer.
|
||||
.. [Davies] Davies, B. (2005). *Integral Transforms and their Applications*,
|
||||
Third Edition. Springer.
|
||||
|
||||
.. [Davies79] Davies, B., B. Martin (1979). Numerical inversion of the Laplace transform: a survey and comparison of methods. *Journal of Computational Physics* 33:1-32, http://dx.doi.org/10.1016/0021-9991(79)90025-1
|
||||
.. [Davies79] Davies, B., B. Martin (1979). Numerical inversion of the Laplace
|
||||
transform: a survey and comparison of methods. *Journal of
|
||||
Computational Physics* 33:1-32,
|
||||
http://dx.doi.org/10.1016/0021-9991(79)90025-1
|
||||
|
||||
.. [Duffy93] Duffy, D.G. (1993). On the numerical inversion of Laplace transforms: Comparison of three new methods on characteristic problems from applications. *ACM Transactions on Mathematical Software* 19(3):333-359, http://dx.doi.org/10.1145/155743.155788
|
||||
.. [Duffy93] Duffy, D.G. (1993). On the numerical inversion of Laplace
|
||||
transforms: Comparison of three new methods on characteristic
|
||||
problems from applications. *ACM Transactions on Mathematical
|
||||
Software* 19(3):333-359, http://dx.doi.org/10.1145/155743.155788
|
||||
|
||||
.. [Duffy98] Duffy, D.G. (1998). Advanced Engineering Mathematics, CRC Press.
|
||||
|
||||
.. [DLMF] NIST Digital Library of Mathematical Functions. http://dlmf.nist.gov/
|
||||
|
||||
.. [Froberg] Carl-Erik Froberg, "On the prime zeta function", BIT 8 (1968), pp. 187-202.
|
||||
.. [Froberg] Carl-Erik Froberg, "On the prime zeta function", BIT 8 (1968),
|
||||
pp. 187-202.
|
||||
|
||||
.. [Glasserman] P. Glasserman, J. Ruiz-Mata (2006). Computing the credit loss distribution in the Gaussian copula model: a comparison of methods. *Journal of Credit Risk* 2(4):33-66, 10.21314/JCR.2006.057
|
||||
.. [Glasserman] P. Glasserman, J. Ruiz-Mata (2006). Computing the credit loss
|
||||
distribution in the Gaussian copula model: a comparison of
|
||||
methods. *Journal of Credit Risk* 2(4):33-66,
|
||||
10.21314/JCR.2006.057
|
||||
|
||||
.. [Golub] golub, "some modified matrix eigenvalue problems", siam review 15, p. 318-334 (1973)
|
||||
.. [Golub] golub, "some modified matrix eigenvalue problems", siam review
|
||||
15, p. 318-334 (1973)
|
||||
|
||||
.. [GolubWelsch] golub and welsch, "calculations of gaussian quadrature rules", mathematics of computation 23, p. 221-230 (1969)
|
||||
.. [GolubWelsch] golub and welsch, "calculations of gaussian quadrature
|
||||
rules", mathematics of computation 23, p. 221-230 (1969)
|
||||
|
||||
.. [Gourdon] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf
|
||||
.. [Gourdon] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma
|
||||
http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf
|
||||
|
||||
.. [GradshteynRyzhik] I S Gradshteyn & I M Ryzhik, A Jeffrey & D Zwillinger (eds.), *Table of Integrals, Series and Products*, Seventh edition (2007), Elsevier
|
||||
.. [GradshteynRyzhik] I S Gradshteyn & I M Ryzhik, A Jeffrey & D Zwillinger
|
||||
(eds.), *Table of Integrals, Series and Products*,
|
||||
Seventh edition (2007), Elsevier
|
||||
|
||||
.. [GravesMorris] P R Graves-Morris, D E Roberts & A Salam. "The epsilon algorithm and related topics", *Journal of Computational and Applied Mathematics*, Volume 122, Issue 1-2 (October 2000)
|
||||
.. [GravesMorris] P R Graves-Morris, D E Roberts & A Salam. "The epsilon
|
||||
algorithm and related topics", *Journal of Computational
|
||||
and Applied Mathematics*, Volume 122, Issue 1-2
|
||||
(October 2000)
|
||||
|
||||
.. [Homeier] H.H.H. Homeier - "Scalar Levin-Type Sequence Transformations" arXiv:math/0005209
|
||||
.. [Homeier] H.H.H. Homeier - "Scalar Levin-Type Sequence Transformations"
|
||||
arXiv:math/0005209
|
||||
|
||||
.. [Hoog] de Hoog, F., J. Knight, A. Stokes (1982). An improved method for numerical inversion of Laplace transforms. *SIAM Journal of Scientific and Statistical Computing* 3:357-366, http://dx.doi.org/10.1137/0903022
|
||||
.. [Hoog] de Hoog, F., J. Knight, A. Stokes (1982). An improved method for
|
||||
numerical inversion of Laplace transforms. *SIAM Journal of
|
||||
Scientific and Statistical Computing* 3:357-366,
|
||||
http://dx.doi.org/10.1137/0903022
|
||||
|
||||
.. [Kresser] Numerical Methods for General and Structured Eigenvalue Problems
|
||||
|
||||
.. [Kuhlman] Kuhlman, K.L., (2013). Review of Inverse Laplace Transform Algorithms for Laplace-Space Numerical Approaches, *Numerical Algorithms*, 63(2):339-355. http://dx.doi.org/10.1007/s11075-012-9625-3
|
||||
.. [Kuhlman] Kuhlman, K.L., (2013). Review of Inverse Laplace Transform
|
||||
Algorithms for Laplace-Space Numerical Approaches,
|
||||
*Numerical Algorithms*, 63(2):339-355.
|
||||
http://dx.doi.org/10.1007/s11075-012-9625-3
|
||||
|
||||
.. [Lune84] J. van de Lune, 'Sums of Equal Powers of Positive Integers', Dissertation, Vrije Universiteit te Amsterdam, Centrum voor Wiskunde en Informatica, Amsterdam, 1984.
|
||||
.. [Lune84] J. van de Lune, 'Sums of Equal Powers of Positive Integers',
|
||||
Dissertation, Vrije Universiteit te Amsterdam, Centrum voor
|
||||
Wiskunde en Informatica, Amsterdam, 1984.
|
||||
|
||||
.. [Lune86] J. van de Lune, H. J. J. te Riele, 'On the Zeros of the Riemann Zeta Function in the Critical Strip. III', Math. Comp. 41 (1983) 759--767.
|
||||
.. [Lune86] J. van de Lune, H. J. J. te Riele, 'On the Zeros of the Riemann
|
||||
Zeta Function in the Critical Strip. III', Math. Comp. 41
|
||||
(1983) 759--767.
|
||||
|
||||
.. [MPFR] The MPFR team. "The MPFR Library: Algorithms and Proofs", http://www.mpfr.org/algorithms.pdf
|
||||
.. [MPFR] The MPFR team. "The MPFR Library: Algorithms and Proofs",
|
||||
http://www.mpfr.org/algorithms.pdf
|
||||
|
||||
.. [Michel] N. Michel, "Precise Coulomb wave functions for a wide range of complex `l`, `\eta` and `z`", http://arxiv.org/abs/physics/0702051v1
|
||||
.. [Michel] N. Michel, "Precise Coulomb wave functions for a wide
|
||||
range of complex `l`, `\eta` and `z`",
|
||||
http://arxiv.org/abs/physics/0702051v1
|
||||
|
||||
.. [OEIS] The On-Line Encyclopedia of Integer Sequences (OEIS).
|
||||
|
||||
.. [Sidi] A. Sidi - "Pratical Extrapolation Methods"
|
||||
.. [Sidi] A. Sidi - "Pratical Extrapolation
|
||||
Methods".
|
||||
|
||||
.. [Slater] L J Slater. *Generalized Hypergeometric Functions*. Cambridge University Press, 1966
|
||||
.. [Slater] L J Slater. *Generalized Hypergeometric Functions*.
|
||||
Cambridge University Press, 1966
|
||||
|
||||
.. [Spouge] J L Spouge. "Computation of the gamma, digamma, and trigamma functions", SIAM J. Numer. Anal. Vol. 31, No. 3, pp. 931-944, June 1994.
|
||||
.. [Spouge] J L Spouge. "Computation of the gamma, digamma, and trigamma
|
||||
functions", SIAM J. Numer. Anal. Vol. 31, No. 3, pp. 931-944,
|
||||
June 1994.
|
||||
|
||||
.. [SrivastavaKarlsson] H M Srivastava & P W Karlsson. *Multiple Gaussian Hypergeometric Series*. Ellis Horwood, 1985.
|
||||
.. [SrivastavaKarlsson] H M Srivastava & P W Karlsson. *Multiple Gaussian
|
||||
Hypergeometric Series*. Ellis Horwood, 1985.
|
||||
|
||||
.. [Stehfest] Stehfest, H. (1970). Algorithm 368: numerical inversion of Laplace transforms. *Communications of the ACM* 13(1):47-49, http://dx.doi.org/10.1145/361953.361969
|
||||
.. [Stehfest] Stehfest, H. (1970). Algorithm 368: numerical inversion of
|
||||
Laplace transforms. *Communications of the ACM* 13(1):47-49,
|
||||
http://dx.doi.org/10.1145/361953.361969
|
||||
|
||||
.. [Stoer] Stoer, Bulirsch - Introduction to Numerical Analysis.
|
||||
|
||||
.. [Stroud] stroud and secrest, "gaussian quadrature formulas", prentice-hall (1966)
|
||||
.. [Stroud] stroud and secrest, "gaussian quadrature formulas",
|
||||
prentice-hall (1966)
|
||||
|
||||
.. [Talbot] Talbot, A. (1979). The accurate numerical inversion of Laplace transforms. *IMA Journal of Applied Mathematics* 23(1):97, http://dx.doi.org/10.1093/imamat/23.1.97
|
||||
.. [Talbot] Talbot, A. (1979). The accurate numerical inversion of Laplace
|
||||
transforms. *IMA Journal of Applied Mathematics* 23(1):97,
|
||||
http://dx.doi.org/10.1093/imamat/23.1.97
|
||||
|
||||
.. [Thompson] I.J. Thompson & A.R. Barnett, "Coulomb and Bessel Functions of Complex Arguments and Order", J. Comp. Phys., vol 64, no. 2, June 1986.
|
||||
.. [Thompson] I.J. Thompson & A.R. Barnett, "Coulomb and Bessel Functions
|
||||
of Complex Arguments and Order", J. Comp. Phys., vol 64, no.
|
||||
2, June 1986.
|
||||
|
||||
.. [Trudgian] T. Trudgian, Improvements to Turing Method, Math. Comp.
|
||||
.. [Trudgian] T. Trudgian, Improvements to Turing Method,
|
||||
Math. Comp.
|
||||
|
||||
.. [Vidunas] R Vidunas. "Identities between Appell's and hypergeometric functions". http://arxiv.org/abs/0804.0655
|
||||
.. [Vidunas] R Vidunas. "Identities between Appell's and hypergeometric
|
||||
functions". http://arxiv.org/abs/0804.0655
|
||||
|
||||
.. [Voros2003] A. Voros, Zeta functions for the Riemann zeros, Ann. Institute Fourier, 53, (2003) 665--699.
|
||||
.. [Voros2003] A. Voros, Zeta functions for the Riemann zeros, Ann.
|
||||
Institute Fourier, 53, (2003) 665--699.
|
||||
|
||||
.. [Voros2009] A. Voros, Zeta functions over Zeros of Zeta Functions, Lecture Notes of the Unione Matematica Italiana, Springer, 2009.
|
||||
.. [Voros2009] A. Voros, Zeta functions over Zeros of Zeta Functions,
|
||||
Lecture Notes of the Unione Matematica Italiana, Springer, 2009.
|
||||
|
||||
.. [Weisstein] E W Weisstein. *MathWorld*. http://mathworld.wolfram.com/
|
||||
|
||||
.. [Weniger] E.J. Weniger - "Nonlinear Sequence Transformations for the Acceleration of Convergence and the Summation of Divergent Series" arXiv:math/0306302
|
||||
.. [Weniger] E.J. Weniger - "Nonlinear Sequence Transformations for the
|
||||
Acceleration of Convergence and the Summation of Divergent
|
||||
Series" arXiv:math/0306302
|
||||
|
||||
.. [WhittakerWatson] E T Whittaker & G N Watson. *A Course of Modern Analysis*. 4th Ed. 1946 Cambridge University Press
|
||||
.. [WhittakerWatson] E T Whittaker & G N Watson. *A Course of Modern Analysis*.
|
||||
4th Ed. 1946 Cambridge University Press
|
||||
|
||||
.. [Widder] Widder, D. (1941). *The Laplace Transform*. Princeton.
|
||||
|
||||
.. [Wikipedia] *Wikipedia, the free encyclopedia*. http://en.wikipedia.org/wiki/Main_Page
|
||||
.. [Wikipedia] *Wikipedia, the free encyclopedia*.
|
||||
http://en.wikipedia.org/wiki/Main_Page
|
||||
|
||||
.. [WolframFunctions] Wolfram Research, Inc. *The Wolfram Functions Site*. http://functions.wolfram.com/
|
||||
.. [WolframFunctions] Wolfram Research, Inc. *The Wolfram Functions Site*.
|
||||
http://functions.wolfram.com/
|
||||
|
||||
+3
-14
@@ -1,8 +1,8 @@
|
||||
Setting up mpmath
|
||||
=================
|
||||
|
||||
Mpmath requires at least Python 3.9. It has been tested with CPython 3.9
|
||||
through 3.14 and for PyPy 3.11.
|
||||
Mpmath requires at least Python 3.10. It has been tested with CPython 3.10
|
||||
through 3.15 and for PyPy 3.11.
|
||||
|
||||
Download and installation
|
||||
-------------------------
|
||||
@@ -40,24 +40,13 @@ See `debian <http://packages.debian.org/stable/python/python3-mpmath>`_ and
|
||||
`ubuntu <https://launchpad.net/ubuntu/+source/mpmath>`_ package information;
|
||||
please verify that you are getting the latest version.
|
||||
|
||||
OpenSUSE
|
||||
........
|
||||
|
||||
Mpmath is provided in the "Science" repository for all recent versions of
|
||||
`openSUSE <https://www.opensuse.org/>`_. To add this repository to the YAST
|
||||
software management tool, see
|
||||
https://en.opensuse.org/SDB:Add_package_repositories
|
||||
|
||||
Look up https://download.opensuse.org/repositories/science/ for a list
|
||||
of supported OpenSUSE versions.
|
||||
|
||||
Current development version
|
||||
...........................
|
||||
|
||||
If you are a developer or like to get the latest updates as they come, be sure
|
||||
to install from git::
|
||||
|
||||
git clone git://github.com/mpmath/mpmath.git
|
||||
git clone https://github.com/mpmath/mpmath.git
|
||||
cd mpmath
|
||||
pip install -e .[develop,docs]
|
||||
|
||||
|
||||
+3
-2
@@ -87,9 +87,9 @@ With *prec* bits of precision, an arbitrary number can be approximated relativel
|
||||
|
||||
More precisely, mpmath uses the following formulas to translate between *prec* and *dps*::
|
||||
|
||||
dps(prec) = max(1, int(round(int(prec) / C - 1)))
|
||||
dps(prec) = max(1, round(int(prec)/C - 1))
|
||||
|
||||
prec(dps) = max(1, int(round((int(dps) + 1) * C)))
|
||||
prec(dps) = max(1, round((int(dps) + 1)*C))
|
||||
|
||||
Note that the dps is set 1 decimal digit lower than the corresponding binary precision. This is done to hide minor rounding errors and artifacts resulting from binary-decimal conversion. As a result, mpmath interprets 53 bits as giving 15 digits of decimal precision, not 16.
|
||||
|
||||
@@ -124,6 +124,7 @@ Operations that are correctly rounded:
|
||||
* Division and square roots of real numbers.
|
||||
* Powers of real numbers, assuming sufficiently small integer exponents (huge powers are rounded in the right direction, but possibly farther than necessary).
|
||||
* Conversion from decimal to binary, for reasonably sized numbers (roughly between `10^{-100}` and `10^{100}`).
|
||||
* Conversion from/to machine floating-point numbers.
|
||||
* Typically, transcendental functions for exact input-output pairs.
|
||||
|
||||
Operations that should be fully accurate (however, the current implementation may be based on a heuristic error analysis):
|
||||
|
||||
+17
-9
@@ -1,7 +1,4 @@
|
||||
from importlib.metadata import version
|
||||
|
||||
__version__ = version(__name__)
|
||||
del version
|
||||
from ._version import __version__
|
||||
|
||||
import functools
|
||||
import sys
|
||||
@@ -13,10 +10,6 @@ from .ctx_fp import FPContext
|
||||
from .ctx_mp import MPContext
|
||||
from .ctx_iv import MPIntervalContext
|
||||
|
||||
# deprecated modules
|
||||
from . import rational
|
||||
from . import math2
|
||||
|
||||
fp = FPContext()
|
||||
mp = MPContext()
|
||||
iv = MPIntervalContext()
|
||||
@@ -50,11 +43,21 @@ mfrom = mp.mfrom
|
||||
kfrom = mp.kfrom
|
||||
taufrom = mp.taufrom
|
||||
qbarfrom = mp.qbarfrom
|
||||
g2g3from = mp.g2g3from
|
||||
omega1omega2from = mp.omega1omega2from
|
||||
ellipfun = mp.ellipfun
|
||||
jtheta = mp.jtheta
|
||||
kleinj = mp.kleinj
|
||||
kleinjinv = mp.kleinjinv
|
||||
eta = mp.eta
|
||||
|
||||
# Weierstrass elliptic functions
|
||||
weierp = mp.weierp
|
||||
weierpprime = mp.weierpprime
|
||||
weiersigma = mp.weiersigma
|
||||
weierzeta = mp.weierzeta
|
||||
weierpinv = mp.weierpinv
|
||||
|
||||
qp = mp.qp
|
||||
qhyper = mp.qhyper
|
||||
qgamma = mp.qgamma
|
||||
@@ -75,7 +78,6 @@ multiplicity = mp.multiplicity
|
||||
isinf = mp.isinf
|
||||
isnan = mp.isnan
|
||||
isnormal = mp.isnormal
|
||||
isspecial = mp.isspecial
|
||||
isint = mp.isint
|
||||
isfinite = mp.isfinite
|
||||
almosteq = mp.almosteq
|
||||
@@ -162,6 +164,7 @@ lu = mp.lu
|
||||
qr = mp.qr
|
||||
unitvector = mp.unitvector
|
||||
inverse = mp.inverse
|
||||
pinv = mp.pinv
|
||||
residual = mp.residual
|
||||
qr_solve = mp.qr_solve
|
||||
cholesky = mp.cholesky
|
||||
@@ -219,6 +222,7 @@ mertens = mp.mertens
|
||||
|
||||
ldexp = mp.ldexp
|
||||
frexp = mp.frexp
|
||||
ulp = mp.ulp
|
||||
|
||||
fsum = mp.fsum
|
||||
fdot = mp.fdot
|
||||
@@ -398,6 +402,8 @@ besseljzero = mp.besseljzero
|
||||
besselyzero = mp.besselyzero
|
||||
spherical_jn = mp.spherical_jn
|
||||
spherical_yn = mp.spherical_yn
|
||||
spherical_in = mp.spherical_in
|
||||
spherical_kn = mp.spherical_kn
|
||||
hankel1 = mp.hankel1
|
||||
hankel2 = mp.hankel2
|
||||
struveh = mp.struveh
|
||||
@@ -446,6 +452,8 @@ trianglew = mp.trianglew
|
||||
sawtoothw = mp.sawtoothw
|
||||
unit_triangle = mp.unit_triangle
|
||||
sigmoid = mp.sigmoid
|
||||
fft = mp.fft
|
||||
invfft = mp.invfft
|
||||
|
||||
|
||||
# Hack to guard against setting module properties instead of 'mp', Issue #657
|
||||
|
||||
@@ -40,6 +40,11 @@ parser.add_argument('--prec', type=int,
|
||||
help='Set default mpmath precision')
|
||||
parser.add_argument('--no-pretty', help='Disable pretty-printing',
|
||||
action='store_true')
|
||||
parser.add_argument('--int-limits',
|
||||
help="Enable string conversion length limitation for int's",
|
||||
action='store_true')
|
||||
parser.add_argument('--shortest-str', help='Use shortest str/repr',
|
||||
action='store_true')
|
||||
|
||||
|
||||
def main():
|
||||
@@ -49,7 +54,11 @@ def main():
|
||||
print(__version__)
|
||||
sys.exit(0)
|
||||
|
||||
if not args.int_limits:
|
||||
sys.set_int_max_str_digits(0)
|
||||
|
||||
lines = ['from mpmath import *',
|
||||
'import mpmath',
|
||||
'from fractions import Fraction']
|
||||
|
||||
if args.prec:
|
||||
@@ -57,6 +66,8 @@ def main():
|
||||
if not args.no_pretty:
|
||||
lines.append('mp.pretty = True')
|
||||
lines.append('mp.pretty_dps = "repr"')
|
||||
if args.shortest_str:
|
||||
lines.append('mp.shortest_str = True')
|
||||
|
||||
try:
|
||||
import IPython
|
||||
|
||||
@@ -4,3 +4,4 @@ from . import approximation
|
||||
from . import differentiation
|
||||
from . import extrapolation
|
||||
from . import polynomials
|
||||
from . import fft
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import warnings
|
||||
|
||||
from .calculus import defun
|
||||
|
||||
|
||||
@@ -38,7 +36,7 @@ def chebT(ctx, a=1, b=0):
|
||||
Ta, Tb = Tmp, Ta
|
||||
|
||||
@defun
|
||||
def chebyfit(ctx, f, interval, N, error=False, asc=None):
|
||||
def chebyfit(ctx, f, interval, N, error=False, asc=True):
|
||||
r"""
|
||||
Computes a polynomial of degree `N-1` that approximates the
|
||||
given function `f` on the interval `[a, b]`. With ``error=True``,
|
||||
@@ -67,7 +65,7 @@ def chebyfit(ctx, f, interval, N, error=False, asc=None):
|
||||
|
||||
>>> from mpmath import mp, chebyfit, cos, nprint, polyval
|
||||
>>> mp.pretty = True
|
||||
>>> poly, err = chebyfit(cos, [1, 2], 5, error=True, asc=True)
|
||||
>>> poly, err = chebyfit(cos, [1, 2], 5, error=True)
|
||||
>>> nprint(poly)
|
||||
[0.949553, 0.174141, -0.732491, 0.146166, 0.00291682]
|
||||
>>> nprint(err, 12)
|
||||
@@ -75,8 +73,8 @@ def chebyfit(ctx, f, interval, N, error=False, asc=None):
|
||||
|
||||
The polynomial can be evaluated using ``polyval``::
|
||||
|
||||
>>> poly = chebyfit(cos, [1, 2], 5, asc=True)
|
||||
>>> nprint(polyval(poly, 1.6, asc=True), 12)
|
||||
>>> poly = chebyfit(cos, [1, 2], 5)
|
||||
>>> nprint(polyval(poly, 1.6), 12)
|
||||
-0.0291858904138
|
||||
>>> nprint(cos(1.6), 12)
|
||||
-0.0291995223013
|
||||
@@ -84,7 +82,7 @@ def chebyfit(ctx, f, interval, N, error=False, asc=None):
|
||||
Sampling the true error at 1000 points shows that the error
|
||||
estimate generated by ``chebyfit`` is remarkably good::
|
||||
|
||||
>>> error = lambda x: abs(cos(x) - polyval(poly, x, asc=True))
|
||||
>>> error = lambda x: abs(cos(x) - polyval(poly, x))
|
||||
>>> nprint(max([error(1+n/1000.) for n in range(1000)]), 12)
|
||||
1.61349954245e-5
|
||||
|
||||
@@ -117,6 +115,8 @@ def chebyfit(ctx, f, interval, N, error=False, asc=None):
|
||||
nonsmooth features, or by dividing the interval into several
|
||||
segments.
|
||||
"""
|
||||
if N <= 0:
|
||||
raise ValueError("chebyfit requires N >= 1")
|
||||
a, b = ctx._as_points(interval)
|
||||
orig = ctx.prec
|
||||
try:
|
||||
@@ -133,15 +133,9 @@ def chebyfit(ctx, f, interval, N, error=False, asc=None):
|
||||
err = ctx.zero
|
||||
for k in range(N):
|
||||
x = ctx.cos(ctx.pi*k/N) * (b-a)*h + (b+a)*h
|
||||
err = max(err, abs(f(x) - ctx.polyval(d, x, asc=True)))
|
||||
err = max(err, abs(f(x) - ctx.polyval(d, x)))
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
if asc is None:
|
||||
warnings.warn("Descending (wrt powers) order of polynomial "
|
||||
"coefficients is deprecated, please adapt you "
|
||||
"code to use ascending order, asc=True.",
|
||||
DeprecationWarning)
|
||||
asc = False
|
||||
if error:
|
||||
return d if asc else d[::-1], +err
|
||||
else:
|
||||
|
||||
@@ -28,17 +28,14 @@ def difference(ctx, s, n):
|
||||
b = (b * (k-n)) // (k+1)
|
||||
return d
|
||||
|
||||
def hsteps(ctx, f, x, n, prec, **options):
|
||||
singular = options.get('singular')
|
||||
addprec = options.get('addprec', 10)
|
||||
direction = options.get('direction', 0)
|
||||
def hsteps(ctx, f, x, n, prec, *, method='step', direction=0, radius=0.25,
|
||||
singular=False, addprec=10, relative=False, h=None):
|
||||
workprec = (prec+2*addprec) * (n+1)
|
||||
orig = ctx.prec
|
||||
try:
|
||||
ctx.prec = workprec
|
||||
h = options.get('h')
|
||||
if h is None:
|
||||
if options.get('relative'):
|
||||
if relative:
|
||||
hextramag = int(ctx.mag(x))
|
||||
else:
|
||||
hextramag = 0
|
||||
@@ -46,7 +43,6 @@ def hsteps(ctx, f, x, n, prec, **options):
|
||||
else:
|
||||
h = ctx.convert(h)
|
||||
# Directed: steps x, x+h, ... x+n*h
|
||||
direction = options.get('direction', 0)
|
||||
if direction:
|
||||
h *= ctx.sign(direction)
|
||||
steps = range(n+1)
|
||||
@@ -65,7 +61,8 @@ def hsteps(ctx, f, x, n, prec, **options):
|
||||
|
||||
|
||||
@defun
|
||||
def diff(ctx, f, x, n=1, **options):
|
||||
def diff(ctx, f, x, n=1, *, method='step', direction=0, radius=0.25,
|
||||
singular=False, addprec=10, relative=False, h=None):
|
||||
r"""
|
||||
Numerically computes the derivative of `f`, `f'(x)`, or generally for
|
||||
an integer `n \ge 0`, the `n`-th derivative `f^{(n)}(x)`.
|
||||
@@ -176,11 +173,13 @@ def diff(ctx, f, x, n=1, **options):
|
||||
partial = True
|
||||
except TypeError:
|
||||
pass
|
||||
options = {'method': method, 'singular': singular,
|
||||
'addprec': addprec, 'direction': direction,
|
||||
'radius': radius, 'relative': relative, 'h': h}
|
||||
if partial:
|
||||
x = [ctx.convert(_) for _ in x]
|
||||
return _partial_diff(ctx, f, x, orders, options)
|
||||
method = options.get('method', 'step')
|
||||
if n == 0 and method != 'quad' and not options.get('singular'):
|
||||
if n == 0 and method != 'quad' and not singular:
|
||||
return f(ctx.convert(x))
|
||||
prec = ctx.prec
|
||||
try:
|
||||
@@ -190,7 +189,6 @@ def diff(ctx, f, x, n=1, **options):
|
||||
v = ctx.difference(values, n) / norm**n
|
||||
elif method == 'quad':
|
||||
ctx.prec += 10
|
||||
radius = ctx.convert(options.get('radius', 0.25))
|
||||
def g(t):
|
||||
rei = radius*ctx.expj(t)
|
||||
z = x + rei
|
||||
@@ -221,7 +219,8 @@ def _partial_diff(ctx, f, xs, orders, options):
|
||||
return _partial_diff(ctx, fdiff_inner, xs, orders, options)
|
||||
|
||||
@defun
|
||||
def diffs(ctx, f, x, n=None, **options):
|
||||
def diffs(ctx, f, x, n=None, *, method='step', direction=0, radius=0.25,
|
||||
singular=False, addprec=10, relative=False, h=None):
|
||||
r"""
|
||||
Returns a generator that yields the sequence of derivatives
|
||||
|
||||
@@ -261,13 +260,15 @@ def diffs(ctx, f, x, n=None, **options):
|
||||
n = ctx.inf
|
||||
else:
|
||||
n = int(n)
|
||||
if options.get('method', 'step') != 'step':
|
||||
options = {'method': method, 'singular': singular,
|
||||
'addprec': addprec, 'direction': direction,
|
||||
'radius': radius, 'relative': relative, 'h': h}
|
||||
if method != 'step':
|
||||
k = 0
|
||||
while k < n + 1:
|
||||
yield ctx.diff(f, x, k, **options)
|
||||
k += 1
|
||||
return
|
||||
singular = options.get('singular')
|
||||
if singular:
|
||||
yield ctx.diff(f, x, 0, singular=True)
|
||||
else:
|
||||
@@ -529,7 +530,8 @@ def differint(ctx, f, x, n=1, x0=0):
|
||||
return ctx.diff(g, x, m) / ctx.gamma(m-n)
|
||||
|
||||
@defun
|
||||
def diffun(ctx, f, n=1, **options):
|
||||
def diffun(ctx, f, n=1, *, method='step', direction=0, radius=0.25,
|
||||
singular=False, addprec=10, relative=False, h=None):
|
||||
r"""
|
||||
Given a function `f`, returns a function `g(x)` that evaluates the nth
|
||||
derivative `f^{(n)}(x)`::
|
||||
@@ -547,6 +549,9 @@ def diffun(ctx, f, n=1, **options):
|
||||
See :func:`~mpmath.diff` for additional details and supported
|
||||
keyword options.
|
||||
"""
|
||||
options = {'method': method, 'singular': singular,
|
||||
'addprec': addprec, 'direction': direction,
|
||||
'radius': radius, 'relative': relative, 'h': h}
|
||||
if n == 0:
|
||||
return f
|
||||
def g(x):
|
||||
@@ -554,7 +559,8 @@ def diffun(ctx, f, n=1, **options):
|
||||
return g
|
||||
|
||||
@defun
|
||||
def taylor(ctx, f, x, n, **options):
|
||||
def taylor(ctx, f, x, n, *, chop=True, method='step', direction=0, radius=0.25,
|
||||
singular=False, addprec=10, relative=False, h=None):
|
||||
r"""
|
||||
Produces a degree-`n` Taylor polynomial around the point `x` of the
|
||||
given function `f`. The coefficients are returned as a list.
|
||||
@@ -574,14 +580,17 @@ def taylor(ctx, f, x, n, **options):
|
||||
the argument:
|
||||
|
||||
>>> p = taylor(exp, 2.0, 10)
|
||||
>>> polyval(p, 2.5 - 2.0, asc=True)
|
||||
>>> polyval(p, 2.5 - 2.0)
|
||||
12.1824939606092
|
||||
>>> exp(2.5)
|
||||
12.1824939607035
|
||||
|
||||
"""
|
||||
options = {'method': method, 'singular': singular,
|
||||
'addprec': addprec, 'direction': direction,
|
||||
'radius': radius, 'relative': relative, 'h': h}
|
||||
gen = enumerate(ctx.diffs(f, x, n, **options))
|
||||
if options.get("chop", True):
|
||||
if chop:
|
||||
return [ctx.chop(d)/ctx.factorial(i) for i, d in gen]
|
||||
else:
|
||||
return [d/ctx.factorial(i) for i, d in gen]
|
||||
@@ -618,7 +627,7 @@ def pade(ctx, a, L, M):
|
||||
>>> a = taylor(f, 0, 6)
|
||||
>>> p, q = pade(a, 3, 3)
|
||||
>>> x = 10
|
||||
>>> polyval(p, x, asc=True)/polyval(q, x, asc=True)
|
||||
>>> polyval(p, x)/polyval(q, x)
|
||||
1.38169105566806
|
||||
>>> f(x)
|
||||
1.38169855941551
|
||||
|
||||
@@ -1084,11 +1084,11 @@ def sumem(ctx, f, interval, tol=None, reject=10, integral=None,
|
||||
def adaptive_extrapolation(ctx, update, emfun, kwargs):
|
||||
option = kwargs.get
|
||||
if ctx._fixed_precision:
|
||||
tol = option('tol', ctx.eps*2**10)
|
||||
tol = option('tol') or ctx.eps*2**10
|
||||
else:
|
||||
tol = option('tol', ctx.eps/2**10)
|
||||
tol = option('tol') or ctx.eps/2**10
|
||||
verbose = option('verbose', False)
|
||||
maxterms = option('maxterms', ctx.dps*10)
|
||||
maxterms = option('maxterms') or ctx.dps*10
|
||||
method = set(option('method', 'r+s').split('+'))
|
||||
skip = option('skip', 0)
|
||||
steps = iter(option('steps', range(10, 10**9, 10)))
|
||||
@@ -1136,7 +1136,7 @@ def adaptive_extrapolation(ctx, update, emfun, kwargs):
|
||||
best = ctx.zero
|
||||
orig = ctx.prec
|
||||
try:
|
||||
if 'workprec' in kwargs:
|
||||
if kwargs.get('workprec'):
|
||||
ctx.prec = kwargs['workprec']
|
||||
elif TRY_RICHARDSON or TRY_SHANKS or len(summer)!=0:
|
||||
ctx.prec = (ctx.prec+10) * 4
|
||||
@@ -1238,7 +1238,10 @@ def adaptive_extrapolation(ctx, update, emfun, kwargs):
|
||||
return best
|
||||
|
||||
@defun
|
||||
def nsum(ctx, f, *intervals, **options):
|
||||
def nsum(ctx, f, *intervals, tol=None, verbose=False,
|
||||
maxterms=None, method='r+s', skip=0, strict=False,
|
||||
levin_variant="u", workprec=None,
|
||||
steps=range(10, 10**9, 10), ignore=False):
|
||||
r"""
|
||||
Computes the sum
|
||||
|
||||
@@ -1686,6 +1689,11 @@ def nsum(ctx, f, *intervals, **options):
|
||||
2. [Weisstein]_ http://mathworld.wolfram.com/MadelungConstants.html
|
||||
|
||||
"""
|
||||
options = {'tol': tol, 'verbose': verbose, 'maxterms': maxterms,
|
||||
'method': method, 'skip': skip, 'strict': strict,
|
||||
'levin_variant': levin_variant, 'workprec': workprec,
|
||||
'steps': steps, 'ignore': ignore}
|
||||
|
||||
infinite, g = standardize(ctx, f, intervals, options)
|
||||
if not infinite:
|
||||
return +g()
|
||||
@@ -1818,7 +1826,11 @@ def fold_infinite(ctx, f, intervals):
|
||||
return fold_infinite(ctx, g, intervals[:-1])
|
||||
|
||||
@defun
|
||||
def nprod(ctx, f, interval, nsum=False, **kwargs):
|
||||
def nprod(ctx, f, interval, nsum=False,
|
||||
*, tol=None, verbose=False,
|
||||
maxterms=None, method='r+s', skip=0, strict=False,
|
||||
levin_variant="u", workprec=None,
|
||||
steps=range(10, 10**9, 10), ignore=False):
|
||||
r"""
|
||||
Computes the product
|
||||
|
||||
@@ -1953,6 +1965,11 @@ def nprod(ctx, f, interval, nsum=False, **kwargs):
|
||||
1. [Weisstein]_ http://mathworld.wolfram.com/InfiniteProduct.html
|
||||
|
||||
"""
|
||||
kwargs = {'tol': tol, 'verbose': verbose, 'maxterms': maxterms,
|
||||
'method': method, 'skip': skip, 'strict': strict,
|
||||
'levin_variant': levin_variant, 'workprec': workprec,
|
||||
'steps': steps, 'ignore': ignore}
|
||||
|
||||
if nsum or ('e' in kwargs.get('method', '')):
|
||||
orig = ctx.prec
|
||||
try:
|
||||
@@ -1989,7 +2006,9 @@ def nprod(ctx, f, interval, nsum=False, **kwargs):
|
||||
|
||||
|
||||
@defun
|
||||
def limit(ctx, f, x, direction=1, exp=False, **kwargs):
|
||||
def limit(ctx, f, x, direction=1, exp=False, *, tol=None, verbose=False,
|
||||
maxterms=None, method='r+s', skip=0, strict=False,
|
||||
levin_variant="u", workprec=None, steps=[10]):
|
||||
r"""
|
||||
Computes an estimate of the limit
|
||||
|
||||
@@ -2100,8 +2119,9 @@ def limit(ctx, f, x, direction=1, exp=False, **kwargs):
|
||||
for k in indices:
|
||||
values.append(g(k+1))
|
||||
|
||||
# XXX: steps used by nsum don't work well
|
||||
if 'steps' not in kwargs:
|
||||
kwargs['steps'] = [10]
|
||||
kwargs = {'tol': tol, 'verbose': verbose, 'maxterms': maxterms,
|
||||
'method': method, 'skip': skip, 'strict': strict,
|
||||
'levin_variant': levin_variant, 'workprec': workprec,
|
||||
'steps': steps} # XXX: steps used by nsum don't work well
|
||||
|
||||
return +ctx.adaptive_extrapolation(update, None, kwargs)
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
from .calculus import defun
|
||||
|
||||
def _fft_cooley_tuckey(ctx, values, inverse=False):
|
||||
"""
|
||||
This function implements the Radix-2 Cooley-Tukey FFT algorithm iteratively.
|
||||
It computes the Fast Fourier Transform (or Inverse FFT) of a sequence of
|
||||
complex numbers.
|
||||
|
||||
https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm
|
||||
"""
|
||||
n = len(values)
|
||||
if n <= 1:
|
||||
return values
|
||||
|
||||
# Bit-Reversal Permutation
|
||||
transformed = [ctx.zero] * n
|
||||
num_bits = n.bit_length() - 1
|
||||
for i in range(n):
|
||||
rev = 0
|
||||
val = values[i]
|
||||
for _ in range(num_bits):
|
||||
rev <<= 1
|
||||
rev |= (i & 1)
|
||||
i >>= 1
|
||||
transformed[rev] = val
|
||||
|
||||
sign = ctx.one if inverse else -ctx.one
|
||||
|
||||
length = 2
|
||||
while length <= n:
|
||||
half = length // 2
|
||||
w_len = ctx.expjpi(2 * sign / length)
|
||||
|
||||
for i in range(0, n, length):
|
||||
w = ctx.one
|
||||
for j in range(half):
|
||||
u = transformed[i + j]
|
||||
v = transformed[i + j + half] * w
|
||||
|
||||
transformed[i + j] = u + v
|
||||
transformed[i + j + half] = u - v
|
||||
|
||||
w *= w_len
|
||||
|
||||
length <<= 1
|
||||
|
||||
return transformed
|
||||
|
||||
@defun
|
||||
def fft(ctx, values):
|
||||
r"""
|
||||
Computes the Discrete Fourier Transform (DFT) of a sequence.
|
||||
|
||||
Raises NotImplementedError if the input sequence length is not a power of 2.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import mp
|
||||
>>> mp.pretty = True
|
||||
>>> mp.fft([1, 0, 0, 0])
|
||||
[1.0, (1.0 + 0.0j), 1.0, (1.0 + 0.0j)]
|
||||
>>> mp.fft([1 + 2j, 1 + 2j])
|
||||
[(2.0 + 4.0j), (0.0 + 0.0j)]
|
||||
>>> mp.fft([1, 2, 3, 4])
|
||||
[10.0, (-2.0 + 2.0j), -2.0, (-2.0 - 2.0j)]
|
||||
"""
|
||||
n = len(values)
|
||||
if n == 0:
|
||||
return []
|
||||
|
||||
is_power_of_two = (n & (n - 1)) == 0
|
||||
if not is_power_of_two:
|
||||
raise NotImplementedError("FFT is only implemented for lengths that "
|
||||
f"are powers of 2, got length: {n}")
|
||||
|
||||
converted_values = [ctx.convert(v) for v in values]
|
||||
with ctx.extraprec(10):
|
||||
result = _fft_cooley_tuckey(ctx, converted_values)
|
||||
return [+v for v in result]
|
||||
|
||||
@defun
|
||||
def invfft(ctx, values):
|
||||
r"""
|
||||
Computes the inverse Discrete Fourier Transform (IDFT) of a sequence.
|
||||
|
||||
Raises NotImplementedError if the input sequence length is not a power of 2.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import mp
|
||||
>>> mp.pretty = True
|
||||
>>> mp.invfft([1, 1, 1, 1])
|
||||
[1.0, (0.0 + 0.0j), 0.0, (0.0 + 0.0j)]
|
||||
>>> x = [1, 2, 3, 4]
|
||||
>>> mp.invfft(mp.fft(x))
|
||||
[(1.0 + 0.0j), (2.0 + 0.0j), (3.0 + 0.0j), (4.0 + 0.0j)]
|
||||
"""
|
||||
n = len(values)
|
||||
if n == 0:
|
||||
return []
|
||||
|
||||
is_power_of_two = (n & (n - 1)) == 0
|
||||
if not is_power_of_two:
|
||||
raise NotImplementedError("Inverse FFT is only implemented for lengths that "
|
||||
f"are powers of 2, got length: {n}")
|
||||
|
||||
converted_values = [ctx.convert(v) for v in values]
|
||||
with ctx.extraprec(10):
|
||||
result = _fft_cooley_tuckey(ctx, converted_values, True)
|
||||
return [val / n for val in result]
|
||||
@@ -38,7 +38,7 @@ class InverseLaplaceTransform:
|
||||
|
||||
class FixedTalbot(InverseLaplaceTransform):
|
||||
|
||||
def calc_laplace_parameter(self, t, **kwargs):
|
||||
def calc_laplace_parameter(self, t, *, tmax=None, degree=None, r=None):
|
||||
r"""The "fixed" Talbot method deforms the Bromwich contour towards
|
||||
`-\infty` in the shape of a parabola. Traditionally the Talbot
|
||||
algorithm has adjustable parameters, but the "fixed" version
|
||||
@@ -101,14 +101,16 @@ class FixedTalbot(InverseLaplaceTransform):
|
||||
# ------------------------------
|
||||
# maximum time desired (used for scaling) default is requested
|
||||
# time.
|
||||
self.tmax = self.ctx.convert(kwargs.get('tmax', self.t))
|
||||
if tmax is None:
|
||||
tmax = self.t
|
||||
self.tmax = self.ctx.convert(tmax)
|
||||
|
||||
# empirical relationships used here based on a linear fit of
|
||||
# requested and delivered dps for exponentially decaying time
|
||||
# functions for requested dps up to 512.
|
||||
|
||||
if 'degree' in kwargs:
|
||||
self.degree = kwargs['degree']
|
||||
if degree is not None:
|
||||
self.degree = degree
|
||||
self.dps_goal = self.degree
|
||||
else:
|
||||
self.dps_goal = int(1.72*self.ctx.dps)
|
||||
@@ -123,7 +125,9 @@ class FixedTalbot(InverseLaplaceTransform):
|
||||
self.ctx.dps = self.dps_goal
|
||||
|
||||
# Abate & Valko rule of thumb for r parameter
|
||||
self.r = kwargs.get('r', self.ctx.fraction(2, 5)*M)
|
||||
if r is None:
|
||||
r = self.ctx.fraction(2, 5)*M
|
||||
self.r = r
|
||||
|
||||
self.theta = self.ctx.linspace(0.0, self.ctx.pi, M+1)
|
||||
|
||||
@@ -215,7 +219,7 @@ class FixedTalbot(InverseLaplaceTransform):
|
||||
|
||||
class Stehfest(InverseLaplaceTransform):
|
||||
|
||||
def calc_laplace_parameter(self, t, **kwargs):
|
||||
def calc_laplace_parameter(self, t, *, degree=None):
|
||||
r"""
|
||||
The Gaver-Stehfest method is a discrete approximation of the
|
||||
Widder-Post inversion algorithm, rather than a direct
|
||||
@@ -249,8 +253,8 @@ class Stehfest(InverseLaplaceTransform):
|
||||
# requested and delivered dps for exponentially decaying time
|
||||
# functions for requested dps up to 512.
|
||||
|
||||
if 'degree' in kwargs:
|
||||
self.degree = kwargs['degree']
|
||||
if degree is not None:
|
||||
self.degree = degree
|
||||
self.dps_goal = int(1.38*self.degree)
|
||||
else:
|
||||
self.dps_goal = int(2.93*self.ctx.dps)
|
||||
@@ -347,7 +351,8 @@ class Stehfest(InverseLaplaceTransform):
|
||||
|
||||
class deHoog(InverseLaplaceTransform):
|
||||
|
||||
def calc_laplace_parameter(self, t, **kwargs):
|
||||
def calc_laplace_parameter(self, t, *, tmax=None, degree=None, alpha=None,
|
||||
scale=2, tol=None, T=None):
|
||||
r"""the de Hoog, Knight & Stokes algorithm is an
|
||||
accelerated form of the Fourier series numerical
|
||||
inverse Laplace transform algorithms.
|
||||
@@ -385,14 +390,16 @@ class deHoog(InverseLaplaceTransform):
|
||||
|
||||
# optional
|
||||
# ------------------------------
|
||||
self.tmax = kwargs.get('tmax', self.t)
|
||||
if tmax is None:
|
||||
tmax = self.t
|
||||
self.tmax = tmax
|
||||
|
||||
# empirical relationships used here based on a linear fit of
|
||||
# requested and delivered dps for exponentially decaying time
|
||||
# functions for requested dps up to 512.
|
||||
|
||||
if 'degree' in kwargs:
|
||||
self.degree = kwargs['degree']
|
||||
if degree is not None:
|
||||
self.degree = degree
|
||||
self.dps_goal = int(1.38*self.degree)
|
||||
else:
|
||||
self.dps_goal = int(self.ctx.dps*1.36)
|
||||
@@ -404,10 +411,14 @@ class deHoog(InverseLaplaceTransform):
|
||||
# adjust alpha component of abscissa of convergence for higher
|
||||
# precision
|
||||
tmp = self.ctx.power(10.0, -self.dps_goal)
|
||||
self.alpha = self.ctx.convert(kwargs.get('alpha', tmp))
|
||||
if alpha is None:
|
||||
alpha = tmp
|
||||
self.alpha = self.ctx.convert(alpha)
|
||||
|
||||
# desired tolerance (here simply related to alpha)
|
||||
self.tol = self.ctx.convert(kwargs.get('tol', self.alpha*10.0))
|
||||
if tol is None:
|
||||
tol = self.alpha*10.0
|
||||
self.tol = self.ctx.convert(tol)
|
||||
self.np = 2*self.degree+1 # number of terms in approximation
|
||||
|
||||
# this is adjusting the dps of the calling context
|
||||
@@ -417,8 +428,10 @@ class deHoog(InverseLaplaceTransform):
|
||||
self.ctx.dps = self.dps_goal
|
||||
|
||||
# scaling factor (likely tun-able, but 2 is typical)
|
||||
self.scale = kwargs.get('scale', 2)
|
||||
self.T = self.ctx.convert(kwargs.get('T', self.scale*self.tmax))
|
||||
self.scale = scale
|
||||
if T is None:
|
||||
T = self.scale*self.tmax
|
||||
self.T = self.ctx.convert(T)
|
||||
|
||||
self.p = self.ctx.matrix(2*M+1, 1)
|
||||
self.gamma = self.alpha - self.ctx.log(self.tol)/(self.scale*self.T)
|
||||
@@ -531,7 +544,7 @@ class deHoog(InverseLaplaceTransform):
|
||||
|
||||
class Cohen(InverseLaplaceTransform):
|
||||
|
||||
def calc_laplace_parameter(self, t, **kwargs):
|
||||
def calc_laplace_parameter(self, t, *, degree=None, alpha=None):
|
||||
r"""The Cohen algorithm accelerates the convergence of the nearly
|
||||
alternating series resulting from the application of the trapezoidal
|
||||
rule to the Bromwich contour inversion integral.
|
||||
@@ -572,8 +585,8 @@ class Cohen(InverseLaplaceTransform):
|
||||
"""
|
||||
self.t = self.ctx.convert(t)
|
||||
|
||||
if 'degree' in kwargs:
|
||||
self.degree = kwargs['degree']
|
||||
if degree is not None:
|
||||
self.degree = degree
|
||||
self.dps_goal = int(1.5 * self.degree)
|
||||
else:
|
||||
self.dps_goal = int(self.ctx.dps * 1.74)
|
||||
@@ -590,7 +603,9 @@ class Cohen(InverseLaplaceTransform):
|
||||
ttwo = 2 * self.t
|
||||
tmp = self.ctx.dps * self.ctx.log(10) + self.ctx.log(ttwo)
|
||||
tmp = self.ctx.fraction(2, 3) * tmp
|
||||
self.alpha = self.ctx.convert(kwargs.get('alpha', tmp))
|
||||
if alpha is None:
|
||||
alpha = tmp
|
||||
self.alpha = self.ctx.convert(alpha)
|
||||
|
||||
# all but time-dependent part of p
|
||||
a_t = self.alpha / ttwo
|
||||
@@ -659,7 +674,8 @@ class LaplaceTransformInversionMethods:
|
||||
ctx._de_hoog = deHoog(ctx)
|
||||
ctx._cohen = Cohen(ctx)
|
||||
|
||||
def invertlaplace(ctx, f, t, **kwargs):
|
||||
def invertlaplace(ctx, f, t, *, method='cohen', tmax=None, degree=None,
|
||||
r=None, alpha=None, scale=2, tol=None, T=None):
|
||||
r"""Computes the numerical inverse Laplace transform for a
|
||||
Laplace-space function at a given time. The function being
|
||||
evaluated is assumed to be a real-valued function of time.
|
||||
@@ -901,7 +917,7 @@ class LaplaceTransformInversionMethods:
|
||||
|
||||
"""
|
||||
|
||||
rule = kwargs.get('method', 'cohen')
|
||||
rule = method
|
||||
if type(rule) is str:
|
||||
lrule = rule.lower()
|
||||
if lrule == 'talbot':
|
||||
@@ -917,6 +933,16 @@ class LaplaceTransformInversionMethods:
|
||||
else:
|
||||
rule = rule(ctx)
|
||||
|
||||
if rule == ctx._fixed_talbot:
|
||||
kwargs = {'tmax': tmax, 'degree': degree, 'r': r}
|
||||
elif rule == ctx._stehfest:
|
||||
kwargs = {'degree': degree}
|
||||
elif rule == ctx._de_hoog:
|
||||
kwargs = {'tmax': tmax, 'degree': degree, 'alpha': alpha,
|
||||
'scale': scale, 'tol': tol, 'T': T}
|
||||
else:
|
||||
kwargs = {'degree': degree, 'alpha': alpha}
|
||||
|
||||
# determine the vector of Laplace-space parameter
|
||||
# needed for the requested method and desired time
|
||||
rule.calc_laplace_parameter(t, **kwargs)
|
||||
@@ -930,18 +956,20 @@ class LaplaceTransformInversionMethods:
|
||||
return rule.calc_time_domain_solution(fp, t)
|
||||
|
||||
# shortcuts for the above function for specific methods
|
||||
def invlaptalbot(ctx, *args, **kwargs):
|
||||
kwargs['method'] = 'talbot'
|
||||
return ctx.invertlaplace(*args, **kwargs)
|
||||
def invlaptalbot(ctx, f, t, *, tmax=None, degree=None,
|
||||
r=None):
|
||||
return ctx.invertlaplace(f, t, method='talbot', tmax=tmax,
|
||||
degree=degree, r=r)
|
||||
|
||||
def invlapstehfest(ctx, *args, **kwargs):
|
||||
kwargs['method'] = 'stehfest'
|
||||
return ctx.invertlaplace(*args, **kwargs)
|
||||
def invlapstehfest(ctx, f, t, *, degree=None):
|
||||
return ctx.invertlaplace(f, t, method='stehfest', degree=degree)
|
||||
|
||||
def invlapdehoog(ctx, *args, **kwargs):
|
||||
kwargs['method'] = 'dehoog'
|
||||
return ctx.invertlaplace(*args, **kwargs)
|
||||
def invlapdehoog(ctx, f, t, *, tmax=None, degree=None,
|
||||
alpha=None, scale=2, tol=None, T=None):
|
||||
return ctx.invertlaplace(f, t, method='dehoog', tmax=tmax,
|
||||
degree=degree, alpha=alpha, scale=scale,
|
||||
tol=tol, T=T)
|
||||
|
||||
def invlapcohen(ctx, *args, **kwargs):
|
||||
kwargs['method'] = 'cohen'
|
||||
return ctx.invertlaplace(*args, **kwargs)
|
||||
def invlapcohen(ctx, f, t, *, degree=None, alpha=None):
|
||||
return ctx.invertlaplace(f, t, method='cohen', degree=degree,
|
||||
alpha=alpha)
|
||||
|
||||
@@ -247,7 +247,7 @@ def odefun(ctx, F, x0, y0, tol=None, degree=None, method='taylor', verbose=False
|
||||
series_data = [(ser, x0, xb)]
|
||||
# We will be working with vectors of Taylor series
|
||||
def mpolyval(ser, a):
|
||||
return [ctx.polyval(s, a, asc=True) for s in ser]
|
||||
return [ctx.polyval(s, a) for s in ser]
|
||||
# Find nearest expansion point; compute if necessary
|
||||
def get_series(x):
|
||||
if x < x0:
|
||||
|
||||
+248
-30
@@ -283,7 +283,6 @@ class Muller:
|
||||
error = abs(x2 - x1)
|
||||
yield x2, error
|
||||
|
||||
# TODO: consider raising a ValueError when there's no sign change in a and b
|
||||
class Bisection:
|
||||
"""
|
||||
1d-solver generating pairs of approximative root and error.
|
||||
@@ -307,17 +306,23 @@ class Bisection:
|
||||
if len(x0) != 2:
|
||||
raise ValueError('expected interval of 2 points, got %i' % len(x0))
|
||||
self.f = f
|
||||
self.a = x0[0]
|
||||
self.b = x0[1]
|
||||
self.a, self.b = x0
|
||||
self.maxsteps = 2*ctx.prec + ctx.ceil(ctx.log2(abs(self.a - self.b)))
|
||||
|
||||
def __iter__(self):
|
||||
ctx = self.ctx
|
||||
f = self.f
|
||||
a = self.a
|
||||
b = self.b
|
||||
l = b - a
|
||||
fa = f(a)
|
||||
fb = f(b)
|
||||
|
||||
if fa*fb > 0:
|
||||
raise ValueError("Function must have opposite signs at interval boundaries.")
|
||||
|
||||
while True:
|
||||
m = self.ctx.ldexp(a + b, -1)
|
||||
m = ctx.ldexp(a + b, -1)
|
||||
fm = f(m)
|
||||
sign = fm * fb
|
||||
if sign < 0:
|
||||
@@ -326,7 +331,7 @@ class Bisection:
|
||||
b = m
|
||||
fb = fm
|
||||
else:
|
||||
yield m, self.ctx.zero
|
||||
yield m, ctx.zero
|
||||
l /= 2
|
||||
yield (a + b)/2, abs(l)
|
||||
|
||||
@@ -502,12 +507,15 @@ class Ridder:
|
||||
print('canceled with f(x4) =', fx4)
|
||||
yield x4, abs(x1 - x2)
|
||||
break
|
||||
if fx4 * fx2 < 0: # root in [x4, x2]
|
||||
x1 = x4
|
||||
fx1 = fx4
|
||||
else: # root in [x1, x4]
|
||||
if fx3 * fx4 < 0: # root in [x4, x3]
|
||||
x1, x2 = x4, x3
|
||||
fx1, fx2 = fx4, fx3
|
||||
elif fx4 * fx1 < 0: # in [x1, x4]
|
||||
x2 = x4
|
||||
fx2 = fx4
|
||||
else: # in [x4, x2]
|
||||
x1 = x4
|
||||
fx1 = fx4
|
||||
error = abs(x1 - x2)
|
||||
yield (x1 + x2)/2, error
|
||||
|
||||
@@ -567,7 +575,208 @@ class ANewton:
|
||||
print('accelerating convergence')
|
||||
yield x0, error
|
||||
|
||||
# TODO: add Brent
|
||||
class Brent:
|
||||
"""
|
||||
1d-solver generating pairs of approximative root and error.
|
||||
|
||||
Uses Brent's method to find a root of f in [a, b]. It combines
|
||||
Bisection, the Secant method, and Inverse Quadratic Interpolation (IQI)
|
||||
for robust and superlinear convergence.
|
||||
|
||||
Pro:
|
||||
* Guaranteed to converge if a root is bracketed (like Bisection).
|
||||
* Can converge much faster than Bisection on smooth functions.
|
||||
|
||||
Contra:
|
||||
* Needs an initial sign-changing bracket.
|
||||
|
||||
http://en.wikipedia.org/wiki/Brent%27s_method
|
||||
"""
|
||||
maxsteps = 100
|
||||
|
||||
def __init__(self, ctx, f, x0, **kwargs):
|
||||
self.ctx = ctx
|
||||
if len(x0) != 2:
|
||||
raise ValueError('expected interval of 2 points, got %i' % len(x0))
|
||||
|
||||
self.f = f
|
||||
self.a, self.b = x0
|
||||
self.tol = kwargs['tol']
|
||||
|
||||
def __iter__(self):
|
||||
ctx = self.ctx
|
||||
f = self.f
|
||||
|
||||
a = self.a
|
||||
b = self.b
|
||||
fa = f(a)
|
||||
fb = f(b)
|
||||
|
||||
if fa*fb > 0:
|
||||
raise ValueError("Function must have opposite signs at interval boundaries.")
|
||||
|
||||
if abs(fa) < abs(fb):
|
||||
a, b = b, a
|
||||
fa, fb = fb, fa
|
||||
|
||||
c = a
|
||||
fc = fa
|
||||
d = c # will be assigned properly on the first interation
|
||||
mflag = True
|
||||
|
||||
while True:
|
||||
|
||||
yield b, abs(b - a)
|
||||
|
||||
if fa != fc and fb != fc:
|
||||
# Inverse Quadratic Interpolation formula
|
||||
s = (a * fb * fc) / ((fa - fb) * (fa - fc)) + \
|
||||
(b * fa * fc) / ((fb - fa) * (fb - fc)) + \
|
||||
(c * fa * fb) / ((fc - fa) * (fc - fb))
|
||||
else:
|
||||
# standard Secant
|
||||
s = b - fb * (b - a) / (fb - fa)
|
||||
|
||||
# Define conditions matching Brent's bounds
|
||||
bound_lower = (3 * a + b) / 4
|
||||
is_between = (bound_lower <= s <= b) or (b <= s <= bound_lower)
|
||||
|
||||
delta = ctx.eps * max(ctx.one, ctx.fabs(b))
|
||||
|
||||
cond1 = not is_between
|
||||
cond2 = mflag and (abs(s - b) >= abs(b - c) / 2)
|
||||
cond3 = (not mflag) and (abs(s - b) >= abs(c - d) / 2)
|
||||
cond4 = mflag and (abs(b - c) < delta)
|
||||
cond5 = (not mflag) and (abs(c - d) < delta)
|
||||
|
||||
if cond1 or cond2 or cond3 or cond4 or cond5:
|
||||
s = ctx.ldexp(a + b, -1)
|
||||
mflag = True
|
||||
else:
|
||||
mflag = False
|
||||
|
||||
fs = f(s)
|
||||
|
||||
d = c
|
||||
c = b
|
||||
fc = fb
|
||||
|
||||
if fa*fs < 0:
|
||||
b = s
|
||||
fb = fs
|
||||
else:
|
||||
a = s
|
||||
fa = fs
|
||||
|
||||
if abs(fa) < abs(fb):
|
||||
a, b = b, a
|
||||
fa, fb = fb, fa
|
||||
|
||||
class ModAB:
|
||||
"""
|
||||
1d-solver generating pairs of approximative root and error.
|
||||
|
||||
Uses the Modified Anderson-Björck (modAB) hybrid method to find
|
||||
a root of f in [a, b]. It dynamically switches between Bisection
|
||||
and False Position (Secant) while correcting for stagnant endpoints.
|
||||
|
||||
Pro:
|
||||
* Robust and guaranteed to converge (like Bisection)
|
||||
* Fast convergence on smooth functions (like Secant)
|
||||
|
||||
Contra:
|
||||
* Needs an initial sign change bracket
|
||||
|
||||
https://doi.org/10.3390/a19050332
|
||||
"""
|
||||
maxsteps = 200
|
||||
|
||||
def __init__(self, ctx, f, x0, **kwargs):
|
||||
self.ctx = ctx
|
||||
if len(x0) != 2:
|
||||
raise ValueError('expected interval of 2 points, got %i' % len(x0))
|
||||
|
||||
self.f = f
|
||||
|
||||
# Enforce ordering: self.a as lower bound, self.b as upper bound
|
||||
self.a, self.b = x0
|
||||
if self.a > self.b:
|
||||
self.a, self.b = self.b, self.a
|
||||
|
||||
def __iter__(self):
|
||||
ctx = self.ctx
|
||||
f = self.f
|
||||
|
||||
a = self.a
|
||||
b = self.b
|
||||
fa = f(a)
|
||||
fb = f(b)
|
||||
|
||||
# Check for initial bracketing
|
||||
if fa*fb > 0:
|
||||
raise ValueError("Function must have opposite signs at interval boundaries.")
|
||||
|
||||
bisection = True
|
||||
side = 0 # -1 for left moved last, 1 for right, 0 for none
|
||||
threshold = b - a
|
||||
C = ctx.mpf(16) # Safety factor threshold scaling constant
|
||||
|
||||
while True:
|
||||
if bisection:
|
||||
x3 = ctx.ldexp(a + b, -1)
|
||||
else:
|
||||
x3 = (a * fb - b * fa) / (fb - fa)
|
||||
|
||||
# Yield the current best guess and the remaining interval length (error)
|
||||
yield x3, abs(b - a)
|
||||
|
||||
# Evaluate function or handle out-of-bounds secant calculations
|
||||
if bisection:
|
||||
fx3 = f(x3)
|
||||
ym = ctx.ldexp(fa + fb, -1)
|
||||
|
||||
# Check linearity to see if we can switch to secant
|
||||
r = ctx.one - ctx.fabs(ym / (fb - fa)) # Symmetry factor
|
||||
k = r * r # Deviation factor
|
||||
|
||||
if ctx.fabs(ym - fx3) < k * (ctx.fabs(fx3) + ctx.fabs(ym)):
|
||||
bisection = False
|
||||
threshold = (b - a) * C
|
||||
else:
|
||||
# Clamp secant point safely within the bounds to handle floating-point rounding
|
||||
if x3 <= a:
|
||||
x3, fx3 = a, fa
|
||||
elif x3 >= b:
|
||||
x3, fx3 = b, fb
|
||||
else:
|
||||
fx3 = f(x3)
|
||||
|
||||
threshold *= 0.5
|
||||
|
||||
# Check for exact root convergence
|
||||
if fx3 == ctx.zero:
|
||||
yield x3, ctx.zero
|
||||
|
||||
# Update the interval and apply Anderson-Björck adjustments
|
||||
if fa*fx3 > 0:
|
||||
if side == 1:
|
||||
m = ctx.one - (fx3 / fa)
|
||||
fb *= ctx.ldexp(ctx.one, -1) if m <= 0 else m
|
||||
elif not bisection:
|
||||
side = 1
|
||||
a, fa = x3, fx3
|
||||
else:
|
||||
if side == -1:
|
||||
m = ctx.one - (fx3 / fb)
|
||||
fa *= ctx.ldexp(ctx.one, -1) if m <= 0 else m
|
||||
elif not bisection:
|
||||
side = -1
|
||||
b, fb = x3, fx3
|
||||
|
||||
# Fallback check: If progress is too slow, force a bisection step next time
|
||||
if (b - a) > threshold:
|
||||
bisection = True
|
||||
side = 0
|
||||
|
||||
############################
|
||||
# MULTIDIMENSIONAL SOLVERS #
|
||||
@@ -686,9 +895,11 @@ class MDNewton:
|
||||
str2solver = {'newton':Newton, 'secant':Secant, 'mnewton':MNewton,
|
||||
'halley':Halley, 'muller':Muller, 'bisect':Bisection,
|
||||
'illinois':Illinois, 'pegasus':Pegasus, 'anderson':Anderson,
|
||||
'ridder':Ridder, 'anewton':ANewton, 'mdnewton':MDNewton}
|
||||
'ridder':Ridder, 'anewton':ANewton, 'mdnewton':MDNewton, 'modAB':ModAB, 'brent':Brent}
|
||||
|
||||
def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True, **kwargs):
|
||||
def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
|
||||
*, d1f=None, df=None, d2f=None, J=None,
|
||||
multidimensional=False, norm=None, maxsteps=None):
|
||||
r"""
|
||||
Find an approximate solution to `f(x) = 0`, using *x0* as starting point or
|
||||
interval for *x*.
|
||||
@@ -739,7 +950,7 @@ def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
|
||||
expected to be positive).
|
||||
You can use the following string aliases:
|
||||
'secant', 'mnewton', 'halley', 'muller', 'illinois', 'pegasus', 'anderson',
|
||||
'ridder', 'anewton', 'bisect'
|
||||
'ridder', 'anewton', 'bisect', 'modAB'
|
||||
|
||||
See mpmath.calculus.optimization for their documentation.
|
||||
|
||||
@@ -879,7 +1090,7 @@ def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
|
||||
**Intersection methods**
|
||||
|
||||
When you need to find a root in a known interval, it's highly recommended to
|
||||
use an intersection-based solver like ``'anderson'`` or ``'ridder'``.
|
||||
use an intersection-based solver like ```'modAB'``` or ``'anderson'`` or ``'ridder'``.
|
||||
Usually they converge faster and more reliable. They have however problems
|
||||
with multiple roots and usually need a sign change to find a root::
|
||||
|
||||
@@ -904,19 +1115,26 @@ def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
|
||||
"""
|
||||
prec = ctx.prec
|
||||
trap_complex = getattr(ctx, 'trap_complex', None)
|
||||
kwargs = {}
|
||||
try:
|
||||
ctx.prec += 20
|
||||
|
||||
# initialize arguments
|
||||
if tol is None:
|
||||
tol = ctx.eps * 2**10
|
||||
|
||||
kwargs['verbose'] = kwargs.get('verbose', verbose)
|
||||
|
||||
if 'd1f' in kwargs:
|
||||
kwargs['df'] = kwargs['d1f']
|
||||
|
||||
kwargs['tol'] = tol
|
||||
|
||||
kwargs['verbose'] = verbose
|
||||
|
||||
if df is not None:
|
||||
kwargs['df'] = df
|
||||
if d1f is not None:
|
||||
kwargs['df'] = d1f
|
||||
if d2f is not None:
|
||||
kwargs['d2f'] = d2f
|
||||
if J is not None:
|
||||
kwargs['J'] = J
|
||||
|
||||
if isinstance(x0, (list, tuple)):
|
||||
x0 = [ctx.convert(x) for x in x0]
|
||||
else:
|
||||
@@ -938,34 +1156,34 @@ def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
|
||||
# detect multidimensional functions
|
||||
try:
|
||||
fx = f(*x0)
|
||||
multidimensional = isinstance(fx, (list, tuple, ctx.matrix))
|
||||
md = isinstance(fx, (list, tuple, ctx.matrix))
|
||||
except TypeError:
|
||||
fx = f(x0[0])
|
||||
multidimensional = False
|
||||
if 'multidimensional' in kwargs:
|
||||
multidimensional = kwargs['multidimensional']
|
||||
md = False
|
||||
if multidimensional:
|
||||
md = multidimensional
|
||||
if md:
|
||||
# only one multidimensional solver available at the moment
|
||||
solver = MDNewton
|
||||
if 'norm' not in kwargs:
|
||||
if norm is None:
|
||||
norm = lambda x: ctx.norm(x, 'inf')
|
||||
kwargs['norm'] = norm
|
||||
else:
|
||||
norm = kwargs['norm']
|
||||
kwargs['norm'] = norm
|
||||
ctx.trap_complex = True # MDNewton assume real input
|
||||
else:
|
||||
norm = abs
|
||||
|
||||
# happily return starting point if it's a root
|
||||
if norm(fx) == 0:
|
||||
if multidimensional:
|
||||
if md:
|
||||
return ctx.matrix(x0)
|
||||
else:
|
||||
return x0[0]
|
||||
|
||||
# use solver
|
||||
iterations = solver(ctx, f, x0, **kwargs)
|
||||
maxsteps = kwargs.get('maxsteps', iterations.maxsteps)
|
||||
if maxsteps is None:
|
||||
maxsteps = iterations.maxsteps
|
||||
kwargs['maxsteps'] = maxsteps
|
||||
i = 0
|
||||
for x, error in iterations:
|
||||
if verbose:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import warnings
|
||||
|
||||
from .calculus import defun
|
||||
|
||||
|
||||
@@ -9,7 +7,7 @@ from .calculus import defun
|
||||
|
||||
# XXX: extra precision
|
||||
@defun
|
||||
def polyval(ctx, coeffs, x, derivative=False, asc=None):
|
||||
def polyval(ctx, coeffs, x, derivative=False, asc=True):
|
||||
r"""
|
||||
Given coefficients `[c_0, c_1, c_2, \ldots, c_n]` and a number `x`,
|
||||
:func:`~mpmath.polyval` evaluates the polynomial
|
||||
@@ -24,9 +22,9 @@ def polyval(ctx, coeffs, x, derivative=False, asc=None):
|
||||
|
||||
>>> from mpmath import mp, polyval
|
||||
>>> mp.pretty = True
|
||||
>>> polyval([2, 0, 3], 0.5, asc=True)
|
||||
>>> polyval([2, 0, 3], 0.5)
|
||||
2.75
|
||||
>>> polyval([2, 0, 3], 0.5, derivative=True, asc=True)
|
||||
>>> polyval([2, 0, 3], 0.5, derivative=True)
|
||||
(2.75, 3.0)
|
||||
|
||||
If *asc=False*, descending order of coefficients is used (the term
|
||||
@@ -37,12 +35,6 @@ def polyval(ctx, coeffs, x, derivative=False, asc=None):
|
||||
"""
|
||||
if not coeffs:
|
||||
return ctx.zero
|
||||
if asc is None:
|
||||
warnings.warn("Descending (wrt powers) order of polynomial "
|
||||
"coefficients is deprecated, please adapt your "
|
||||
"code to use ascending order, asc=True.",
|
||||
DeprecationWarning)
|
||||
asc = False
|
||||
if not asc:
|
||||
coeffs = coeffs[::-1]
|
||||
p = ctx.convert(coeffs[-1])
|
||||
@@ -58,7 +50,7 @@ def polyval(ctx, coeffs, x, derivative=False, asc=None):
|
||||
|
||||
@defun
|
||||
def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10,
|
||||
error=False, roots_init=None, asc=None):
|
||||
error=False, roots_init=None, asc=True):
|
||||
"""
|
||||
Computes all roots (real or complex) of a given polynomial.
|
||||
|
||||
@@ -79,13 +71,13 @@ def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10,
|
||||
|
||||
>>> from mpmath import mp, polyroots, nprint, sqrt, polyval
|
||||
>>> mp.pretty = True
|
||||
>>> nprint(polyroots([24,-14,-1,1],asc=True), 4)
|
||||
>>> nprint(polyroots([24,-14,-1,1]), 4)
|
||||
[-4.0, 2.0, 3.0]
|
||||
|
||||
Finding the two complex conjugate roots of `4x^2 + 3x + 2`, with an
|
||||
error estimate::
|
||||
|
||||
>>> roots, err = polyroots([2,3,4], error=True, asc=True)
|
||||
>>> roots, err = polyroots([2,3,4], error=True)
|
||||
>>> for r in roots:
|
||||
... print(r)
|
||||
...
|
||||
@@ -95,16 +87,16 @@ def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10,
|
||||
>>> err
|
||||
2.22044604925031e-16
|
||||
>>>
|
||||
>>> polyval([2,3,4], roots[0], asc=True)
|
||||
>>> polyval([2,3,4], roots[0])
|
||||
(2.22044604925031e-16 + 0.0j)
|
||||
>>> polyval([2,3,4], roots[1], asc=True)
|
||||
>>> polyval([2,3,4], roots[1])
|
||||
(2.22044604925031e-16 + 0.0j)
|
||||
|
||||
The following example computes all the 5th roots of unity; that is,
|
||||
the roots of `x^5 - 1`::
|
||||
|
||||
>>> mp.dps = 20
|
||||
>>> for r in polyroots([-1, 0, 0, 0, 0, 1], asc=True):
|
||||
>>> for r in polyroots([-1, 0, 0, 0, 0, 1]):
|
||||
... print(r)
|
||||
...
|
||||
1.0
|
||||
@@ -133,7 +125,7 @@ def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10,
|
||||
typically compute all roots of an arbitrary polynomial to high precision::
|
||||
|
||||
>>> mp.dps = 60
|
||||
>>> for r in polyroots([1, 0, -10, 0, 1], asc=True):
|
||||
>>> for r in polyroots([1, 0, -10, 0, 1]):
|
||||
... print(r)
|
||||
...
|
||||
-3.14626436994197234232913506571557044551247712918732870123249
|
||||
@@ -172,13 +164,6 @@ def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10,
|
||||
raise ValueError("Input to polyroots must not be the zero polynomial")
|
||||
# Constant polynomial with no roots
|
||||
return []
|
||||
|
||||
if asc is None:
|
||||
warnings.warn("Descending (wrt powers) order of polynomial "
|
||||
"coefficients is deprecated, please adapt you "
|
||||
"code to use ascending order, asc=True.",
|
||||
DeprecationWarning)
|
||||
asc = False
|
||||
if not asc:
|
||||
coeffs = coeffs[::-1]
|
||||
|
||||
@@ -192,7 +177,7 @@ def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10,
|
||||
coeffs = [ctx.convert(c) for c in coeffs]
|
||||
else:
|
||||
coeffs = [c/lead for c in coeffs]
|
||||
f = lambda x: ctx.polyval(coeffs, x, asc=True)
|
||||
f = lambda x: ctx.polyval(coeffs, x)
|
||||
if roots_init is None:
|
||||
roots = [ctx.mpc((0.4+0.9j)**n) for n in range(deg)]
|
||||
else:
|
||||
|
||||
@@ -456,7 +456,8 @@ class QuadratureMethods:
|
||||
ctx._gauss_legendre = GaussLegendre(ctx)
|
||||
ctx._tanh_sinh = TanhSinh(ctx)
|
||||
|
||||
def quad(ctx, f, *points, **kwargs):
|
||||
def quad(ctx, f, *points, method='tanh-sinh', verbose=False,
|
||||
maxdegree=None, error=False):
|
||||
r"""
|
||||
Computes a single, double or triple integral over a given
|
||||
1D interval, 2D rectangle, or 3D cuboid. A basic example::
|
||||
@@ -720,7 +721,7 @@ class QuadratureMethods:
|
||||
1. [Weisstein]_ http://mathworld.wolfram.com/DoubleIntegral.html
|
||||
|
||||
"""
|
||||
rule = kwargs.get('method', 'tanh-sinh')
|
||||
rule = method
|
||||
if type(rule) is str:
|
||||
if rule == 'tanh-sinh':
|
||||
rule = ctx._tanh_sinh
|
||||
@@ -730,11 +731,10 @@ class QuadratureMethods:
|
||||
raise ValueError("unknown quadrature rule: %s" % rule)
|
||||
else:
|
||||
rule = rule(ctx)
|
||||
verbose = kwargs.get('verbose')
|
||||
dim = len(points)
|
||||
orig = prec = ctx.prec
|
||||
epsilon = ctx.eps/8
|
||||
m = kwargs.get('maxdegree') or rule.guess_degree(prec)
|
||||
m = maxdegree or rule.guess_degree(prec)
|
||||
points = [ctx._as_points(p) for p in points]
|
||||
try:
|
||||
ctx.prec += 20
|
||||
@@ -756,11 +756,12 @@ class QuadratureMethods:
|
||||
raise NotImplementedError("quadrature must have dim 1, 2 or 3")
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
if kwargs.get("error"):
|
||||
if error:
|
||||
return +v, err
|
||||
return +v
|
||||
|
||||
def quadts(ctx, *args, **kwargs):
|
||||
def quadts(ctx, f, *points, verbose=False,
|
||||
maxdegree=None, error=False):
|
||||
"""
|
||||
Performs tanh-sinh quadrature. The call
|
||||
|
||||
@@ -781,10 +782,11 @@ class QuadratureMethods:
|
||||
See documentation for TanhSinh for algorithmic information about
|
||||
tanh-sinh quadrature.
|
||||
"""
|
||||
kwargs['method'] = 'tanh-sinh'
|
||||
return ctx.quad(*args, **kwargs)
|
||||
return ctx.quad(f, *points, method='tanh-sinh', verbose=verbose,
|
||||
maxdegree=maxdegree, error=error)
|
||||
|
||||
def quadgl(ctx, *args, **kwargs):
|
||||
def quadgl(ctx, f, *points, verbose=False,
|
||||
maxdegree=None, error=False):
|
||||
"""
|
||||
Performs Gauss-Legendre quadrature. The call
|
||||
|
||||
@@ -805,8 +807,8 @@ class QuadratureMethods:
|
||||
See documentation for TanhSinh for algorithmic information about
|
||||
tanh-sinh quadrature.
|
||||
"""
|
||||
kwargs['method'] = 'gauss-legendre'
|
||||
return ctx.quad(*args, **kwargs)
|
||||
return ctx.quad(f, *points, method='gauss-legendre', verbose=verbose,
|
||||
maxdegree=maxdegree, error=error)
|
||||
|
||||
def quadosc(ctx, f, interval, omega=None, period=None, zeros=None):
|
||||
r"""
|
||||
@@ -1005,7 +1007,9 @@ class QuadratureMethods:
|
||||
s += ctx.nsum(term, [n, ctx.inf])
|
||||
return s
|
||||
|
||||
def quadsubdiv(ctx, f, interval, tol=None, maxintervals=None, **kwargs):
|
||||
def quadsubdiv(ctx, f, interval, tol=None, maxintervals=None, *,
|
||||
method='tanh-sinh', verbose=False,
|
||||
maxdegree=None, error=False):
|
||||
"""
|
||||
Computes the integral of *f* over the interval or path specified
|
||||
by *interval*, using :func:`~mpmath.quad` together with adaptive
|
||||
@@ -1072,7 +1076,8 @@ class QuadratureMethods:
|
||||
if maxintervals is None:
|
||||
maxintervals = 10 * ctx.prec
|
||||
count = 0
|
||||
quad_args = kwargs.copy()
|
||||
quad_args = {'method': method, 'verbose': verbose,
|
||||
'maxdegree': maxdegree, 'error': error}
|
||||
quad_args["verbose"] = False
|
||||
quad_args["error"] = True
|
||||
if tol is None:
|
||||
@@ -1083,14 +1088,14 @@ class QuadratureMethods:
|
||||
while queue:
|
||||
a, b = queue.pop()
|
||||
s, err = ctx.quad(f, [a, b], **quad_args)
|
||||
if kwargs.get("verbose"):
|
||||
if verbose:
|
||||
print("subinterval", count, a, b, err)
|
||||
if err < tol or count > maxintervals:
|
||||
total += s
|
||||
total_error += err
|
||||
else:
|
||||
count += 1
|
||||
if count == maxintervals and kwargs.get("verbose"):
|
||||
if count == maxintervals and verbose:
|
||||
print("warning: number of intervals exceeded maxintervals")
|
||||
if a == -ctx.inf and b == ctx.inf:
|
||||
m = 0
|
||||
@@ -1104,7 +1109,7 @@ class QuadratureMethods:
|
||||
queue.append((m, b))
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
if kwargs.get("error"):
|
||||
if error:
|
||||
return +total, +total_error
|
||||
else:
|
||||
return +total
|
||||
|
||||
+18
-12
@@ -1,5 +1,5 @@
|
||||
from operator import gt, lt
|
||||
import random
|
||||
from operator import gt, lt
|
||||
|
||||
from . import libmp
|
||||
from .calculus.calculus import CalculusMethods
|
||||
@@ -118,10 +118,16 @@ class StandardBaseContext(Context,
|
||||
prod *= arg
|
||||
return prod
|
||||
|
||||
def nprint(ctx, x, n=6, **kwargs):
|
||||
def nprint(ctx, x, n=6, *, strip_zeros=True, min_fixed=None, max_fixed=None,
|
||||
show_zero_exponent=False, base=10, binary_exp=False,
|
||||
rnd=libmp.round_nearest):
|
||||
"""
|
||||
Equivalent to ``print(nstr(x, n))``.
|
||||
"""
|
||||
kwargs = {'strip_zeros': strip_zeros, 'min_fixed': min_fixed,
|
||||
'max_fixed': max_fixed, 'show_zero_exponent': show_zero_exponent,
|
||||
'base': base, 'binary_exp': binary_exp,
|
||||
'rnd': rnd}
|
||||
print(ctx.nstr(x, n, **kwargs))
|
||||
|
||||
def chop(ctx, x, tol=None):
|
||||
@@ -284,7 +290,7 @@ class StandardBaseContext(Context,
|
||||
break
|
||||
return result
|
||||
|
||||
def linspace(ctx, *args, **kwargs):
|
||||
def linspace(ctx, *args, endpoint=True):
|
||||
"""
|
||||
``linspace(a, b, n)`` returns a list of `n` evenly spaced
|
||||
samples from `a` to `b`. The syntax ``linspace(mpi(a,b), n)``
|
||||
@@ -318,7 +324,7 @@ class StandardBaseContext(Context,
|
||||
% len(args))
|
||||
if n < 1:
|
||||
raise ValueError('n must be greater than 0')
|
||||
if 'endpoint' not in kwargs or kwargs['endpoint']:
|
||||
if endpoint:
|
||||
if n == 1:
|
||||
return [ctx.mpf(a)]
|
||||
step = (b - a) / ctx.mpf(n - 1)
|
||||
@@ -338,15 +344,15 @@ class StandardBaseContext(Context,
|
||||
def _default_hyper_maxprec(ctx, p):
|
||||
return int(1000 * p**0.25 + 4*p)
|
||||
|
||||
_gcd = staticmethod(libmp.gcd)
|
||||
list_primes = staticmethod(libmp.list_primes)
|
||||
isprime = staticmethod(libmp.isprime)
|
||||
bernfrac = staticmethod(libmp.bernfrac)
|
||||
moebius = staticmethod(libmp.moebius)
|
||||
_gcd = staticmethod(libmp.libintmath.gcd)
|
||||
list_primes = staticmethod(libmp.libintmath.list_primes)
|
||||
isprime = staticmethod(libmp.libintmath.isprime)
|
||||
bernfrac = staticmethod(libmp.gammazeta.bernfrac)
|
||||
moebius = staticmethod(libmp.libintmath.moebius)
|
||||
_ifac = staticmethod(libmp.ifac)
|
||||
_eulernum = staticmethod(libmp.eulernum)
|
||||
_stirling1 = staticmethod(libmp.stirling1)
|
||||
_stirling2 = staticmethod(libmp.stirling2)
|
||||
_eulernum = staticmethod(libmp.libintmath.eulernum)
|
||||
_stirling1 = staticmethod(libmp.libintmath.stirling1)
|
||||
_stirling2 = staticmethod(libmp.libintmath.stirling2)
|
||||
|
||||
def sum_accurately(ctx, terms, check_step=1):
|
||||
prec = ctx.prec
|
||||
|
||||
+11
-12
@@ -3,7 +3,6 @@ import functools
|
||||
import inspect
|
||||
import math
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from . import function_docs, libfp, libmp
|
||||
from .ctx_base import StandardBaseContext
|
||||
@@ -88,9 +87,6 @@ class FPContext(StandardBaseContext):
|
||||
|
||||
absmin = absmax = abs
|
||||
|
||||
def isspecial(ctx, x):
|
||||
return not x or x - x != 0.0
|
||||
|
||||
def isnan(ctx, x):
|
||||
return x != x
|
||||
|
||||
@@ -103,11 +99,10 @@ class FPContext(StandardBaseContext):
|
||||
return math.isfinite(x)
|
||||
|
||||
def isnormal(ctx, x):
|
||||
warnings.warn("the isnormal() method is deprecated",
|
||||
DeprecationWarning)
|
||||
if x:
|
||||
return x - x == 0.0
|
||||
return False
|
||||
if type(x) is complex:
|
||||
return ctx.isnormal(abs(x))
|
||||
# XXX: can use math.isnormal() on Python 3.15+
|
||||
return bool(x) and math.isfinite(x) and abs(x) >= sys.float_info.min
|
||||
|
||||
def isnpint(ctx, x):
|
||||
if type(x) is complex:
|
||||
@@ -166,6 +161,7 @@ class FPContext(StandardBaseContext):
|
||||
ldexp = math.ldexp
|
||||
frexp = math.frexp
|
||||
hypot = math.hypot
|
||||
ulp = math.ulp
|
||||
|
||||
def mag(ctx, z):
|
||||
if z:
|
||||
@@ -193,11 +189,13 @@ class FPContext(StandardBaseContext):
|
||||
def _convert_param(ctx, z):
|
||||
if type(z) is tuple:
|
||||
p, q = z
|
||||
return ctx.mpf(p) / q, 'R'
|
||||
return ctx.mpf(p / q), 'R'
|
||||
intz = int(z.real)
|
||||
if z == intz:
|
||||
return intz, 'Z'
|
||||
return z, 'R'
|
||||
if not z.imag:
|
||||
return ctx.mpf(z), 'R'
|
||||
return ctx.mpc(z), 'C'
|
||||
|
||||
def _is_real_type(ctx, z):
|
||||
return isinstance(z, float) or isinstance(z, int_types)
|
||||
@@ -238,7 +236,8 @@ class FPContext(StandardBaseContext):
|
||||
try:
|
||||
for i in den: t /= (coeffs[i]+k)
|
||||
except ZeroDivisionError:
|
||||
raise NotImplementedError
|
||||
assert not t # poles are handled above
|
||||
return s
|
||||
k += 1; t /= k; t *= z; s += t
|
||||
if abs(t) < tol:
|
||||
return s
|
||||
|
||||
+49
-38
@@ -2,15 +2,17 @@ import inspect
|
||||
import numbers
|
||||
import sys
|
||||
|
||||
from . import function_docs
|
||||
from . import libmp
|
||||
from . import function_docs, libmp
|
||||
from .libmp import (MPZ_ONE, ComplexResult, dps_to_prec, finf, fnan, fninf,
|
||||
from_float, from_int, from_str, fzero, int_types, mpc_hash,
|
||||
mpci_abs, mpci_add, mpci_div, mpci_mul, mpci_neg, mpci_pos,
|
||||
mpci_pow, mpci_sub, mpf_hash, mpf_le, mpf_neg, mpf_pos,
|
||||
mpi_abs, mpi_add, mpi_delta, mpi_div, mpi_from_str,
|
||||
mpi_mid, mpi_mul, mpi_neg, mpi_pos, mpi_pow, mpi_str,
|
||||
mpi_sub, prec_to_dps, repr_dps, round_ceiling, round_floor)
|
||||
from_float, from_int, from_str, fzero, int_types, mpf_le,
|
||||
mpf_neg, prec_to_dps, repr_dps, round_ceiling, round_floor,
|
||||
round_nearest)
|
||||
from .libmp.libmpc import mpc_hash
|
||||
from .libmp.libmpf import mpf_hash, mpf_pos
|
||||
from .libmp.libmpi import (mpci_abs, mpci_add, mpci_div, mpci_mul, mpci_neg,
|
||||
mpci_pos, mpci_pow, mpci_sub, mpi_abs, mpi_add,
|
||||
mpi_delta, mpi_div, mpi_from_str, mpi_mid, mpi_mul,
|
||||
mpi_neg, mpi_pos, mpi_pow, mpi_str, mpi_sub)
|
||||
from .matrices.matrices import _matrix
|
||||
|
||||
|
||||
@@ -134,12 +136,12 @@ class ivmpf:
|
||||
return NotImplemented
|
||||
return cmpfun(s._mpi_, t._mpi_)
|
||||
|
||||
def __eq__(s, t): return s._compare(t, libmp.mpi_eq)
|
||||
def __ne__(s, t): return s._compare(t, libmp.mpi_ne)
|
||||
def __lt__(s, t): return s._compare(t, libmp.mpi_lt)
|
||||
def __le__(s, t): return s._compare(t, libmp.mpi_le)
|
||||
def __gt__(s, t): return s._compare(t, libmp.mpi_gt)
|
||||
def __ge__(s, t): return s._compare(t, libmp.mpi_ge)
|
||||
def __eq__(s, t): return s._compare(t, libmp.libmpi.mpi_eq)
|
||||
def __ne__(s, t): return s._compare(t, libmp.libmpi.mpi_ne)
|
||||
def __lt__(s, t): return s._compare(t, libmp.libmpi.mpi_lt)
|
||||
def __le__(s, t): return s._compare(t, libmp.libmpi.mpi_le)
|
||||
def __gt__(s, t): return s._compare(t, libmp.libmpi.mpi_gt)
|
||||
def __ge__(s, t): return s._compare(t, libmp.libmpi.mpi_ge)
|
||||
|
||||
def __abs__(self):
|
||||
return self.ctx.make_mpf(mpi_abs(self._mpi_, self.ctx.prec))
|
||||
@@ -329,29 +331,29 @@ class MPIntervalContext(StandardBaseContext):
|
||||
ctx.ninf = -ctx.inf
|
||||
ctx.nan = ctx.mpf('nan')
|
||||
ctx.j = ctx.mpc(0,1)
|
||||
ctx.exp = ctx._wrap_mpi_function(libmp.mpi_exp, libmp.mpci_exp)
|
||||
ctx.sqrt = ctx._wrap_mpi_function(libmp.mpi_sqrt)
|
||||
ctx.ln = ctx._wrap_mpi_function(libmp.mpi_log, libmp.mpci_log)
|
||||
ctx.cos = ctx._wrap_mpi_function(libmp.mpi_cos, libmp.mpci_cos)
|
||||
ctx.sin = ctx._wrap_mpi_function(libmp.mpi_sin, libmp.mpci_sin)
|
||||
ctx.tan = ctx._wrap_mpi_function(libmp.mpi_tan)
|
||||
ctx.gamma = ctx._wrap_mpi_function(libmp.mpi_gamma, libmp.mpci_gamma)
|
||||
ctx.loggamma = ctx._wrap_mpi_function(libmp.mpi_loggamma, libmp.mpci_loggamma)
|
||||
ctx.rgamma = ctx._wrap_mpi_function(libmp.mpi_rgamma, libmp.mpci_rgamma)
|
||||
ctx.factorial = ctx._wrap_mpi_function(libmp.mpi_factorial, libmp.mpci_factorial)
|
||||
ctx.exp = ctx._wrap_mpi_function(libmp.libmpi.mpi_exp, libmp.libmpi.mpci_exp)
|
||||
ctx.sqrt = ctx._wrap_mpi_function(libmp.libmpi.mpi_sqrt)
|
||||
ctx.ln = ctx._wrap_mpi_function(libmp.libmpi.mpi_log, libmp.libmpi.mpci_log)
|
||||
ctx.cos = ctx._wrap_mpi_function(libmp.libmpi.mpi_cos, libmp.libmpi.mpci_cos)
|
||||
ctx.sin = ctx._wrap_mpi_function(libmp.libmpi.mpi_sin, libmp.libmpi.mpci_sin)
|
||||
ctx.tan = ctx._wrap_mpi_function(libmp.libmpi.mpi_tan)
|
||||
ctx.gamma = ctx._wrap_mpi_function(libmp.libmpi.mpi_gamma, libmp.libmpi.mpci_gamma)
|
||||
ctx.loggamma = ctx._wrap_mpi_function(libmp.libmpi.mpi_loggamma, libmp.libmpi.mpci_loggamma)
|
||||
ctx.rgamma = ctx._wrap_mpi_function(libmp.libmpi.mpi_rgamma, libmp.libmpi.mpci_rgamma)
|
||||
ctx.factorial = ctx._wrap_mpi_function(libmp.libmpi.mpi_factorial, libmp.libmpi.mpci_factorial)
|
||||
ctx.fac = ctx.factorial
|
||||
|
||||
ctx.eps = ctx._constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1))
|
||||
ctx.pi = ctx._constant(libmp.mpf_pi)
|
||||
ctx.e = ctx._constant(libmp.mpf_e)
|
||||
ctx.ln2 = ctx._constant(libmp.mpf_ln2)
|
||||
ctx.ln10 = ctx._constant(libmp.mpf_ln10)
|
||||
ctx.phi = ctx._constant(libmp.mpf_phi)
|
||||
ctx.euler = ctx._constant(libmp.mpf_euler)
|
||||
ctx.catalan = ctx._constant(libmp.mpf_catalan)
|
||||
ctx.glaisher = ctx._constant(libmp.mpf_glaisher)
|
||||
ctx.khinchin = ctx._constant(libmp.mpf_khinchin)
|
||||
ctx.twinprime = ctx._constant(libmp.mpf_twinprime)
|
||||
ctx.ln2 = ctx._constant(libmp.libelefun.mpf_ln2)
|
||||
ctx.ln10 = ctx._constant(libmp.libelefun.mpf_ln10)
|
||||
ctx.phi = ctx._constant(libmp.libelefun.mpf_phi)
|
||||
ctx.euler = ctx._constant(libmp.gammazeta.mpf_euler)
|
||||
ctx.catalan = ctx._constant(libmp.gammazeta.mpf_catalan)
|
||||
ctx.glaisher = ctx._constant(libmp.gammazeta.mpf_glaisher)
|
||||
ctx.khinchin = ctx._constant(libmp.gammazeta.mpf_khinchin)
|
||||
ctx.twinprime = ctx._constant(libmp.gammazeta.mpf_twinprime)
|
||||
|
||||
def _wrap_mpi_function(ctx, f_real, f_complex=None):
|
||||
def g(x, **kwargs):
|
||||
@@ -441,20 +443,29 @@ class MPIntervalContext(StandardBaseContext):
|
||||
assert mpf_le(a, b), "endpoints must be properly ordered"
|
||||
return ctx.make_mpf((a, b))
|
||||
|
||||
def nstr(ctx, x, n=5, **kwargs):
|
||||
def nstr(ctx, x, n=5, *, strip_zeros=True, min_fixed=None, max_fixed=None,
|
||||
show_zero_exponent=False, base=10, binary_exp=False,
|
||||
rnd=round_nearest, mode='brackets', use_spaces=True,
|
||||
brackets='[]', error_dps=4):
|
||||
x = ctx.convert(x)
|
||||
kwargs = {'strip_zeros': strip_zeros, 'min_fixed': min_fixed,
|
||||
'max_fixed': max_fixed, 'show_zero_exponent': show_zero_exponent,
|
||||
'base': base, 'binary_exp': binary_exp,
|
||||
'rnd': rnd, 'use_spaces': use_spaces,
|
||||
'brackets': brackets, 'mode': mode,
|
||||
'error_dps': error_dps}
|
||||
if hasattr(x, "_mpi_"):
|
||||
return libmp.mpi_to_str(x._mpi_, n, **kwargs)
|
||||
return libmp.libmpi.mpi_to_str(x._mpi_, n, **kwargs)
|
||||
if hasattr(x, "_mpci_"):
|
||||
re = libmp.mpi_to_str(x._mpci_[0], n, **kwargs)
|
||||
im = libmp.mpi_to_str(x._mpci_[1], n, **kwargs)
|
||||
re = libmp.libmpi.mpi_to_str(x._mpci_[0], n, **kwargs)
|
||||
im = libmp.libmpi.mpi_to_str(x._mpci_[1], n, **kwargs)
|
||||
return "(%s + %s*j)" % (re, im)
|
||||
|
||||
def mag(ctx, x):
|
||||
x = ctx.convert(x)
|
||||
if isinstance(x, ctx.mpc):
|
||||
return max(ctx.mag(x.real), ctx.mag(x.imag)) + 1
|
||||
a, b = libmp.mpi_abs(x._mpi_)
|
||||
a, b = libmp.libmpi.mpi_abs(x._mpi_)
|
||||
sign, man, exp, bc = b
|
||||
if man:
|
||||
return exp+bc
|
||||
@@ -495,7 +506,7 @@ class MPIntervalContext(StandardBaseContext):
|
||||
def atan2(ctx, y, x):
|
||||
y = ctx.convert(y)._mpi_
|
||||
x = ctx.convert(x)._mpi_
|
||||
return ctx.make_mpf(libmp.mpi_atan2(y,x,ctx.prec))
|
||||
return ctx.make_mpf(libmp.libmpi.mpi_atan2(y,x,ctx.prec))
|
||||
|
||||
def _convert_param(ctx, x):
|
||||
if isinstance(x, libmp.int_types):
|
||||
|
||||
+142
-105
@@ -6,19 +6,23 @@ operating with them.
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from . import function_docs, libmp
|
||||
from .ctx_base import StandardBaseContext
|
||||
from .libmp import (MPQ, MPZ_ONE, ComplexResult, dps_to_prec, finf, fnan,
|
||||
fninf, fone, from_rational, fzero, int_types, mpc_add,
|
||||
mpc_add_mpf, mpc_div, mpc_div_mpf, mpc_mpf_div,
|
||||
mpc_mpf_sub, mpc_mul, mpc_mul_mpf, mpc_neg, mpc_sub,
|
||||
mpc_sub_mpf, mpc_to_str, mpf_add, mpf_apery, mpf_catalan,
|
||||
mpf_degree, mpf_div, mpf_e, mpf_euler, mpf_glaisher,
|
||||
mpf_khinchin, mpf_ln2, mpf_ln10, mpf_mertens, mpf_mul,
|
||||
mpf_neg, mpf_phi, mpf_pi, mpf_rand, mpf_sub, mpf_twinprime,
|
||||
repr_dps, round_nearest, to_man_exp, to_str)
|
||||
from .ctx_mp_python import PythonMPContext as BaseMPContext
|
||||
from .libmp import (MPZ_ONE, ComplexResult, dps_to_prec, finf, fnan, fninf,
|
||||
fone, from_rational, fzero, int_types, mpf_add, mpf_div,
|
||||
mpf_mul, mpf_neg, mpf_sub, repr_dps, round_nearest,
|
||||
to_man_exp, to_str)
|
||||
from .libmp.backend import MPQ
|
||||
from .libmp.gammazeta import (mpf_apery, mpf_catalan, mpf_euler, mpf_glaisher,
|
||||
mpf_khinchin, mpf_mertens, mpf_twinprime)
|
||||
from .libmp.libelefun import (mpf_degree, mpf_e, mpf_ln2, mpf_ln10, mpf_phi,
|
||||
mpf_pi)
|
||||
from .libmp.libmpc import (mpc_add, mpc_add_mpf, mpc_div, mpc_div_mpf,
|
||||
mpc_mpf_div, mpc_mpf_sub, mpc_mul, mpc_mul_mpf,
|
||||
mpc_neg, mpc_sub, mpc_sub_mpf, mpc_to_str)
|
||||
from .libmp.libmpf import mpf_rand
|
||||
|
||||
|
||||
get_complex = re.compile(r"""
|
||||
@@ -29,25 +33,26 @@ get_complex = re.compile(r"""
|
||||
""", re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
if name == 'mpnumeric':
|
||||
from .ctx_mp_python import mpnumeric
|
||||
warnings.warn(f"{name} is deprecated", DeprecationWarning)
|
||||
return mpnumeric
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
from .ctx_mp_python import PythonMPContext as BaseMPContext
|
||||
|
||||
|
||||
class MPContext(BaseMPContext, StandardBaseContext):
|
||||
"""
|
||||
Context for multiple precision floatng-point arithmetic.
|
||||
|
||||
**Arguments**
|
||||
|
||||
*prec*
|
||||
precision in bits, default is 53
|
||||
*rounding*
|
||||
rounding mode, default is round to nearest
|
||||
*trap_complex*
|
||||
enable complex answers, where real aren't possible, default is False
|
||||
|
||||
"""
|
||||
|
||||
def __init__(ctx, prec=sys.float_info.mant_dig,
|
||||
rounding=round_nearest, trap_complex=False):
|
||||
BaseMPContext.__init__(ctx)
|
||||
ctx.pretty = False
|
||||
ctx.shortest_str = False
|
||||
ctx.types = [ctx.mpf, ctx.mpc, ctx.constant]
|
||||
ctx.default()
|
||||
ctx._set_prec(prec)
|
||||
@@ -67,8 +72,10 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
ctx.atan2.__func__.__doc__ = function_docs.atan2
|
||||
|
||||
ctx.digamma.__doc__ = function_docs.digamma
|
||||
ctx.cospi.__doc_ = function_docs.cospi
|
||||
ctx.sinpi.__doc_ = function_docs.sinpi
|
||||
ctx.cospi.__doc__ = function_docs.cospi
|
||||
ctx.sinpi.__doc__ = function_docs.sinpi
|
||||
ctx.sinpi.__name__ = 'sinpi'
|
||||
ctx.cospi.__name__ = 'cospi'
|
||||
|
||||
def init_builtins(ctx):
|
||||
# Exact constants
|
||||
@@ -100,50 +107,50 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
|
||||
# Standard functions
|
||||
ctx.sqrt = ctx._wrap_libmp_function(libmp.mpf_sqrt, libmp.mpc_sqrt)
|
||||
ctx.cbrt = ctx._wrap_libmp_function(libmp.mpf_cbrt, libmp.mpc_cbrt)
|
||||
ctx.ln = ctx._wrap_libmp_function(libmp.mpf_ln, libmp.mpc_ln)
|
||||
ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan)
|
||||
ctx.cbrt = ctx._wrap_libmp_function(libmp.libelefun.mpf_cbrt, libmp.libmpc.mpc_cbrt)
|
||||
ctx.ln = ctx._wrap_libmp_function(libmp.libelefun.mpf_ln, libmp.libmpc.mpc_ln)
|
||||
ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.libmpc.mpc_atan)
|
||||
ctx.exp = ctx._wrap_libmp_function(libmp.mpf_exp, libmp.mpc_exp)
|
||||
ctx.expj = ctx._wrap_libmp_function(libmp.mpf_expj, libmp.mpc_expj)
|
||||
ctx.expjpi = ctx._wrap_libmp_function(libmp.mpf_expjpi, libmp.mpc_expjpi)
|
||||
ctx.sin = ctx._wrap_libmp_function(libmp.mpf_sin, libmp.mpc_sin)
|
||||
ctx.cos = ctx._wrap_libmp_function(libmp.mpf_cos, libmp.mpc_cos)
|
||||
ctx.tan = ctx._wrap_libmp_function(libmp.mpf_tan, libmp.mpc_tan)
|
||||
ctx.sinh = ctx._wrap_libmp_function(libmp.mpf_sinh, libmp.mpc_sinh)
|
||||
ctx.cosh = ctx._wrap_libmp_function(libmp.mpf_cosh, libmp.mpc_cosh)
|
||||
ctx.tanh = ctx._wrap_libmp_function(libmp.mpf_tanh, libmp.mpc_tanh)
|
||||
ctx.asin = ctx._wrap_libmp_function(libmp.mpf_asin, libmp.mpc_asin)
|
||||
ctx.acos = ctx._wrap_libmp_function(libmp.mpf_acos, libmp.mpc_acos)
|
||||
ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan)
|
||||
ctx.asinh = ctx._wrap_libmp_function(libmp.mpf_asinh, libmp.mpc_asinh)
|
||||
ctx.acosh = ctx._wrap_libmp_function(libmp.mpf_acosh, libmp.mpc_acosh)
|
||||
ctx.atanh = ctx._wrap_libmp_function(libmp.mpf_atanh, libmp.mpc_atanh)
|
||||
ctx.sinpi = ctx._wrap_libmp_function(libmp.mpf_sin_pi, libmp.mpc_sin_pi)
|
||||
ctx.cospi = ctx._wrap_libmp_function(libmp.mpf_cos_pi, libmp.mpc_cos_pi)
|
||||
ctx.floor = ctx._wrap_libmp_function(libmp.mpf_floor, libmp.mpc_floor)
|
||||
ctx.ceil = ctx._wrap_libmp_function(libmp.mpf_ceil, libmp.mpc_ceil)
|
||||
ctx.nint = ctx._wrap_libmp_function(libmp.mpf_nint, libmp.mpc_nint)
|
||||
ctx.frac = ctx._wrap_libmp_function(libmp.mpf_frac, libmp.mpc_frac)
|
||||
ctx.fib = ctx.fibonacci = ctx._wrap_libmp_function(libmp.mpf_fibonacci, libmp.mpc_fibonacci)
|
||||
ctx.expj = ctx._wrap_libmp_function(libmp.libmpc.mpf_expj, libmp.libmpc.mpc_expj)
|
||||
ctx.expjpi = ctx._wrap_libmp_function(libmp.libmpc.mpf_expjpi, libmp.libmpc.mpc_expjpi)
|
||||
ctx.sin = ctx._wrap_libmp_function(libmp.mpf_sin, libmp.libmpc.mpc_sin)
|
||||
ctx.cos = ctx._wrap_libmp_function(libmp.mpf_cos, libmp.libmpc.mpc_cos)
|
||||
ctx.tan = ctx._wrap_libmp_function(libmp.mpf_tan, libmp.libmpc.mpc_tan)
|
||||
ctx.sinh = ctx._wrap_libmp_function(libmp.libelefun.mpf_sinh, libmp.libmpc.mpc_sinh)
|
||||
ctx.cosh = ctx._wrap_libmp_function(libmp.libelefun.mpf_cosh, libmp.libmpc.mpc_cosh)
|
||||
ctx.tanh = ctx._wrap_libmp_function(libmp.libelefun.mpf_tanh, libmp.libmpc.mpc_tanh)
|
||||
ctx.asin = ctx._wrap_libmp_function(libmp.libelefun.mpf_asin, libmp.libmpc.mpc_asin)
|
||||
ctx.acos = ctx._wrap_libmp_function(libmp.libelefun.mpf_acos, libmp.libmpc.mpc_acos)
|
||||
ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.libmpc.mpc_atan)
|
||||
ctx.asinh = ctx._wrap_libmp_function(libmp.libelefun.mpf_asinh, libmp.libmpc.mpc_asinh)
|
||||
ctx.acosh = ctx._wrap_libmp_function(libmp.libelefun.mpf_acosh, libmp.libmpc.mpc_acosh)
|
||||
ctx.atanh = ctx._wrap_libmp_function(libmp.libelefun.mpf_atanh, libmp.libmpc.mpc_atanh)
|
||||
ctx.sinpi = ctx._wrap_libmp_function(libmp.libelefun.mpf_sin_pi, libmp.libmpc.mpc_sin_pi)
|
||||
ctx.cospi = ctx._wrap_libmp_function(libmp.libelefun.mpf_cos_pi, libmp.libmpc.mpc_cos_pi)
|
||||
ctx.floor = ctx._wrap_libmp_function(libmp.mpf_floor, libmp.libmpc.mpc_floor)
|
||||
ctx.ceil = ctx._wrap_libmp_function(libmp.mpf_ceil, libmp.libmpc.mpc_ceil)
|
||||
ctx.nint = ctx._wrap_libmp_function(libmp.libmpf.mpf_nint, libmp.libmpc.mpc_nint)
|
||||
ctx.frac = ctx._wrap_libmp_function(libmp.libmpf.mpf_frac, libmp.libmpc.mpc_frac)
|
||||
ctx.fib = ctx.fibonacci = ctx._wrap_libmp_function(libmp.libelefun.mpf_fibonacci, libmp.libmpc.mpc_fibonacci)
|
||||
|
||||
ctx.gamma = ctx._wrap_libmp_function(libmp.mpf_gamma, libmp.mpc_gamma)
|
||||
ctx.rgamma = ctx._wrap_libmp_function(libmp.mpf_rgamma, libmp.mpc_rgamma)
|
||||
ctx.loggamma = ctx._wrap_libmp_function(libmp.mpf_loggamma, libmp.mpc_loggamma)
|
||||
ctx.fac = ctx.factorial = ctx._wrap_libmp_function(libmp.mpf_factorial, libmp.mpc_factorial)
|
||||
ctx.gamma = ctx._wrap_libmp_function(libmp.gammazeta.mpf_gamma, libmp.gammazeta.mpc_gamma)
|
||||
ctx.rgamma = ctx._wrap_libmp_function(libmp.gammazeta.mpf_rgamma, libmp.gammazeta.mpc_rgamma)
|
||||
ctx.loggamma = ctx._wrap_libmp_function(libmp.gammazeta.mpf_loggamma, libmp.gammazeta.mpc_loggamma)
|
||||
ctx.fac = ctx.factorial = ctx._wrap_libmp_function(libmp.gammazeta.mpf_factorial, libmp.gammazeta.mpc_factorial)
|
||||
|
||||
ctx.digamma = ctx._wrap_libmp_function(libmp.mpf_psi0, libmp.mpc_psi0)
|
||||
ctx.harmonic = ctx._wrap_libmp_function(libmp.mpf_harmonic, libmp.mpc_harmonic)
|
||||
ctx.ei = ctx._wrap_libmp_function(libmp.mpf_ei, libmp.mpc_ei)
|
||||
ctx.e1 = ctx._wrap_libmp_function(libmp.mpf_e1, libmp.mpc_e1)
|
||||
ctx._ci = ctx._wrap_libmp_function(libmp.mpf_ci, libmp.mpc_ci)
|
||||
ctx._si = ctx._wrap_libmp_function(libmp.mpf_si, libmp.mpc_si)
|
||||
ctx.ellipk = ctx._wrap_libmp_function(libmp.mpf_ellipk, libmp.mpc_ellipk)
|
||||
ctx._ellipe = ctx._wrap_libmp_function(libmp.mpf_ellipe, libmp.mpc_ellipe)
|
||||
ctx.agm1 = ctx._wrap_libmp_function(libmp.mpf_agm1, libmp.mpc_agm1)
|
||||
ctx._erf = ctx._wrap_libmp_function(libmp.mpf_erf, None)
|
||||
ctx._erfc = ctx._wrap_libmp_function(libmp.mpf_erfc, None)
|
||||
ctx._zeta = ctx._wrap_libmp_function(libmp.mpf_zeta, libmp.mpc_zeta)
|
||||
ctx._altzeta = ctx._wrap_libmp_function(libmp.mpf_altzeta, libmp.mpc_altzeta)
|
||||
ctx.digamma = ctx._wrap_libmp_function(libmp.gammazeta.mpf_psi0, libmp.gammazeta.mpc_psi0)
|
||||
ctx.harmonic = ctx._wrap_libmp_function(libmp.gammazeta.mpf_harmonic, libmp.gammazeta.mpc_harmonic)
|
||||
ctx.ei = ctx._wrap_libmp_function(libmp.libhyper.mpf_ei, libmp.libhyper.mpc_ei)
|
||||
ctx.e1 = ctx._wrap_libmp_function(libmp.libhyper.mpf_e1, libmp.libhyper.mpc_e1)
|
||||
ctx._ci = ctx._wrap_libmp_function(libmp.libhyper.mpf_ci, libmp.libhyper.mpc_ci)
|
||||
ctx._si = ctx._wrap_libmp_function(libmp.libhyper.mpf_si, libmp.libhyper.mpc_si)
|
||||
ctx.ellipk = ctx._wrap_libmp_function(libmp.libhyper.mpf_ellipk, libmp.libhyper.mpc_ellipk)
|
||||
ctx._ellipe = ctx._wrap_libmp_function(libmp.libhyper.mpf_ellipe, libmp.libhyper.mpc_ellipe)
|
||||
ctx.agm1 = ctx._wrap_libmp_function(libmp.libhyper.mpf_agm1, libmp.libhyper.mpc_agm1)
|
||||
ctx._erf = ctx._wrap_libmp_function(libmp.libhyper.mpf_erf, None)
|
||||
ctx._erfc = ctx._wrap_libmp_function(libmp.libhyper.mpf_erfc, None)
|
||||
ctx._zeta = ctx._wrap_libmp_function(libmp.gammazeta.mpf_zeta, libmp.gammazeta.mpc_zeta)
|
||||
ctx._altzeta = ctx._wrap_libmp_function(libmp.gammazeta.mpf_altzeta, libmp.gammazeta.mpc_altzeta)
|
||||
|
||||
def to_fixed(ctx, x, prec):
|
||||
return x.to_fixed(prec)
|
||||
@@ -154,7 +161,7 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
to `\sqrt{x^2 + y^2}`. Both `x` and `y` must be real."""
|
||||
x = ctx.convert(x)
|
||||
y = ctx.convert(y)
|
||||
return ctx.make_mpf(libmp.mpf_hypot(x._mpf_, y._mpf_, *ctx._prec_rounding))
|
||||
return ctx.make_mpf(libmp.libmpf.mpf_hypot(x._mpf_, y._mpf_, *ctx._prec_rounding))
|
||||
|
||||
def _gamma_upper_int(ctx, n, z):
|
||||
n = int(ctx._re(n))
|
||||
@@ -163,7 +170,7 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
if not hasattr(z, '_mpf_'):
|
||||
raise NotImplementedError
|
||||
prec, rounding = ctx._prec_rounding
|
||||
real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding, gamma=True)
|
||||
real, imag = libmp.libhyper.mpf_expint(n, z._mpf_, prec, rounding, gamma=True)
|
||||
if imag is None:
|
||||
return ctx.make_mpf(real)
|
||||
else:
|
||||
@@ -176,7 +183,7 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
if not hasattr(z, '_mpf_'):
|
||||
raise NotImplementedError
|
||||
prec, rounding = ctx._prec_rounding
|
||||
real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding)
|
||||
real, imag = libmp.libhyper.mpf_expint(n, z._mpf_, prec, rounding)
|
||||
if imag is None:
|
||||
return ctx.make_mpf(real)
|
||||
else:
|
||||
@@ -185,27 +192,27 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
def _nthroot(ctx, x, n):
|
||||
if hasattr(x, '_mpf_'):
|
||||
try:
|
||||
return ctx.make_mpf(libmp.mpf_nthroot(x._mpf_, n, *ctx._prec_rounding))
|
||||
return ctx.make_mpf(libmp.libelefun.mpf_nthroot(x._mpf_, n, *ctx._prec_rounding))
|
||||
except ComplexResult:
|
||||
if ctx.trap_complex:
|
||||
raise
|
||||
x = (x._mpf_, libmp.fzero)
|
||||
else:
|
||||
x = x._mpc_
|
||||
return ctx.make_mpc(libmp.mpc_nthroot(x, n, *ctx._prec_rounding))
|
||||
return ctx.make_mpc(libmp.libmpc.mpc_nthroot(x, n, *ctx._prec_rounding))
|
||||
|
||||
def _besselj(ctx, n, z):
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if hasattr(z, '_mpf_'):
|
||||
return ctx.make_mpf(libmp.mpf_besseljn(n, z._mpf_, prec, rounding))
|
||||
return ctx.make_mpf(libmp.libhyper.mpf_besseljn(n, z._mpf_, prec, rounding))
|
||||
elif hasattr(z, '_mpc_'):
|
||||
return ctx.make_mpc(libmp.mpc_besseljn(n, z._mpc_, prec, rounding))
|
||||
return ctx.make_mpc(libmp.libhyper.mpc_besseljn(n, z._mpc_, prec, rounding))
|
||||
|
||||
def _agm(ctx, a, b=1):
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if hasattr(a, '_mpf_') and hasattr(b, '_mpf_'):
|
||||
try:
|
||||
v = libmp.mpf_agm(a._mpf_, b._mpf_, prec, rounding)
|
||||
v = libmp.libhyper.mpf_agm(a._mpf_, b._mpf_, prec, rounding)
|
||||
return ctx.make_mpf(v)
|
||||
except ComplexResult:
|
||||
pass
|
||||
@@ -213,13 +220,13 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
else: a = a._mpc_
|
||||
if hasattr(b, '_mpf_'): b = (b._mpf_, libmp.fzero)
|
||||
else: b = b._mpc_
|
||||
return ctx.make_mpc(libmp.mpc_agm(a, b, prec, rounding))
|
||||
return ctx.make_mpc(libmp.libhyper.mpc_agm(a, b, prec, rounding))
|
||||
|
||||
def bernoulli(ctx, n, plus=False):
|
||||
return ctx.make_mpf(libmp.mpf_bernoulli(int(n), *ctx._prec_rounding, plus=plus))
|
||||
|
||||
def _zeta_int(ctx, n):
|
||||
return ctx.make_mpf(libmp.mpf_zeta_int(int(n), *ctx._prec_rounding))
|
||||
return ctx.make_mpf(libmp.gammazeta.mpf_zeta_int(int(n), *ctx._prec_rounding))
|
||||
|
||||
def atan2(ctx, y, x):
|
||||
x = ctx.convert(x)
|
||||
@@ -230,32 +237,34 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
z = ctx.convert(z)
|
||||
m = int(m)
|
||||
if ctx._is_real_type(z):
|
||||
return ctx.make_mpf(libmp.mpf_psi(m, z._mpf_, *ctx._prec_rounding))
|
||||
return ctx.make_mpf(libmp.gammazeta.mpf_psi(m, z._mpf_, *ctx._prec_rounding))
|
||||
else:
|
||||
return ctx.make_mpc(libmp.mpc_psi(m, z._mpc_, *ctx._prec_rounding))
|
||||
return ctx.make_mpc(libmp.gammazeta.mpc_psi(m, z._mpc_, *ctx._prec_rounding))
|
||||
|
||||
def cos_sin(ctx, x, **kwargs):
|
||||
def cos_sin(ctx, x, *, prec=None, dps=None, rounding=round_nearest, exact=False):
|
||||
if type(x) not in ctx.types:
|
||||
x = ctx.convert(x)
|
||||
prec, rounding = ctx._parse_prec(kwargs)
|
||||
prec, rounding = ctx._parse_prec({'prec': prec, 'dps': dps,
|
||||
'rounding': rounding, 'exact': exact})
|
||||
if hasattr(x, '_mpf_'):
|
||||
c, s = libmp.mpf_cos_sin(x._mpf_, prec, rounding)
|
||||
c, s = libmp.libelefun.mpf_cos_sin(x._mpf_, prec, rounding)
|
||||
return ctx.make_mpf(c), ctx.make_mpf(s)
|
||||
elif hasattr(x, '_mpc_'):
|
||||
c, s = libmp.mpc_cos_sin(x._mpc_, prec, rounding)
|
||||
c, s = libmp.libmpc.mpc_cos_sin(x._mpc_, prec, rounding)
|
||||
return ctx.make_mpc(c), ctx.make_mpc(s)
|
||||
else:
|
||||
return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs)
|
||||
|
||||
def cospi_sinpi(ctx, x, **kwargs):
|
||||
def cospi_sinpi(ctx, x, prec=None, dps=None, rounding=round_nearest, exact=False):
|
||||
if type(x) not in ctx.types:
|
||||
x = ctx.convert(x)
|
||||
prec, rounding = ctx._parse_prec(kwargs)
|
||||
prec, rounding = ctx._parse_prec({'prec': prec, 'dps': dps,
|
||||
'rounding': rounding, 'exact': exact})
|
||||
if hasattr(x, '_mpf_'):
|
||||
c, s = libmp.mpf_cos_sin_pi(x._mpf_, prec, rounding)
|
||||
c, s = libmp.libelefun.mpf_cos_sin_pi(x._mpf_, prec, rounding)
|
||||
return ctx.make_mpf(c), ctx.make_mpf(s)
|
||||
elif hasattr(x, '_mpc_'):
|
||||
c, s = libmp.mpc_cos_sin_pi(x._mpc_, prec, rounding)
|
||||
c, s = libmp.libmpc.mpc_cos_sin_pi(x._mpc_, prec, rounding)
|
||||
return ctx.make_mpc(c), ctx.make_mpc(s)
|
||||
else:
|
||||
return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs)
|
||||
@@ -343,7 +352,7 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
return True
|
||||
if hasattr(x, '_mpf_'):
|
||||
if ctx.isfinite(x):
|
||||
man, exp = to_man_exp(x._mpf_, signed=True)
|
||||
man, exp = to_man_exp(x._mpf_)
|
||||
return man < 0 and exp >= 0
|
||||
return False
|
||||
if hasattr(x, '_mpc_'):
|
||||
@@ -363,6 +372,8 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
(" mp.dps = %s" % ctx.dps).ljust(30) + f"[default: {sys.float_info.dig}]",
|
||||
(" mp.rounding = '%s'" % ctx.rounding).ljust(30) + f"[default: 'n']",
|
||||
(" mp.trap_complex = %s" % ctx.trap_complex).ljust(30) + "[default: False]",
|
||||
(" mp.pretty_dps = '%s'" % ctx.pretty_dps).ljust(30) + "[default: 'str']",
|
||||
(" mp.shortest_str = %s" % ctx.shortest_str).ljust(30) + "[default: False]",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -533,7 +544,9 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
return +v2
|
||||
return f_autoprec_wrapped
|
||||
|
||||
def nstr(ctx, x, n=6, **kwargs):
|
||||
def nstr(ctx, x, n=6, *, strip_zeros=True, min_fixed=None, max_fixed=None,
|
||||
show_zero_exponent=False, base=10, binary_exp=False,
|
||||
rnd=round_nearest):
|
||||
"""
|
||||
Convert an ``mpf`` or ``mpc`` to a decimal string literal with *n*
|
||||
significant digits. The small default value for *n* is chosen to
|
||||
@@ -573,6 +586,11 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
'0.0e+0'
|
||||
|
||||
"""
|
||||
kwargs = {'strip_zeros': strip_zeros, 'min_fixed': min_fixed,
|
||||
'max_fixed': max_fixed, 'show_zero_exponent': show_zero_exponent,
|
||||
'base': base, 'binary_exp': binary_exp,
|
||||
'rnd': rnd}
|
||||
|
||||
if isinstance(x, list):
|
||||
return "[%s]" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x))
|
||||
if isinstance(x, tuple):
|
||||
@@ -625,13 +643,13 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if 'rounding' in kwargs:
|
||||
rounding = ctx._MPFR_rounding_map[kwargs['rounding']]
|
||||
if 'prec' in kwargs:
|
||||
if kwargs.get('prec') is not None:
|
||||
prec = kwargs['prec']
|
||||
if prec == ctx.inf:
|
||||
return 0, 'f'
|
||||
else:
|
||||
prec = int(prec)
|
||||
elif 'dps' in kwargs:
|
||||
elif kwargs.get('dps') is not None:
|
||||
dps = kwargs['dps']
|
||||
if dps == ctx.inf:
|
||||
return 0, 'f'
|
||||
@@ -677,7 +695,7 @@ maxterms, or set zeroprec."""
|
||||
t /= (k+1)
|
||||
return t
|
||||
if key not in ctx.hyp_summators:
|
||||
ctx.hyp_summators[key] = libmp.make_hyp_summator(key)[1]
|
||||
ctx.hyp_summators[key] = libmp.libhyper.make_hyp_summator(key)[1]
|
||||
summator = ctx.hyp_summators[key]
|
||||
prec = ctx.prec
|
||||
maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(prec))
|
||||
@@ -774,10 +792,23 @@ maxterms, or set zeroprec."""
|
||||
|
||||
"""
|
||||
x = ctx.convert(x)
|
||||
y, n = libmp.mpf_frexp(x._mpf_)
|
||||
y, n = libmp.libmpf.mpf_frexp(x._mpf_)
|
||||
return ctx.make_mpf(y), n
|
||||
|
||||
def fneg(ctx, x, **kwargs):
|
||||
def ulp(ctx, x):
|
||||
"""
|
||||
Return the value of the least significant bit of the `x`.
|
||||
|
||||
>>> from mpmath import ulp
|
||||
>>> ulp(1)
|
||||
mpf('2.2204460492503131e-16')
|
||||
|
||||
"""
|
||||
x = ctx.convert(x)
|
||||
*_, e, bc = x._mpf_
|
||||
return ctx.make_mpf((0, 1, e + bc - ctx.prec, 1))
|
||||
|
||||
def fneg(ctx, x, prec=None, dps=None, rounding=round_nearest, exact=False):
|
||||
"""
|
||||
Negates the number *x*, giving a floating-point result, optionally
|
||||
using a custom precision and rounding mode.
|
||||
@@ -822,7 +853,8 @@ maxterms, or set zeroprec."""
|
||||
-200000000000000000000001
|
||||
|
||||
"""
|
||||
prec, rounding = ctx._parse_prec(kwargs)
|
||||
prec, rounding = ctx._parse_prec({'prec': prec, 'dps': dps,
|
||||
'rounding': rounding, 'exact': exact})
|
||||
x = ctx.convert(x)
|
||||
if hasattr(x, '_mpf_'):
|
||||
return ctx.make_mpf(mpf_neg(x._mpf_, prec, rounding))
|
||||
@@ -830,7 +862,7 @@ maxterms, or set zeroprec."""
|
||||
return ctx.make_mpc(mpc_neg(x._mpc_, prec, rounding))
|
||||
raise ValueError("Arguments need to be mpf or mpc compatible numbers")
|
||||
|
||||
def fadd(ctx, x, y, **kwargs):
|
||||
def fadd(ctx, x, y, prec=None, dps=None, rounding=round_nearest, exact=False):
|
||||
"""
|
||||
Adds the numbers *x* and *y*, giving a floating-point result,
|
||||
optionally using a custom precision and rounding mode.
|
||||
@@ -845,7 +877,7 @@ maxterms, or set zeroprec."""
|
||||
specifies the direction of rounding. Valid options are:
|
||||
|
||||
* ``'f'`` (alias ``'D'``) for floor, towards minus infinity
|
||||
* ``'c'`` (alias ``'U'``) )for ceiling, towards plus infinity
|
||||
* ``'c'`` (alias ``'U'``) for ceiling, towards plus infinity
|
||||
* ``'d'`` (alias ``'Z'``) for down, towards zero
|
||||
* ``'u'`` (alias ``'Y'``) for up, away from zero
|
||||
* ``'n'`` (alias ``'N'``) for rounding to nearest (default)
|
||||
@@ -889,7 +921,8 @@ maxterms, or set zeroprec."""
|
||||
OverflowError: the exact result does not fit in memory
|
||||
|
||||
"""
|
||||
prec, rounding = ctx._parse_prec(kwargs)
|
||||
prec, rounding = ctx._parse_prec({'prec': prec, 'dps': dps,
|
||||
'rounding': rounding, 'exact': exact})
|
||||
x = ctx.convert(x)
|
||||
y = ctx.convert(y)
|
||||
try:
|
||||
@@ -907,7 +940,7 @@ maxterms, or set zeroprec."""
|
||||
raise OverflowError(ctx._exact_overflow_msg)
|
||||
raise ValueError("Arguments need to be mpf or mpc compatible numbers")
|
||||
|
||||
def fsub(ctx, x, y, **kwargs):
|
||||
def fsub(ctx, x, y, prec=None, dps=None, rounding=round_nearest, exact=False):
|
||||
"""
|
||||
Subtracts the numbers *x* and *y*, giving a floating-point result,
|
||||
optionally using a custom precision and rounding mode.
|
||||
@@ -954,7 +987,8 @@ maxterms, or set zeroprec."""
|
||||
OverflowError: the exact result does not fit in memory
|
||||
|
||||
"""
|
||||
prec, rounding = ctx._parse_prec(kwargs)
|
||||
prec, rounding = ctx._parse_prec({'prec': prec, 'dps': dps,
|
||||
'rounding': rounding, 'exact': exact})
|
||||
x = ctx.convert(x)
|
||||
y = ctx.convert(y)
|
||||
try:
|
||||
@@ -972,7 +1006,7 @@ maxterms, or set zeroprec."""
|
||||
raise OverflowError(ctx._exact_overflow_msg)
|
||||
raise ValueError("Arguments need to be mpf or mpc compatible numbers")
|
||||
|
||||
def fmul(ctx, x, y, **kwargs):
|
||||
def fmul(ctx, x, y, prec=None, dps=None, rounding=round_nearest, exact=False):
|
||||
"""
|
||||
Multiplies the numbers *x* and *y*, giving a floating-point result,
|
||||
optionally using a custom precision and rounding mode.
|
||||
@@ -1022,7 +1056,9 @@ maxterms, or set zeroprec."""
|
||||
OverflowError: the exact result does not fit in memory
|
||||
|
||||
"""
|
||||
prec, rounding = ctx._parse_prec(kwargs)
|
||||
prec, rounding = ctx._parse_prec({'prec': prec, 'dps': dps,
|
||||
'rounding': rounding, 'exact': exact})
|
||||
|
||||
x = ctx.convert(x)
|
||||
y = ctx.convert(y)
|
||||
try:
|
||||
@@ -1040,7 +1076,7 @@ maxterms, or set zeroprec."""
|
||||
raise OverflowError(ctx._exact_overflow_msg)
|
||||
raise ValueError("Arguments need to be mpf or mpc compatible numbers")
|
||||
|
||||
def fdiv(ctx, x, y, **kwargs):
|
||||
def fdiv(ctx, x, y, prec=None, dps=None, rounding=round_nearest, exact=False):
|
||||
"""
|
||||
Divides the numbers *x* and *y*, giving a floating-point result,
|
||||
optionally using a custom precision and rounding mode.
|
||||
@@ -1088,7 +1124,8 @@ maxterms, or set zeroprec."""
|
||||
ValueError: division is not an exact operation
|
||||
|
||||
"""
|
||||
prec, rounding = ctx._parse_prec(kwargs)
|
||||
prec, rounding = ctx._parse_prec({'prec': prec, 'dps': dps,
|
||||
'rounding': rounding, 'exact': exact})
|
||||
if not prec:
|
||||
raise ValueError("division is not an exact operation")
|
||||
x = ctx.convert(x)
|
||||
@@ -1162,7 +1199,7 @@ maxterms, or set zeroprec."""
|
||||
im_dist = ctx.ninf
|
||||
elif hasattr(x, "_mpc_"):
|
||||
re, im = x._mpc_
|
||||
iman, iexp = to_man_exp(im, signed=True)
|
||||
iman, iexp = to_man_exp(im)
|
||||
if iman:
|
||||
im_dist = iexp + iman.bit_length()
|
||||
else:
|
||||
@@ -1173,7 +1210,7 @@ maxterms, or set zeroprec."""
|
||||
return ctx.nint_distance(x)
|
||||
else:
|
||||
raise TypeError("requires an mpf/mpc")
|
||||
man, exp = to_man_exp(re, signed=True)
|
||||
man, exp = to_man_exp(re)
|
||||
mag = exp+man.bit_length()
|
||||
# |x| < 0.5
|
||||
if mag < 0:
|
||||
@@ -1280,9 +1317,9 @@ maxterms, or set zeroprec."""
|
||||
s = ctx.convert(s)
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if hasattr(s, '_mpf_'):
|
||||
v = ctx.make_mpf(libmp.mpf_zetasum(s._mpf_, a, b, prec))
|
||||
v = ctx.make_mpf(libmp.gammazeta.mpf_zetasum(s._mpf_, a, b, prec))
|
||||
elif hasattr(s, '_mpc_'):
|
||||
v = ctx.make_mpc(libmp.mpc_zetasum(s._mpc_, a, b, prec))
|
||||
v = ctx.make_mpc(libmp.gammazeta.mpc_zetasum(s._mpc_, a, b, prec))
|
||||
return v
|
||||
'''
|
||||
|
||||
@@ -1291,7 +1328,7 @@ maxterms, or set zeroprec."""
|
||||
raise NotImplementedError
|
||||
a = int(a)
|
||||
prec = ctx._prec
|
||||
xs, ys = libmp.mpc_zetasum(s._mpc_, a, n, derivatives, reflect, prec)
|
||||
xs, ys = libmp.gammazeta.mpc_zetasum(s._mpc_, a, n, derivatives, reflect, prec)
|
||||
xs = [ctx.make_mpc(x) for x in xs]
|
||||
ys = [ctx.make_mpc(y) for y in ys]
|
||||
return xs, ys
|
||||
|
||||
+87
-87
@@ -1,22 +1,23 @@
|
||||
import inspect
|
||||
import numbers
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from . import function_docs
|
||||
from .libmp import (MPQ, MPZ, ComplexResult, dps_to_prec, finf, fnan, fninf,
|
||||
format_mpc, format_mpf, from_Decimal, from_float, from_int,
|
||||
from_man_exp, from_npfloat, from_rational, from_str, fzero,
|
||||
int_types, mpc_abs, mpc_add, mpc_add_mpf, mpc_conjugate,
|
||||
mpc_div, mpc_div_mpf, mpc_hash, mpc_is_inf, mpc_is_nonzero,
|
||||
mpc_mpf_div, mpc_mpf_sub, mpc_mul, mpc_mul_int,
|
||||
mpc_mul_mpf, mpc_neg, mpc_pos, mpc_pow, mpc_pow_int,
|
||||
mpc_pow_mpf, mpc_sub, mpc_sub_mpf, mpc_to_complex,
|
||||
mpc_to_str, mpf_abs, mpf_add, mpf_div, mpf_eq, mpf_ge,
|
||||
mpf_gt, mpf_hash, mpf_le, mpf_lt, mpf_mod, mpf_mul,
|
||||
mpf_neg, mpf_pos, mpf_pow, mpf_sub, mpf_sum, prec_to_dps,
|
||||
round_nearest, to_fixed, to_float, to_int, to_man_exp,
|
||||
to_rational, to_str)
|
||||
from .libmp import (MPZ, ComplexResult, dps_to_prec, finf, fnan, fninf,
|
||||
from_float, from_int, from_man_exp, from_rational,
|
||||
from_str, fzero, int_types, mpc_abs, mpc_pow, mpc_pow_int,
|
||||
mpc_pow_mpf, mpf_abs, mpf_add, mpf_div, mpf_eq, mpf_ge,
|
||||
mpf_gt, mpf_le, mpf_lt, mpf_mod, mpf_mul, mpf_neg, mpf_pow,
|
||||
mpf_sub, prec_to_dps, round_nearest, to_float, to_int,
|
||||
to_man_exp, to_rational, to_str)
|
||||
from .libmp.backend import MPQ
|
||||
from .libmp.libmpc import (mpc_add, mpc_add_mpf, mpc_conjugate, mpc_div,
|
||||
mpc_div_mpf, mpc_hash, mpc_is_inf, mpc_is_nonzero,
|
||||
mpc_mpf_div, mpc_mpf_sub, mpc_mul, mpc_mul_int,
|
||||
mpc_mul_mpf, mpc_neg, mpc_pos, mpc_sub, mpc_sub_mpf,
|
||||
mpc_to_complex, mpc_to_str)
|
||||
from .libmp.libmpf import (format_mpc, format_mpf, from_Decimal, from_npfloat,
|
||||
mpf_hash, mpf_pos, mpf_sum, to_fixed)
|
||||
|
||||
|
||||
new = object.__new__
|
||||
@@ -42,19 +43,21 @@ class _mpf(mpnumeric):
|
||||
"""
|
||||
__slots__ = ['_mpf_', 'context']
|
||||
|
||||
def __new__(cls, val=fzero, **kwargs):
|
||||
def __new__(cls, val=fzero, *, prec=None, dps=None,
|
||||
rounding=round_nearest, base=0):
|
||||
"""A new mpf can be created from a Python float, an int, a
|
||||
or a decimal string representing a number in floating-point
|
||||
format."""
|
||||
ctx = cls.context
|
||||
prec, rounding = ctx._prec_rounding
|
||||
base = 0
|
||||
if kwargs:
|
||||
prec = kwargs.get('prec', prec)
|
||||
if 'dps' in kwargs:
|
||||
prec = dps_to_prec(kwargs['dps'])
|
||||
rounding = kwargs.get('rounding', rounding)
|
||||
base = kwargs.get('base', base)
|
||||
ctx_prec, ctx_rounding = ctx._prec_rounding
|
||||
if prec and dps:
|
||||
raise ValueError("both prec and dps can't be specified")
|
||||
if dps:
|
||||
prec = dps_to_prec(dps)
|
||||
if prec is None:
|
||||
prec = ctx_prec
|
||||
if rounding is None:
|
||||
rounding = ctx_rounding
|
||||
v = new(cls)
|
||||
if type(val) is cls:
|
||||
val = val._mpf_
|
||||
@@ -132,17 +135,24 @@ class _mpf(mpnumeric):
|
||||
|
||||
def __repr__(self):
|
||||
ctx = self.context
|
||||
rounding = ctx._prec_rounding[1]
|
||||
if ctx.pretty:
|
||||
if ctx.shortest_str:
|
||||
return str(self)
|
||||
ndigits = (ctx._repr_digits
|
||||
if ctx._pretty_repr_dps else ctx._str_digits)
|
||||
return to_str(self._mpf_, ndigits, rnd=rounding)
|
||||
return f"mpf({to_str(self._mpf_, ctx._repr_digits, rnd=rounding)!r})"
|
||||
return to_str(self._mpf_, ndigits)
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if ctx.shortest_str:
|
||||
return f"mpf({format_mpf(self._mpf_, '', prec, rounding, ctx._pretty_repr_dps, True)!r})"
|
||||
return f"mpf({to_str(self._mpf_, ctx._repr_digits)!r})"
|
||||
|
||||
def __str__(self):
|
||||
ctx = self.context
|
||||
rounding = ctx._prec_rounding[1]
|
||||
return to_str(self._mpf_, ctx._str_digits, rnd=rounding)
|
||||
if ctx.shortest_str:
|
||||
prec, rounding = ctx._prec_rounding
|
||||
return format_mpf(self._mpf_, '', prec, rounding,
|
||||
ctx._pretty_repr_dps, True)
|
||||
return to_str(self._mpf_, ctx._str_digits)
|
||||
|
||||
def __hash__(self): return mpf_hash(self._mpf_)
|
||||
def __int__(self): return int(to_int(self._mpf_))
|
||||
@@ -449,7 +459,8 @@ class _mpf(mpnumeric):
|
||||
_, _, (prec, rounding) = self._ctxdata
|
||||
ctx = self.context
|
||||
return format_mpf(self._mpf_, format_spec, prec, rounding,
|
||||
ctx._pretty_repr_dps)
|
||||
ctx._pretty_repr_dps,
|
||||
ctx.shortest_str)
|
||||
|
||||
def sqrt(self):
|
||||
ctx = self.context
|
||||
@@ -546,6 +557,8 @@ class _mpc(mpnumeric):
|
||||
def __repr__(self):
|
||||
ctx = self.context
|
||||
if ctx.pretty:
|
||||
if ctx.shortest_str:
|
||||
return str(self)
|
||||
ndigits = (ctx._repr_digits
|
||||
if ctx._pretty_repr_dps else ctx._str_digits)
|
||||
return f"({mpc_to_str(self._mpc_, ndigits)})"
|
||||
@@ -555,6 +568,10 @@ class _mpc(mpnumeric):
|
||||
|
||||
def __str__(self):
|
||||
ctx = self.context
|
||||
if ctx.shortest_str:
|
||||
prec, rounding = ctx._prec_rounding
|
||||
return format_mpc(self._mpc_, '', prec, rounding,
|
||||
ctx._pretty_repr_dps, True)
|
||||
return f"({mpc_to_str(self._mpc_, ctx._str_digits)})"
|
||||
|
||||
def __complex__(self):
|
||||
@@ -760,7 +777,8 @@ class _mpc(mpnumeric):
|
||||
ctx = self.context
|
||||
_, _, (prec, rounding) = self._ctxdata
|
||||
return format_mpc(self._mpc_, format_spec, prec, rounding,
|
||||
ctx._pretty_repr_dps)
|
||||
ctx._pretty_repr_dps,
|
||||
ctx.shortest_str)
|
||||
|
||||
|
||||
complex_types = (complex, _mpc)
|
||||
@@ -909,11 +927,32 @@ class PythonMPContext:
|
||||
return ctx.isinf(x)
|
||||
|
||||
def isnormal(ctx, x):
|
||||
warnings.warn("the isnormal() method is deprecated",
|
||||
DeprecationWarning)
|
||||
"""
|
||||
Determine whether *x* is "normal" in the sense of floating-point
|
||||
representation; that is, return *False* if *x* is zero, an
|
||||
infinity or NaN; otherwise return *True*. By extension, a
|
||||
complex number *x* is considered "normal" if its magnitude is
|
||||
normal::
|
||||
|
||||
>>> from mpmath import isnormal, inf, nan, mpc
|
||||
>>> isnormal(3)
|
||||
True
|
||||
>>> isnormal(0)
|
||||
False
|
||||
>>> isnormal(inf); isnormal(-inf); isnormal(nan)
|
||||
False
|
||||
False
|
||||
False
|
||||
>>> isnormal(0+0j)
|
||||
False
|
||||
>>> isnormal(0+3j)
|
||||
True
|
||||
>>> isnormal(mpc(2,nan))
|
||||
False
|
||||
"""
|
||||
if hasattr(x, "_mpf_"):
|
||||
if ctx.isfinite(x):
|
||||
return bool(to_man_exp(x._mpf_, signed=True)[0])
|
||||
return bool(to_man_exp(x._mpf_)[0])
|
||||
return False
|
||||
if hasattr(x, "_mpc_"):
|
||||
re, im = x._mpc_
|
||||
@@ -927,48 +966,6 @@ class PythonMPContext:
|
||||
x = ctx.convert(x)
|
||||
return ctx.isnormal(x)
|
||||
|
||||
def isspecial(ctx, x):
|
||||
"""
|
||||
Determine whether *x* is a "special" in the sense of floating-point
|
||||
representation; that is, return *True* if *x* is zero, an
|
||||
infinity or NaN; otherwise return *False*. By extension, a
|
||||
complex number *x* is considered "special" if its magnitude is
|
||||
special::
|
||||
|
||||
>>> from mpmath import isspecial, inf, nan, mpc
|
||||
>>> isspecial(3)
|
||||
False
|
||||
>>> isspecial(0)
|
||||
True
|
||||
>>> isspecial(inf)
|
||||
True
|
||||
>>> isspecial(-inf)
|
||||
True
|
||||
>>> isspecial(nan)
|
||||
True
|
||||
>>> isspecial(0+0j)
|
||||
True
|
||||
>>> isspecial(0+3j)
|
||||
False
|
||||
>>> isspecial(mpc(2,nan))
|
||||
True
|
||||
"""
|
||||
if hasattr(x, "_mpf_"):
|
||||
if ctx.isfinite(x):
|
||||
return not bool(to_man_exp(x._mpf_, signed=True)[0])
|
||||
return True
|
||||
if hasattr(x, "_mpc_"):
|
||||
re, im = x._mpc_
|
||||
re_special = not bool(re[1])
|
||||
im_special = not bool(im[1])
|
||||
if re == fzero: return im_special
|
||||
if im == fzero: return re_special
|
||||
return re_special or im_special
|
||||
if isinstance(x, int_types) or isinstance(x, MPQ):
|
||||
return not bool(x)
|
||||
x = ctx.convert(x)
|
||||
return ctx.isspecial(x)
|
||||
|
||||
def isint(ctx, x, gaussian=False):
|
||||
"""
|
||||
Return *True* if *x* is integer-valued; otherwise return
|
||||
@@ -998,15 +995,15 @@ class PythonMPContext:
|
||||
return True
|
||||
if hasattr(x, "_mpf_"):
|
||||
if ctx.isfinite(x):
|
||||
man, exp = to_man_exp(x._mpf_, signed=True)
|
||||
man, exp = to_man_exp(x._mpf_)
|
||||
return bool((man and exp >= 0) or x._mpf_ == fzero)
|
||||
return False
|
||||
if hasattr(x, "_mpc_"):
|
||||
re, im = x._mpc_
|
||||
if ctx.isfinite(x):
|
||||
man, exp = to_man_exp(re, signed=True)
|
||||
man, exp = to_man_exp(re)
|
||||
re_isint = bool((man and exp >= 0) or re == fzero)
|
||||
man, exp = to_man_exp(im, signed=True)
|
||||
man, exp = to_man_exp(im)
|
||||
im_isint = bool((man and exp >= 0) or im == fzero)
|
||||
else:
|
||||
return False
|
||||
@@ -1174,15 +1171,18 @@ class PythonMPContext:
|
||||
by raising ComplexResult.
|
||||
|
||||
"""
|
||||
def f(x, **kwargs):
|
||||
def f(x, *, prec=None, dps=None, rounding=None):
|
||||
if type(x) not in ctx.types:
|
||||
x = ctx.convert(x)
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if kwargs:
|
||||
prec = kwargs.get('prec', prec)
|
||||
if 'dps' in kwargs:
|
||||
prec = dps_to_prec(kwargs['dps'])
|
||||
rounding = kwargs.get('rounding', rounding)
|
||||
ctx_prec, ctx_rounding = ctx._prec_rounding
|
||||
if prec and dps:
|
||||
raise ValueError("both prec and dps can't be specified")
|
||||
if dps:
|
||||
prec = dps_to_prec(dps)
|
||||
if prec is None:
|
||||
prec = ctx_prec
|
||||
if rounding is None:
|
||||
rounding = ctx_rounding
|
||||
if hasattr(x, '_mpf_'):
|
||||
try:
|
||||
return ctx.make_mpf(mpf_f(x._mpf_, prec, rounding))
|
||||
@@ -1250,7 +1250,7 @@ class PythonMPContext:
|
||||
v = x._mpf_
|
||||
else:
|
||||
raise NotImplementedError
|
||||
man, exp = to_man_exp(v, signed=True)
|
||||
man, exp = to_man_exp(v)
|
||||
if man:
|
||||
if exp >= -4:
|
||||
if exp >= 0:
|
||||
@@ -1268,7 +1268,7 @@ class PythonMPContext:
|
||||
return ctx.ninf
|
||||
if x in (finf, fninf, fnan):
|
||||
return ctx.make_mpf(mpf_abs(x))
|
||||
man, exp = to_man_exp(x, signed=True)
|
||||
man, exp = to_man_exp(x)
|
||||
return exp+man.bit_length()
|
||||
|
||||
def mag(ctx, x):
|
||||
|
||||
+10
-12
@@ -2189,7 +2189,7 @@ an entirely real-valued sum::
|
||||
|
||||
>>> nsum(lambda k: 1/(k**2-2*k+3), [0, inf])
|
||||
1.694361433907061256154665
|
||||
>>> nprint(polyroots([3,-2,1], asc=True))
|
||||
>>> nprint(polyroots([3,-2,1]))
|
||||
[(1.0 - 1.41421j), (1.0 + 1.41421j)]
|
||||
>>> r1 = 1-sqrt(2)*j
|
||||
>>> r2 = r1.conjugate()
|
||||
@@ -3015,7 +3015,7 @@ with ``eliminate_all=True``:
|
||||
>>> hyper([2], [], 3)
|
||||
0.25
|
||||
|
||||
** References **
|
||||
**References**
|
||||
|
||||
* [Buhring]_
|
||||
|
||||
@@ -5676,7 +5676,7 @@ The roots of Legendre polynomials are located symmetrically
|
||||
on the interval `[-1, 1]`::
|
||||
|
||||
>>> for n in range(5):
|
||||
... nprint(polyroots(taylor(lambda x: legendre(n, x), 0, n), asc=True))
|
||||
... nprint(polyroots(taylor(lambda x: legendre(n, x), 0, n)))
|
||||
...
|
||||
[]
|
||||
[0.0]
|
||||
@@ -7093,7 +7093,7 @@ investigating the zeros of the Riemann zeta function.
|
||||
For example, one can use a root-finding algorithm based
|
||||
on sign changes::
|
||||
|
||||
>>> findroot(siegelz, [100, 200], solver='bisect')
|
||||
>>> findroot(siegelz, [176, 177], solver='bisect')
|
||||
176.4414342977104188888926
|
||||
|
||||
To locate roots, Gram points `g_n` which can be computed
|
||||
@@ -7152,9 +7152,9 @@ integer::
|
||||
>>> mp.pretty = True
|
||||
>>> primepi(50), riemannr(50)
|
||||
(15, 14.9757023241462)
|
||||
>>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(100))
|
||||
>>> max(abs(primepi(n)-round(riemannr(n))) for n in range(100))
|
||||
1
|
||||
>>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(300))
|
||||
>>> max(abs(primepi(n)-round(riemannr(n))) for n in range(300))
|
||||
2
|
||||
|
||||
The Riemann R function can be evaluated for arguments far too large
|
||||
@@ -7760,7 +7760,7 @@ Up to permutation, the roots of a given cyclotomic polynomial
|
||||
can be checked to agree with the list of primitive roots::
|
||||
|
||||
>>> p = taylor(lambda x: cyclotomic(6,x), 0, 6)[:3]
|
||||
>>> for r in polyroots(p, asc=True):
|
||||
>>> for r in polyroots(p):
|
||||
... print(r)
|
||||
...
|
||||
(0.5 - 0.8660254037844386467637232j)
|
||||
@@ -9979,14 +9979,12 @@ Evaluation of derivatives::
|
||||
**Possible issues**
|
||||
|
||||
For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`~mpmath.jtheta` raises
|
||||
``ValueError``. This exception is also raised for `|q|` extremely
|
||||
close to 1 (or equivalently `\tau` very close to 0), since the
|
||||
series would converge too slowly::
|
||||
``ValueError``::
|
||||
|
||||
>>> jtheta(1, 10, 0.99999999 * exp(0.5*j))
|
||||
>>> jtheta(1, 10, 2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: abs(q) > THETA_Q_LIM = 1.000000
|
||||
ValueError: abs(q) >= 1
|
||||
|
||||
"""
|
||||
|
||||
|
||||
+238
-40
@@ -1,7 +1,6 @@
|
||||
from ..libmp.backend import MPQ
|
||||
from .functions import defun, defun_wrapped
|
||||
|
||||
|
||||
@defun
|
||||
def j0(ctx, x):
|
||||
"""Computes the Bessel function `J_0(x)`. See :func:`~mpmath.besselj`."""
|
||||
@@ -13,7 +12,12 @@ def j1(ctx, x):
|
||||
return ctx.besselj(1, x)
|
||||
|
||||
@defun
|
||||
def besselj(ctx, n, z, derivative=0, **kwargs):
|
||||
def besselj(ctx, n, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if type(n) is int:
|
||||
n_isint = True
|
||||
else:
|
||||
@@ -80,7 +84,12 @@ def besselj(ctx, n, z, derivative=0, **kwargs):
|
||||
return v
|
||||
|
||||
@defun
|
||||
def besseli(ctx, n, z, derivative=0, **kwargs):
|
||||
def besseli(ctx, n, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n = ctx.convert(n)
|
||||
z = ctx.convert(z)
|
||||
if n and ctx.isnpint(n):
|
||||
@@ -118,7 +127,12 @@ def besseli(ctx, n, z, derivative=0, **kwargs):
|
||||
return v
|
||||
|
||||
@defun_wrapped
|
||||
def bessely(ctx, n, z, derivative=0, **kwargs):
|
||||
def bessely(ctx, n, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if not z:
|
||||
if derivative:
|
||||
# Not implemented
|
||||
@@ -154,7 +168,12 @@ def bessely(ctx, n, z, derivative=0, **kwargs):
|
||||
ctx.besselj(-n,z,derivative,**kwargs))/sin
|
||||
|
||||
@defun_wrapped
|
||||
def besselk(ctx, n, z, derivative=0, **kwargs):
|
||||
def besselk(ctx, n, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if derivative:
|
||||
raise NotImplementedError
|
||||
if not z:
|
||||
@@ -180,17 +199,27 @@ def besselk(ctx, n, z, derivative=0, **kwargs):
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def hankel1(ctx,n,x,**kwargs):
|
||||
def hankel1(ctx, n, x, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
def terms():
|
||||
return [ctx.besselj(n,x,**kwargs),
|
||||
ctx.j*ctx.bessely(n,x,**kwargs)]
|
||||
return [ctx.besselj(n,x,derivative,**kwargs),
|
||||
ctx.j*ctx.bessely(n,x,derivative,**kwargs)]
|
||||
return ctx.sum_accurately(terms)
|
||||
|
||||
@defun_wrapped
|
||||
def hankel2(ctx,n,x,**kwargs):
|
||||
def hankel2(ctx, n, x, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
def terms():
|
||||
return [ctx.besselj(n,x,**kwargs),
|
||||
-ctx.j*ctx.bessely(n,x,**kwargs)]
|
||||
return [ctx.besselj(n,x,derivative,**kwargs),
|
||||
-ctx.j*ctx.bessely(n,x,derivative,**kwargs)]
|
||||
return ctx.sum_accurately(terms)
|
||||
|
||||
@defun
|
||||
@@ -215,10 +244,10 @@ def spherical_jn(ctx, n, z):
|
||||
|
||||
>>> from mpmath import spherical_jn
|
||||
>>> spherical_jn(0, 1)
|
||||
mpf('0.84147098480789639')
|
||||
mpf('0.8414709848078965')
|
||||
|
||||
"""
|
||||
return ctx.besselj(n + ctx.one/2, z) * ctx.sqrt(ctx.pi/(2*z))
|
||||
return ctx.besselj(n + ctx.one/2, z) / ctx.sqrt(2*z/ctx.pi)
|
||||
|
||||
@defun
|
||||
def spherical_yn(ctx, n, z):
|
||||
@@ -237,13 +266,90 @@ def spherical_yn(ctx, n, z):
|
||||
|
||||
>>> from mpmath import spherical_yn
|
||||
>>> spherical_yn(0, 1)
|
||||
mpf('-0.54030230586813965')
|
||||
mpf('-0.54030230586813977')
|
||||
|
||||
"""
|
||||
return ctx.bessely(n + ctx.one/2, z) * ctx.sqrt(ctx.pi/(2*z))
|
||||
return ctx.bessely(n + ctx.one/2, z) / ctx.sqrt(2*z/ctx.pi)
|
||||
|
||||
@defun
|
||||
def spherical_in(ctx, n, z):
|
||||
r"""
|
||||
Modified spherical Bessel function of the first kind.
|
||||
|
||||
This function is a solution to the spherical Bessel equation
|
||||
(equation 10.47.2 of [DLMF]_):
|
||||
|
||||
.. math ::
|
||||
z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2}
|
||||
+ 2z \frac{\mathrm{d}w}{\mathrm{d}z} - (z^2 + \nu(\nu + 1)) w = 0.
|
||||
|
||||
It can be defined as
|
||||
|
||||
.. math ::
|
||||
i_\nu(z) = \sqrt{\frac{\pi}{2z}} I_{\nu + \frac{1}{2}}(z),
|
||||
|
||||
where `I_\nu(z)` is the modified Bessel function of the first kind.
|
||||
|
||||
**References**
|
||||
|
||||
1. [DLMF]_ Chapter 10.47.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import spherical_in
|
||||
|
||||
>>> spherical_in(0, 1)
|
||||
mpf('1.1752011936438014')
|
||||
>>> spherical_in(6, 0.5 + 3j)
|
||||
mpc(real='-0.0027505520810430402', imag='0.0033767606983784665')
|
||||
|
||||
"""
|
||||
return ctx.besseli(n + ctx.one/2, z) / ctx.sqrt(2*z/ctx.pi)
|
||||
|
||||
|
||||
@defun
|
||||
def spherical_kn(ctx, n, z):
|
||||
r"""
|
||||
Modified spherical Bessel function of the second kind.
|
||||
|
||||
This function is a solution to the spherical Bessel equation
|
||||
(equation 10.47.2 of [DLMF]_):
|
||||
|
||||
.. math ::
|
||||
z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2}
|
||||
+ 2z \frac{\mathrm{d}w}{\mathrm{d}z} - (z^2 + \nu(\nu + 1)) w = 0.
|
||||
|
||||
It can be defined as
|
||||
|
||||
.. math ::
|
||||
k_\nu(z) = \sqrt{\frac{\pi}{2z}} K_{\nu + \frac{1}{2}}(z),
|
||||
|
||||
where `K_\nu(z)` is the modified Bessel function of the second kind.
|
||||
|
||||
**References**
|
||||
|
||||
1. [DLMF]_ Chapter 10.47.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import spherical_kn
|
||||
|
||||
>>> spherical_kn(0, 1)
|
||||
mpf('0.57786367489546075')
|
||||
>>> spherical_kn(6, 0.5 + 3j)
|
||||
mpc(real='-8.6615736788078621', imag='5.5165801484422294')
|
||||
|
||||
"""
|
||||
return ctx.besselk(n + ctx.one/2, z) / ctx.sqrt(2*z/ctx.pi)
|
||||
|
||||
|
||||
@defun_wrapped
|
||||
def whitm(ctx,k,m,z,**kwargs):
|
||||
def whitm(ctx,k,m,z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if z == 0:
|
||||
# M(k,m,z) = 0^(1/2+m)
|
||||
if ctx.re(m) > -0.5:
|
||||
@@ -257,7 +363,12 @@ def whitm(ctx,k,m,z,**kwargs):
|
||||
return ctx.exp(x) * z**y * ctx.hyp1f1(y-k, 1+2*m, z, **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def whitw(ctx,k,m,z,**kwargs):
|
||||
def whitw(ctx,k,m,z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if z == 0:
|
||||
g = abs(ctx.re(m))
|
||||
if g < 0.5:
|
||||
@@ -271,7 +382,12 @@ def whitw(ctx,k,m,z,**kwargs):
|
||||
return ctx.exp(x) * z**y * ctx.hyperu(y-k, 1+2*m, z, **kwargs)
|
||||
|
||||
@defun
|
||||
def hyperu(ctx, a, b, z, **kwargs):
|
||||
def hyperu(ctx, a, b, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
a, atype = ctx._convert_param(a)
|
||||
b, btype = ctx._convert_param(b)
|
||||
z = ctx.convert(z)
|
||||
@@ -300,7 +416,12 @@ def hyperu(ctx, a, b, z, **kwargs):
|
||||
return ctx.hypercomb(h, [a,b], **kwargs)
|
||||
|
||||
@defun
|
||||
def struveh(ctx,n,z, **kwargs):
|
||||
def struveh(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n = ctx.convert(n)
|
||||
z = ctx.convert(z)
|
||||
# http://functions.wolfram.com/Bessel-TypeFunctions/StruveH/26/01/02/
|
||||
@@ -309,7 +430,12 @@ def struveh(ctx,n,z, **kwargs):
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
@defun
|
||||
def struvel(ctx,n,z, **kwargs):
|
||||
def struvel(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n = ctx.convert(n)
|
||||
z = ctx.convert(z)
|
||||
# http://functions.wolfram.com/Bessel-TypeFunctions/StruveL/26/01/02/
|
||||
@@ -337,15 +463,30 @@ def _anger(ctx,which,v,z,**kwargs):
|
||||
return ctx.hypercomb(h, [v], **kwargs)
|
||||
|
||||
@defun
|
||||
def angerj(ctx, v, z, **kwargs):
|
||||
def angerj(ctx, v, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return _anger(ctx, 0, v, z, **kwargs)
|
||||
|
||||
@defun
|
||||
def webere(ctx, v, z, **kwargs):
|
||||
def webere(ctx, v, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return _anger(ctx, 1, v, z, **kwargs)
|
||||
|
||||
@defun
|
||||
def lommels1(ctx, u, v, z, **kwargs):
|
||||
def lommels1(ctx, u, v, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
u = ctx._convert_param(u)[0]
|
||||
v = ctx._convert_param(v)[0]
|
||||
z = ctx.convert(z)
|
||||
@@ -357,7 +498,12 @@ def lommels1(ctx, u, v, z, **kwargs):
|
||||
return ctx.hypercomb(h, [u,v], **kwargs)
|
||||
|
||||
@defun
|
||||
def lommels2(ctx, u, v, z, **kwargs):
|
||||
def lommels2(ctx, u, v, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
u = ctx._convert_param(u)[0]
|
||||
v = ctx._convert_param(v)[0]
|
||||
z = ctx.convert(z)
|
||||
@@ -386,7 +532,12 @@ def lommels2(ctx, u, v, z, **kwargs):
|
||||
return ctx.hypercomb(h, [u,v], **kwargs)
|
||||
|
||||
@defun
|
||||
def ber(ctx, n, z, **kwargs):
|
||||
def ber(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n = ctx.convert(n)
|
||||
z = ctx.convert(z)
|
||||
# http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBer2/26/01/02/0001/
|
||||
@@ -399,7 +550,12 @@ def ber(ctx, n, z, **kwargs):
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
@defun
|
||||
def bei(ctx, n, z, **kwargs):
|
||||
def bei(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n = ctx.convert(n)
|
||||
z = ctx.convert(z)
|
||||
# http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBei2/26/01/02/0001/
|
||||
@@ -412,7 +568,12 @@ def bei(ctx, n, z, **kwargs):
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
@defun
|
||||
def ker(ctx, n, z, **kwargs):
|
||||
def ker(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n = ctx.convert(n)
|
||||
z = ctx.convert(z)
|
||||
# http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKer2/26/01/02/0001/
|
||||
@@ -428,7 +589,12 @@ def ker(ctx, n, z, **kwargs):
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
@defun
|
||||
def kei(ctx, n, z, **kwargs):
|
||||
def kei(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n = ctx.convert(n)
|
||||
z = ctx.convert(z)
|
||||
# http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKei2/26/01/02/0001/
|
||||
@@ -506,14 +672,19 @@ def _airyderiv_0(ctx, z, n, ntype, which):
|
||||
raise NotImplementedError
|
||||
|
||||
@defun
|
||||
def airyai(ctx, z, derivative=0, **kwargs):
|
||||
def airyai(ctx, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
z = ctx.convert(z)
|
||||
if derivative:
|
||||
n, ntype = ctx._convert_param(derivative)
|
||||
else:
|
||||
n = 0
|
||||
# Values at infinities
|
||||
if ctx.isspecial(z) and z:
|
||||
if not ctx.isnormal(z) and z:
|
||||
if n and ntype == 'Z':
|
||||
if n == -1:
|
||||
if z == ctx.inf:
|
||||
@@ -598,14 +769,19 @@ def airyai(ctx, z, derivative=0, **kwargs):
|
||||
return ctx.hypercomb(h, [], **kwargs)
|
||||
|
||||
@defun
|
||||
def airybi(ctx, z, derivative=0, **kwargs):
|
||||
def airybi(ctx, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
z = ctx.convert(z)
|
||||
if derivative:
|
||||
n, ntype = ctx._convert_param(derivative)
|
||||
else:
|
||||
n = 0
|
||||
# Values at infinities
|
||||
if ctx.isspecial(z) and z:
|
||||
if not ctx.isnormal(z) and z:
|
||||
if n and ntype == 'Z':
|
||||
if z == ctx.inf:
|
||||
return z
|
||||
@@ -709,7 +885,7 @@ def airyaizero(ctx, k, derivative=0):
|
||||
def airybizero(ctx, k, derivative=0, complex=False):
|
||||
return _airy_zero(ctx, 1, k, derivative, complex)
|
||||
|
||||
def _scorer(ctx, z, which, kwargs):
|
||||
def _scorer(ctx, z, which, derivative=0, **kwargs):
|
||||
z = ctx.convert(z)
|
||||
if ctx.isinf(z):
|
||||
if z == ctx.inf:
|
||||
@@ -722,7 +898,7 @@ def _scorer(ctx, z, which, kwargs):
|
||||
extraprec = max(0, int(1.5*ctx.mag(z)))
|
||||
else:
|
||||
extraprec = 0
|
||||
if kwargs.get('derivative'):
|
||||
if derivative != 0:
|
||||
raise NotImplementedError
|
||||
# Direct asymptotic expansions, to avoid
|
||||
# exponentially large cancellation
|
||||
@@ -753,12 +929,22 @@ def _scorer(ctx, z, which, kwargs):
|
||||
return ctx.hypercomb(h, [], **kwargs)
|
||||
|
||||
@defun
|
||||
def scorergi(ctx, z, **kwargs):
|
||||
return _scorer(ctx, z, 0, kwargs)
|
||||
def scorergi(ctx, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return _scorer(ctx, z, 0, derivative, **kwargs)
|
||||
|
||||
@defun
|
||||
def scorerhi(ctx, z, **kwargs):
|
||||
return _scorer(ctx, z, 1, kwargs)
|
||||
def scorerhi(ctx, z, derivative=0, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return _scorer(ctx, z, 1, derivative, **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def coulombc(ctx, l, eta, _cache={}):
|
||||
@@ -774,7 +960,13 @@ def coulombc(ctx, l, eta, _cache={}):
|
||||
return v
|
||||
|
||||
@defun_wrapped
|
||||
def coulombf(ctx, l, eta, z, w=1, chop=True, **kwargs):
|
||||
def coulombf(ctx, l, eta, z, w=1, chop=True, *, eliminate=True,
|
||||
eliminate_all=False, force_series=False, asymp_tol=None,
|
||||
maxprec=None, maxterms=None, zeroprec=None, infprec=None,
|
||||
verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# Regular Coulomb wave function
|
||||
# Note: w can be either 1 or -1; the other may be better in some cases
|
||||
# TODO: check that chop=True chops when and only when it should
|
||||
@@ -813,7 +1005,13 @@ def _coulomb_chi(ctx, l, eta, _cache={}):
|
||||
return v
|
||||
|
||||
@defun_wrapped
|
||||
def coulombg(ctx, l, eta, z, w=1, chop=True, **kwargs):
|
||||
def coulombg(ctx, l, eta, z, w=1, chop=True, *, eliminate=True,
|
||||
eliminate_all=False, force_series=False, asymp_tol=None,
|
||||
maxprec=None, maxterms=None, zeroprec=None, infprec=None,
|
||||
verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# Irregular Coulomb wave function
|
||||
# Note: w can be either 1 or -1; the other may be better in some cases
|
||||
# TODO: check that chop=True chops when and only when it should
|
||||
|
||||
+803
-29
File diff suppressed because it is too large
Load Diff
@@ -2,22 +2,30 @@ from .functions import defun, defun_wrapped
|
||||
|
||||
@defun_wrapped
|
||||
def _erf_complex(ctx, z):
|
||||
z2 = ctx.square_exp_arg(z, -1)
|
||||
#z2 = -z**2
|
||||
v = (2/ctx.sqrt(ctx.pi))*z * ctx.hyp1f1((1,2),(3,2), z2)
|
||||
if not ctx._re(z):
|
||||
re_z = ctx.re(z)
|
||||
if re_z > 2:
|
||||
nz = ctx.fneg(z, exact=True)
|
||||
v = ctx._erf_complex(nz)
|
||||
return ctx.fneg(v, exact=True)
|
||||
elif re_z < -2:
|
||||
v = ctx._erfc_complex(ctx.fneg(z, exact=True)) - 1
|
||||
else:
|
||||
z2 = ctx.square_exp_arg(z, -1)
|
||||
v = (2/ctx.sqrt(ctx.pi))*z * ctx.hyp1f1((1,2),(3,2), z2)
|
||||
if not re_z:
|
||||
v = ctx._im(v)*ctx.j
|
||||
return v
|
||||
|
||||
@defun_wrapped
|
||||
def _erfc_complex(ctx, z):
|
||||
if ctx.re(z) > 2:
|
||||
re_z = ctx.re(z)
|
||||
if re_z > 2:
|
||||
z2 = ctx.square_exp_arg(z)
|
||||
nz2 = ctx.fneg(z2, exact=True)
|
||||
v = ctx.exp(nz2)/ctx.sqrt(ctx.pi) * ctx.hyperu((1,2),(1,2), z2)
|
||||
else:
|
||||
v = 1 - ctx._erf_complex(z)
|
||||
if not ctx._re(z):
|
||||
if not re_z:
|
||||
v = 1+ctx._im(v)*ctx.j
|
||||
return v
|
||||
|
||||
|
||||
@@ -31,8 +31,19 @@ def gammaprod(ctx, a, b, _infsign=False):
|
||||
i = poles_num.pop()
|
||||
j = poles_den.pop()
|
||||
p *= (-1)**(i+j) * ctx.gamma(1-j) / ctx.gamma(1-i)
|
||||
for x in regular_num: p *= ctx.gamma(x)
|
||||
for x in regular_den: p /= ctx.gamma(x)
|
||||
try:
|
||||
q = ctx.one
|
||||
for x in regular_num: q *= ctx.gamma(x)
|
||||
for x in regular_den: q /= ctx.gamma(x)
|
||||
except OverflowError:
|
||||
# In the fp context an individual gamma value can exceed the
|
||||
# double range even when the quotient is representable, e.g.
|
||||
# binomial(1100, 1). Evaluate the regular part in log space.
|
||||
s = ctx.zero
|
||||
for x in regular_num: s += ctx.loggamma(x)
|
||||
for x in regular_den: s -= ctx.loggamma(x)
|
||||
q = ctx.exp(s)
|
||||
p *= q
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
return +p
|
||||
@@ -91,7 +102,7 @@ def barnesg(ctx, z):
|
||||
return ctx.nan
|
||||
if ctx.isnan(z):
|
||||
return z
|
||||
if (not ctx._im(z)) and ctx._re(z) <= 0 and ctx.isint(ctx._re(z)):
|
||||
if ctx.isnpint(z):
|
||||
return z*0
|
||||
# Account for size (would not be needed if computing log(G))
|
||||
if abs(z) > 5:
|
||||
@@ -146,7 +157,7 @@ def hyperfac(ctx, z):
|
||||
else:
|
||||
extra = 0
|
||||
ctx.prec += extra
|
||||
if not ctx._im(z) and ctx._re(z) < 0 and ctx.isint(ctx._re(z)):
|
||||
if z and ctx.isnpint(z):
|
||||
n = int(ctx.re(z))
|
||||
h = ctx.hyperfac(-n-1)
|
||||
if ((n+1)//2) & 1:
|
||||
|
||||
@@ -8,11 +8,6 @@ class SpecialFunctions:
|
||||
"""
|
||||
defined_functions = {}
|
||||
|
||||
# The series for the Jacobi theta functions converge for |q| < 1;
|
||||
# in the current implementation they throw a ValueError for
|
||||
# abs(q) > THETA_Q_LIM
|
||||
THETA_Q_LIM = 1 - 10**-7
|
||||
|
||||
def __init__(self):
|
||||
cls = self.__class__
|
||||
for name in cls.defined_functions:
|
||||
@@ -56,7 +51,7 @@ class SpecialFunctions:
|
||||
def _e1(ctx, z): raise NotImplementedError
|
||||
def _ci(ctx, z): raise NotImplementedError
|
||||
def _si(ctx, z): raise NotImplementedError
|
||||
def _altzeta(ctx, s): raise NotImplementedError
|
||||
def _altzeta(ctx, s, **kwargs): raise NotImplementedError
|
||||
|
||||
def defun_wrapped(f):
|
||||
SpecialFunctions.defined_functions[f.__name__] = f, True
|
||||
@@ -279,22 +274,22 @@ def _rootof1(ctx, k, n):
|
||||
return ctx.expjpi(2*ctx.mpf(k)/n)
|
||||
|
||||
@defun
|
||||
def root(ctx, x, n, k=0):
|
||||
def root(ctx, z, n, k=0):
|
||||
n = int(n)
|
||||
x = ctx.convert(x)
|
||||
z = ctx.convert(z)
|
||||
if k:
|
||||
# Special case: there is an exact real root
|
||||
if (n & 1 and 2*k == n-1) and (not ctx.im(x)) and (ctx.re(x) < 0):
|
||||
return -ctx.root(-x, n)
|
||||
if (n & 1 and 2*k == n-1) and (not ctx.im(z)) and (ctx.re(z) < 0):
|
||||
return -ctx.root(-z, n)
|
||||
# Multiply by root of unity
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
v = ctx.root(x, n, 0) * ctx._rootof1(k, n)
|
||||
v = ctx.root(z, n, 0) * ctx._rootof1(k, n)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return +v
|
||||
return ctx._nthroot(x, n)
|
||||
return ctx._nthroot(z, n)
|
||||
|
||||
@defun
|
||||
def unitroots(ctx, n, primitive=False):
|
||||
@@ -395,9 +390,8 @@ def _lambertw_special(ctx, z, k):
|
||||
# Some kind of nan or complex inf/nan?
|
||||
return ctx.ln(z)
|
||||
|
||||
import cmath
|
||||
import math
|
||||
|
||||
import cmath
|
||||
|
||||
def _lambertw_approx_hybrid(z, k):
|
||||
imag_sign = 0
|
||||
@@ -515,7 +509,7 @@ def _lambertw_series(ctx, z, k, tol):
|
||||
def lambertw(ctx, z, k=0):
|
||||
z = ctx.convert(z)
|
||||
k = int(k)
|
||||
if ctx.isspecial(z):
|
||||
if not ctx.isnormal(z):
|
||||
return _lambertw_special(ctx, z, k)
|
||||
prec = ctx.prec
|
||||
ctx.prec += 20 + ctx.mag(k or 1)
|
||||
|
||||
@@ -49,6 +49,26 @@ def _check_need_perturb(ctx, terms, prec, discard_known_zeros):
|
||||
perturb = recompute = True
|
||||
return perturb, recompute, extraprec, discard
|
||||
|
||||
@defun
|
||||
def _set_hyper_kwargs(ctx, eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose):
|
||||
if asymp_tol is None:
|
||||
asymp_tol = ctx.eps/4
|
||||
if maxprec is None:
|
||||
maxprec = ctx._default_hyper_maxprec(ctx.prec)
|
||||
kwargs = dict(eliminate=eliminate, eliminate_all=eliminate_all,
|
||||
force_series=force_series, asymp_tol=asymp_tol,
|
||||
maxprec=maxprec, verbose=verbose)
|
||||
if zeroprec:
|
||||
kwargs['zeroprec'] = zeroprec
|
||||
if infprec:
|
||||
kwargs['infprec'] = infprec
|
||||
if maxterms:
|
||||
kwargs['maxterms'] = maxterms
|
||||
|
||||
return kwargs
|
||||
|
||||
_hypercomb_msg = """
|
||||
hypercomb() failed to converge to the requested %i bits of accuracy
|
||||
using a working precision of %i bits. The function value may be zero or
|
||||
@@ -57,7 +77,13 @@ infinite; try passing zeroprec=N or infprec=M to bound finite values between
|
||||
"""
|
||||
|
||||
@defun
|
||||
def hypercomb(ctx, function, params=[], discard_known_zeros=True, **kwargs):
|
||||
def hypercomb(ctx, function, params=[], discard_known_zeros=True,
|
||||
*, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
orig = ctx.prec
|
||||
sumvalue = ctx.zero
|
||||
dist = ctx.nint_distance
|
||||
@@ -193,10 +219,15 @@ def hypercomb(ctx, function, params=[], discard_known_zeros=True, **kwargs):
|
||||
return +sumvalue
|
||||
|
||||
@defun
|
||||
def hyper(ctx, a_s, b_s, z, **kwargs):
|
||||
def hyper(ctx, a_s, b_s, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
"""
|
||||
Hypergeometric function, general case.
|
||||
"""
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
z = ctx.convert(z)
|
||||
if ctx.isnan(z):
|
||||
return ctx.nan
|
||||
@@ -205,12 +236,11 @@ def hyper(ctx, a_s, b_s, z, **kwargs):
|
||||
a_s = [ctx._convert_param(a) for a in a_s]
|
||||
b_s = [ctx._convert_param(b) for b in b_s]
|
||||
# Reduce degree by eliminating common parameters
|
||||
if kwargs.get('eliminate', True):
|
||||
elim_nonpositive = kwargs.get('eliminate_all', False)
|
||||
if eliminate:
|
||||
i = 0
|
||||
while i < q and a_s:
|
||||
b = b_s[i]
|
||||
if b in a_s and (elim_nonpositive or not ctx.isnpint(b[0])):
|
||||
if b in a_s and (eliminate_all or not ctx.isnpint(b[0])):
|
||||
a_s.remove(b)
|
||||
b_s.remove(b)
|
||||
p -= 1
|
||||
@@ -232,41 +262,81 @@ def hyper(ctx, a_s, b_s, z, **kwargs):
|
||||
elif q == 0: return ctx._hyp2f0(a_s, b_s, z, **kwargs)
|
||||
elif p == q+1:
|
||||
return ctx._hypq1fq(p, q, a_s, b_s, z, **kwargs)
|
||||
elif p > q+1 and not kwargs.get('force_series'):
|
||||
elif p > q+1 and not force_series:
|
||||
return ctx._hyp_borel(p, q, a_s, b_s, z, **kwargs)
|
||||
coeffs, types = zip(*(a_s+b_s))
|
||||
return ctx.hypsum(p, q, types, coeffs, z, **kwargs)
|
||||
|
||||
@defun
|
||||
def hyp0f1(ctx,b,z,**kwargs):
|
||||
def hyp0f1(ctx, b, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([],[b],z,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyp1f1(ctx,a,b,z,**kwargs):
|
||||
def hyp1f1(ctx, a, b, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([a],[b],z,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyp1f2(ctx,a1,b1,b2,z,**kwargs):
|
||||
def hyp1f2(ctx, a1, b1, b2, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([a1],[b1,b2],z,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyp2f1(ctx,a,b,c,z,**kwargs):
|
||||
def hyp2f1(ctx, a, b, c, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([a,b],[c],z,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyp2f2(ctx,a1,a2,b1,b2,z,**kwargs):
|
||||
def hyp2f2(ctx, a1, a2, b1, b2, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([a1,a2],[b1,b2],z,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyp2f3(ctx,a1,a2,b1,b2,b3,z,**kwargs):
|
||||
def hyp2f3(ctx, a1, a2, b1, b2, b3, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([a1,a2],[b1,b2,b3],z,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyp2f0(ctx,a,b,z,**kwargs):
|
||||
def hyp2f0(ctx, a, b, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([a,b],[],z,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyp3f2(ctx,a1,a2,a3,b1,b2,z,**kwargs):
|
||||
def hyp3f2(ctx, a1, a2, a3, b1, b2, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hyper([a1,a2,a3],[b1,b2],z,**kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
@@ -316,7 +386,7 @@ def _hyp1f1(ctx, a_s, b_s, z, **kwargs):
|
||||
if not z:
|
||||
return ctx.one+z
|
||||
magz = ctx.mag(z)
|
||||
if magz >= 7 and not (ctx.isint(a) and ctx.re(a) <= 0):
|
||||
if magz >= 7 and not ctx.isnpint(a):
|
||||
if ctx.isinf(z) and ctx.sign(a) == ctx.sign(b) == ctx.sign(z) == 1:
|
||||
return ctx.inf
|
||||
if ctx.isinf(magz):
|
||||
@@ -406,9 +476,9 @@ def _hyp2f1(ctx, a_s, b_s, z, **kwargs):
|
||||
if z == 1:
|
||||
# TODO: the following logic can be simplified
|
||||
convergent = ctx.re(c-a-b) > 0
|
||||
finite = (ctx.isint(a) and a <= 0) or (ctx.isint(b) and b <= 0)
|
||||
zerodiv = ctx.isint(c) and c <= 0 and not \
|
||||
((ctx.isint(a) and c <= a <= 0) or (ctx.isint(b) and c <= b <= 0))
|
||||
finite = ctx.isnpint(a) or ctx.isnpint(b)
|
||||
zerodiv = ctx.isnpint(c) and not \
|
||||
((ctx.isnpint(a) and c <= a) or (ctx.isnpint(b) and c <= b))
|
||||
#print "bz", a, b, c, z, convergent, finite, zerodiv
|
||||
# Gauss's theorem gives the value if convergent
|
||||
if (convergent or finite) and not zerodiv:
|
||||
@@ -429,9 +499,8 @@ def _hyp2f1(ctx, a_s, b_s, z, **kwargs):
|
||||
return ctx.nan
|
||||
|
||||
# Hit zero denominator unless numerator goes to 0 first
|
||||
if ctx.isint(c) and c <= 0:
|
||||
if (ctx.isint(a) and c <= a <= 0) or \
|
||||
(ctx.isint(b) and c <= b <= 0):
|
||||
if ctx.isnpint(c):
|
||||
if (ctx.isnpint(a) and c <= a) or (ctx.isnpint(b) and c <= b):
|
||||
pass
|
||||
else:
|
||||
# Pole in series
|
||||
@@ -442,8 +511,8 @@ def _hyp2f1(ctx, a_s, b_s, z, **kwargs):
|
||||
# Fast case: standard series converges rapidly,
|
||||
# possibly in finitely many terms
|
||||
if ctx.isfinite(z) and (absz <= 0.8 or
|
||||
(ctx.isint(a) and -1000 <= a <= 0) or
|
||||
(ctx.isint(b) and -1000 <= b <= 0)):
|
||||
(ctx.isnpint(a) and -1000 <= a) or
|
||||
(ctx.isnpint(b) and -1000 <= b)):
|
||||
try:
|
||||
return ctx.hypsum(2, 1, (atype, btype, ctype), [a, b, c], z, **kwargs)
|
||||
except ctx.NoConvergence:
|
||||
@@ -496,7 +565,7 @@ def _hypq1fq(ctx, p, q, a_s, b_s, z, **kwargs):
|
||||
absz = abs(z)
|
||||
ispoly = False
|
||||
for a in a_s:
|
||||
if ctx.isint(a) and a <= 0:
|
||||
if ctx.isnpint(a):
|
||||
ispoly = True
|
||||
break
|
||||
# Direct summation
|
||||
@@ -1002,7 +1071,13 @@ def _hyp2f0(ctx, a_s, b_s, z, **kwargs):
|
||||
return ctx.hypercomb(h, [a, 1+a-b], **kwargs)
|
||||
|
||||
@defun
|
||||
def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs):
|
||||
def meijerg(ctx, a_s, b_s, z, r=1, series=None, *, eliminate=True,
|
||||
eliminate_all=False, force_series=False, asymp_tol=None,
|
||||
maxprec=None, maxterms=None, zeroprec=None, infprec=None,
|
||||
verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
an, ap = a_s
|
||||
bm, bq = b_s
|
||||
n = len(an)
|
||||
@@ -1022,7 +1097,7 @@ def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs):
|
||||
series = 2
|
||||
else:
|
||||
series = 1
|
||||
if kwargs.get('verbose'):
|
||||
if verbose:
|
||||
print("Meijer G m,n,p,q,series =", m,n,p,q,series)
|
||||
if series == 1:
|
||||
def h(*args):
|
||||
@@ -1064,7 +1139,13 @@ def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs):
|
||||
return ctx.hypercomb(h, a+b, **kwargs)
|
||||
|
||||
@defun
|
||||
def foxh(ctx, aA_s, bB_s, z, r=1, series=None, **kwargs):
|
||||
def foxh(ctx, aA_s, bB_s, z, r=1, series=None,
|
||||
*, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
aAn, aAp = aA_s
|
||||
bBm, bBq = bB_s
|
||||
n = len(aAn)
|
||||
@@ -1138,7 +1219,12 @@ def foxh(ctx, aA_s, bB_s, z, r=1, series=None, **kwargs):
|
||||
)
|
||||
|
||||
@defun_wrapped
|
||||
def appellf1(ctx,a,b1,b2,c,x,y,**kwargs):
|
||||
def appellf1(ctx, a, b1, b2, c, x, y, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# Assume x smaller
|
||||
# We will use x for the outer loop
|
||||
if abs(x) > abs(y):
|
||||
@@ -1167,13 +1253,25 @@ def appellf1(ctx,a,b1,b2,c,x,y,**kwargs):
|
||||
return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, {'m+n':[c]}, x,y, **kwargs)
|
||||
|
||||
@defun
|
||||
def appellf2(ctx,a,b1,b2,c1,c2,x,y,**kwargs):
|
||||
def appellf2(ctx, a, b1, b2, c1, c2, x, y,
|
||||
*, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# TODO: continuation
|
||||
return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]},
|
||||
{'m':[c1],'n':[c2]}, x,y, **kwargs)
|
||||
|
||||
@defun
|
||||
def appellf3(ctx,a1,a2,b1,b2,c,x,y,**kwargs):
|
||||
def appellf3(ctx, a1, a2, b1, b2, c, x, y,
|
||||
*, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
outer_polynomial = ctx.isnpint(a1) or ctx.isnpint(b1)
|
||||
inner_polynomial = ctx.isnpint(a2) or ctx.isnpint(b2)
|
||||
if not outer_polynomial:
|
||||
@@ -1183,12 +1281,20 @@ def appellf3(ctx,a1,a2,b1,b2,c,x,y,**kwargs):
|
||||
return ctx.hyper2d({'m':[a1,b1],'n':[a2,b2]}, {'m+n':[c]},x,y,**kwargs)
|
||||
|
||||
@defun
|
||||
def appellf4(ctx,a,b,c1,c2,x,y,**kwargs):
|
||||
def appellf4(ctx, a, b, c1, c2, x, y,
|
||||
*, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# TODO: continuation
|
||||
return ctx.hyper2d({'m+n':[a,b]}, {'m':[c1],'n':[c2]},x,y,**kwargs)
|
||||
|
||||
@defun
|
||||
def hyper2d(ctx, a, b, x, y, **kwargs):
|
||||
def hyper2d(ctx, a, b, x, y, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
r"""
|
||||
Sums the generalized 2D hypergeometric series
|
||||
|
||||
@@ -1325,6 +1431,9 @@ def hyper2d(ctx, a, b, x, y, **kwargs):
|
||||
3. [Weisstein]_ http://mathworld.wolfram.com/AppellHypergeometricFunction.html
|
||||
|
||||
"""
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
x = ctx.convert(x)
|
||||
y = ctx.convert(y)
|
||||
def parse(dct, key):
|
||||
@@ -1428,7 +1537,9 @@ def kampe_de_feriet(ctx,a,b,c,d,e,f,x,y,**kwargs):
|
||||
"""
|
||||
|
||||
@defun
|
||||
def bihyper(ctx, a_s, b_s, z, **kwargs):
|
||||
def bihyper(ctx, a_s, b_s, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
r"""
|
||||
Evaluates the bilateral hypergeometric series
|
||||
|
||||
@@ -1476,6 +1587,9 @@ def bihyper(ctx, a_s, b_s, z, **kwargs):
|
||||
2. [Wikipedia]_ http://en.wikipedia.org/wiki/Bilateral_hypergeometric_series
|
||||
|
||||
"""
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
z = ctx.convert(z)
|
||||
c_s = a_s + b_s
|
||||
p = len(a_s)
|
||||
|
||||
@@ -59,11 +59,18 @@ def _hermite_param(ctx, n, z, parabolic_cylinder):
|
||||
return tuple(terms)
|
||||
|
||||
@defun
|
||||
def hermite(ctx, n, z, **kwargs):
|
||||
def hermite(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 0), [], **kwargs)
|
||||
|
||||
@defun
|
||||
def pcfd(ctx, n, z, **kwargs):
|
||||
def pcfd(ctx, n, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
r"""
|
||||
Gives the parabolic cylinder function in Whittaker's notation
|
||||
`D_n(z) = U(-n-1/2, z)` (see :func:`~mpmath.pcfu`).
|
||||
@@ -121,10 +128,15 @@ def pcfd(ctx, n, z, **kwargs):
|
||||
[0.0, 15.0, 0.0, -13.75, 0.0, 3.96875, 0.0, -0.6015625]
|
||||
|
||||
"""
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 1), [], **kwargs)
|
||||
|
||||
@defun
|
||||
def pcfu(ctx, a, z, **kwargs):
|
||||
def pcfu(ctx, a, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
r"""
|
||||
Gives the parabolic cylinder function `U(a,z)`, which may be
|
||||
defined for `\Re(z) > 0` in terms of the confluent
|
||||
@@ -168,11 +180,16 @@ def pcfu(ctx, a, z, **kwargs):
|
||||
23.75012332835297233711255
|
||||
|
||||
"""
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n, _ = ctx._convert_param(a)
|
||||
return ctx.pcfd(-n-MPQ(1,2), z)
|
||||
return ctx.pcfd(-n-MPQ(1,2), z, **kwargs)
|
||||
|
||||
@defun
|
||||
def pcfv(ctx, a, z, **kwargs):
|
||||
def pcfv(ctx, a, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
r"""
|
||||
Gives the parabolic cylinder function `V(a,z)`, which can be
|
||||
represented in terms of :func:`~mpmath.pcfu` as
|
||||
@@ -204,6 +221,9 @@ def pcfv(ctx, a, z, **kwargs):
|
||||
0.7978845608028653558798921
|
||||
|
||||
"""
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n, ntype = ctx._convert_param(a)
|
||||
z = ctx.convert(z)
|
||||
q = MPQ(1,2)
|
||||
@@ -246,7 +266,9 @@ def pcfv(ctx, a, z, **kwargs):
|
||||
|
||||
|
||||
@defun
|
||||
def pcfw(ctx, a, z, **kwargs):
|
||||
def pcfw(ctx, a, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
r"""
|
||||
Gives the parabolic cylinder function `W(a,z)` defined in (DLMF 12.14).
|
||||
|
||||
@@ -268,6 +290,9 @@ def pcfw(ctx, a, z, **kwargs):
|
||||
-0.5142533944210078966003624
|
||||
|
||||
"""
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
n, _ = ctx._convert_param(a)
|
||||
z = ctx.convert(z)
|
||||
def terms():
|
||||
@@ -277,8 +302,8 @@ def pcfw(ctx, a, z, **kwargs):
|
||||
# XXX: cancellation computing k
|
||||
k = ctx.sqrt(1 + ctx.exp(2*ctx.pi*n)) - ctx.exp(ctx.pi*n)
|
||||
C = ctx.sqrt(k/2) * ctx.exp(0.25*ctx.pi*n)
|
||||
yield C * ctx.expj(rho) * ctx.pcfu(ctx.j*n, z*ctx.expjpi(-0.25))
|
||||
yield C * ctx.expj(-rho) * ctx.pcfu(-ctx.j*n, z*ctx.expjpi(0.25))
|
||||
yield C * ctx.expj(rho) * ctx.pcfu(ctx.j*n, z*ctx.expjpi(-0.25), **kwargs)
|
||||
yield C * ctx.expj(-rho) * ctx.pcfu(-ctx.j*n, z*ctx.expjpi(0.25), **kwargs)
|
||||
v = ctx.sum_accurately(terms)
|
||||
if ctx._is_real_type(n) and ctx._is_real_type(z):
|
||||
v = ctx._re(v)
|
||||
@@ -314,10 +339,17 @@ def pcfy2(ctx, a, z, **kwargs):
|
||||
"""
|
||||
|
||||
@defun_wrapped
|
||||
def gegenbauer(ctx, n, a, z, **kwargs):
|
||||
def gegenbauer(ctx, n, a, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# Special cases: a+0.5, a*2 poles
|
||||
if ctx.isnpint(a):
|
||||
return 0*(z+n)
|
||||
if not z and ctx.isint(n) and int(n.real) % 2:
|
||||
return ctx.zero
|
||||
if ctx.isnpint(a+0.5):
|
||||
# TODO: something else is required here
|
||||
# E.g.: gegenbauer(-2, -0.5, 3) == -12
|
||||
@@ -335,7 +367,12 @@ def gegenbauer(ctx, n, a, z, **kwargs):
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def jacobi(ctx, n, a, b, x, **kwargs):
|
||||
def jacobi(ctx, n, a, b, x, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if not ctx.isnpint(a):
|
||||
def h(n):
|
||||
return (([], [], [a+n+1], [n+1, a+1], [-n, a+b+n+1], [a+1], (1-x)*0.5),)
|
||||
@@ -348,7 +385,12 @@ def jacobi(ctx, n, a, b, x, **kwargs):
|
||||
return ctx.binomial(n+a,n) * ctx.hyp2f1(-n,1+n+a+b,a+1,(1-x)/2, **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def laguerre(ctx, n, a, z, **kwargs):
|
||||
def laguerre(ctx, n, a, z, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# XXX: limits, poles
|
||||
#if ctx.isnpint(n):
|
||||
# return 0*(a+z)
|
||||
@@ -357,7 +399,12 @@ def laguerre(ctx, n, a, z, **kwargs):
|
||||
return ctx.hypercomb(h, [a], **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def legendre(ctx, n, x, **kwargs):
|
||||
def legendre(ctx, n, x, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if ctx.isint(n):
|
||||
n = int(n)
|
||||
# Accuracy near zeros
|
||||
@@ -372,7 +419,12 @@ def legendre(ctx, n, x, **kwargs):
|
||||
return ctx.hyp2f1(-n,n+1,1,(1-x)/2, **kwargs)
|
||||
|
||||
@defun
|
||||
def legenp(ctx, n, m, z, type=2, **kwargs):
|
||||
def legenp(ctx, n, m, z, type=2, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# Legendre function, 1st kind
|
||||
n = ctx.convert(n)
|
||||
m = ctx.convert(m)
|
||||
@@ -395,7 +447,12 @@ def legenp(ctx, n, m, z, type=2, **kwargs):
|
||||
raise ValueError("requires type=2 or type=3")
|
||||
|
||||
@defun
|
||||
def legenq(ctx, n, m, z, type=2, **kwargs):
|
||||
def legenq(ctx, n, m, z, type=2, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
# Legendre function, 2nd kind
|
||||
n = ctx.convert(n)
|
||||
m = ctx.convert(m)
|
||||
@@ -449,23 +506,34 @@ def legenq(ctx, n, m, z, type=2, **kwargs):
|
||||
raise ValueError("requires type=2 or type=3")
|
||||
|
||||
@defun_wrapped
|
||||
def chebyt(ctx, n, x, **kwargs):
|
||||
def chebyt(ctx, n, x, *, eliminate=True, eliminate_all=False,
|
||||
force_series=True, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1:
|
||||
return x * 0
|
||||
if kwargs.get('force_series') is None:
|
||||
kwargs['force_series'] = True
|
||||
return ctx.hyp2f1(-n,n,(1,2),(1-x)/2, **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def chebyu(ctx, n, x, **kwargs):
|
||||
def chebyu(ctx, n, x, *, eliminate=True, eliminate_all=False,
|
||||
force_series=True, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1:
|
||||
return x * 0
|
||||
if kwargs.get('force_series') is None:
|
||||
kwargs['force_series'] = True
|
||||
return (n+1) * ctx.hyp2f1(-n, n+2, (3,2), (1-x)/2, **kwargs)
|
||||
|
||||
@defun
|
||||
def spherharm(ctx, l, m, theta, phi, **kwargs):
|
||||
def spherharm(ctx, l, m, theta, phi, *, eliminate=True, eliminate_all=False,
|
||||
force_series=False, asymp_tol=None, maxprec=None,
|
||||
maxterms=None, zeroprec=None, infprec=None, verbose=False):
|
||||
kwargs = ctx._set_hyper_kwargs(eliminate, eliminate_all,
|
||||
force_series, asymp_tol, maxprec,
|
||||
maxterms, zeroprec, infprec, verbose)
|
||||
l = ctx.convert(l)
|
||||
m = ctx.convert(m)
|
||||
theta = ctx.convert(theta)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from .functions import defun, defun_wrapped
|
||||
|
||||
@defun
|
||||
def qp(ctx, a, q=None, n=None, **kwargs):
|
||||
def qp(ctx, a, q=None, n=None, *, maxterms=None):
|
||||
r"""
|
||||
Evaluates the q-Pochhammer symbol (or q-rising factorial)
|
||||
|
||||
@@ -98,7 +98,8 @@ def qp(ctx, a, q=None, n=None, **kwargs):
|
||||
raise ValueError("q-function only defined for |q| < 1")
|
||||
elif q == 0:
|
||||
return ctx.one - a
|
||||
maxterms = kwargs.get('maxterms', 50*ctx.prec)
|
||||
if maxterms is None:
|
||||
maxterms = 50*ctx.prec
|
||||
if infinite and same:
|
||||
# Euler's pentagonal theorem
|
||||
def terms():
|
||||
@@ -131,7 +132,7 @@ def qp(ctx, a, q=None, n=None, **kwargs):
|
||||
return ctx.mul_accurately(factors)
|
||||
|
||||
@defun_wrapped
|
||||
def qgamma(ctx, z, q, **kwargs):
|
||||
def qgamma(ctx, z, q, *, maxterms=None):
|
||||
r"""
|
||||
Evaluates the q-gamma function
|
||||
|
||||
@@ -167,11 +168,11 @@ def qgamma(ctx, z, q, **kwargs):
|
||||
"""
|
||||
if abs(q) > 1:
|
||||
return ctx.qgamma(z,1/q)*q**((z-2)*(z-1)*0.5)
|
||||
return ctx.qp(q, q, None, **kwargs) / \
|
||||
ctx.qp(q**z, q, None, **kwargs) * (1-q)**(1-z)
|
||||
return ctx.qp(q, q, None, maxterms=maxterms) / \
|
||||
ctx.qp(q**z, q, None, maxterms=maxterms) * (1-q)**(1-z)
|
||||
|
||||
@defun_wrapped
|
||||
def qfac(ctx, z, q, **kwargs):
|
||||
def qfac(ctx, z, q, *, maxterms=None):
|
||||
r"""
|
||||
Evaluates the q-factorial,
|
||||
|
||||
@@ -202,11 +203,11 @@ def qfac(ctx, z, q, **kwargs):
|
||||
"""
|
||||
if ctx.isint(z) and ctx._re(z) > 0:
|
||||
n = int(ctx._re(z))
|
||||
return ctx.qp(q, q, n, **kwargs) / (1-q)**n
|
||||
return ctx.qgamma(z+1, q, **kwargs)
|
||||
return ctx.qp(q, q, n, maxterms=maxterms) / (1-q)**n
|
||||
return ctx.qgamma(z+1, q, maxterms=maxterms)
|
||||
|
||||
@defun
|
||||
def qhyper(ctx, a_s, b_s, q, z, **kwargs):
|
||||
def qhyper(ctx, a_s, b_s, q, z, *, maxterms=None):
|
||||
r"""
|
||||
Evaluates the basic hypergeometric series or hypergeometric q-series
|
||||
|
||||
@@ -258,7 +259,8 @@ def qhyper(ctx, a_s, b_s, q, z, **kwargs):
|
||||
r = len(a_s)
|
||||
s = len(b_s)
|
||||
d = 1+s-r
|
||||
maxterms = kwargs.get('maxterms', 50*ctx.prec)
|
||||
if maxterms is None:
|
||||
maxterms = 50*ctx.prec
|
||||
def terms():
|
||||
t = ctx.one
|
||||
yield t
|
||||
|
||||
@@ -1376,7 +1376,7 @@ def z_offline(ctx, w, k=0):
|
||||
return zv
|
||||
|
||||
@defun
|
||||
def rs_zeta(ctx, s, derivative=0, **kwargs):
|
||||
def rs_zeta(ctx, s, derivative=0):
|
||||
if derivative > 4:
|
||||
raise NotImplementedError
|
||||
s = ctx.convert(s)
|
||||
|
||||
+216
-713
File diff suppressed because it is too large
Load Diff
+69
-33
@@ -81,8 +81,8 @@ def grampoint(ctx, n):
|
||||
|
||||
|
||||
@defun_wrapped
|
||||
def siegelz(ctx, t, **kwargs):
|
||||
d = int(kwargs.get("derivative", 0))
|
||||
def siegelz(ctx, t, *, derivative=0):
|
||||
d = int(derivative)
|
||||
t = ctx.convert(t)
|
||||
t1 = ctx._re(t)
|
||||
t2 = ctx._im(t)
|
||||
@@ -403,7 +403,7 @@ def polylog_continuation(ctx, n, z):
|
||||
if n < 0:
|
||||
return z*0
|
||||
if ctx._is_real_type(z) and ctx.isinf(z) and n > 0:
|
||||
return ctx.ninf
|
||||
return ctx.ninf if z < 0 else ctx.mpc(ctx.ninf, ctx.nan)
|
||||
twopij = 2j * ctx.pi
|
||||
a = -twopij**n/ctx.fac(n) * ctx.bernpoly(n, ctx.ln(z)/twopij)
|
||||
if ctx._is_real_type(z) and z < 0:
|
||||
@@ -493,6 +493,10 @@ def polylog(ctx, s, z):
|
||||
return polylog_series(ctx, s, z)
|
||||
if abs(z) >= 1.4 and ctx.isint(s):
|
||||
return (-1)**(s+1)*polylog_series(ctx, s, 1/z) + polylog_continuation(ctx, int(ctx.re(s)), z)
|
||||
if ctx.isnan(z):
|
||||
if ctx._is_real_type(z) and ctx.isnpint(s):
|
||||
return ctx.nan
|
||||
return ctx.mpc(ctx.nan, ctx.nan)
|
||||
if ctx.isint(s):
|
||||
return polylog_unitcircle(ctx, int(ctx.re(s)), z)
|
||||
return polylog_general(ctx, s, z)
|
||||
@@ -524,9 +528,9 @@ def clcos(ctx, s, z, pi=False):
|
||||
return 0.5*(ctx.polylog(s,a) + ctx.polylog(s,b))
|
||||
|
||||
@defun
|
||||
def altzeta(ctx, s, **kwargs):
|
||||
def altzeta(ctx, s, *, prec=None, dps=None, rounding=None):
|
||||
try:
|
||||
return ctx._altzeta(s, **kwargs)
|
||||
return ctx._altzeta(s, prec=prec, dps=dps, rounding=rounding)
|
||||
except NotImplementedError:
|
||||
return ctx._altzeta_generic(s)
|
||||
|
||||
@@ -537,16 +541,19 @@ def _altzeta_generic(ctx, s):
|
||||
return -ctx.powm1(2, 1-s) * ctx.zeta(s)
|
||||
|
||||
@defun
|
||||
def zeta(ctx, s, a=1, derivative=0, method=None, **kwargs):
|
||||
def zeta(ctx, s, a=1, derivative=0, method=None, *, prec=None,
|
||||
dps=None, rounding=None, verbose=False, maxprec=None):
|
||||
d = int(derivative)
|
||||
if a == 1 and not (d or method):
|
||||
try:
|
||||
return ctx._zeta(s, **kwargs)
|
||||
return ctx._zeta(s, prec=prec, dps=dps, rounding=rounding)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
s = ctx.convert(s)
|
||||
prec = ctx.prec
|
||||
verbose = kwargs.get('verbose')
|
||||
if prec is None:
|
||||
prec = ctx.prec
|
||||
if maxprec is None:
|
||||
maxprec = 100*prec
|
||||
if (not s) and (not derivative):
|
||||
return ctx.mpf(0.5) - ctx._convert_param(a)[0]
|
||||
if a == 1 and method != 'euler-maclaurin':
|
||||
@@ -566,7 +573,7 @@ def zeta(ctx, s, a=1, derivative=0, method=None, **kwargs):
|
||||
try:
|
||||
if verbose:
|
||||
print("zeta: Attempting to use the Riemann-Siegel algorithm")
|
||||
return ctx.rs_zeta(s, derivative, **kwargs)
|
||||
return ctx.rs_zeta(s, derivative)
|
||||
except NotImplementedError:
|
||||
if verbose:
|
||||
print("zeta: Could not use the Riemann-Siegel algorithm")
|
||||
@@ -585,12 +592,11 @@ def zeta(ctx, s, a=1, derivative=0, method=None, **kwargs):
|
||||
return 1/s
|
||||
if ctx.re(s) > 2*ctx.prec and a == 1 and not derivative:
|
||||
return ctx.one + ctx.power(2, -s)
|
||||
return +ctx._hurwitz(s, a, d, **kwargs)
|
||||
return +ctx._hurwitz(s, a, d, verbose=verbose, maxprec=maxprec)
|
||||
|
||||
@defun
|
||||
def _hurwitz(ctx, s, a=1, d=0, **kwargs):
|
||||
def _hurwitz(ctx, s, a=1, d=0, *, verbose=False, maxprec=None):
|
||||
prec = ctx.prec
|
||||
verbose = kwargs.get('verbose')
|
||||
try:
|
||||
extraprec = 10
|
||||
ctx.prec += extraprec
|
||||
@@ -623,7 +629,7 @@ def _hurwitz(ctx, s, a=1, d=0, **kwargs):
|
||||
return T1 + T2
|
||||
else:
|
||||
extraprec = max(2*extraprec, min(cancellation + 5, 100*prec))
|
||||
if extraprec > kwargs.get('maxprec', 100*prec):
|
||||
if extraprec > maxprec:
|
||||
raise ctx.NoConvergence("zeta: too much cancellation")
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
@@ -871,10 +877,6 @@ def secondzeta_prime_term(ctx, s, a, **kwargs):
|
||||
return +totsum, err, n
|
||||
|
||||
def secondzeta_exp_term(ctx, s, a):
|
||||
if ctx.isint(s) and ctx.re(s) <= 0:
|
||||
m = int(round(ctx.re(s)))
|
||||
if not m & 1:
|
||||
return ctx.mpf('-0.25')**(-m//2)
|
||||
tol = ctx.eps
|
||||
f = lambda n: (0.25*a)**n/((n+0.5*s)*ctx.fac(n))
|
||||
totsum = ctx.zero
|
||||
@@ -926,7 +928,7 @@ def secondzeta_singular_term(ctx, s, a, **kwargs):
|
||||
return +st, err
|
||||
|
||||
@defun
|
||||
def secondzeta(ctx, s, a = 0.015, **kwargs):
|
||||
def secondzeta(ctx, s, a = 0.015, *, verbose=False, error=False):
|
||||
r"""
|
||||
Evaluates the secondary zeta function `Z(s)`, defined for
|
||||
`\mathrm{Re}(s)>1` by
|
||||
@@ -1018,10 +1020,10 @@ def secondzeta(ctx, s, a = 0.015, **kwargs):
|
||||
s = ctx.convert(s)
|
||||
a = ctx.convert(a)
|
||||
tol = ctx.eps
|
||||
if ctx.isint(s) and ctx.re(s) <= 1:
|
||||
if ctx.isnpint(s-1):
|
||||
if abs(s-1) < tol*1000:
|
||||
return ctx.inf
|
||||
m = int(round(ctx.re(s)))
|
||||
m = round(ctx.re(s))
|
||||
if m & 1:
|
||||
return ctx.inf
|
||||
else:
|
||||
@@ -1038,7 +1040,7 @@ def secondzeta(ctx, s, a = 0.015, **kwargs):
|
||||
t3 = secondzeta_exp_term(ctx, s, a)
|
||||
err = r1+r2+r4
|
||||
t = t1-t2+t3-t4
|
||||
if kwargs.get("verbose"):
|
||||
if verbose:
|
||||
print('main term =', t1)
|
||||
print(' computed using', gt, 'zeros of zeta')
|
||||
print('prime term =', t2)
|
||||
@@ -1047,7 +1049,7 @@ def secondzeta(ctx, s, a = 0.015, **kwargs):
|
||||
print('singular term =', t4)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
if kwargs.get("error"):
|
||||
if error:
|
||||
w = max(ctx.mag(abs(t)),0)
|
||||
err = max(err*2**w, ctx.eps*1.*2**w)
|
||||
return +t, err
|
||||
@@ -1158,13 +1160,47 @@ def lerchphi(ctx, z, s, a):
|
||||
v += zpow / (a+n)**s
|
||||
zpow *= z
|
||||
return zpow * ctx.lerchphi(z,s, a+m) + v
|
||||
g = ctx.ln(z)
|
||||
v = 1/(2*a**s) + ctx.gammainc(1-s, -a*g) * (-g)**(s-1) / z**a
|
||||
h = s / 2
|
||||
r = 2*ctx.pi
|
||||
f = lambda t: ctx.sin(s*ctx.atan(t/a)-t*g) / \
|
||||
((a**2+t**2)**h * ctx.expm1(r*t))
|
||||
v += 2*ctx.quad(f, [0, ctx.inf])
|
||||
if not ctx.im(z) and not ctx.im(s) and not ctx.im(a) and ctx.re(z) < 1:
|
||||
v = ctx.chop(v)
|
||||
return v
|
||||
if abs(z) < 0.5:
|
||||
return ctx.nsum(lambda k: z**k/(a+k)**s, [0, ctx.inf])
|
||||
g = lambda t: t**(s - 1)*ctx.exp(-a*t)/(1 - z*ctx.exp(-t))
|
||||
h = lambda t: (-t)**(s - 1)*ctx.exp(-a*t)/(1 - z*ctx.exp(-t))
|
||||
L = ctx.log(z)
|
||||
if ctx.isint(s) and s.real >= 1:
|
||||
if abs(L.imag) < 0.25 and L.real >= 0:
|
||||
if z.imag <= 0:
|
||||
I = ctx.quad(g, [0, +1j, +1j + abs(L) + 1, abs(L) + 1, ctx.inf])
|
||||
else:
|
||||
I = ctx.quad(g, [0, -1j, -1j + abs(L) + 1, abs(L) + 1, ctx.inf])
|
||||
else:
|
||||
I = ctx.quad(g, [0, ctx.inf])
|
||||
return ctx.rgamma(s)*I
|
||||
if L.real < -0.5:
|
||||
residue = 0
|
||||
c = min(abs(L.real)/2, 1)
|
||||
left = right = top = c
|
||||
elif abs(L.imag) > 0.5:
|
||||
residue = 0
|
||||
c = min(abs(L.imag)/2, 1)
|
||||
left = right = top = c
|
||||
else:
|
||||
residue = (-L)**s/L/z**a
|
||||
left = max(0, -L.real) + 1
|
||||
top = abs(L.imag) + 1
|
||||
right = abs(L) + 1
|
||||
isreal = not z.imag and z.real < 1 and not s.imag and not a.imag and a.real > 0
|
||||
w = ctx.mpc(-1)**(s - 1)
|
||||
I = 0
|
||||
if isreal:
|
||||
I += 2j*ctx.im(ctx.quad(g, [right, right + top*1j]) / w)
|
||||
I += 2j*ctx.im(ctx.quad(g, [right + top*1j, -left + top*1j]) / w)
|
||||
I += 2j*ctx.im(ctx.quad(h, [-left + top*1j, -left]))
|
||||
I += ctx.quad(g, [right, ctx.inf]) * (w - 1/w)
|
||||
else:
|
||||
I += ctx.quad(g, [right, right + top*1j])/w
|
||||
I += ctx.quad(g, [right + top*1j, -left + top*1j])/w
|
||||
I += ctx.quad(h, [-left + top*1j, -left - top*1j])
|
||||
I += ctx.quad(g, [-left - top*1j, right - top*1j])*w
|
||||
I += ctx.quad(g, [right - top*1j, right])*w
|
||||
I += ctx.quad(g, [right, ctx.inf])*(w - 1/w)
|
||||
I = I/(2*ctx.pi*1j) + residue
|
||||
return -ctx.gamma(1 - s)*I
|
||||
|
||||
@@ -398,7 +398,7 @@ def zetazero(ctx, n, info=False, round=True):
|
||||
in each Gram interval (Rosser blocks between parenthesis). In this case
|
||||
there is only one Rosser block of length nine.
|
||||
|
||||
** References **
|
||||
**References**
|
||||
|
||||
* [Brent79]_
|
||||
* [Trudgian]_
|
||||
@@ -893,6 +893,7 @@ _ROSSER_EXCEPTIONS = \
|
||||
[320822347, 320822350], '3(00)',
|
||||
[321733242, 321733245], '3(00)',
|
||||
[324413970, 324413973], '(00)3',
|
||||
[325890638, 325890641], '(00)3',
|
||||
[325950140, 325950143], '(00)3',
|
||||
[326675884, 326675887], '(00)3',
|
||||
[326704208, 326704211], '3(00)',
|
||||
@@ -942,6 +943,7 @@ _ROSSER_EXCEPTIONS = \
|
||||
[356586657, 356586660], '3(00)',
|
||||
[356892926, 356892929], '(00)3',
|
||||
[356908232, 356908235], '3(00)',
|
||||
[357738762, 357738765], '(00)3',
|
||||
[357912730, 357912733], '3(00)',
|
||||
[358120344, 358120347], '3(00)',
|
||||
[359044096, 359044099], '(00)3',
|
||||
|
||||
+21
-27
@@ -3,9 +3,8 @@ Implements the PSLQ algorithm for integer relation detection,
|
||||
and derivative algorithms for constant recognition.
|
||||
"""
|
||||
|
||||
import warnings
|
||||
|
||||
from .libmp import int_types, sqrt_fixed
|
||||
from .libmp import int_types
|
||||
from .libmp.libintmath import sqrt_fixed
|
||||
|
||||
|
||||
# round to nearest integer (can be done more elegantly...)
|
||||
@@ -26,9 +25,9 @@ def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False):
|
||||
|
||||
|c_1 x_1 + c_2 x_2 + ... + c_n x_n| < \mathrm{tol}
|
||||
|
||||
and such that `\max |c_k| < \mathrm{maxcoeff}`. If no such vector
|
||||
exists, :func:`~mpmath.pslq` returns ``None``. The tolerance defaults to
|
||||
3/4 of the working precision.
|
||||
and such that `\max |c_k| < \mathrm{maxcoeff}`. If no such vector
|
||||
found in no more than ``maxsteps`` iterations, :func:`~mpmath.pslq`
|
||||
returns ``None``. The tolerance defaults to 3/4 of the working precision.
|
||||
|
||||
**Examples**
|
||||
|
||||
@@ -313,7 +312,8 @@ def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False):
|
||||
print("Could not find an integer relation. Norm bound: %s" % norm)
|
||||
return None
|
||||
|
||||
def findpoly(ctx, x, n=1, asc=None, **kwargs):
|
||||
def findpoly(ctx, x, n=1, asc=True, *,
|
||||
tol=None, maxcoeff=1000, maxsteps=100, verbose=False):
|
||||
r"""
|
||||
``findpoly(x, n)`` returns the coefficients of an integer
|
||||
polynomial `P` of degree at most `n` such that `P(x) \approx 0`.
|
||||
@@ -341,15 +341,15 @@ def findpoly(ctx, x, n=1, asc=None, **kwargs):
|
||||
>>> from mpmath import (mp, findpoly, nprint, polyval, polyroots,
|
||||
... sqrt, pi, phi, euler, findroot)
|
||||
>>> mp.pretty = True
|
||||
>>> findpoly(0.7, asc=True)
|
||||
>>> findpoly(0.7)
|
||||
[7, -10]
|
||||
|
||||
The generated coefficient list is valid input to ``polyval`` and
|
||||
``polyroots``::
|
||||
|
||||
>>> nprint(polyval(findpoly(phi, 2, asc=True), phi, asc=True), 1)
|
||||
>>> nprint(polyval(findpoly(phi, 2), phi), 1)
|
||||
-2.0e-16
|
||||
>>> for r in polyroots(findpoly(phi, 2, asc=True), asc=True):
|
||||
>>> for r in polyroots(findpoly(phi, 2)):
|
||||
... print(r)
|
||||
...
|
||||
-0.618033988749895
|
||||
@@ -359,15 +359,15 @@ def findpoly(ctx, x, n=1, asc=None, **kwargs):
|
||||
solutions to quadratic equations. As we find here, `1+\sqrt 2`
|
||||
is a root of the polynomial `x^2 - 2x - 1`::
|
||||
|
||||
>>> findpoly(1+sqrt(2), 2, asc=True)
|
||||
>>> findpoly(1+sqrt(2), 2)
|
||||
[-1, -2, 1]
|
||||
>>> findroot(lambda x: x**2 - 2*x - 1, 1, asc=True)
|
||||
>>> findroot(lambda x: x**2 - 2*x - 1, 1)
|
||||
2.4142135623731
|
||||
|
||||
Despite only containing square roots, the following number results
|
||||
in a polynomial of degree 4::
|
||||
|
||||
>>> findpoly(sqrt(2)+sqrt(3), 4, asc=True)
|
||||
>>> findpoly(sqrt(2)+sqrt(3), 4)
|
||||
[1, 0, -10, 0, 1]
|
||||
|
||||
In fact, `x^4 - 10x^2 + 1` is the *minimal polynomial* of
|
||||
@@ -385,7 +385,7 @@ def findpoly(ctx, x, n=1, asc=None, **kwargs):
|
||||
We can verify that `\pi` is not an algebraic number of degree 3 with
|
||||
coefficients less than 1000::
|
||||
|
||||
>>> findpoly(pi, 3, asc=True)
|
||||
>>> findpoly(pi, 3)
|
||||
>>>
|
||||
|
||||
It is always possible to find an algebraic approximation of a number
|
||||
@@ -397,11 +397,11 @@ def findpoly(ctx, x, n=1, asc=None, **kwargs):
|
||||
|
||||
One example of each method is shown below::
|
||||
|
||||
>>> findpoly(pi, 4, asc=True)
|
||||
>>> findpoly(pi, 4)
|
||||
[-298, -183, 863, -545, 95]
|
||||
>>> findpoly(pi, 3, maxcoeff=10000, asc=True)
|
||||
>>> findpoly(pi, 3, maxcoeff=10000)
|
||||
[-457, -2658, -1734, 836]
|
||||
>>> findpoly(pi, 3, tol=1e-7, asc=True)
|
||||
>>> findpoly(pi, 3, tol=1e-7)
|
||||
[-2, -29, 22, -4]
|
||||
|
||||
It is unknown whether Euler's constant is transcendental (or even
|
||||
@@ -410,8 +410,7 @@ def findpoly(ctx, x, n=1, asc=None, **kwargs):
|
||||
at least 7 and a coefficient of magnitude at least 1000000::
|
||||
|
||||
>>> mp.dps = 200
|
||||
>>> findpoly(euler, 6, maxcoeff=10**6, tol=1e-100,
|
||||
... maxsteps=1000, asc=True)
|
||||
>>> findpoly(euler, 6, maxcoeff=10**6, tol=1e-100, maxsteps=1000)
|
||||
>>>
|
||||
|
||||
Note that the high precision and strict tolerance is necessary
|
||||
@@ -426,16 +425,11 @@ def findpoly(ctx, x, n=1, asc=None, **kwargs):
|
||||
raise ValueError("n cannot be less than 1")
|
||||
if x == 0:
|
||||
return [1, 0]
|
||||
if asc is None:
|
||||
warnings.warn("Descending (wrt powers) order of polynomial "
|
||||
"coefficients is deprecated, please adapt you "
|
||||
"code to use ascending order, asc=True.",
|
||||
DeprecationWarning)
|
||||
asc = False
|
||||
xs = [ctx.mpf(1)]
|
||||
for i in range(1,n+1):
|
||||
xs.append(x**i)
|
||||
a = ctx.pslq(xs, **kwargs)
|
||||
a = ctx.pslq(xs, tol=tol, maxcoeff=maxcoeff,
|
||||
maxsteps=maxsteps, verbose=verbose)
|
||||
if a is not None:
|
||||
return a if asc else a[::-1]
|
||||
|
||||
@@ -834,7 +828,7 @@ def identify(ctx, x, constants=[], tol=None, maxcoeff=1000, full=False,
|
||||
# Watch out for existing fractional powers of fractions
|
||||
logs = []
|
||||
for a, s in constants:
|
||||
if not sum(bool(ctx.findpoly(ctx.ln(a)/ctx.ln(i),1,asc=True)) for i in ilogs):
|
||||
if not sum(bool(ctx.findpoly(ctx.ln(a)/ctx.ln(i),1)) for i in ilogs):
|
||||
logs.append((ctx.ln(a), s))
|
||||
logs = [(ctx.ln(i),str(i)) for i in ilogs] + logs
|
||||
r = ctx.pslq([ctx.ln(x)] + [a[0] for a in logs], tol, M)
|
||||
|
||||
+1
-1
@@ -488,7 +488,7 @@ _zeta_0 = [-3.46092485016748794e-10, -6.42610089468292485e-9,
|
||||
0.000122641099800668209, -0.000905894913516772796, -0.00239303348507992713,
|
||||
0.0842396947501199816, 0.418938533204660256, 0.500000000000000052]
|
||||
|
||||
def zeta(s):
|
||||
def zeta(s, **kwargs):
|
||||
"""
|
||||
Riemann zeta function, real argument
|
||||
"""
|
||||
|
||||
+17
-67
@@ -1,68 +1,18 @@
|
||||
from .backend import (BACKEND, MPQ, MPZ, MPZ_FIVE, MPZ_ONE, MPZ_THREE, MPZ_TWO,
|
||||
MPZ_ZERO, gmpy, int_types)
|
||||
from .gammazeta import (apery_fixed, bernfrac, catalan_fixed, euler_fixed,
|
||||
glaisher_fixed, khinchin_fixed, mertens_fixed,
|
||||
mpc_altzeta, mpc_factorial, mpc_gamma, mpc_harmonic,
|
||||
mpc_loggamma, mpc_psi, mpc_psi0, mpc_rgamma, mpc_zeta,
|
||||
mpc_zetasum, mpf_altzeta, mpf_apery, mpf_bernoulli,
|
||||
mpf_catalan, mpf_euler, mpf_factorial, mpf_gamma,
|
||||
mpf_gamma_int, mpf_glaisher, mpf_harmonic,
|
||||
mpf_khinchin, mpf_loggamma, mpf_mertens, mpf_psi,
|
||||
mpf_psi0, mpf_rgamma, mpf_twinprime, mpf_zeta,
|
||||
mpf_zeta_int, mpf_zetasum, twinprime_fixed)
|
||||
from .libelefun import (agm_fixed, degree_fixed, e_fixed, ln2_fixed,
|
||||
ln10_fixed, log_int_fixed, mpf_acos, mpf_acosh,
|
||||
mpf_asin, mpf_asinh, mpf_atan, mpf_atan2, mpf_atanh,
|
||||
mpf_cbrt, mpf_cos, mpf_cos_pi, mpf_cos_sin,
|
||||
mpf_cos_sin_pi, mpf_cosh, mpf_cosh_sinh, mpf_degree,
|
||||
mpf_e, mpf_exp, mpf_fibonacci, mpf_ln, mpf_ln2,
|
||||
mpf_ln10, mpf_log, mpf_log_hypot, mpf_nthroot, mpf_phi,
|
||||
mpf_pi, mpf_pow, mpf_sin, mpf_sin_pi, mpf_sinh,
|
||||
mpf_tan, mpf_tanh, phi_fixed, pi_fixed)
|
||||
from .libhyper import (NoConvergence, make_hyp_summator, mpc_agm, mpc_agm1,
|
||||
mpc_besseljn, mpc_ci, mpc_e1, mpc_ei, mpc_ellipe,
|
||||
mpc_ellipk, mpc_si, mpf_agm, mpf_agm1, mpf_besseljn,
|
||||
mpf_ci, mpf_ci_si, mpf_e1, mpf_ei, mpf_ellipe,
|
||||
mpf_ellipk, mpf_erf, mpf_erfc, mpf_expint, mpf_si)
|
||||
from .libintmath import (bin_to_radix, bitcount, eulernum, gcd, giant_steps,
|
||||
ifac, ifib, isprime, isqrt, isqrt_fast, isqrt_small,
|
||||
list_primes, moebius, numeral, sqrt_fixed, sqrtrem,
|
||||
stirling1, stirling2, trailing)
|
||||
from .libmpc import (complex_int_pow, mpc_abs, mpc_acos, mpc_acosh, mpc_add,
|
||||
mpc_add_mpf, mpc_arg, mpc_asin, mpc_asinh, mpc_atan,
|
||||
mpc_atanh, mpc_cbrt, mpc_ceil, mpc_conjugate, mpc_cos,
|
||||
mpc_cos_pi, mpc_cos_sin, mpc_cos_sin_pi, mpc_cosh,
|
||||
mpc_div, mpc_div_mpf, mpc_exp, mpc_expj, mpc_expjpi,
|
||||
mpc_fibonacci, mpc_floor, mpc_frac, mpc_half, mpc_hash,
|
||||
mpc_is_inf, mpc_is_infnan, mpc_is_nonzero, mpc_ln,
|
||||
mpc_log, mpc_mpf_div, mpc_mpf_sub, mpc_mul, mpc_mul_int,
|
||||
mpc_mul_mpf, mpc_neg, mpc_nint, mpc_nthroot, mpc_one,
|
||||
mpc_pos, mpc_pow, mpc_pow_int, mpc_pow_mpf,
|
||||
mpc_reciprocal, mpc_shift, mpc_sin, mpc_sin_pi, mpc_sinh,
|
||||
mpc_sqrt, mpc_square, mpc_sub, mpc_sub_mpf, mpc_tan,
|
||||
mpc_tanh, mpc_to_complex, mpc_to_str, mpc_two, mpc_zero,
|
||||
mpf_expj, mpf_expjpi)
|
||||
from .backend import BACKEND, MPZ, MPZ_ONE, int_types
|
||||
from .gammazeta import catalan_fixed, euler_fixed, mpf_bernoulli
|
||||
from .libelefun import (mpf_atan, mpf_atan2, mpf_cos, mpf_cosh_sinh, mpf_e,
|
||||
mpf_exp, mpf_log, mpf_pi, mpf_pow, mpf_sin, mpf_tan,
|
||||
phi_fixed)
|
||||
from .libhyper import NoConvergence
|
||||
from .libintmath import giant_steps, ifac, ifib, isqrt, sqrtrem
|
||||
from .libmpc import (mpc_abs, mpc_exp, mpc_pow, mpc_pow_int, mpc_pow_mpf,
|
||||
mpc_sqrt)
|
||||
from .libmpf import (ComplexResult, dps_to_prec, fhalf, finf, fnan, fninf,
|
||||
fnone, fone, format_mpc, format_mpf, from_Decimal,
|
||||
from_float, from_int, from_man_exp, from_npfloat,
|
||||
from_pickable, from_rational, from_str, ften, ftwo, fzero,
|
||||
mpf_abs, mpf_add, mpf_ceil, mpf_cmp, mpf_div, mpf_eq,
|
||||
mpf_floor, mpf_frac, mpf_frexp, mpf_ge, mpf_gt, mpf_hash,
|
||||
mpf_hypot, mpf_le, mpf_lt, mpf_mod, mpf_mul, mpf_mul_int,
|
||||
mpf_neg, mpf_nint, mpf_perturb, mpf_pos, mpf_pow_int,
|
||||
mpf_rand, mpf_rdiv_int, mpf_shift, mpf_sign, mpf_sqrt,
|
||||
mpf_sub, mpf_sum, normalize, prec_to_dps, repr_dps,
|
||||
round_ceiling, round_down, round_floor, round_int,
|
||||
round_nearest, round_up, str_to_man_exp, to_digits_exp,
|
||||
to_fixed, to_float, to_int, to_man_exp, to_pickable,
|
||||
to_rational, to_str)
|
||||
from .libmpi import (mpci_abs, mpci_add, mpci_cos, mpci_div, mpci_exp,
|
||||
mpci_factorial, mpci_gamma, mpci_log, mpci_loggamma,
|
||||
mpci_mul, mpci_neg, mpci_pos, mpci_pow, mpci_rgamma,
|
||||
mpci_sin, mpci_sub, mpi_abs, mpi_add, mpi_atan, mpi_atan2,
|
||||
mpi_cos, mpi_cos_sin, mpi_cot, mpi_delta, mpi_div, mpi_eq,
|
||||
mpi_exp, mpi_factorial, mpi_from_str, mpi_gamma, mpi_ge,
|
||||
mpi_gt, mpi_le, mpi_log, mpi_loggamma, mpi_lt, mpi_mid,
|
||||
mpi_mul, mpi_ne, mpi_neg, mpi_pos, mpi_pow, mpi_pow_int,
|
||||
mpi_rgamma, mpi_sin, mpi_sqrt, mpi_str, mpi_sub, mpi_tan,
|
||||
mpi_to_str)
|
||||
fnone, fone, from_float, from_int, from_man_exp,
|
||||
from_rational, from_str, fzero, mpf_abs, mpf_add,
|
||||
mpf_ceil, mpf_cmp, mpf_div, mpf_eq, mpf_floor, mpf_ge,
|
||||
mpf_gt, mpf_le, mpf_lt, mpf_mod, mpf_mul, mpf_neg,
|
||||
mpf_pow_int, mpf_shift, mpf_sign, mpf_sqrt, mpf_sub,
|
||||
normalize, prec_to_dps, repr_dps, round_ceiling,
|
||||
round_down, round_floor, round_nearest, round_up,
|
||||
to_float, to_int, to_man_exp, to_rational, to_str)
|
||||
|
||||
+22
-22
@@ -35,7 +35,7 @@ from .libmpf import (ComplexResult, fhalf, finf, fnan, fninf, fone, from_int,
|
||||
mpf_add, mpf_div, mpf_floor, mpf_gt, mpf_le, mpf_lt,
|
||||
mpf_mul, mpf_mul_int, mpf_neg, mpf_perturb, mpf_pos,
|
||||
mpf_pow_int, mpf_rdiv_int, mpf_shift, mpf_sign, mpf_sub,
|
||||
negative_rnd, round_fast, round_nearest, to_fixed,
|
||||
negative_rnd, round_down, round_nearest, to_fixed,
|
||||
to_float, to_int)
|
||||
|
||||
|
||||
@@ -380,7 +380,7 @@ def bernoulli_size(n):
|
||||
|
||||
BERNOULLI_PREC_CUTOFF = bernoulli_size(MAX_BERNOULLI_CACHE)
|
||||
|
||||
def mpf_bernoulli(n, prec, rnd=round_fast, plus=False):
|
||||
def mpf_bernoulli(n, prec, rnd=round_down, plus=False):
|
||||
"""Computation of Bernoulli numbers (numerically)"""
|
||||
if n < 2:
|
||||
if n < 0:
|
||||
@@ -453,7 +453,7 @@ def mpf_bernoulli(n, prec, rnd=round_fast, plus=False):
|
||||
state[:] = [m, bin, bin1]
|
||||
return mpf_pos(numbers[n], prec, rnd)
|
||||
|
||||
def mpf_bernoulli_huge(n, prec, rnd=round_fast):
|
||||
def mpf_bernoulli_huge(n, prec, rnd=round_down):
|
||||
wp = prec + 10
|
||||
piprec = wp + int(math.log(n,2))
|
||||
v = mpf_gamma_int(n+1, wp)
|
||||
@@ -640,7 +640,7 @@ def mpc_harmonic(z, prec, rnd):
|
||||
a = mpc_psi0(mpc_add_mpf(z, fone, prec+5), prec)
|
||||
return mpc_add_mpf(a, mpf_euler(prec+5, rnd), prec, rnd)
|
||||
|
||||
def mpf_psi0(x, prec, rnd=round_fast):
|
||||
def mpf_psi0(x, prec, rnd=round_down):
|
||||
"""
|
||||
Computation of the digamma function (psi function of order 0)
|
||||
of a real argument.
|
||||
@@ -699,7 +699,7 @@ def mpf_psi0(x, prec, rnd=round_fast):
|
||||
k += 1
|
||||
return from_man_exp(s, -wp, wp, rnd)
|
||||
|
||||
def mpc_psi0(z, prec, rnd=round_fast):
|
||||
def mpc_psi0(z, prec, rnd=round_down):
|
||||
"""
|
||||
Computation of the digamma function (psi function of order 0)
|
||||
of a complex argument.
|
||||
@@ -753,16 +753,16 @@ def mpc_psi0(z, prec, rnd=round_fast):
|
||||
return s
|
||||
|
||||
# Currently unoptimized
|
||||
def mpf_psi(m, x, prec, rnd=round_fast):
|
||||
def mpf_psi(m, x, prec, rnd=round_down):
|
||||
"""
|
||||
Computation of the polygamma function of arbitrary integer order
|
||||
m >= 0, for a real argument x.
|
||||
"""
|
||||
if m == 0:
|
||||
return mpf_psi0(x, prec, rnd=round_fast)
|
||||
return mpf_psi0(x, prec, rnd=round_down)
|
||||
return mpc_psi(m, (x, fzero), prec, rnd)[0]
|
||||
|
||||
def mpc_psi(m, z, prec, rnd=round_fast):
|
||||
def mpc_psi(m, z, prec, rnd=round_down):
|
||||
"""
|
||||
Computation of the polygamma function of arbitrary integer order
|
||||
m >= 0, for a complex argument z.
|
||||
@@ -891,7 +891,7 @@ def borwein_coefficients(n):
|
||||
ZETA_INT_CACHE_MAX_PREC = 1000
|
||||
zeta_int_cache = local.zeta_int_cache = {}
|
||||
|
||||
def mpf_zeta_int(s, prec, rnd=round_fast):
|
||||
def mpf_zeta_int(s, prec, rnd=round_down):
|
||||
"""
|
||||
Optimized computation of zeta(s) for an integer s.
|
||||
"""
|
||||
@@ -944,7 +944,7 @@ def mpf_zeta_int(s, prec, rnd=round_fast):
|
||||
zeta_int_cache[s] = (wp, from_man_exp(t, -wp-wp))
|
||||
return from_man_exp(t, -wp-wp, prec, rnd)
|
||||
|
||||
def mpf_zeta(s, prec, rnd=round_fast, alt=0):
|
||||
def mpf_zeta(s, prec, rnd=round_down, alt=0):
|
||||
sign, man, exp, bc = s
|
||||
if not man:
|
||||
if s == fzero:
|
||||
@@ -1031,7 +1031,7 @@ def mpf_zeta(s, prec, rnd=round_fast, alt=0):
|
||||
q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp)
|
||||
return mpf_div(t, q, prec, rnd)
|
||||
|
||||
def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False):
|
||||
def mpc_zeta(s, prec, rnd=round_down, alt=0, force=False):
|
||||
re, im = s
|
||||
if im == fzero:
|
||||
return mpf_zeta(re, prec, rnd, alt), fzero
|
||||
@@ -1121,10 +1121,10 @@ def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False):
|
||||
q = mpc_sub(mpc_one, mpc_pow(mpc_two, r, wp), wp)
|
||||
return mpc_div((tre, tim), q, prec, rnd)
|
||||
|
||||
def mpf_altzeta(s, prec, rnd=round_fast):
|
||||
def mpf_altzeta(s, prec, rnd=round_down):
|
||||
return mpf_zeta(s, prec, rnd, 1)
|
||||
|
||||
def mpc_altzeta(s, prec, rnd=round_fast):
|
||||
def mpc_altzeta(s, prec, rnd=round_down):
|
||||
return mpc_zeta(s, prec, rnd, 1)
|
||||
|
||||
# Not optimized currently
|
||||
@@ -1678,7 +1678,7 @@ def complex_stirling_series(x, y, prec):
|
||||
return sre, sim
|
||||
|
||||
|
||||
def mpf_gamma(x, prec, rnd=round_fast, type=0):
|
||||
def mpf_gamma(x, prec, rnd=round_down, type=0):
|
||||
"""
|
||||
This function implements multipurpose evaluation of the gamma
|
||||
function, G(x), as well as the following versions of the same:
|
||||
@@ -1884,7 +1884,7 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
|
||||
return mpf_pos(w, prec, rnd)
|
||||
|
||||
|
||||
def mpc_gamma(z, prec, rnd=round_fast, type=0):
|
||||
def mpc_gamma(z, prec, rnd=round_down, type=0):
|
||||
a, b = z
|
||||
asign, aman, aexp, abc = a
|
||||
bsign, bman, bexp, bbc = b
|
||||
@@ -2118,25 +2118,25 @@ def mpc_gamma(z, prec, rnd=round_fast, type=0):
|
||||
if type == 3:
|
||||
return mpc_pos(y, prec, rnd)
|
||||
|
||||
def mpf_factorial(x, prec, rnd=round_fast):
|
||||
def mpf_factorial(x, prec, rnd=round_down):
|
||||
return mpf_gamma(x, prec, rnd, 1)
|
||||
|
||||
def mpc_factorial(x, prec, rnd=round_fast):
|
||||
def mpc_factorial(x, prec, rnd=round_down):
|
||||
return mpc_gamma(x, prec, rnd, 1)
|
||||
|
||||
def mpf_rgamma(x, prec, rnd=round_fast):
|
||||
def mpf_rgamma(x, prec, rnd=round_down):
|
||||
return mpf_gamma(x, prec, rnd, 2)
|
||||
|
||||
def mpc_rgamma(x, prec, rnd=round_fast):
|
||||
def mpc_rgamma(x, prec, rnd=round_down):
|
||||
return mpc_gamma(x, prec, rnd, 2)
|
||||
|
||||
def mpf_loggamma(x, prec, rnd=round_fast):
|
||||
def mpf_loggamma(x, prec, rnd=round_down):
|
||||
sign, man, exp, bc = x
|
||||
if sign:
|
||||
raise ComplexResult
|
||||
return mpf_gamma(x, prec, rnd, 3)
|
||||
|
||||
def mpc_loggamma(z, prec, rnd=round_fast):
|
||||
def mpc_loggamma(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
asign, aman, aexp, abc = a
|
||||
bsign, bman, bexp, bbc = b
|
||||
@@ -2147,7 +2147,7 @@ def mpc_loggamma(z, prec, rnd=round_fast):
|
||||
return re, im
|
||||
return mpc_gamma(z, prec, rnd, 3)
|
||||
|
||||
def mpf_gamma_int(n, prec, rnd=round_fast):
|
||||
def mpf_gamma_int(n, prec, rnd=round_down):
|
||||
if n < SMALL_FACTORIAL_CACHE_SIZE:
|
||||
return mpf_pos(small_factorial_cache[n-1], prec, rnd)
|
||||
return mpf_gamma(from_int(n), prec, rnd)
|
||||
|
||||
+44
-44
@@ -21,7 +21,7 @@ from .libmpf import (ComplexResult, bctable, finf, fnan, fninf, fnone, fone,
|
||||
mpf_add, mpf_cmp, mpf_div, mpf_mul, mpf_mul_int, mpf_neg,
|
||||
mpf_perturb, mpf_pos, mpf_pow_int, mpf_rdiv_int,
|
||||
mpf_shift, mpf_sign, mpf_sqrt, mpf_sub, negative_rnd,
|
||||
normalize, reciprocal_rnd, round_ceiling, round_fast,
|
||||
normalize, reciprocal_rnd, round_ceiling, round_down,
|
||||
round_up, to_fixed, to_int)
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ for k in range(1, LOG_TAYLOR_PREC.bit_length()+1):
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
|
||||
def constant_memo(f):
|
||||
"""
|
||||
Decorator for caching computed values of mathematical
|
||||
@@ -84,16 +85,15 @@ def constant_memo(f):
|
||||
function taking a single argument prec as input and
|
||||
returning a fixed-point value with the given precision.
|
||||
"""
|
||||
f.memo_prec = -1
|
||||
f.memo_val = None
|
||||
f._prec_val = -1, None
|
||||
def g(prec, **kwargs):
|
||||
memo_prec = f.memo_prec
|
||||
memo_prec, memo_val = f._prec_val
|
||||
if prec <= memo_prec:
|
||||
return f.memo_val >> (memo_prec-prec)
|
||||
newprec = int(prec*1.05+10)
|
||||
f.memo_val = f(newprec, **kwargs)
|
||||
f.memo_prec = newprec
|
||||
return f.memo_val >> (newprec-prec)
|
||||
return memo_val >> (memo_prec-prec)
|
||||
memo_prec = int(prec*1.05+10)
|
||||
memo_val = f(memo_prec, **kwargs)
|
||||
f._prec_val = memo_prec, memo_val
|
||||
return memo_val >> (memo_prec-prec)
|
||||
g.__name__ = f.__name__
|
||||
g.__doc__ = f.__doc__
|
||||
return g
|
||||
@@ -106,7 +106,7 @@ def def_mpf_constant(fixed):
|
||||
Assumptions: the constant is positive and has magnitude ~= 1;
|
||||
the fixed-point function rounds to floor.
|
||||
"""
|
||||
def f(prec, rnd=round_fast):
|
||||
def f(prec, rnd=round_down):
|
||||
wp = prec + 20
|
||||
v = fixed(wp)
|
||||
if rnd in (round_up, round_ceiling):
|
||||
@@ -310,7 +310,7 @@ mpf_ln_sqrt2pi = def_mpf_constant(ln_sqrt2pi_fixed)
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def mpf_pow(s, t, prec, rnd=round_fast):
|
||||
def mpf_pow(s, t, prec, rnd=round_down):
|
||||
"""
|
||||
Compute s**t. Raises ComplexResult if s is negative and t is
|
||||
fractional.
|
||||
@@ -416,7 +416,7 @@ def nthroot_fixed(y, n, prec, exp1):
|
||||
prevp = p
|
||||
return r
|
||||
|
||||
def mpf_nthroot(s, n, prec, rnd=round_fast):
|
||||
def mpf_nthroot(s, n, prec, rnd=round_down):
|
||||
"""nth-root of a positive number
|
||||
|
||||
Use the Newton method when faster, otherwise use x**(1/n)
|
||||
@@ -499,7 +499,7 @@ def mpf_nthroot(s, n, prec, rnd=round_fast):
|
||||
else:
|
||||
return s
|
||||
|
||||
def mpf_cbrt(s, prec, rnd=round_fast):
|
||||
def mpf_cbrt(s, prec, rnd=round_down):
|
||||
"""cubic root of a positive number"""
|
||||
return mpf_nthroot(s, 3, prec, rnd)
|
||||
|
||||
@@ -656,7 +656,7 @@ def log_taylor_cached(x, prec):
|
||||
s = (s0+s1) << 1
|
||||
return log_a + s
|
||||
|
||||
def mpf_ln(x, prec, rnd=round_fast):
|
||||
def mpf_ln(x, prec, rnd=round_down):
|
||||
"""
|
||||
Compute the natural logarithm of the mpf value x. If x is negative,
|
||||
ComplexResult is raised.
|
||||
@@ -737,20 +737,20 @@ def mpf_ln(x, prec, rnd=round_fast):
|
||||
m -= n*ln2_fixed(wp)
|
||||
return from_man_exp(m, -wp, prec, rnd)
|
||||
|
||||
def mpf_log(x, prec, rnd=round_fast):
|
||||
warnings.warn("mpf_log is deprecated, use mpf_ln",
|
||||
DeprecationWarning)
|
||||
return mpf_ln(x, prec, rnd)
|
||||
mpf_log = mpf_ln # deprecated alias
|
||||
|
||||
def mpf_log1p(x, prec, rnd=round_fast):
|
||||
def mpf_log1p(x, prec, rnd=round_down):
|
||||
"""
|
||||
Computes log(1+x) accurately.
|
||||
"""
|
||||
wp = prec + 10
|
||||
u = mpf_add(fone, x, wp*2)
|
||||
return mpf_mul(mpf_ln(u, wp),
|
||||
mpf_div(x, mpf_sub(u, fone, wp),
|
||||
wp), prec, rnd)
|
||||
wp = prec + 20
|
||||
wp2 = wp*2
|
||||
_, man, exp, bc = x
|
||||
if exp + bc < -wp and (man or exp):
|
||||
# x - x**2/2
|
||||
x2 = mpf_sub(fone, mpf_shift(x, -1), wp2, rnd)
|
||||
return mpf_mul(x, x2, wp, rnd)
|
||||
return mpf_ln(mpf_add(fone, x, wp2), wp, rnd)
|
||||
|
||||
def mpf_log_hypot(a, b, prec, rnd):
|
||||
"""
|
||||
@@ -854,7 +854,7 @@ def atan_inf(sign, prec, rnd):
|
||||
return mpf_shift(mpf_pi(prec, rnd), -1)
|
||||
return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1))
|
||||
|
||||
def mpf_atan(x, prec, rnd=round_fast):
|
||||
def mpf_atan(x, prec, rnd=round_down):
|
||||
sign, man, exp, bc = x
|
||||
if not man:
|
||||
if x == fzero: return fzero
|
||||
@@ -889,7 +889,7 @@ def mpf_atan(x, prec, rnd=round_fast):
|
||||
return from_man_exp(a, -wp, prec, rnd)
|
||||
|
||||
# TODO: cleanup the special cases
|
||||
def mpf_atan2(y, x, prec, rnd=round_fast):
|
||||
def mpf_atan2(y, x, prec, rnd=round_down):
|
||||
xsign, xman, xexp, xbc = x
|
||||
ysign, yman, yexp, ybc = y
|
||||
if not yman:
|
||||
@@ -934,7 +934,7 @@ def mpf_atan2(y, x, prec, rnd=round_fast):
|
||||
else:
|
||||
return mpf_pos(tquo, prec, rnd)
|
||||
|
||||
def mpf_asin(x, prec, rnd=round_fast):
|
||||
def mpf_asin(x, prec, rnd=round_down):
|
||||
sign, man, exp, bc = x
|
||||
if bc+exp > 0 and x not in (fone, fnone):
|
||||
raise ComplexResult("asin(x) is real only for -1 <= x <= 1")
|
||||
@@ -945,7 +945,7 @@ def mpf_asin(x, prec, rnd=round_fast):
|
||||
c = mpf_div(x, b, wp)
|
||||
return mpf_shift(mpf_atan(c, prec, rnd), 1)
|
||||
|
||||
def mpf_acos(x, prec, rnd=round_fast):
|
||||
def mpf_acos(x, prec, rnd=round_down):
|
||||
# acos(x) = 2*atan(sqrt(1-x**2)/(1+x))
|
||||
sign, man, exp, bc = x
|
||||
if bc + exp > 0:
|
||||
@@ -959,7 +959,7 @@ def mpf_acos(x, prec, rnd=round_fast):
|
||||
c = mpf_div(b, mpf_add(fone, x, wp), wp)
|
||||
return mpf_shift(mpf_atan(c, prec, rnd), 1)
|
||||
|
||||
def mpf_asinh(x, prec, rnd=round_fast):
|
||||
def mpf_asinh(x, prec, rnd=round_down):
|
||||
wp = prec + 20
|
||||
sign, man, exp, bc = x
|
||||
mag = exp+bc
|
||||
@@ -976,7 +976,7 @@ def mpf_asinh(x, prec, rnd=round_fast):
|
||||
else:
|
||||
return mpf_ln(q, prec, rnd)
|
||||
|
||||
def mpf_acosh(x, prec, rnd=round_fast):
|
||||
def mpf_acosh(x, prec, rnd=round_down):
|
||||
# acosh(x) = log(x+sqrt(x**2-1))
|
||||
wp = prec + 15
|
||||
if mpf_cmp(x, fone) == -1:
|
||||
@@ -984,7 +984,7 @@ def mpf_acosh(x, prec, rnd=round_fast):
|
||||
q = mpf_sqrt(mpf_add(mpf_mul(x,x), fnone, wp), wp)
|
||||
return mpf_ln(mpf_add(x, q, wp), prec, rnd)
|
||||
|
||||
def mpf_atanh(x, prec, rnd=round_fast):
|
||||
def mpf_atanh(x, prec, rnd=round_down):
|
||||
# atanh(x) = log((1+x)/(1-x))/2
|
||||
sign, man, exp, bc = x
|
||||
if (not man) and exp:
|
||||
@@ -1005,7 +1005,7 @@ def mpf_atanh(x, prec, rnd=round_fast):
|
||||
b = mpf_sub(fone, x, wp)
|
||||
return mpf_shift(mpf_ln(mpf_div(a, b, wp), prec, rnd), -1)
|
||||
|
||||
def mpf_fibonacci(x, prec, rnd=round_fast):
|
||||
def mpf_fibonacci(x, prec, rnd=round_down):
|
||||
sign, man, exp, bc = x
|
||||
if not man:
|
||||
if x == fninf:
|
||||
@@ -1173,7 +1173,7 @@ def cos_sin_basecase(x, prec):
|
||||
a //= k; sin += a; k += 1; a = -((a*x) >> prec)
|
||||
return ((cos*cos_t-sin*sin_t) >> prec), ((sin*cos_t+cos*sin_t) >> prec)
|
||||
|
||||
def mpf_exp(x, prec, rnd=round_fast):
|
||||
def mpf_exp(x, prec, rnd=round_down):
|
||||
sign, man, exp, bc = x
|
||||
if man:
|
||||
mag = bc + exp
|
||||
@@ -1218,7 +1218,7 @@ def mpf_exp(x, prec, rnd=round_fast):
|
||||
return x
|
||||
|
||||
|
||||
def mpf_cosh_sinh(x, prec, rnd=round_fast, tanh=0):
|
||||
def mpf_cosh_sinh(x, prec, rnd=round_down, tanh=0):
|
||||
"""Simultaneously compute (cosh(x), sinh(x)) for real x"""
|
||||
sign, man, exp, bc = x
|
||||
if (not man) and exp:
|
||||
@@ -1321,7 +1321,7 @@ def mod_pi2(man, exp, mag, wp):
|
||||
return t, n, wp
|
||||
|
||||
|
||||
def mpf_cos_sin(x, prec, rnd=round_fast, which=0, pi=False):
|
||||
def mpf_cos_sin(x, prec, rnd=round_down, which=0, pi=False):
|
||||
"""
|
||||
which:
|
||||
0 -- return cos(x), sin(x)
|
||||
@@ -1400,15 +1400,15 @@ def mpf_cos_sin(x, prec, rnd=round_fast, which=0, pi=False):
|
||||
if which == 3:
|
||||
return from_rational(s, c, prec, rnd)
|
||||
|
||||
def mpf_cos(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1)
|
||||
def mpf_sin(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2)
|
||||
def mpf_tan(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 3)
|
||||
def mpf_cos_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 0, 1)
|
||||
def mpf_cos_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1, 1)
|
||||
def mpf_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2, 1)
|
||||
def mpf_cosh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[0]
|
||||
def mpf_sinh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[1]
|
||||
def mpf_tanh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd, tanh=1)
|
||||
def mpf_cos(x, prec, rnd=round_down): return mpf_cos_sin(x, prec, rnd, 1)
|
||||
def mpf_sin(x, prec, rnd=round_down): return mpf_cos_sin(x, prec, rnd, 2)
|
||||
def mpf_tan(x, prec, rnd=round_down): return mpf_cos_sin(x, prec, rnd, 3)
|
||||
def mpf_cos_sin_pi(x, prec, rnd=round_down): return mpf_cos_sin(x, prec, rnd, 0, 1)
|
||||
def mpf_cos_pi(x, prec, rnd=round_down): return mpf_cos_sin(x, prec, rnd, 1, 1)
|
||||
def mpf_sin_pi(x, prec, rnd=round_down): return mpf_cos_sin(x, prec, rnd, 2, 1)
|
||||
def mpf_cosh(x, prec, rnd=round_down): return mpf_cosh_sinh(x, prec, rnd)[0]
|
||||
def mpf_sinh(x, prec, rnd=round_down): return mpf_cosh_sinh(x, prec, rnd)[1]
|
||||
def mpf_tanh(x, prec, rnd=round_down): return mpf_cosh_sinh(x, prec, rnd, tanh=1)
|
||||
|
||||
|
||||
# Low-overhead fixed-point versions
|
||||
|
||||
+23
-23
@@ -20,7 +20,7 @@ from .libmpf import (ComplexResult, finf, fnan, fninf, fnone, fone, from_int,
|
||||
from_man_exp, from_rational, ftwo, fzero, mpf_abs,
|
||||
mpf_add, mpf_div, mpf_le, mpf_lt, mpf_min_max, mpf_mul,
|
||||
mpf_neg, mpf_perturb, mpf_pos, mpf_pow_int, mpf_shift,
|
||||
mpf_sign, mpf_sqrt, mpf_sub, negative_rnd, round_fast,
|
||||
mpf_sign, mpf_sqrt, mpf_sub, negative_rnd, round_down,
|
||||
to_fixed, to_int)
|
||||
|
||||
|
||||
@@ -300,7 +300,7 @@ def make_hyp_summator(key):
|
||||
# TODO: mpf_erf should call mpf_erfc when appropriate (currently
|
||||
# only the converse delegation is implemented)
|
||||
|
||||
def mpf_erf(x, prec, rnd=round_fast):
|
||||
def mpf_erf(x, prec, rnd=round_down):
|
||||
sign, man, exp, bc = x
|
||||
if not man:
|
||||
if x == fzero: return fzero
|
||||
@@ -351,7 +351,7 @@ def erfc_check_series(x, prec):
|
||||
return True
|
||||
return False
|
||||
|
||||
def mpf_erfc(x, prec, rnd=round_fast):
|
||||
def mpf_erfc(x, prec, rnd=round_down):
|
||||
sign, man, exp, bc = x
|
||||
if not man:
|
||||
if x == fzero: return fone
|
||||
@@ -449,7 +449,7 @@ def complex_ei_asymptotic(zre, zim, prec):
|
||||
raise NoConvergence
|
||||
return sre, sim
|
||||
|
||||
def mpf_ei(x, prec, rnd=round_fast, e1=False):
|
||||
def mpf_ei(x, prec, rnd=round_down, e1=False):
|
||||
if e1:
|
||||
x = mpf_neg(x)
|
||||
sign, man, exp, bc = x
|
||||
@@ -491,7 +491,7 @@ def mpf_ei(x, prec, rnd=round_fast, e1=False):
|
||||
v = mpf_neg(v)
|
||||
return v
|
||||
|
||||
def mpc_ei(z, prec, rnd=round_fast, e1=False):
|
||||
def mpc_ei(z, prec, rnd=round_down, e1=False):
|
||||
if e1:
|
||||
z = mpc_neg(z)
|
||||
a, b = z
|
||||
@@ -556,13 +556,13 @@ def mpc_ei(z, prec, rnd=round_fast, e1=False):
|
||||
v = mpc_neg(v)
|
||||
return v
|
||||
|
||||
def mpf_e1(x, prec, rnd=round_fast):
|
||||
def mpf_e1(x, prec, rnd=round_down):
|
||||
return mpf_ei(x, prec, rnd, True)
|
||||
|
||||
def mpc_e1(x, prec, rnd=round_fast):
|
||||
def mpc_e1(x, prec, rnd=round_down):
|
||||
return mpc_ei(x, prec, rnd, True)
|
||||
|
||||
def mpf_expint(n, x, prec, rnd=round_fast, gamma=False):
|
||||
def mpf_expint(n, x, prec, rnd=round_down, gamma=False):
|
||||
"""
|
||||
E_n(x), n an integer, x real
|
||||
|
||||
@@ -728,7 +728,7 @@ def mpc_ci_si_taylor(re, im, wp, which=0):
|
||||
k += 2
|
||||
return from_man_exp(sre, -wp), from_man_exp(sim, -wp)
|
||||
|
||||
def mpf_ci_si(x, prec, rnd=round_fast, which=2):
|
||||
def mpf_ci_si(x, prec, rnd=round_down, which=2):
|
||||
"""
|
||||
Calculation of Ci(x), Si(x) for real x.
|
||||
|
||||
@@ -821,15 +821,15 @@ def mpf_ci_si(x, prec, rnd=round_fast, which=2):
|
||||
ci = mpf_sub(mpf_mul(sin, s1), mpf_mul(cos, s2), prec, rnd)
|
||||
return ci, si
|
||||
|
||||
def mpf_ci(x, prec, rnd=round_fast):
|
||||
def mpf_ci(x, prec, rnd=round_down):
|
||||
if mpf_sign(x) < 0:
|
||||
raise ComplexResult
|
||||
return mpf_ci_si(x, prec, rnd, 0)[0]
|
||||
|
||||
def mpf_si(x, prec, rnd=round_fast):
|
||||
def mpf_si(x, prec, rnd=round_down):
|
||||
return mpf_ci_si(x, prec, rnd, 1)[1]
|
||||
|
||||
def mpc_ci(z, prec, rnd=round_fast):
|
||||
def mpc_ci(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
if im == fzero:
|
||||
ci = mpf_ci_si(re, prec, rnd, 0)[0]
|
||||
@@ -842,7 +842,7 @@ def mpc_ci(z, prec, rnd=round_fast):
|
||||
ci = mpc_add((cre, cim), mpc_ln(z, wp), prec, rnd)
|
||||
return ci
|
||||
|
||||
def mpc_si(z, prec, rnd=round_fast):
|
||||
def mpc_si(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
if im == fzero:
|
||||
return (mpf_ci_si(re, prec, rnd, 1)[1], fzero)
|
||||
@@ -882,7 +882,7 @@ def mpc_si(z, prec, rnd=round_fast):
|
||||
# TODO: recompute at higher precision if the fixed-point mantissa
|
||||
# is very small
|
||||
|
||||
def mpf_besseljn(n, x, prec, rnd=round_fast):
|
||||
def mpf_besseljn(n, x, prec, rnd=round_down):
|
||||
prec += 50
|
||||
negate = n < 0 and n & 1
|
||||
mag = x[2]+x[3]
|
||||
@@ -905,7 +905,7 @@ def mpf_besseljn(n, x, prec, rnd=round_fast):
|
||||
s = -s
|
||||
return from_man_exp(s, -wp, prec, rnd)
|
||||
|
||||
def mpc_besseljn(n, z, prec, rnd=round_fast):
|
||||
def mpc_besseljn(n, z, prec, rnd=round_down):
|
||||
negate = n < 0 and n & 1
|
||||
n = abs(n)
|
||||
origprec = prec
|
||||
@@ -941,7 +941,7 @@ def mpc_besseljn(n, z, prec, rnd=round_fast):
|
||||
im = from_man_exp(sim, -prec, origprec, rnd)
|
||||
return (re, im)
|
||||
|
||||
def mpf_agm(a, b, prec, rnd=round_fast):
|
||||
def mpf_agm(a, b, prec, rnd=round_down):
|
||||
"""
|
||||
Computes the arithmetic-geometric mean agm(a,b) for
|
||||
nonnegative mpf values a, b.
|
||||
@@ -1000,14 +1000,14 @@ def mpf_agm(a, b, prec, rnd=round_fast):
|
||||
g = agm_fixed(af, bf, wp)
|
||||
return from_man_exp(g, -wp-n, prec, rnd)
|
||||
|
||||
def mpf_agm1(a, prec, rnd=round_fast):
|
||||
def mpf_agm1(a, prec, rnd=round_down):
|
||||
"""
|
||||
Computes the arithmetic-geometric mean agm(1,a) for a nonnegative
|
||||
mpf value a.
|
||||
"""
|
||||
return mpf_agm(fone, a, prec, rnd)
|
||||
|
||||
def mpc_agm(a, b, prec, rnd=round_fast):
|
||||
def mpc_agm(a, b, prec, rnd=round_down):
|
||||
"""
|
||||
Complex AGM.
|
||||
|
||||
@@ -1033,10 +1033,10 @@ def mpc_agm(a, b, prec, rnd=round_fast):
|
||||
if size == fzero or mpf_lt(err, mpf_mul(eps, size)):
|
||||
return a
|
||||
|
||||
def mpc_agm1(a, prec, rnd=round_fast):
|
||||
def mpc_agm1(a, prec, rnd=round_down):
|
||||
return mpc_agm(mpc_one, a, prec, rnd)
|
||||
|
||||
def mpf_ellipk(x, prec, rnd=round_fast):
|
||||
def mpf_ellipk(x, prec, rnd=round_down):
|
||||
if not x[1]:
|
||||
if x == fzero:
|
||||
return mpf_shift(mpf_pi(prec, rnd), -1)
|
||||
@@ -1056,7 +1056,7 @@ def mpf_ellipk(x, prec, rnd=round_fast):
|
||||
r = mpf_div(mpf_pi(wp), v, prec, rnd)
|
||||
return mpf_shift(r, -1)
|
||||
|
||||
def mpc_ellipk(z, prec, rnd=round_fast):
|
||||
def mpc_ellipk(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
if im == fzero:
|
||||
if re == finf:
|
||||
@@ -1069,7 +1069,7 @@ def mpc_ellipk(z, prec, rnd=round_fast):
|
||||
r = mpc_mpf_div(mpf_pi(wp), v, prec, rnd)
|
||||
return mpc_shift(r, -1)
|
||||
|
||||
def mpf_ellipe(x, prec, rnd=round_fast):
|
||||
def mpf_ellipe(x, prec, rnd=round_down):
|
||||
# http://functions.wolfram.com/EllipticIntegrals/
|
||||
# EllipticK/20/01/0001/
|
||||
# E = (1-m)*(K'(m)*2*m + K(m))
|
||||
@@ -1099,7 +1099,7 @@ def mpf_ellipe(x, prec, rnd=round_fast):
|
||||
b = mpf_mul(Kdiff, mpf_shift(x,1), wp)
|
||||
return mpf_mul(t, mpf_add(K, b), prec, rnd)
|
||||
|
||||
def mpc_ellipe(z, prec, rnd=round_fast):
|
||||
def mpc_ellipe(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
if im == fzero:
|
||||
if re == finf:
|
||||
|
||||
+39
-23
@@ -8,16 +8,11 @@ here from settings.py
|
||||
|
||||
import math
|
||||
import sys
|
||||
import warnings
|
||||
from functools import lru_cache
|
||||
|
||||
from .backend import MPZ, MPZ_ONE, MPZ_ZERO, gmpy
|
||||
|
||||
|
||||
small_trailing = [0] * 256
|
||||
for j in range(1,8):
|
||||
small_trailing[1<<j::1<<(j+1)] = [j] * (1<<(7-j))
|
||||
|
||||
def giant_steps(start, target, n=2):
|
||||
"""
|
||||
Return a list of integers ~=
|
||||
@@ -58,30 +53,13 @@ def lshift(x, n):
|
||||
|
||||
def trailing(n):
|
||||
"""Count the number of trailing zero bits in abs(n)."""
|
||||
if not n:
|
||||
return 0
|
||||
low_byte = n & 0xff
|
||||
if low_byte:
|
||||
return small_trailing[low_byte]
|
||||
t = 8
|
||||
n >>= 8
|
||||
while not n & 0xff:
|
||||
n >>= 8
|
||||
t += 8
|
||||
return t + small_trailing[n & 0xff]
|
||||
|
||||
def bitcount(n):
|
||||
"""Calculate bit size of abs(n)."""
|
||||
warnings.warn("bitcount function is deprecated",
|
||||
DeprecationWarning)
|
||||
return MPZ(n).bit_length()
|
||||
return MPZ((n & (-n)).bit_length() - 1 if n else 0)
|
||||
|
||||
if gmpy and hasattr(MPZ, 'bit_scan1'):
|
||||
def trailing(n):
|
||||
return MPZ(n).bit_scan1() if n else MPZ(0)
|
||||
|
||||
# Used to avoid slow function calls as far as possible
|
||||
trailtable = [trailing(n) for n in range(256)]
|
||||
bctable = [n.bit_length() for n in range(1024)]
|
||||
|
||||
# TODO: speed up for bases 2, 4, 8, 16, ...
|
||||
@@ -505,3 +483,41 @@ def stirling2(n, k):
|
||||
s += t * MPZ(j)**n
|
||||
t = t * (k - j) // (j + 1)
|
||||
return s // ifac(k)
|
||||
|
||||
def jacobi_symbol(m, n):
|
||||
"""Returns the Jacobi symbol (m / n)."""
|
||||
m, n = MPZ(m), MPZ(n)
|
||||
if not n % 2:
|
||||
raise ValueError('n should be an odd integer')
|
||||
if n < 0:
|
||||
return jacobi_symbol(m, -n)*(MPZ(-1) if m < 0 else MPZ_ONE)
|
||||
if m < 0 or m > n:
|
||||
m = m % n
|
||||
if not m:
|
||||
return MPZ(n == 1)
|
||||
if n == 1 or m == 1:
|
||||
return MPZ_ONE
|
||||
if math.gcd(m, n) != 1:
|
||||
return MPZ_ZERO
|
||||
|
||||
j = MPZ_ONE
|
||||
s = trailing(m)
|
||||
m = m >> s
|
||||
if s % 2 and n % 8 in [3, 5]:
|
||||
j *= -1
|
||||
|
||||
while m != 1:
|
||||
if m % 4 == 3 and n % 4 == 3:
|
||||
j *= -1
|
||||
m, n = n % m, m
|
||||
s = trailing(m)
|
||||
m = m >> s
|
||||
if s % 2 and n % 8 in [3, 5]:
|
||||
j *= -1
|
||||
return j
|
||||
|
||||
if gmpy and hasattr(gmpy, 'jacobi'):
|
||||
def jacobi_symbol(m, n):
|
||||
if n < 0:
|
||||
return gmpy.jacobi(m, -n)*(MPZ(-1) if m < 0 else MPZ_ONE)
|
||||
return gmpy.jacobi(m, n)
|
||||
|
||||
+56
-60
@@ -3,7 +3,6 @@ Low-level functions for complex arithmetic.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from .backend import MPZ
|
||||
from .libelefun import (mpf_acos, mpf_acosh, mpf_asin, mpf_atan, mpf_atan2,
|
||||
@@ -18,7 +17,7 @@ from .libmpf import (ComplexResult, fhalf, finf, fnan, fninf, fnone, fone,
|
||||
mpf_add, mpf_ceil, mpf_div, mpf_floor, mpf_frac, mpf_hash,
|
||||
mpf_hypot, mpf_mul, mpf_mul_int, mpf_neg, mpf_nint,
|
||||
mpf_pos, mpf_rdiv_int, mpf_shift, mpf_sqrt, mpf_sub,
|
||||
normalize, reciprocal_rnd, round_fast, round_floor,
|
||||
normalize, reciprocal_rnd, round_down, round_floor,
|
||||
to_fixed, to_float, to_int, to_str)
|
||||
|
||||
|
||||
@@ -53,7 +52,7 @@ def mpc_to_str(z, dps, **kwargs):
|
||||
else:
|
||||
return rs + " + " + to_str(im, dps, **kwargs) + "j"
|
||||
|
||||
def mpc_to_complex(z, strict=False, rnd=round_fast):
|
||||
def mpc_to_complex(z, strict=False, rnd=round_down):
|
||||
re, im = z
|
||||
return complex(to_float(re, strict, rnd), to_float(im, strict, rnd))
|
||||
|
||||
@@ -64,40 +63,40 @@ def mpc_hash(z):
|
||||
h = -2
|
||||
return int(h)
|
||||
|
||||
def mpc_conjugate(z, prec, rnd=round_fast):
|
||||
def mpc_conjugate(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
return re, mpf_neg(im, prec, rnd)
|
||||
|
||||
def mpc_is_nonzero(z):
|
||||
return z != mpc_zero
|
||||
|
||||
def mpc_add(z, w, prec, rnd=round_fast):
|
||||
def mpc_add(z, w, prec, rnd=round_down):
|
||||
a, b = z
|
||||
c, d = w
|
||||
return mpf_add(a, c, prec, rnd), mpf_add(b, d, prec, rnd)
|
||||
|
||||
def mpc_add_mpf(z, x, prec, rnd=round_fast):
|
||||
def mpc_add_mpf(z, x, prec, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_add(a, x, prec, rnd), b
|
||||
|
||||
def mpc_sub(z, w, prec=0, rnd=round_fast):
|
||||
def mpc_sub(z, w, prec=0, rnd=round_down):
|
||||
a, b = z
|
||||
c, d = w
|
||||
return mpf_sub(a, c, prec, rnd), mpf_sub(b, d, prec, rnd)
|
||||
|
||||
def mpc_sub_mpf(z, p, prec=0, rnd=round_fast):
|
||||
def mpc_sub_mpf(z, p, prec=0, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_sub(a, p, prec, rnd), b
|
||||
|
||||
def mpc_mpf_sub(p, z, prec=0, rnd=round_fast):
|
||||
def mpc_mpf_sub(p, z, prec=0, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_sub(p, a, prec, rnd), mpf_neg(b, prec, rnd)
|
||||
|
||||
def mpc_pos(z, prec, rnd=round_fast):
|
||||
def mpc_pos(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_pos(a, prec, rnd), mpf_pos(b, prec, rnd)
|
||||
|
||||
def mpc_neg(z, prec=0, rnd=round_fast):
|
||||
def mpc_neg(z, prec=0, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_neg(a, prec, rnd), mpf_neg(b, prec, rnd)
|
||||
|
||||
@@ -105,35 +104,35 @@ def mpc_shift(z, n):
|
||||
a, b = z
|
||||
return mpf_shift(a, n), mpf_shift(b, n)
|
||||
|
||||
def mpc_abs(z, prec, rnd=round_fast):
|
||||
def mpc_abs(z, prec, rnd=round_down):
|
||||
"""Absolute value of a complex number, |a+bi|.
|
||||
Returns an mpf value."""
|
||||
a, b = z
|
||||
return mpf_hypot(a, b, prec, rnd)
|
||||
|
||||
def mpc_arg(z, prec, rnd=round_fast):
|
||||
def mpc_arg(z, prec, rnd=round_down):
|
||||
"""Argument of a complex number. Returns an mpf value."""
|
||||
a, b = z
|
||||
return mpf_atan2(b, a, prec, rnd)
|
||||
|
||||
def mpc_floor(z, prec, rnd=round_fast):
|
||||
def mpc_floor(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_floor(a, prec, rnd), mpf_floor(b, prec, rnd)
|
||||
|
||||
def mpc_ceil(z, prec, rnd=round_fast):
|
||||
def mpc_ceil(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_ceil(a, prec, rnd), mpf_ceil(b, prec, rnd)
|
||||
|
||||
def mpc_nint(z, prec, rnd=round_fast):
|
||||
def mpc_nint(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_nint(a, prec, rnd), mpf_nint(b, prec, rnd)
|
||||
|
||||
def mpc_frac(z, prec, rnd=round_fast):
|
||||
def mpc_frac(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
return mpf_frac(a, prec, rnd), mpf_frac(b, prec, rnd)
|
||||
|
||||
|
||||
def mpc_mul(z, w, prec, rnd=round_fast):
|
||||
def mpc_mul(z, w, prec, rnd=round_down):
|
||||
"""
|
||||
Complex multiplication.
|
||||
|
||||
@@ -151,7 +150,7 @@ def mpc_mul(z, w, prec, rnd=round_fast):
|
||||
im = mpf_add(r, s, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_square(z, prec, rnd=round_fast):
|
||||
def mpc_square(z, prec, rnd=round_down):
|
||||
# (a+b*I)**2 == a**2 - b**2 + 2*I*a*b
|
||||
a, b = z
|
||||
p = mpf_mul(a,a)
|
||||
@@ -161,19 +160,19 @@ def mpc_square(z, prec, rnd=round_fast):
|
||||
im = mpf_shift(r, 1)
|
||||
return re, im
|
||||
|
||||
def mpc_mul_mpf(z, p, prec, rnd=round_fast):
|
||||
def mpc_mul_mpf(z, p, prec, rnd=round_down):
|
||||
a, b = z
|
||||
re = mpf_mul(a, p, prec, rnd)
|
||||
im = mpf_mul(b, p, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_mul_int(z, n, prec, rnd=round_fast):
|
||||
def mpc_mul_int(z, n, prec, rnd=round_down):
|
||||
a, b = z
|
||||
re = mpf_mul_int(a, n, prec, rnd)
|
||||
im = mpf_mul_int(b, n, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_div(z, w, prec, rnd=round_fast):
|
||||
def mpc_div(z, w, prec, rnd=round_down):
|
||||
if mpc_is_inf(w) and not mpc_is_infnan(z):
|
||||
return fzero, fzero
|
||||
a, b = z
|
||||
@@ -186,14 +185,14 @@ def mpc_div(z, w, prec, rnd=round_fast):
|
||||
u = mpf_sub(mpf_mul(b,c), mpf_mul(a,d), wp)
|
||||
return mpf_div(t,mag,prec,rnd), mpf_div(u,mag,prec,rnd)
|
||||
|
||||
def mpc_div_mpf(z, p, prec, rnd=round_fast):
|
||||
def mpc_div_mpf(z, p, prec, rnd=round_down):
|
||||
"""Calculate z/p where p is real"""
|
||||
a, b = z
|
||||
re = mpf_div(a, p, prec, rnd)
|
||||
im = mpf_div(b, p, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_reciprocal(z, prec, rnd=round_fast):
|
||||
def mpc_reciprocal(z, prec, rnd=round_down):
|
||||
"""Calculate 1/z efficiently"""
|
||||
if mpc_is_inf(z):
|
||||
return fzero, fzero
|
||||
@@ -203,7 +202,7 @@ def mpc_reciprocal(z, prec, rnd=round_fast):
|
||||
im = mpf_neg(mpf_div(b, m, prec, rnd))
|
||||
return re, im
|
||||
|
||||
def mpc_mpf_div(p, z, prec, rnd=round_fast):
|
||||
def mpc_mpf_div(p, z, prec, rnd=round_down):
|
||||
"""Calculate p/z where p is real efficiently"""
|
||||
if mpc_is_inf(z) and p not in (finf, fninf, fnan):
|
||||
return fzero, fzero
|
||||
@@ -226,12 +225,12 @@ def complex_int_pow(a, b, n):
|
||||
n //= 2
|
||||
return wre, wim
|
||||
|
||||
def mpc_pow(z, w, prec, rnd=round_fast):
|
||||
def mpc_pow(z, w, prec, rnd=round_down):
|
||||
if w[1] == fzero:
|
||||
return mpc_pow_mpf(z, w[0], prec, rnd)
|
||||
return mpc_exp(mpc_mul(mpc_ln(z, prec+10), w, prec+10), prec, rnd)
|
||||
|
||||
def mpc_pow_mpf(z, p, prec, rnd=round_fast):
|
||||
def mpc_pow_mpf(z, p, prec, rnd=round_down):
|
||||
psign, pman, pexp, pbc = p
|
||||
if pexp >= 0:
|
||||
return mpc_pow_int(z, (-1)**psign * (pman<<pexp), prec, rnd)
|
||||
@@ -240,7 +239,7 @@ def mpc_pow_mpf(z, p, prec, rnd=round_fast):
|
||||
return mpc_pow_int(sqrtz, (-1)**psign * pman, prec, rnd)
|
||||
return mpc_exp(mpc_mul_mpf(mpc_ln(z, prec+10), p, prec+10), prec, rnd)
|
||||
|
||||
def mpc_pow_int(z, n, prec, rnd=round_fast):
|
||||
def mpc_pow_int(z, n, prec, rnd=round_down):
|
||||
a, b = z
|
||||
if b == fzero:
|
||||
return mpf_pow_int(a, n, prec, rnd), fzero
|
||||
@@ -267,7 +266,7 @@ def mpc_pow_int(z, n, prec, rnd=round_fast):
|
||||
de = aexp - bexp
|
||||
abs_de = abs(de)
|
||||
exact_size = n*(abs_de + max(abc, bbc))
|
||||
if exact_size < 10000 and min(abc, bbc) >= 0:
|
||||
if exact_size < 10000 and min(abc, bbc) > 0:
|
||||
if de > 0:
|
||||
aman <<= de
|
||||
aexp = bexp
|
||||
@@ -280,7 +279,7 @@ def mpc_pow_int(z, n, prec, rnd=round_fast):
|
||||
return re, im
|
||||
return mpc_exp(mpc_mul_int(mpc_ln(z, prec+10), n, prec+10), prec, rnd)
|
||||
|
||||
def mpc_sqrt(z, prec, rnd=round_fast):
|
||||
def mpc_sqrt(z, prec, rnd=round_down):
|
||||
"""Complex square root (principal branch).
|
||||
|
||||
We have sqrt(a+bi) = sqrt((r+a)/2) + b/sqrt(2*(r+a))*i where
|
||||
@@ -357,7 +356,7 @@ def mpc_nthroot_fixed(a, b, n, prec):
|
||||
prevp = p
|
||||
return re, im
|
||||
|
||||
def mpc_nthroot(z, n, prec, rnd=round_fast):
|
||||
def mpc_nthroot(z, n, prec, rnd=round_down):
|
||||
"""
|
||||
Complex n-th root.
|
||||
|
||||
@@ -398,13 +397,13 @@ def mpc_nthroot(z, n, prec, rnd=round_fast):
|
||||
im = normalize(im[0], im[1], im[2], im[3], prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_cbrt(z, prec, rnd=round_fast):
|
||||
def mpc_cbrt(z, prec, rnd=round_down):
|
||||
"""
|
||||
Complex cubic root.
|
||||
"""
|
||||
return mpc_nthroot(z, 3, prec, rnd)
|
||||
|
||||
def mpc_exp(z, prec, rnd=round_fast):
|
||||
def mpc_exp(z, prec, rnd=round_down):
|
||||
"""
|
||||
Complex exponential function.
|
||||
|
||||
@@ -431,17 +430,14 @@ def mpc_exp(z, prec, rnd=round_fast):
|
||||
im = mpf_mul(mag, s, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_ln(z, prec, rnd=round_fast):
|
||||
def mpc_ln(z, prec, rnd=round_down):
|
||||
re = mpf_log_hypot(z[0], z[1], prec, rnd)
|
||||
im = mpc_arg(z, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_log(x, prec, rnd=round_fast):
|
||||
warnings.warn("mpc_log is deprecated, use mpc_ln",
|
||||
DeprecationWarning)
|
||||
return mpc_ln(x, prec, rnd)
|
||||
mpc_log = mpc_ln # deprecated alias
|
||||
|
||||
def mpc_cos(z, prec, rnd=round_fast):
|
||||
def mpc_cos(z, prec, rnd=round_down):
|
||||
"""Complex cosine. The formula used is cos(a+bi) = cos(a)*cosh(b) -
|
||||
sin(a)*sinh(b)*i.
|
||||
|
||||
@@ -461,7 +457,7 @@ def mpc_cos(z, prec, rnd=round_fast):
|
||||
im = mpf_mul(s, sh, prec, rnd)
|
||||
return re, mpf_neg(im)
|
||||
|
||||
def mpc_sin(z, prec, rnd=round_fast):
|
||||
def mpc_sin(z, prec, rnd=round_down):
|
||||
"""Complex sine. We have sin(a+bi) = sin(a)*cosh(b) +
|
||||
cos(a)*sinh(b)*i. See the docstring for mpc_cos for additional
|
||||
comments."""
|
||||
@@ -477,7 +473,7 @@ def mpc_sin(z, prec, rnd=round_fast):
|
||||
im = mpf_mul(c, sh, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_tan(z, prec, rnd=round_fast):
|
||||
def mpc_tan(z, prec, rnd=round_down):
|
||||
"""Complex tangent. Computed as tan(a+bi) = sin(2a)/M + sinh(2b)/M*i
|
||||
where M = cos(2a) + cosh(2b)."""
|
||||
a, b = z
|
||||
@@ -504,7 +500,7 @@ def mpc_tan(z, prec, rnd=round_fast):
|
||||
im = mpf_div(sh, mag, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_cos_pi(z, prec, rnd=round_fast):
|
||||
def mpc_cos_pi(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
if b == fzero:
|
||||
return mpf_cos_pi(a, prec, rnd), fzero
|
||||
@@ -518,7 +514,7 @@ def mpc_cos_pi(z, prec, rnd=round_fast):
|
||||
im = mpf_mul(s, sh, prec, rnd)
|
||||
return re, mpf_neg(im)
|
||||
|
||||
def mpc_sin_pi(z, prec, rnd=round_fast):
|
||||
def mpc_sin_pi(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
if b == fzero:
|
||||
return mpf_sin_pi(a, prec, rnd), fzero
|
||||
@@ -532,7 +528,7 @@ def mpc_sin_pi(z, prec, rnd=round_fast):
|
||||
im = mpf_mul(c, sh, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpc_cos_sin(z, prec, rnd=round_fast):
|
||||
def mpc_cos_sin(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
if a == fzero:
|
||||
ch, sh = mpf_cosh_sinh(b, prec, rnd)
|
||||
@@ -549,7 +545,7 @@ def mpc_cos_sin(z, prec, rnd=round_fast):
|
||||
sim = mpf_mul(c, sh, prec, rnd)
|
||||
return (cre, mpf_neg(cim)), (sre, sim)
|
||||
|
||||
def mpc_cos_sin_pi(z, prec, rnd=round_fast):
|
||||
def mpc_cos_sin_pi(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
if b == fzero:
|
||||
c, s = mpf_cos_sin_pi(a, prec, rnd)
|
||||
@@ -567,25 +563,25 @@ def mpc_cos_sin_pi(z, prec, rnd=round_fast):
|
||||
sim = mpf_mul(c, sh, prec, rnd)
|
||||
return (cre, mpf_neg(cim)), (sre, sim)
|
||||
|
||||
def mpc_cosh(z, prec, rnd=round_fast):
|
||||
def mpc_cosh(z, prec, rnd=round_down):
|
||||
"""Complex hyperbolic cosine. Computed as cosh(z) = cos(z*i)."""
|
||||
a, b = z
|
||||
return mpc_cos((b, mpf_neg(a)), prec, rnd)
|
||||
|
||||
def mpc_sinh(z, prec, rnd=round_fast):
|
||||
def mpc_sinh(z, prec, rnd=round_down):
|
||||
"""Complex hyperbolic sine. Computed as sinh(z) = -i*sin(z*i)."""
|
||||
a, b = z
|
||||
b, a = mpc_sin((b, a), prec, rnd)
|
||||
return a, b
|
||||
|
||||
def mpc_tanh(z, prec, rnd=round_fast):
|
||||
def mpc_tanh(z, prec, rnd=round_down):
|
||||
"""Complex hyperbolic tangent. Computed as tanh(z) = -i*tan(z*i)."""
|
||||
a, b = z
|
||||
b, a = mpc_tan((b, a), prec, rnd)
|
||||
return a, b
|
||||
|
||||
# TODO: avoid loss of accuracy
|
||||
def mpc_atan(z, prec, rnd=round_fast):
|
||||
def mpc_atan(z, prec, rnd=round_down):
|
||||
a, b = z
|
||||
# atan(z) = (I/2)*(log(1-I*z) - log(1+I*z))
|
||||
# x = 1-I*z = 1 + b - I*a
|
||||
@@ -770,19 +766,19 @@ def acos_asin(z, prec, rnd, n):
|
||||
return fnan, b
|
||||
return re, im
|
||||
|
||||
def mpc_acos(z, prec, rnd=round_fast):
|
||||
def mpc_acos(z, prec, rnd=round_down):
|
||||
return acos_asin(z, prec, rnd, 0)
|
||||
|
||||
def mpc_asin(z, prec, rnd=round_fast):
|
||||
def mpc_asin(z, prec, rnd=round_down):
|
||||
return acos_asin(z, prec, rnd, 1)
|
||||
|
||||
def mpc_asinh(z, prec, rnd=round_fast):
|
||||
def mpc_asinh(z, prec, rnd=round_down):
|
||||
# asinh(z) = I * asin(-I z)
|
||||
a, b = z
|
||||
a, b = mpc_asin((b, mpf_neg(a)), prec, rnd)
|
||||
return mpf_neg(b), a
|
||||
|
||||
def mpc_acosh(z, prec, rnd=round_fast):
|
||||
def mpc_acosh(z, prec, rnd=round_down):
|
||||
# acosh(z) = -I * acos(z) for Im(acos(z)) <= 0
|
||||
# +I * acos(z) otherwise
|
||||
a, b = mpc_acos(z, prec, rnd)
|
||||
@@ -791,7 +787,7 @@ def mpc_acosh(z, prec, rnd=round_fast):
|
||||
else:
|
||||
return b, mpf_neg(a)
|
||||
|
||||
def mpc_atanh(z, prec, rnd=round_fast):
|
||||
def mpc_atanh(z, prec, rnd=round_down):
|
||||
# atanh(z) = (log(1+z)-log(1-z))/2
|
||||
wp = prec + 15
|
||||
a = mpc_add(z, mpc_one, wp)
|
||||
@@ -805,11 +801,11 @@ def mpc_atanh(z, prec, rnd=round_fast):
|
||||
v = (fzero, v[1])
|
||||
return v
|
||||
|
||||
def mpc_fibonacci(z, prec, rnd=round_fast):
|
||||
def mpc_fibonacci(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
if im == fzero:
|
||||
return (mpf_fibonacci(re, prec, rnd), fzero)
|
||||
size = max(abs(re[2]+re[3]), abs(re[2]+re[3]))
|
||||
size = max(abs(re[2]+re[3]), abs(im[2]+im[3]))
|
||||
wp = prec + size + 20
|
||||
a = mpf_phi(wp)
|
||||
b = mpf_add(mpf_shift(a, 1), fnone, wp)
|
||||
@@ -820,10 +816,10 @@ def mpc_fibonacci(z, prec, rnd=round_fast):
|
||||
u = mpc_div_mpf(u, b, prec, rnd)
|
||||
return u
|
||||
|
||||
def mpf_expj(x, prec, rnd=round_floor):
|
||||
def mpf_expj(x, prec, rnd=round_down):
|
||||
raise ComplexResult
|
||||
|
||||
def mpc_expj(z, prec, rnd=round_floor):
|
||||
def mpc_expj(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
if im == fzero:
|
||||
return mpf_cos_sin(re, prec, rnd)
|
||||
@@ -835,10 +831,10 @@ def mpc_expj(z, prec, rnd=round_floor):
|
||||
im = mpf_mul(ey, s, prec, rnd)
|
||||
return re, im
|
||||
|
||||
def mpf_expjpi(x, prec, rnd=round_floor):
|
||||
def mpf_expjpi(x, prec, rnd=round_down):
|
||||
raise ComplexResult
|
||||
|
||||
def mpc_expjpi(z, prec, rnd=round_floor):
|
||||
def mpc_expjpi(z, prec, rnd=round_down):
|
||||
re, im = z
|
||||
if im == fzero:
|
||||
return mpf_cos_sin_pi(re, prec, rnd)
|
||||
|
||||
+311
-179
@@ -3,26 +3,14 @@ Low-level functions for arbitrary-precision floating-point arithmetic.
|
||||
"""
|
||||
|
||||
import math
|
||||
import operator
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from .backend import BACKEND, MPZ, MPZ_FIVE, MPZ_ONE, MPZ_ZERO, gmpy, int_types
|
||||
from .libintmath import (bctable, bin_to_radix, isqrt, numeral, sqrtrem,
|
||||
stddigits, trailtable)
|
||||
|
||||
|
||||
def to_pickable(x):
|
||||
warnings.warn("to_pickable helper function is deprecated",
|
||||
DeprecationWarning)
|
||||
return x
|
||||
|
||||
|
||||
def from_pickable(x):
|
||||
warnings.warn("from_pickable helper function is deprecated",
|
||||
DeprecationWarning)
|
||||
return x
|
||||
stddigits, trailing)
|
||||
|
||||
|
||||
class ComplexResult(ValueError):
|
||||
@@ -34,26 +22,22 @@ round_floor = sys.intern('f')
|
||||
round_ceiling = sys.intern('c')
|
||||
round_up = sys.intern('u')
|
||||
round_down = sys.intern('d')
|
||||
round_fast = round_down
|
||||
|
||||
def prec_to_dps(n):
|
||||
"""Return number of accurate decimals that can be represented
|
||||
with a precision of n bits."""
|
||||
return max(1, int(round(int(n)/blog2_10)-1))
|
||||
return max(1, round(int(n)/blog2_10) - 1)
|
||||
|
||||
def dps_to_prec(n):
|
||||
"""Return the number of bits required to represent n decimals
|
||||
accurately."""
|
||||
return max(1, int(round((int(n)+1)*blog2_10)))
|
||||
return max(1, round((int(n) + 1)*blog2_10))
|
||||
|
||||
def repr_dps(n):
|
||||
"""Return the number of decimal digits required to represent
|
||||
a number with n-bit precision so that it can be uniquely
|
||||
reconstructed from the representation."""
|
||||
dps = prec_to_dps(n)
|
||||
if dps == 15:
|
||||
return 17
|
||||
return dps + 3
|
||||
return 1 + math.ceil(int(n)/blog2_10)
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Some commonly needed float values #
|
||||
@@ -74,8 +58,17 @@ finf = (0, MPZ_ZERO, -456, -2)
|
||||
fninf = (1, MPZ_ZERO, -789, -3)
|
||||
|
||||
math_float_inf = math.inf
|
||||
math_float_nan = math.nan
|
||||
blog2_10 = 3.3219280948873626
|
||||
|
||||
float_mant_dig = sys.float_info.mant_dig
|
||||
float_min_exp = sys.float_info.min_exp
|
||||
float_max_exp = sys.float_info.max_exp
|
||||
float_eps = sys.float_info.epsilon
|
||||
float_max = sys.float_info.max
|
||||
float_min = sys.float_info.min
|
||||
float_min_subnormal_exp = float_min_exp - float_mant_dig
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Rounding #
|
||||
@@ -129,11 +122,11 @@ shifts_down = {round_floor:(1,0), round_ceiling:(0,1),
|
||||
# This function is called almost every time an mpf is created.
|
||||
# It has been optimized accordingly.
|
||||
|
||||
def _normalize(sign, man, exp, bc, prec, rnd):
|
||||
def normalize(sign, man, exp, bc, prec, rnd):
|
||||
"""
|
||||
Create a raw mpf tuple with value (-1)**sign * man * 2**exp and
|
||||
normalized mantissa. The mantissa is rounded in the specified
|
||||
direction if its size exceeds the precision. Trailing zero bits
|
||||
normalized mantissa. The mantissa is rounded according to the specified
|
||||
rounding mode if its size exceeds the precision. Trailing zero bits
|
||||
are also stripped from the mantissa to ensure that the
|
||||
representation is canonical.
|
||||
|
||||
@@ -147,6 +140,12 @@ def _normalize(sign, man, exp, bc, prec, rnd):
|
||||
If these conditions are not met, use from_man_exp, mpf_pos, or any
|
||||
of the conversion functions to create normalized raw mpf tuples.
|
||||
"""
|
||||
assert type(man) == MPZ
|
||||
assert type(bc) in _exp_types
|
||||
assert type(exp) in _exp_types
|
||||
assert bc == man.bit_length()
|
||||
assert man >= 0
|
||||
|
||||
if not man:
|
||||
return fzero
|
||||
# Cut mantissa down to size if larger than target precision
|
||||
@@ -166,13 +165,7 @@ def _normalize(sign, man, exp, bc, prec, rnd):
|
||||
bc = prec
|
||||
# Strip trailing bits
|
||||
if not man & 1:
|
||||
t = trailtable[man & 255]
|
||||
if not t:
|
||||
while not man & 255:
|
||||
man >>= 8
|
||||
exp += 8
|
||||
bc -= 8
|
||||
t = trailtable[man & 255]
|
||||
t = trailing(man)
|
||||
man >>= t
|
||||
exp += t
|
||||
bc -= t
|
||||
@@ -182,26 +175,18 @@ def _normalize(sign, man, exp, bc, prec, rnd):
|
||||
# so this is easy to check for.
|
||||
if man == 1:
|
||||
bc = 1
|
||||
return sign, man, exp, bc
|
||||
return sign, man, int(exp), int(bc)
|
||||
|
||||
_exp_types = (int,)
|
||||
|
||||
if gmpy:
|
||||
_normalize = gmpy._mpmath_normalize
|
||||
|
||||
def normalize(sign, man, exp, bc, prec, rnd):
|
||||
assert type(man) == MPZ
|
||||
assert type(bc) in _exp_types
|
||||
assert type(exp) in _exp_types
|
||||
assert bc == man.bit_length()
|
||||
assert man >= 0
|
||||
return _normalize(sign, man, exp, bc, prec, rnd)
|
||||
normalize = gmpy._mpmath_normalize
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Conversion functions #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def from_man_exp(man, exp, prec=0, rnd=round_fast):
|
||||
def from_man_exp(man, exp, prec=0, rnd=round_down):
|
||||
"""Create raw mpf from (man, exp) pair. The mantissa may be signed.
|
||||
If no precision is specified, the mantissa is stored exactly."""
|
||||
if isinstance(man, int_types):
|
||||
@@ -220,19 +205,9 @@ def from_man_exp(man, exp, prec=0, rnd=round_fast):
|
||||
if not man:
|
||||
return fzero
|
||||
if not man & 1:
|
||||
if man & 2:
|
||||
return (sign, man >> 1, exp + 1, bc - 1)
|
||||
t = trailtable[man & 255]
|
||||
if not t:
|
||||
while not man & 255:
|
||||
man >>= 8
|
||||
exp += 8
|
||||
bc -= 8
|
||||
t = trailtable[man & 255]
|
||||
man >>= t
|
||||
exp += t
|
||||
bc -= t
|
||||
return (sign, man, exp, bc)
|
||||
t = trailing(man)
|
||||
return sign, man >> t, int(exp + t), int(bc - t)
|
||||
return sign, man, exp, bc
|
||||
return normalize(sign, man, exp, bc, prec, rnd)
|
||||
|
||||
int_cache = dict((n, from_man_exp(n, 0)) for n in range(-10, 257))
|
||||
@@ -240,7 +215,7 @@ int_cache = dict((n, from_man_exp(n, 0)) for n in range(-10, 257))
|
||||
if gmpy:
|
||||
from_man_exp = gmpy._mpmath_create
|
||||
|
||||
def from_int(n, prec=0, rnd=round_fast):
|
||||
def from_int(n, prec=0, rnd=round_down):
|
||||
"""Create a raw mpf from an integer. If no precision is specified,
|
||||
the mantissa is stored exactly."""
|
||||
if not prec:
|
||||
@@ -248,14 +223,8 @@ def from_int(n, prec=0, rnd=round_fast):
|
||||
return int_cache[n]
|
||||
return from_man_exp(MPZ(n), 0, prec, rnd)
|
||||
|
||||
def to_man_exp(s, signed=None):
|
||||
def to_man_exp(s, signed=True):
|
||||
"""Return (man, exp) of a raw mpf. Raise an error if inf/nan."""
|
||||
if signed is None:
|
||||
warnings.warn("Returning unsigned mantissa value per default "
|
||||
"is deprecated. Please adapt your code to use "
|
||||
"signed=True (return a signed mantissa).",
|
||||
DeprecationWarning)
|
||||
signed = False
|
||||
sign, man, exp, bc = s
|
||||
if (not man) and exp:
|
||||
raise ValueError("mantissa and exponent are defined "
|
||||
@@ -264,7 +233,7 @@ def to_man_exp(s, signed=None):
|
||||
man = -man
|
||||
return man, exp
|
||||
|
||||
def to_int(s, rnd=round_fast):
|
||||
def to_int(s, rnd=round_down):
|
||||
"""Convert a raw mpf to the nearest int. Rounding is done down by
|
||||
default (same as int(float) in Python), but can be changed. If the
|
||||
input is inf/nan, an exception is raised."""
|
||||
@@ -278,7 +247,7 @@ def to_int(s, rnd=round_fast):
|
||||
return (-man) << exp
|
||||
return man << exp
|
||||
# Make default rounding fast
|
||||
if rnd == round_fast:
|
||||
if rnd == round_down:
|
||||
if sign:
|
||||
return -(man >> (-exp))
|
||||
else:
|
||||
@@ -310,28 +279,28 @@ def mpf_round_int(s, rnd):
|
||||
raise NotImplementedError
|
||||
return mpf_pos(s, min(bc, mag), rnd)
|
||||
|
||||
def mpf_floor(s, prec=0, rnd=round_fast):
|
||||
def mpf_floor(s, prec=0, rnd=round_down):
|
||||
v = mpf_round_int(s, round_floor)
|
||||
if prec:
|
||||
v = mpf_pos(v, prec, rnd)
|
||||
return v
|
||||
|
||||
def mpf_ceil(s, prec=0, rnd=round_fast):
|
||||
def mpf_ceil(s, prec=0, rnd=round_down):
|
||||
v = mpf_round_int(s, round_ceiling)
|
||||
if prec:
|
||||
v = mpf_pos(v, prec, rnd)
|
||||
return v
|
||||
|
||||
def mpf_nint(s, prec=0, rnd=round_fast):
|
||||
def mpf_nint(s, prec=0, rnd=round_down):
|
||||
v = mpf_round_int(s, round_nearest)
|
||||
if prec:
|
||||
v = mpf_pos(v, prec, rnd)
|
||||
return v
|
||||
|
||||
def mpf_frac(s, prec=0, rnd=round_fast):
|
||||
def mpf_frac(s, prec=0, rnd=round_down):
|
||||
return mpf_sub(s, mpf_floor(s), prec, rnd)
|
||||
|
||||
def from_float(x, prec=53, rnd=round_fast):
|
||||
def from_float(x, prec=53, rnd=round_down):
|
||||
"""Create a raw mpf from a Python float, rounding if necessary.
|
||||
If prec >= 53, the result is guaranteed to represent exactly the
|
||||
same number as the input. If prec is not specified, use prec=53."""
|
||||
@@ -342,7 +311,7 @@ def from_float(x, prec=53, rnd=round_fast):
|
||||
m, e = math.frexp(x)
|
||||
return from_man_exp(MPZ(m*(1<<53)), e-53, prec, rnd)
|
||||
|
||||
def from_npfloat(x, prec=113, rnd=round_fast):
|
||||
def from_npfloat(x, prec=113, rnd=round_down):
|
||||
"""Create a raw mpf from a numpy float, rounding if necessary.
|
||||
If prec >= 113, the result is guaranteed to represent exactly the
|
||||
same number as the input. If prec is not specified, use prec=113."""
|
||||
@@ -355,7 +324,7 @@ def from_npfloat(x, prec=113, rnd=round_fast):
|
||||
return from_man_exp(MPZ(np.ldexp(m, 113)), int(e)-113, prec, rnd)
|
||||
return fnan
|
||||
|
||||
def from_Decimal(x, prec=0, rnd=round_fast):
|
||||
def from_Decimal(x, prec=0, rnd=round_down):
|
||||
"""Create a raw mpf from a Decimal, rounding if necessary.
|
||||
If prec is not specified, use the equivalent bit precision
|
||||
of the number of significant digits in x."""
|
||||
@@ -365,44 +334,67 @@ def from_Decimal(x, prec=0, rnd=round_fast):
|
||||
prec = int(len(x.as_tuple()[1])*blog2_10)
|
||||
return from_str(str(x), prec, rnd)
|
||||
|
||||
def to_float(s, strict=False, rnd=round_fast):
|
||||
def to_float(s, strict=False, rnd=round_down):
|
||||
"""
|
||||
Convert a raw mpf to a Python float. The result is exact if
|
||||
s.bit_length() <= 53 and no underflow/overflow occurs.
|
||||
Convert a raw mpf to a Python float. The result is exact
|
||||
if s.bit_length() <= sys.float_info.mant_dig and no
|
||||
underflow/overflow occurs. Else result is correctly rounded.
|
||||
|
||||
If the number is too large or too small to represent as a regular
|
||||
float, it will be converted to inf or 0.0. Setting strict=True
|
||||
forces an OverflowError to be raised instead.
|
||||
|
||||
Warning: with a directed rounding mode, the correct nearest representable
|
||||
floating-point number in the specified direction might not be computed
|
||||
in case of overflow or (gradual) underflow.
|
||||
If the magnitude of rounded number is too large to represent as
|
||||
a regular float, it will be converted to infinity. Setting
|
||||
strict=True forces an OverflowError to be raised instead.
|
||||
"""
|
||||
sign, man, exp, bc = s
|
||||
|
||||
if not man:
|
||||
if s == fzero: return 0.0
|
||||
if s == finf: return math_float_inf
|
||||
if s == fninf: return -math_float_inf
|
||||
return math_float_inf/math_float_inf
|
||||
if bc > 53:
|
||||
sign, man, exp, bc = normalize(sign, man, exp, bc, 53, rnd)
|
||||
if sign:
|
||||
man = -man
|
||||
try:
|
||||
return math.ldexp(man, exp)
|
||||
except OverflowError:
|
||||
if strict:
|
||||
raise
|
||||
# Overflow to infinity
|
||||
if exp + bc > 0:
|
||||
if sign:
|
||||
return -math_float_inf
|
||||
else:
|
||||
return math_float_inf
|
||||
# Underflow to zero
|
||||
return math_float_nan
|
||||
|
||||
exp2 = exp + bc
|
||||
# The smallest normal number is 2^(-1022)=0.1p-1021, and the smallest
|
||||
# subnormal is 2^(-1074)=0.1p-1073
|
||||
if exp2 <= float_min_subnormal_exp:
|
||||
if sign:
|
||||
if rnd == round_floor or (rnd == round_nearest
|
||||
and mpf_cmp(s, (1, MPZ(1), float_min_subnormal_exp
|
||||
- 1, 1)) < 0):
|
||||
return -float_min * float_eps
|
||||
return 0.0
|
||||
if rnd == round_ceiling or (rnd == round_nearest
|
||||
and mpf_cmp(s, (0, MPZ(1), float_min_subnormal_exp
|
||||
- 1, 1)) > 0):
|
||||
return float_min * float_eps
|
||||
return 0.0
|
||||
|
||||
def from_rational(p, q, prec, rnd=round_fast):
|
||||
# The largest normal number is 2^1024*(1-2^(-53))=0.111...111p1024
|
||||
if exp2 > float_max_exp:
|
||||
if sign:
|
||||
if rnd == round_down or rnd == round_ceiling:
|
||||
return -float_max
|
||||
if strict:
|
||||
raise OverflowError("math range error")
|
||||
return -math_float_inf
|
||||
if rnd == round_down or rnd == round_floor:
|
||||
return float_max
|
||||
if strict:
|
||||
raise OverflowError("math range error")
|
||||
return math_float_inf
|
||||
|
||||
nbits = float_mant_dig
|
||||
if exp2 < float_min_exp:
|
||||
# In the subnormal case, compute the exact number of significant bits.
|
||||
nbits += exp2 - float_min_exp
|
||||
assert 1 <= nbits < float_mant_dig
|
||||
if bc > nbits:
|
||||
sign, man, exp, bc = normalize(sign, man, exp, bc, nbits, rnd)
|
||||
if sign:
|
||||
man = -man
|
||||
# Should be exact:
|
||||
return math.ldexp(man, exp)
|
||||
|
||||
def from_rational(p, q, prec, rnd=round_down):
|
||||
"""Create a raw mpf from a rational number p/q, round if
|
||||
necessary."""
|
||||
return mpf_div(from_int(p), from_int(q), prec, rnd)
|
||||
@@ -460,11 +452,7 @@ def mpf_hash(s):
|
||||
|
||||
# Handle special numbers
|
||||
if not sman:
|
||||
if s == fnan:
|
||||
if sys.version_info >= (3, 10):
|
||||
return object.__hash__(s)
|
||||
else:
|
||||
return sys.hash_info.nan
|
||||
if s == fnan: return object.__hash__(s)
|
||||
if s == finf: return sys.hash_info.inf
|
||||
if s == fninf: return -sys.hash_info.inf
|
||||
|
||||
@@ -559,7 +547,7 @@ def mpf_min_max(seq):
|
||||
if mpf_gt(x, max): max = x
|
||||
return min, max
|
||||
|
||||
def mpf_pos(s, prec=0, rnd=round_fast):
|
||||
def mpf_pos(s, prec=0, rnd=round_down):
|
||||
"""Calculate 0+s for a raw mpf (i.e., just round s to the specified
|
||||
precision)."""
|
||||
if prec:
|
||||
@@ -569,7 +557,7 @@ def mpf_pos(s, prec=0, rnd=round_fast):
|
||||
return normalize(sign, man, exp, bc, prec, rnd)
|
||||
return s
|
||||
|
||||
def mpf_neg(s, prec=0, rnd=round_fast):
|
||||
def mpf_neg(s, prec=0, rnd=round_down):
|
||||
"""Negate a raw mpf (return -s), rounding the result to the
|
||||
specified precision. The prec argument can be omitted to do the
|
||||
operation exactly."""
|
||||
@@ -583,7 +571,7 @@ def mpf_neg(s, prec=0, rnd=round_fast):
|
||||
return (1-sign, man, exp, bc)
|
||||
return normalize(1-sign, man, exp, bc, prec, rnd)
|
||||
|
||||
def mpf_abs(s, prec=0, rnd=round_fast):
|
||||
def mpf_abs(s, prec=0, rnd=round_down):
|
||||
"""Return abs(s) of the raw mpf s, rounded to the specified
|
||||
precision. The prec argument can be omitted to generate an
|
||||
exact result."""
|
||||
@@ -608,7 +596,7 @@ def mpf_sign(s):
|
||||
return 0
|
||||
return (-1) ** sign
|
||||
|
||||
def mpf_add(s, t, prec=0, rnd=round_fast, _sub=0):
|
||||
def mpf_add(s, t, prec=0, rnd=round_down, _sub=0):
|
||||
"""
|
||||
Add the two raw mpf values s and t.
|
||||
|
||||
@@ -703,12 +691,12 @@ def mpf_add(s, t, prec=0, rnd=round_fast, _sub=0):
|
||||
return normalize(ssign, sman, sexp, sbc, prec or sbc, rnd)
|
||||
return s
|
||||
|
||||
def mpf_sub(s, t, prec=0, rnd=round_fast):
|
||||
def mpf_sub(s, t, prec=0, rnd=round_down):
|
||||
"""Return the difference of two raw mpfs, s-t. This function is
|
||||
simply a wrapper of mpf_add that changes the sign of t."""
|
||||
return mpf_add(s, t, prec, rnd, 1)
|
||||
|
||||
def mpf_sum(xs, prec=0, rnd=round_fast, absolute=False):
|
||||
def mpf_sum(xs, prec=0, rnd=round_down, absolute=False):
|
||||
"""
|
||||
Sum a list of mpf values efficiently and accurately
|
||||
(typically no temporary roundoff occurs). If prec=0,
|
||||
@@ -756,7 +744,7 @@ def mpf_sum(xs, prec=0, rnd=round_fast, absolute=False):
|
||||
return special
|
||||
return from_man_exp(man, exp, prec, rnd)
|
||||
|
||||
def mpf_mul(s, t, prec=0, rnd=round_fast):
|
||||
def mpf_mul(s, t, prec=0, rnd=round_down):
|
||||
"""Multiply two raw mpfs"""
|
||||
ssign, sman, sexp, sbc = s
|
||||
tsign, tman, texp, tbc = t
|
||||
@@ -777,7 +765,7 @@ def mpf_mul(s, t, prec=0, rnd=round_fast):
|
||||
if t == fzero: return fnan
|
||||
return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)]
|
||||
|
||||
def gmpy_mpf_mul_int(s, n, prec, rnd=round_fast):
|
||||
def gmpy_mpf_mul_int(s, n, prec, rnd=round_down):
|
||||
"""Multiply by a Python integer."""
|
||||
sign, man, exp, bc = s
|
||||
if not man:
|
||||
@@ -790,7 +778,7 @@ def gmpy_mpf_mul_int(s, n, prec, rnd=round_fast):
|
||||
man *= n
|
||||
return normalize(sign, man, exp, man.bit_length(), prec, rnd)
|
||||
|
||||
def python_mpf_mul_int(s, n, prec, rnd=round_fast):
|
||||
def python_mpf_mul_int(s, n, prec, rnd=round_down):
|
||||
"""Multiply by a Python integer."""
|
||||
sign, man, exp, bc = s
|
||||
if not man:
|
||||
@@ -825,13 +813,10 @@ def mpf_frexp(x):
|
||||
"""Convert x = y*2**n to (y, n) with abs(y) in [0.5, 1) if nonzero"""
|
||||
sign, man, exp, bc = x
|
||||
if not man:
|
||||
if x == fzero:
|
||||
return (fzero, 0)
|
||||
else:
|
||||
raise ValueError
|
||||
return (x, 0)
|
||||
return mpf_shift(x, -bc-exp), bc+exp
|
||||
|
||||
def mpf_div(s, t, prec, rnd=round_fast):
|
||||
def mpf_div(s, t, prec, rnd=round_down):
|
||||
"""Floating-point division"""
|
||||
ssign, sman, sexp, sbc = s
|
||||
tsign, tman, texp, tbc = t
|
||||
@@ -871,7 +856,7 @@ def mpf_div(s, t, prec, rnd=round_fast):
|
||||
bc = quot.bit_length()
|
||||
return normalize(sign, quot, sexp-texp-extra, bc, prec or bc, rnd)
|
||||
|
||||
def mpf_rdiv_int(n, t, prec, rnd=round_fast):
|
||||
def mpf_rdiv_int(n, t, prec, rnd=round_down):
|
||||
"""Floating-point division n/t with a Python integer as numerator"""
|
||||
sign, man, exp, bc = t
|
||||
if not n or not man:
|
||||
@@ -887,7 +872,7 @@ def mpf_rdiv_int(n, t, prec, rnd=round_fast):
|
||||
return normalize(sign, quot, -exp-extra, quot.bit_length(), prec, rnd)
|
||||
return normalize(sign, quot, -exp-extra, quot.bit_length(), prec, rnd)
|
||||
|
||||
def mpf_mod(s, t, prec, rnd=round_fast):
|
||||
def mpf_mod(s, t, prec, rnd=round_down):
|
||||
ssign, sman, sexp, sbc = s
|
||||
tsign, tman, texp, tbc = t
|
||||
if ((not sman) and sexp) or ((not tman) and texp):
|
||||
@@ -928,7 +913,7 @@ negative_rnd = {
|
||||
round_nearest : round_nearest
|
||||
}
|
||||
|
||||
def mpf_pow_int(s, n, prec, rnd=round_fast):
|
||||
def mpf_pow_int(s, n, prec, rnd=round_down):
|
||||
"""Compute s**n, where s is a raw mpf and n is a Python integer."""
|
||||
sign, man, exp, bc = s
|
||||
|
||||
@@ -1041,6 +1026,97 @@ def mpf_perturb(x, eps_sign, prec, rnd):
|
||||
# Radix conversion #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
stddigits_as_bytes = bytearray(stddigits.encode('ascii'))
|
||||
|
||||
def fpp2(x, prec=0, base=10):
|
||||
"""
|
||||
(FPP)² algorithm from "How to Print Floating-Point Numbers Accurately"
|
||||
by Steele & White. Assume round_nearest rounding mode.
|
||||
|
||||
The output is correctly rounded. Carry doesn't propagate on rounding. The
|
||||
original x can be recreated, when output submitted to from_str() with
|
||||
round_nearest rounding. No "garbage digits" produced.
|
||||
"""
|
||||
_, man, exp, bc = x
|
||||
if not man:
|
||||
assert not exp
|
||||
return "0", 0
|
||||
prec = prec if prec else bc
|
||||
man <<= prec - bc
|
||||
exp += bc
|
||||
assert 0 < man < 2**prec
|
||||
|
||||
# Original version doesn't implement rounding correctly, we take this
|
||||
# into account, using strict inequatities for low/high conditions,
|
||||
# following the Burger & Dybvig Scheme code from "Printing Floating-Point
|
||||
# Numbers Quickly and Accurately".
|
||||
is_even = man & 1 == 0
|
||||
cmp = operator.le if is_even else operator.lt
|
||||
rev_cmp = operator.lt if is_even else operator.le
|
||||
|
||||
# Step 1. Initialize variables.
|
||||
ep = exp - prec
|
||||
R = man << max(ep, 0) + 1
|
||||
S = 1 << max(-ep, 0) + 1
|
||||
Mminus = Mplus = 1 << max(ep, 0)
|
||||
if man == 1 << (prec - 1):
|
||||
Mplus <<= 1
|
||||
R <<= 1
|
||||
S <<= 1
|
||||
|
||||
# Step 2. Compute ceil(log((R + Mplus)/S, base)).
|
||||
# We use (undocumented) support for computing logarithms of
|
||||
# big integers (that overflows floats). This is available
|
||||
# also on PyPy and GraalPy.
|
||||
k = math.ceil((math.log2(int(R + Mplus)) -
|
||||
math.log2(int(S)))/math.log2(int(base)))
|
||||
if k < 0:
|
||||
bk = base**-k
|
||||
R *= bk
|
||||
Mplus *= bk
|
||||
Mminus *= bk
|
||||
if k > 0:
|
||||
S *= base**k
|
||||
# k might be either exact or by 1 too big.
|
||||
if rev_cmp(R + Mplus, S):
|
||||
k -= 1
|
||||
R *= base
|
||||
Mplus *= base
|
||||
Mminus *= base
|
||||
assert cmp(S, R + Mplus)
|
||||
D = bytearray()
|
||||
|
||||
# Step 3. Generate digits.
|
||||
while True:
|
||||
U, R = divmod(R, S)
|
||||
low = cmp(R, Mminus)
|
||||
high = cmp(S, R + Mplus)
|
||||
D.append(stddigits_as_bytes[U])
|
||||
|
||||
if low or high:
|
||||
# Step 4. Break the loop, round last digit.
|
||||
round_up = high
|
||||
if low and high:
|
||||
round_up = 2*R >= S
|
||||
# Theorem 4 in the Burger & Dybvig article is invalid,
|
||||
# and the algorithm actually depends on how the input
|
||||
# routine break ties. Following code assumes default IEEE
|
||||
# rounding mode, i.e. the mpmath's round_nearest.
|
||||
if round_up and 2*R == S:
|
||||
round_up = U & 1
|
||||
if round_up:
|
||||
# But Theorem 1 is still valid: no carry should
|
||||
# be generated on rounding up.
|
||||
assert ord('0') <= D[-1] < ord(stddigits[base - 1])
|
||||
D[-1] += 1
|
||||
break
|
||||
|
||||
R *= base
|
||||
Mminus *= base
|
||||
Mplus *= base
|
||||
|
||||
return D.decode(), k
|
||||
|
||||
def to_digits_exp(s, dps, base=10):
|
||||
"""Helper function for representing the floating-point number s as
|
||||
a string with dps digits. Returns (sign, string, exponent) where
|
||||
@@ -1101,27 +1177,28 @@ def to_digits_exp(s, dps, base=10):
|
||||
exponent += len(digits) - fixdps - 1
|
||||
return sign, digits, exponent
|
||||
|
||||
def round_digits(sign, digits, dps, base, rnd=round_nearest, fixed=False):
|
||||
'''
|
||||
def round_digits(s, digits, exponent, dps, base, rnd=round_down, fixed=False):
|
||||
"""
|
||||
Returns the rounded digits, and the number of places the decimal point was
|
||||
shifted.
|
||||
|
||||
Supports three kinds of rounding: up, down, or nearest.
|
||||
'''
|
||||
"""
|
||||
|
||||
assert len(digits) > dps
|
||||
assert rnd in (round_nearest, round_up, round_down, round_ceiling,
|
||||
round_floor)
|
||||
sign = s[0]
|
||||
|
||||
# to_digits_exp truncates; flag a nonzero remainder past the last digit so
|
||||
# rounding is not fooled by a short zero tail.
|
||||
inexact = s[2] + len(digits) - 1 - exponent < 0
|
||||
|
||||
if rnd == round_ceiling:
|
||||
rnd = round_down if sign else round_up
|
||||
elif rnd == round_floor:
|
||||
rnd = round_up if sign else round_down
|
||||
|
||||
exponent = 0
|
||||
|
||||
if rnd == round_down:
|
||||
return digits[:dps], 0
|
||||
return digits[:dps], exponent
|
||||
elif rnd == round_nearest:
|
||||
rnd_digs = stddigits[(base//2 + base % 2):base]
|
||||
else:
|
||||
@@ -1134,7 +1211,7 @@ def round_digits(sign, digits, dps, base, rnd=round_nearest, fixed=False):
|
||||
# The first digit after dps is a 5 and we should determine whether we
|
||||
# round it up or down.
|
||||
if digits[dps] == rnd_digs[0]:
|
||||
tie_down = True
|
||||
tie_down = not inexact
|
||||
|
||||
# If the digit we round to is even, we may round down if all the
|
||||
# following digits are 0.
|
||||
@@ -1149,6 +1226,7 @@ def round_digits(sign, digits, dps, base, rnd=round_nearest, fixed=False):
|
||||
elif rnd == round_up:
|
||||
# If any digit following a 0 is different from zero, we round up.
|
||||
if digits[dps] == '0':
|
||||
tie_up = inexact
|
||||
for i in range(dps+1, len(digits)):
|
||||
if digits[i] != '0':
|
||||
tie_up = True
|
||||
@@ -1245,7 +1323,11 @@ def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None,
|
||||
|
||||
# to_digits_exp rounds to floor.
|
||||
# This sometimes kills some instances of "...00001"
|
||||
sign, digits, exponent = to_digits_exp(s, dps+10, base)
|
||||
# For base 10 widen the window to the full mantissa (as format_scientific
|
||||
# does), otherwise a value just above a decimal boundary is extracted as
|
||||
# "...99999" one ULP low and directed rounding lands one ULP short.
|
||||
ndig = (max(dps, int(s[3]/blog2_10)) if base == 10 else dps) + 10
|
||||
sign, digits, exponent = to_digits_exp(s, ndig, base)
|
||||
|
||||
rnd_digs = stddigits[(base//2 + base%2):base]
|
||||
|
||||
@@ -1265,8 +1347,7 @@ def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None,
|
||||
n = int(digits, 16) >> shift
|
||||
digits = hex(n)[2:]
|
||||
|
||||
digits, exp_add = round_digits(s[0], digits, dps, base, rnd)
|
||||
exponent += exp_add
|
||||
digits, exponent = round_digits(s, digits, exponent, dps, base, rnd)
|
||||
|
||||
# Prettify numbers close to unit magnitude
|
||||
if not binary_exp and min_fixed < exponent < max_fixed:
|
||||
@@ -1335,7 +1416,7 @@ def str_to_man_exp(x, base=10):
|
||||
special_str = {'inf':finf, '+inf':finf, '-inf':fninf, 'nan':fnan,
|
||||
'oo':finf, '+oo':finf, '-oo':fninf}
|
||||
|
||||
def from_str(x, prec=0, rnd=round_fast, base=0):
|
||||
def from_str(x, prec=0, rnd=round_down, base=0):
|
||||
"""Create a raw mpf from a string x in a given base, rounding in the
|
||||
specified direction if the input number cannot be represented
|
||||
exactly as a binary floating-point number with the given number of
|
||||
@@ -1491,7 +1572,7 @@ def read_format_spec(format_spec):
|
||||
return format_dict
|
||||
|
||||
|
||||
def format_fixed(s, dps, rnd=round_nearest):
|
||||
def format_fixed(s, dps, rnd=round_down):
|
||||
# First, get the exponent to know how many digits we will need
|
||||
base = 10
|
||||
_, _, exponent = to_digits_exp(s, 1, base)
|
||||
@@ -1501,38 +1582,30 @@ def format_fixed(s, dps, rnd=round_nearest):
|
||||
# exponent by +- 1)
|
||||
_, digits, exponent = to_digits_exp(
|
||||
s, max(dps+exponent+4, int(s[3]/blog2_10)), base)
|
||||
orig_dps = dps
|
||||
dps += exponent + 1
|
||||
|
||||
# The number we want to print is lower in magnitude that the requested
|
||||
# precision. We should only print 0s.
|
||||
if dps < 0:
|
||||
int_part = '0'
|
||||
frac_part = orig_dps*'0'
|
||||
# The number we want to print is lower in magnitude that the
|
||||
# requested precision.
|
||||
digits = '0'*(-dps) + digits
|
||||
exponent -= dps
|
||||
dps = 0
|
||||
|
||||
digits, exponent = round_digits(s, digits, exponent, dps, base, rnd, True)
|
||||
|
||||
# Here we prepend the corresponding 0s to the digits string, according
|
||||
# to the value of exponent
|
||||
split = 1
|
||||
if exponent < 0:
|
||||
digits = "0"*(-exponent) + digits
|
||||
else:
|
||||
digits, exp_add = round_digits(s[0], digits, dps, base, rnd, True)
|
||||
exponent += exp_add
|
||||
split += exponent
|
||||
|
||||
# Here we prepend the corresponding 0s to the digits string, according
|
||||
# to the value of exponent
|
||||
if exponent < 0:
|
||||
digits = ("0"*(-exponent)) + digits
|
||||
split = 1
|
||||
else:
|
||||
split = exponent + 1
|
||||
int_part = digits[:split]
|
||||
|
||||
# Finally, assemble the digits including the decimal point
|
||||
if orig_dps == 0:
|
||||
return int_part, ''
|
||||
|
||||
frac_part = digits[split:]
|
||||
|
||||
return int_part, frac_part
|
||||
# Finally, assemble the digits including the decimal point
|
||||
return digits[:split], digits[split:]
|
||||
|
||||
|
||||
def format_scientific(s, dps, rnd=round_nearest):
|
||||
def format_scientific(s, dps, rnd=round_down):
|
||||
base = 10
|
||||
|
||||
# First, get the exponent to know how many digits we will need
|
||||
@@ -1540,13 +1613,12 @@ def format_scientific(s, dps, rnd=round_nearest):
|
||||
_, digits, exponent = to_digits_exp(s, max(dps + 10,
|
||||
int(s[3]/blog2_10) + 10),
|
||||
base)
|
||||
digits, exp_add = round_digits(s[0], digits, dps, base, rnd)
|
||||
exponent += exp_add
|
||||
digits, exponent = round_digits(s, digits, exponent, dps, base, rnd)
|
||||
|
||||
return digits[0], digits[1:], f'e{exponent:+03d}'
|
||||
|
||||
|
||||
def format_hexadecimal(s, dps, rnd=round_nearest):
|
||||
def format_hexadecimal(s, dps, rnd=round_down):
|
||||
prec = 4*dps + 1 if dps >= 0 else s[1].bit_length()
|
||||
|
||||
if s[1]:
|
||||
@@ -1573,7 +1645,7 @@ def format_hexadecimal(s, dps, rnd=round_nearest):
|
||||
return digits, frac_digits, f'p{exponent:+01d}'
|
||||
|
||||
|
||||
def format_binary(s, dps, rnd=round_nearest):
|
||||
def format_binary(s, dps, rnd=round_down):
|
||||
prec = dps + 1 if dps >= 0 else s[1].bit_length()
|
||||
s = mpf_pos(s, prec, rnd)
|
||||
|
||||
@@ -1593,7 +1665,7 @@ def fill_sep(digits, sep, prev, nmod, sep_range):
|
||||
for pos in range(nmod, len(digits), sep_range))
|
||||
|
||||
|
||||
def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps):
|
||||
def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps, unique):
|
||||
capitalize = False
|
||||
if format_dict['type'] in list('AFGE'):
|
||||
capitalize = True
|
||||
@@ -1607,10 +1679,12 @@ def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps):
|
||||
num = mpf_mul(num, from_int(100), prec, rnd=round_nearest)
|
||||
|
||||
dps = format_dict['precision']
|
||||
if dps >= 0 or fmt_type:
|
||||
unique = False
|
||||
|
||||
int_part = ''
|
||||
exponent = ''
|
||||
sign = ''
|
||||
sign = '-' if num[0] else ''
|
||||
|
||||
# Now the general case
|
||||
strip_last_zero = False
|
||||
@@ -1618,7 +1692,7 @@ def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps):
|
||||
|
||||
rnd = format_dict.get('rounding', rnd)
|
||||
|
||||
if not fmt_type or fmt_type == 'g':
|
||||
if not unique and (not fmt_type or fmt_type == 'g'):
|
||||
if not format_dict['alternate']:
|
||||
strip_zeros = True
|
||||
if fmt_type == 'g':
|
||||
@@ -1631,8 +1705,7 @@ def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps):
|
||||
|
||||
_, tdigits, exp = to_digits_exp(num, max(53/blog2_10, dps), 10)
|
||||
if num[1]:
|
||||
_, exp_add = round_digits(num, tdigits, dps, 10, rnd)
|
||||
exp += exp_add
|
||||
_, exp = round_digits(num, tdigits, exp, dps, 10, rnd)
|
||||
|
||||
fix0 = 0 if fmt_type else 1
|
||||
if -4 <= exp < dps - fix0:
|
||||
@@ -1646,6 +1719,54 @@ def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps):
|
||||
if capitalize:
|
||||
frac_part = frac_part.upper()
|
||||
|
||||
elif unique:
|
||||
if abs(num[2] + num[3] - prec) > 10000:
|
||||
dps = repr_dps(prec)
|
||||
_, digits, exp = to_digits_exp(num, dps, 10)
|
||||
if len(digits) > dps:
|
||||
digits, exp = round_digits(num, digits, exp, dps,
|
||||
10, round_nearest)
|
||||
prev_digits = digits
|
||||
prev_exp = exp
|
||||
while dps > 1:
|
||||
dps -= 1
|
||||
new_digits, new_exp = round_digits(num, digits, exp, dps,
|
||||
10, round_down)
|
||||
new_str = f"{sign}{new_digits[0]}.{new_digits[1:]}e{exp}"
|
||||
if from_str(new_str, prec, round_nearest, 10) != num:
|
||||
new_digits, new_exp = round_digits(num, digits, exp, dps,
|
||||
10, round_up)
|
||||
new_str = f"{sign}{new_digits[0]}.{new_digits[1:]}e{exp}"
|
||||
if from_str(new_str, prec, round_nearest, 10) != num:
|
||||
digits = prev_digits
|
||||
exp = prev_exp
|
||||
break
|
||||
prev_digits = new_digits
|
||||
prev_exp = new_exp
|
||||
else:
|
||||
digits = new_digits
|
||||
exp = new_exp
|
||||
else:
|
||||
num = mpf_pos(num, prec, rnd) # workaround issue 1158
|
||||
# Here be dragons.
|
||||
digits, exp = fpp2(num, prec, 10)
|
||||
|
||||
split = 1
|
||||
if exp < -4 or exp > prec_to_dps(prec):
|
||||
exponent = f'e{exp:+03d}'
|
||||
else:
|
||||
digits += "0"*(exp + 2 - len(digits))
|
||||
if exp < 0:
|
||||
digits = "0"*(-exp) + digits
|
||||
else:
|
||||
split += exp
|
||||
|
||||
int_part = digits[:split]
|
||||
frac_part = digits[split:]
|
||||
|
||||
if frac_part or format_dict['alternate']:
|
||||
frac_part = '.' + frac_part
|
||||
|
||||
elif fmt_type == 'e':
|
||||
int_part, frac_part, exponent = format_scientific(num, dps, rnd=rnd)
|
||||
if strip_zeros:
|
||||
@@ -1688,7 +1809,6 @@ def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps):
|
||||
frac_part = fill_sep(frac_part, sep, frac_part[0], 1, sep_range)
|
||||
digits = frac_part + exponent
|
||||
|
||||
sign = '-' if num[0] else ''
|
||||
if sign != '-' and format_dict['sign'] != '-':
|
||||
sign = format_dict['sign']
|
||||
if fmt_type == 'f' and format_dict['no_neg_0']:
|
||||
@@ -1725,9 +1845,10 @@ def format_digits(num, format_dict, prec, rnd, _pretty_repr_dps):
|
||||
return sign, int_part + digits
|
||||
|
||||
|
||||
def format_mpf(num, format_spec, prec, rnd, _pretty_repr_dps):
|
||||
def format_mpf(num, format_spec, prec, rnd, _pretty_repr_dps, unique):
|
||||
format_dict = read_format_spec(format_spec)
|
||||
sign, digits = format_digits(num, format_dict, prec, rnd, _pretty_repr_dps)
|
||||
sign, digits = format_digits(num, format_dict, prec, rnd,
|
||||
_pretty_repr_dps, unique)
|
||||
nchars = len(digits) + len(sign)
|
||||
lpad, rpad = calc_padding(
|
||||
nchars, format_dict['width'], format_dict['align'])
|
||||
@@ -1740,7 +1861,7 @@ def format_mpf(num, format_spec, prec, rnd, _pretty_repr_dps):
|
||||
+ rpad*format_dict['fill_char']
|
||||
|
||||
|
||||
def format_mpc(num, format_spec, prec, rnd, _pretty_repr_dps):
|
||||
def format_mpc(num, format_spec, prec, rnd, _pretty_repr_dps, unique):
|
||||
format_dict = read_format_spec(format_spec)
|
||||
|
||||
if format_dict['fill_char'] == '0':
|
||||
@@ -1754,12 +1875,23 @@ def format_mpc(num, format_spec, prec, rnd, _pretty_repr_dps):
|
||||
"format specifier.")
|
||||
|
||||
fmt_type = format_dict['type'].lower()
|
||||
if not fmt_type:
|
||||
if not fmt_type and format_dict['precision'] >= 0:
|
||||
format_dict['type'] = 'g'
|
||||
sign_re, digits_re = format_digits(num[0], format_dict, prec, rnd, _pretty_repr_dps)
|
||||
sign_re, digits_re = format_digits(num[0], format_dict, prec, rnd,
|
||||
_pretty_repr_dps, unique)
|
||||
fmt_sign = format_dict['sign']
|
||||
format_dict['sign'] = '+'
|
||||
sign_im, digits_im = format_digits(num[1], format_dict, prec, rnd, _pretty_repr_dps)
|
||||
sign_im, digits_im = format_digits(num[1], format_dict, prec, rnd,
|
||||
_pretty_repr_dps, unique)
|
||||
if not format_dict['type']:
|
||||
if format_dict['alternate']:
|
||||
if 'e' not in digits_re:
|
||||
digits_re = digits_re.rstrip('0')
|
||||
if 'e' not in digits_im:
|
||||
digits_im = digits_im.rstrip('0')
|
||||
else:
|
||||
digits_re = digits_re.removesuffix('.0')
|
||||
digits_im = digits_im.removesuffix('.0')
|
||||
digits_im += 'j'
|
||||
|
||||
if not fmt_type:
|
||||
@@ -1786,7 +1918,7 @@ def format_mpc(num, format_spec, prec, rnd, _pretty_repr_dps):
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
|
||||
def mpf_sqrt(s, prec, rnd=round_fast):
|
||||
def mpf_sqrt(s, prec, rnd=round_down):
|
||||
"""
|
||||
Compute the square root of a nonnegative mpf value. The
|
||||
result is correctly rounded.
|
||||
@@ -1814,10 +1946,10 @@ def mpf_sqrt(s, prec, rnd=round_fast):
|
||||
shift += 2
|
||||
return from_man_exp(man, (exp-shift)//2, prec, rnd)
|
||||
|
||||
def mpf_hypot(x, y, prec, rnd=round_fast):
|
||||
def mpf_hypot(x, y, prec, rnd=round_down):
|
||||
"""Compute the Euclidean norm sqrt(x**2 + y**2) of two raw mpfs
|
||||
x and y."""
|
||||
if y == fzero: return mpf_abs(x, prec, rnd)
|
||||
if x == fzero: return mpf_abs(y, prec, rnd)
|
||||
hypot2 = mpf_add(mpf_mul(x,x), mpf_mul(y,y), prec+4)
|
||||
hypot2 = mpf_add(mpf_mul(x,x), mpf_mul(y,y), prec+10, rnd)
|
||||
return mpf_sqrt(hypot2, prec, rnd)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import warnings
|
||||
|
||||
def __getattr__(name):
|
||||
warnings.warn("the math2 module is deprecated, use libfp instead",
|
||||
DeprecationWarning)
|
||||
from . import libfp
|
||||
return getattr(libfp, name)
|
||||
@@ -433,6 +433,8 @@ class MatrixCalculusMethods:
|
||||
|
||||
"""
|
||||
A = ctx.matrix(A)
|
||||
if ctx.mnorm(A, 'inf') == 0:
|
||||
raise ValueError("The logarithm is undefined for the zero matrix.")
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
|
||||
@@ -511,9 +511,9 @@ def schur(ctx, A, overwrite_a = False):
|
||||
>>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]])
|
||||
>>> Q, R = mp.schur(A)
|
||||
>>> mp.nprint(R, 3)
|
||||
[2.0 0.417 -2.53]
|
||||
[0.0 4.0 -4.74]
|
||||
[0.0 0.0 9.0]
|
||||
[2.0 0.417 2.53]
|
||||
[0.0 4.0 4.74]
|
||||
[0.0 0.0 9.0]
|
||||
>>> print(mp.chop(A - Q * R * Q.transpose_conj()))
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
|
||||
+66
-22
@@ -188,7 +188,7 @@ class LinearAlgebraMethods:
|
||||
x[i] /= U[i,i]
|
||||
return x
|
||||
|
||||
def lu_solve(ctx, A, b, **kwargs):
|
||||
def lu_solve(ctx, A, b):
|
||||
"""
|
||||
Ax = b => x
|
||||
|
||||
@@ -202,7 +202,7 @@ class LinearAlgebraMethods:
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A nor b
|
||||
A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy()
|
||||
A, b = ctx.matrix(A).copy(), ctx.matrix(b).copy()
|
||||
if A.rows < A.cols:
|
||||
raise ValueError('cannot solve underdetermined system')
|
||||
if A.rows > A.cols:
|
||||
@@ -278,7 +278,7 @@ class LinearAlgebraMethods:
|
||||
assert 0 < i <= n, 'this unit vector does not exist'
|
||||
return [ctx.zero]*(i-1) + [ctx.one] + [ctx.zero]*(n-i)
|
||||
|
||||
def inverse(ctx, A, **kwargs):
|
||||
def inverse(ctx, A):
|
||||
"""
|
||||
Calculate the inverse of a matrix.
|
||||
|
||||
@@ -289,7 +289,7 @@ class LinearAlgebraMethods:
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A
|
||||
A = ctx.matrix(A, **kwargs).copy()
|
||||
A = ctx.matrix(A).copy()
|
||||
n = A.rows
|
||||
# get LU factorisation
|
||||
A, p = ctx.LU_decomp(A)
|
||||
@@ -306,11 +306,50 @@ class LinearAlgebraMethods:
|
||||
for j in range(n):
|
||||
row.append(cols[j][i])
|
||||
inv.append(row)
|
||||
result = ctx.matrix(inv, **kwargs)
|
||||
result = ctx.matrix(inv)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return result
|
||||
|
||||
def pinv(ctx, A, *, rtol=None):
|
||||
"""
|
||||
Returns Moore-Penrose pseudoinverse of the matrix `A`.
|
||||
|
||||
This is a generalization of the matrix inverse that provides a unique
|
||||
result even for singular and non-square matrices. In the overdetermined
|
||||
case, it provides the least squares solution. In the underdetermined
|
||||
case, it provides the minimum norm solution.
|
||||
|
||||
The Moore-Penrose inverse of `A` is computed using its singular-value
|
||||
decomposition. If `s` is the maximum singular value of `A`, then the
|
||||
significance cut-off value is determined by `rtol * s`. Any singular
|
||||
value below this value is assumed insignificant.
|
||||
|
||||
**Arguments**
|
||||
|
||||
A : The matrix to compute the pseudoinverse for.
|
||||
rtol: Optional relative threshold term.
|
||||
The default value is ctx.eps * max(A.rows, A.cols).
|
||||
|
||||
**References**
|
||||
|
||||
* [Wikipedia]_ https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse
|
||||
"""
|
||||
U, S, V = ctx.svd(A)
|
||||
|
||||
if not rtol:
|
||||
rtol = max(A.rows, A.cols) * S[0] * ctx.eps
|
||||
assert rtol > 0
|
||||
|
||||
Splus = ctx.zeros(V.cols, U.cols)
|
||||
for ind, val in enumerate(S):
|
||||
if val > rtol * max(S):
|
||||
Splus[ind, ind] = 1/val
|
||||
|
||||
v_conj_T = V.apply(lambda x: ctx.conj(x)).T
|
||||
u_conj_T = U.apply(lambda x: ctx.conj(x)).T
|
||||
return v_conj_T * Splus * u_conj_T
|
||||
|
||||
def householder(ctx, A):
|
||||
"""
|
||||
(A|b) -> H, p, x, res
|
||||
@@ -332,7 +371,10 @@ class LinearAlgebraMethods:
|
||||
s = ctx.fsum(abs(A[i,j])**2 for i in range(j, m))
|
||||
if not abs(s) > ctx.eps:
|
||||
raise ValueError('matrix is numerically singular')
|
||||
p.append(-ctx.sign(ctx.re(A[j,j])) * ctx.sqrt(s))
|
||||
sign = ctx.sign(ctx.re(A[j,j]))
|
||||
if sign == 0:
|
||||
sign = ctx.one
|
||||
p.append(-sign * ctx.sqrt(s))
|
||||
kappa = ctx.one / (s - p[j] * A[j,j])
|
||||
A[j,j] -= p[j]
|
||||
for k in range(j+1, n):
|
||||
@@ -364,7 +406,7 @@ class LinearAlgebraMethods:
|
||||
# H, p, x, res = householder(A)
|
||||
# TODO: implement this
|
||||
|
||||
def residual(ctx, A, x, b, **kwargs):
|
||||
def residual(ctx, A, x, b):
|
||||
"""
|
||||
Calculate the residual of a solution to a linear equation system.
|
||||
|
||||
@@ -373,12 +415,12 @@ class LinearAlgebraMethods:
|
||||
oldprec = ctx.prec
|
||||
try:
|
||||
ctx.prec *= 2
|
||||
A, x, b = ctx.matrix(A, **kwargs), ctx.matrix(x, **kwargs), ctx.matrix(b, **kwargs)
|
||||
A, x, b = ctx.matrix(A), ctx.matrix(x), ctx.matrix(b)
|
||||
return A*x - b
|
||||
finally:
|
||||
ctx.prec = oldprec
|
||||
|
||||
def qr_solve(ctx, A, b, norm=None, **kwargs):
|
||||
def qr_solve(ctx, A, b, norm=None):
|
||||
"""
|
||||
Ax = b => x, ||Ax - b||
|
||||
|
||||
@@ -394,7 +436,7 @@ class LinearAlgebraMethods:
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A nor b
|
||||
A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy()
|
||||
A, b = ctx.matrix(A).copy(), ctx.matrix(b).copy()
|
||||
if A.rows < A.cols:
|
||||
raise ValueError('cannot solve underdetermined system')
|
||||
H, p, x, r = ctx.householder(ctx.extend(A, b))
|
||||
@@ -402,7 +444,7 @@ class LinearAlgebraMethods:
|
||||
# calculate residual "manually" for determined systems
|
||||
if res == 0:
|
||||
res = ctx.norm(ctx.residual(A, x, b))
|
||||
return ctx.matrix(x, **kwargs), res
|
||||
return ctx.matrix(x), res
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
|
||||
@@ -494,7 +536,7 @@ class LinearAlgebraMethods:
|
||||
L[i,j] = (A[i,j] - t) / L[j,j]
|
||||
return L
|
||||
|
||||
def cholesky_solve(ctx, A, b, **kwargs):
|
||||
def cholesky_solve(ctx, A, b):
|
||||
"""
|
||||
Ax = b => x
|
||||
|
||||
@@ -510,7 +552,7 @@ class LinearAlgebraMethods:
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A nor b
|
||||
A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy()
|
||||
A, b = ctx.matrix(A).copy(), ctx.matrix(b).copy()
|
||||
if A.rows != A.cols:
|
||||
raise ValueError('can only solve determined system')
|
||||
# Cholesky factorization
|
||||
@@ -539,33 +581,35 @@ class LinearAlgebraMethods:
|
||||
|
||||
Determinant of identity is 1.
|
||||
|
||||
>>> from mpmath import eye, matrix, det
|
||||
>>> from mpmath import eye, matrix, det, mp
|
||||
>>> mp.pretty = True
|
||||
>>> A = eye(3)
|
||||
>>> print(det(A))
|
||||
>>> det(A)
|
||||
1.0
|
||||
|
||||
The determinant of a 0 by 0 matrix is 1 as the product of no factors
|
||||
is by convention the multiplicative identity.
|
||||
|
||||
>>> A = matrix(0, 0)
|
||||
>>> print(det(A))
|
||||
>>> det(A)
|
||||
1
|
||||
|
||||
But in general a matrix can have any number as its determinant.
|
||||
|
||||
>>> A = matrix([[2, 6, 4],[3, 8, 6],[1, 1, 2]])
|
||||
>>> print(det(A))
|
||||
>>> det(A)
|
||||
0
|
||||
|
||||
The determinant is vanishing if a matrix has no inverse.
|
||||
|
||||
>>> A = matrix([[1, 3, 2],[0, 1, 0],[0, 0, 0]])
|
||||
>>> print(det(A))
|
||||
>>> det(A)
|
||||
0
|
||||
|
||||
But, matrix has determinate different from zero full rank if and only is is equivalent to identity,
|
||||
|
||||
>>> A = matrix([[1, 3, -2], [1, 9, -6], [1, 4, -3]])
|
||||
>>> print(det(A))
|
||||
>>> det(A)
|
||||
-2.0
|
||||
|
||||
i.e. has an inverse matrix.
|
||||
@@ -573,7 +617,7 @@ class LinearAlgebraMethods:
|
||||
>>> B = matrix([[3, -1, 0], [3, 1, -4], [5, 1, -6]]) / 2
|
||||
>>> A*B == eye(3)
|
||||
True
|
||||
>>> print(det(B))
|
||||
>>> det(B)
|
||||
-0.5
|
||||
|
||||
Moreover, a matrix of integers has an inverse matrix of integers
|
||||
@@ -583,8 +627,8 @@ class LinearAlgebraMethods:
|
||||
>>> B = matrix([[3, -1, 1],[2, 1, 0],[-2, 1, -1]])
|
||||
>>> A*B == eye(3)
|
||||
True
|
||||
>>> print(det(A), det(B))
|
||||
-1.0 -1.0
|
||||
>>> det(A), det(B)
|
||||
(-1.0, -1.0)
|
||||
|
||||
"""
|
||||
prec = ctx.prec
|
||||
|
||||
+11
-20
@@ -1,5 +1,3 @@
|
||||
import warnings
|
||||
|
||||
# TODO: interpret list as vectors (for multiplication)
|
||||
|
||||
# pickling helper
|
||||
@@ -284,19 +282,12 @@ class _matrix:
|
||||
mathematical properties you might expect from a norm.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, *args):
|
||||
self._data = {}
|
||||
# LU decompostion cache, this is useful when solving the same system
|
||||
# multiple times, when calculating the inverse and when calculating the
|
||||
# determinant
|
||||
self._LU = None
|
||||
if "force_type" in kwargs:
|
||||
warnings.warn("The force_type argument was removed, it did not work"
|
||||
" properly anyway. If you want to force floating-point or"
|
||||
" interval computations, use the respective methods from `fp`"
|
||||
" or `mp` instead, e.g., `fp.matrix()` or `iv.matrix()`."
|
||||
" If you want to truncate values to integer, use .apply(int) instead.",
|
||||
DeprecationWarning)
|
||||
if isinstance(args[0], (list, tuple)):
|
||||
if not args[0]:
|
||||
self._rows = 0
|
||||
@@ -829,16 +820,16 @@ class MatrixMethods:
|
||||
ctx.matrix.ctx = ctx
|
||||
ctx.matrix.convert = ctx.convert
|
||||
|
||||
def eye(ctx, n, **kwargs):
|
||||
def eye(ctx, n):
|
||||
"""
|
||||
Create square identity matrix n x n.
|
||||
"""
|
||||
A = ctx.matrix(n, **kwargs)
|
||||
A = ctx.matrix(n)
|
||||
for i in range(n):
|
||||
A[i,i] = 1
|
||||
return A
|
||||
|
||||
def diag(ctx, diagonal, **kwargs):
|
||||
def diag(ctx, diagonal):
|
||||
"""
|
||||
Create square diagonal matrix using given list.
|
||||
|
||||
@@ -850,12 +841,12 @@ class MatrixMethods:
|
||||
['0.0', '2.0', '0.0'],
|
||||
['0.0', '0.0', '3.0']])
|
||||
"""
|
||||
A = ctx.matrix(len(diagonal), **kwargs)
|
||||
A = ctx.matrix(len(diagonal))
|
||||
for i in range(len(diagonal)):
|
||||
A[i,i] = diagonal[i]
|
||||
return A
|
||||
|
||||
def zeros(ctx, *args, **kwargs):
|
||||
def zeros(ctx, *args):
|
||||
"""
|
||||
Create matrix m x n filled with zeros.
|
||||
One given dimension will create square matrix n x n.
|
||||
@@ -874,13 +865,13 @@ class MatrixMethods:
|
||||
n = args[1]
|
||||
else:
|
||||
raise TypeError('zeros expected at most 2 arguments, got %i' % len(args))
|
||||
A = ctx.matrix(m, n, **kwargs)
|
||||
A = ctx.matrix(m, n)
|
||||
for i in range(m):
|
||||
for j in range(n):
|
||||
A[i,j] = 0
|
||||
return A
|
||||
|
||||
def ones(ctx, *args, **kwargs):
|
||||
def ones(ctx, *args):
|
||||
"""
|
||||
Create matrix m x n filled with ones.
|
||||
One given dimension will create square matrix n x n.
|
||||
@@ -899,7 +890,7 @@ class MatrixMethods:
|
||||
n = args[1]
|
||||
else:
|
||||
raise TypeError('ones expected at most 2 arguments, got %i' % len(args))
|
||||
A = ctx.matrix(m, n, **kwargs)
|
||||
A = ctx.matrix(m, n)
|
||||
for i in range(m):
|
||||
for j in range(n):
|
||||
A[i,j] = 1
|
||||
@@ -921,7 +912,7 @@ class MatrixMethods:
|
||||
A[i,j] = ctx.one / (i + j + 1)
|
||||
return A
|
||||
|
||||
def randmatrix(ctx, m, n=None, min=0, max=1, **kwargs):
|
||||
def randmatrix(ctx, m, n=None, min=0, max=1):
|
||||
"""
|
||||
Create a random m x n matrix.
|
||||
|
||||
@@ -937,7 +928,7 @@ class MatrixMethods:
|
||||
"""
|
||||
if not n:
|
||||
n = m
|
||||
A = ctx.matrix(m, n, **kwargs)
|
||||
A = ctx.matrix(m, n)
|
||||
for i in range(m):
|
||||
for j in range(n):
|
||||
A[i,j] = ctx.rand() * (max - min) + min
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import warnings
|
||||
|
||||
def __getattr__(name):
|
||||
warnings.warn("the rational private module is deprecated",
|
||||
DeprecationWarning)
|
||||
if name == 'mpq':
|
||||
from fractions import Fraction
|
||||
class mpq(Fraction):
|
||||
_mpq_ = property(Fraction.as_integer_ratio)
|
||||
return mpq
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
+179
-83
@@ -3,6 +3,8 @@ import decimal
|
||||
import math
|
||||
import operator
|
||||
import random
|
||||
import sys
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import pytest
|
||||
@@ -11,13 +13,15 @@ from hypothesis import strategies as st
|
||||
|
||||
import mpmath
|
||||
from mpmath import (ceil, fadd, fdiv, floor, fmul, fneg, fp, frac, fsub, inf,
|
||||
isinf, isint, isnan, isnormal, isspecial, iv, monitor, mp,
|
||||
mpc, mpf, mpi, nan, ninf, nint, nint_distance, nstr, pi,
|
||||
isinf, isint, isnan, isnormal, iv, monitor, mp, mpc, mpf,
|
||||
mpi, nan, ninf, nint, nint_distance, nstr, pi, rand,
|
||||
workprec)
|
||||
from mpmath.libmp import (MPQ, MPZ, finf, fnan, fninf, fnone, fone, from_float,
|
||||
from_int, from_pickable, from_str, isprime, mpf_add,
|
||||
mpf_mul, mpf_sub, round_down, round_nearest,
|
||||
round_up, to_int, to_man_exp, to_pickable)
|
||||
from mpmath.libmp import (MPZ, finf, fnan, fninf, fnone, fone, from_float,
|
||||
from_int, from_str, mpf_add, mpf_mul, mpf_sub,
|
||||
round_down, round_nearest, round_up, to_float,
|
||||
to_int, to_man_exp)
|
||||
from mpmath.libmp.backend import MPQ
|
||||
from mpmath.libmp.libintmath import isprime, jacobi_symbol
|
||||
|
||||
|
||||
def test_type_compare():
|
||||
@@ -121,6 +125,9 @@ def test_pow():
|
||||
assert inf ** mpf(0) == mpf(1)
|
||||
assert ninf ** mpf(0) == mpf(1)
|
||||
assert nan ** mpf(0) == mpf(1)
|
||||
assert mpc(1, -inf)**3 == mpc(-inf, inf)
|
||||
assert mpc(1, -inf)**4 == mpc(inf, inf)
|
||||
|
||||
|
||||
def test_mixed_misc():
|
||||
assert 1 + mpf(3) == mpf(3) + 1 == 4
|
||||
@@ -152,10 +159,11 @@ def test_mpf_init():
|
||||
assert a1 != a3
|
||||
assert str(a1) == '0.300000190734863'
|
||||
assert str(a3) == '0.3'
|
||||
pytest.raises(ValueError, lambda: mpf((1, 2, 3)))
|
||||
pytest.raises(ValueError, lambda: mpf((1,)))
|
||||
pytest.raises(ValueError, lambda: mpf(mpi(1, 2)))
|
||||
pytest.raises(TypeError, lambda: mpf(object()))
|
||||
pytest.raises(TypeError, lambda: mpf(1 + 1j))
|
||||
pytest.raises(ValueError, lambda: mpf(1, prec=111, dps=222))
|
||||
class SomethingReal:
|
||||
def _mpmath_(self, prec, rounding):
|
||||
return mp.make_mpf(from_str('1.3', prec, rounding))
|
||||
@@ -180,6 +188,8 @@ def test_mpf_init():
|
||||
assert mpf('0x1.4ace478p+33') == mpf(11100000000.0)
|
||||
assert mpf('0x1.4ace478p+33', base=0) == mpf(11100000000.0)
|
||||
assert mpf('1.4ace478p+33', base=16) == mpf(11100000000.0)
|
||||
assert mpf((1, 17813873926281399, -78, 54), prec=5,
|
||||
rounding='u') == mpf('-5.9604644775390625e-8')
|
||||
|
||||
assert mpf(float('+inf')) == +inf
|
||||
assert mpf(float('-inf')) == -inf
|
||||
@@ -462,40 +472,50 @@ def test_isnan_etc():
|
||||
assert isinf(MPQ(3, 2)) is False
|
||||
assert isinf(MPQ(0, 1)) is False
|
||||
pytest.raises(TypeError, lambda: isinf(object()))
|
||||
assert isspecial(3) is False
|
||||
assert isspecial(3.5) is False
|
||||
assert isspecial(mpf(3.5)) is False
|
||||
assert isspecial(0) is True
|
||||
assert isspecial(mpf(0)) is True
|
||||
assert isspecial(0.0) is True
|
||||
assert isspecial(inf) is True
|
||||
assert isspecial(-inf) is True
|
||||
assert isspecial(nan) is True
|
||||
assert isspecial(float(inf)) is True
|
||||
assert isspecial(mpc(0, 0)) is True
|
||||
assert isspecial(mpc(3, 0)) is False
|
||||
assert isspecial(mpc(0, 3)) is False
|
||||
assert isspecial(mpc(3, 3)) is False
|
||||
assert isspecial(mpc(0, nan)) is True
|
||||
assert isspecial(mpc(0, inf)) is True
|
||||
assert isspecial(mpc(3, nan)) is True
|
||||
assert isspecial(mpc(3, inf)) is True
|
||||
assert isspecial(mpc(3, -inf)) is True
|
||||
assert isspecial(mpc(nan, 0)) is True
|
||||
assert isspecial(mpc(inf, 0)) is True
|
||||
assert isspecial(mpc(nan, 3)) is True
|
||||
assert isspecial(mpc(inf, 3)) is True
|
||||
assert isspecial(mpc(inf, nan)) is True
|
||||
assert isspecial(mpc(nan, inf)) is True
|
||||
assert isspecial(mpc(nan, nan)) is True
|
||||
assert isspecial(mpc(inf, inf)) is True
|
||||
assert isspecial(MPQ(3, 2)) is False
|
||||
assert isspecial(MPQ(0, 1)) is True
|
||||
pytest.raises(TypeError, lambda: isspecial(object()))
|
||||
assert isspecial(5e-324) is False # issue 946
|
||||
assert fp.isspecial(5e-324) is False
|
||||
assert fp.isspecial(0.0) is True
|
||||
assert fp.isspecial(-0.0) is True
|
||||
assert isnormal(3) is True
|
||||
assert isnormal(3.5) is True
|
||||
assert isnormal(mpf(3.5)) is True
|
||||
assert isnormal(0) is False
|
||||
assert isnormal(mpf(0)) is False
|
||||
assert isnormal(0.0) is False
|
||||
assert isnormal(inf) is False
|
||||
assert isnormal(-inf) is False
|
||||
assert isnormal(nan) is False
|
||||
assert isnormal(float(inf)) is False
|
||||
assert isnormal(mpc(0, 0)) is False
|
||||
assert isnormal(mpc(3, 0)) is True
|
||||
assert isnormal(mpc(0, 3)) is True
|
||||
assert isnormal(mpc(3, 3)) is True
|
||||
assert isnormal(mpc(0, nan)) is False
|
||||
assert isnormal(mpc(0, inf)) is False
|
||||
assert isnormal(mpc(3, nan)) is False
|
||||
assert isnormal(mpc(3, inf)) is False
|
||||
assert isnormal(mpc(3, -inf)) is False
|
||||
assert isnormal(mpc(nan, 0)) is False
|
||||
assert isnormal(mpc(inf, 0)) is False
|
||||
assert isnormal(mpc(nan, 3)) is False
|
||||
assert isnormal(mpc(inf, 3)) is False
|
||||
assert isnormal(mpc(inf, nan)) is False
|
||||
assert isnormal(mpc(nan, inf)) is False
|
||||
assert isnormal(mpc(nan, nan)) is False
|
||||
assert isnormal(mpc(inf, inf)) is False
|
||||
assert isnormal(MPQ(3, 2)) is True
|
||||
assert isnormal(MPQ(0, 1)) is False
|
||||
pytest.raises(TypeError, lambda: isnormal(object()))
|
||||
assert isnormal(math.nextafter(0, 1)) is True # issue 946
|
||||
assert fp.isnormal(math.nextafter(0, 1)) is False
|
||||
assert fp.isnormal(0.0) is False
|
||||
assert fp.isnormal(-0.0) is False
|
||||
assert fp.isnormal(fp.nan) is False
|
||||
assert fp.isnormal(fp.inf) is False
|
||||
assert fp.isnormal(fp.ninf) is False
|
||||
assert fp.isnormal(1.0) is True
|
||||
assert fp.isnormal(sys.float_info.min) is True
|
||||
assert fp.isnormal(1+0j) is True
|
||||
assert fp.isnormal(0j) is False
|
||||
assert fp.isnormal(-0j) is False
|
||||
assert fp.isnormal(1+1j) is True
|
||||
assert fp.isnormal(complex('inf+1j')) is False
|
||||
assert isint(3) is True
|
||||
assert isint(0) is True
|
||||
assert isint(int(3)) is True
|
||||
@@ -544,13 +564,6 @@ def test_isnan_etc():
|
||||
assert mp.isnpint(-1 + 0.1j) is False
|
||||
assert mp.isnpint(0 + 0.1j) is False
|
||||
assert mp.isnpint(inf) is False
|
||||
with pytest.deprecated_call():
|
||||
for ctx in [mp, fp]:
|
||||
assert ctx.isnormal(1) is True
|
||||
assert ctx.isnormal(0.0) is False
|
||||
assert ctx.isnormal(ctx.mpc(0)) is False
|
||||
assert ctx.isnormal(ctx.mpc(0, 1)) is True
|
||||
assert ctx.isnormal(ctx.mpc(1, inf)) is False
|
||||
|
||||
|
||||
def test_isprime():
|
||||
@@ -569,34 +582,8 @@ def test_ctx_mag():
|
||||
assert mp.mag(MPQ(2)) == 2
|
||||
assert mp.mag(MPQ(0)) == mpf('-inf')
|
||||
|
||||
|
||||
def test_ctx_mp_mpnumeric():
|
||||
with pytest.deprecated_call():
|
||||
from mpmath.ctx_mp import mpnumeric
|
||||
|
||||
def test_to_man_exp_deprecation():
|
||||
with pytest.deprecated_call():
|
||||
to_man_exp(fnone)
|
||||
|
||||
def test_rational_deprecation():
|
||||
with pytest.deprecated_call():
|
||||
assert mpmath.rational.mpq(1, 2) == MPQ(1, 2)
|
||||
with pytest.deprecated_call():
|
||||
pytest.raises(AttributeError, lambda: mpmath.rational.spam)
|
||||
|
||||
|
||||
def test_math2_deprecation():
|
||||
with pytest.deprecated_call():
|
||||
assert mpmath.math2.log == mpmath.libfp.log
|
||||
|
||||
|
||||
def test_to_from_pickable():
|
||||
x = mpf(1.2)._mpf_
|
||||
with pytest.deprecated_call():
|
||||
assert to_pickable(x) == x
|
||||
with pytest.deprecated_call():
|
||||
assert from_pickable(x) == x
|
||||
|
||||
def test_to_man_exp():
|
||||
assert to_man_exp(fnone, signed=False) == (1, 0)
|
||||
|
||||
def test_rand_precision():
|
||||
"""
|
||||
@@ -652,6 +639,7 @@ def test_issue_260():
|
||||
@example(2.675, 2)
|
||||
@example(math.inf, 3)
|
||||
@example(-math.inf, 1)
|
||||
@example(8.9884656743115795e+307, 0)
|
||||
def test_round_bulk(x, n):
|
||||
mp.prec = fp.prec
|
||||
m = mpf(x)
|
||||
@@ -700,13 +688,6 @@ def test_issue_985():
|
||||
assert mpc(-1) in {1, -1}
|
||||
|
||||
|
||||
def test_mpfmpc_log_deprecation():
|
||||
with pytest.deprecated_call():
|
||||
mpmath.libmp.mpf_log(mpf(123)._mpf_, 53)
|
||||
with pytest.deprecated_call():
|
||||
mpmath.libmp.mpc_log(mpc(123)._mpc_, 53)
|
||||
|
||||
|
||||
def test_issue_975():
|
||||
def worker():
|
||||
mp = mpmath.MPContext()
|
||||
@@ -717,3 +698,118 @@ def test_issue_975():
|
||||
for i in range(sz):
|
||||
futures[i] = tpe.submit(worker)
|
||||
assert len(collections.Counter(f.result() for f in futures))
|
||||
|
||||
|
||||
def test_to_float():
|
||||
# coverage tests
|
||||
mp.dps = 1000
|
||||
|
||||
x = mpf('0b1.1111111111111111111111111111111111111'
|
||||
'11111111111111011p-1023')
|
||||
assert float(x).hex() == '0x0.fffffffffffffp-1022'
|
||||
x = mpf('0b1.1111111111111111111111111111111111111'
|
||||
'11111111111111111p-1023')
|
||||
assert float(x).hex() == '0x1.0000000000000p-1022'
|
||||
|
||||
assert math.isnan(float(mpf('nan')))
|
||||
assert float(-mpf('0x1.1p-1075')) == float.fromhex('-0x0.0000000000001p-1022')
|
||||
assert float(mpf('0x1.1p-1075')) == float.fromhex('0x0.0000000000001p-1022')
|
||||
|
||||
assert to_float(mpf('0x1p3000')._mpf_) == sys.float_info.max
|
||||
assert to_float((-mpf('0x1p3000'))._mpf_) == -sys.float_info.max
|
||||
pytest.raises(OverflowError, lambda: to_float(mpf('0x1p3000')._mpf_,
|
||||
strict=True,
|
||||
rnd=round_nearest))
|
||||
pytest.raises(OverflowError, lambda: to_float((-mpf('0x1p3000'))._mpf_,
|
||||
strict=True,
|
||||
rnd=round_nearest))
|
||||
|
||||
def test_issue_1078():
|
||||
mp.dps = 5000 # way too large
|
||||
|
||||
# These are adjacent denormals (in 64-bit doubles)
|
||||
lo = mpf("0x0.0000000000001p-1022")
|
||||
hi = mpf("0x0.0000000000002p-1022")
|
||||
|
||||
# Take a value that's a tiny bit below the
|
||||
# midpoint (i.e. closer to `lo`):
|
||||
mid = (lo + hi) / 2
|
||||
|
||||
# Offset of 2^-52 ULP: correctly rounds to lo
|
||||
val_ok = mid - mpf(2) ** -(1074 + 52)
|
||||
# Offset of 2^-53 ULP: was incorrectly rounded to hi (even)
|
||||
val_bad = mid - mpf(2) ** -(1074 + 53)
|
||||
|
||||
assert float(val_ok) == float(val_bad) == float(lo)
|
||||
|
||||
|
||||
def test_jacobi_symbol():
|
||||
assert jacobi_symbol(25, 41) == 1
|
||||
assert jacobi_symbol(-23, 83) == -1
|
||||
assert jacobi_symbol(3, 9) == 0
|
||||
assert jacobi_symbol(42, 97) == -1
|
||||
assert jacobi_symbol(3, 5) == -1
|
||||
assert jacobi_symbol(7, 9) == 1
|
||||
assert jacobi_symbol(0, 3) == 0
|
||||
assert jacobi_symbol(0, 1) == 1
|
||||
assert jacobi_symbol(2, 1) == 1
|
||||
assert jacobi_symbol(1, 3) == 1
|
||||
pytest.raises(ValueError, lambda: jacobi_symbol(3, 8))
|
||||
assert jacobi_symbol(10, 3) == 1
|
||||
assert jacobi_symbol(10, -3) == 1
|
||||
assert jacobi_symbol(-10, 3) == -1
|
||||
assert jacobi_symbol(-10, -3) == 1
|
||||
assert jacobi_symbol(11, 3) == -1
|
||||
assert jacobi_symbol(11, -3) == -1
|
||||
assert jacobi_symbol(-11, 3) == 1
|
||||
assert jacobi_symbol(-11, -3) == -1
|
||||
|
||||
|
||||
def test_issue_1116():
|
||||
mp.prec = 54
|
||||
x = mpf('0x1.d55368e2bef2p-4')
|
||||
assert repr(x) != "mpf('0.11458149882303958')"
|
||||
assert eval(repr(x)) == x
|
||||
|
||||
|
||||
def test_eval_repr_roundtrip():
|
||||
for _ in range(10):
|
||||
prec = random.randint(10, 1001)
|
||||
with workprec(prec):
|
||||
for _ in range(1000):
|
||||
x = rand()
|
||||
assert eval(repr(x)) == x, (prec, x)
|
||||
n = random.randint(-100, 300)
|
||||
if n > 0:
|
||||
x *= 10**n
|
||||
elif x < 0:
|
||||
x /= 10**n
|
||||
assert eval(repr(x)) == x, (prec, x)
|
||||
|
||||
|
||||
def test_issue_1135():
|
||||
for _ in range(100):
|
||||
n = 4
|
||||
barrier = threading.Barrier(n)
|
||||
bad = []
|
||||
|
||||
def worker(index):
|
||||
mp = mpmath.MPContext()
|
||||
|
||||
for iteration in range(100):
|
||||
mp.prec = 100 + 100 * iteration + 10 * index
|
||||
barrier.wait()
|
||||
|
||||
value = float(+mp.pi)
|
||||
if value != math.pi:
|
||||
bad.append((mp.prec, value))
|
||||
|
||||
threads = [threading.Thread(target=worker, args=(i,))
|
||||
for i in range(n)]
|
||||
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
assert not bad
|
||||
|
||||
@@ -2,24 +2,14 @@
|
||||
Test bit-level integer and mpf operations
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from mpmath import eps, fadd, ldexp, mp, mpc, mpf
|
||||
from mpmath.libmp import (MPZ, bitcount, fone, from_float, from_man_exp, fzero,
|
||||
mpf_add, mpf_neg, mpf_perturb, mpf_sub,
|
||||
round_ceiling, round_down, round_floor,
|
||||
round_nearest, round_up, to_float, trailing)
|
||||
from mpmath.libmp import (MPZ, fone, from_float, from_man_exp, fzero, mpf_add,
|
||||
mpf_neg, mpf_sub, round_ceiling, round_down,
|
||||
round_floor, round_nearest, round_up, to_float)
|
||||
from mpmath.libmp.libintmath import trailing
|
||||
from mpmath.libmp.libmpf import mpf_perturb
|
||||
|
||||
|
||||
def test_bitcount():
|
||||
with pytest.deprecated_call():
|
||||
assert bitcount(0) == 0
|
||||
assert bitcount(1) == 1
|
||||
assert bitcount(7) == 3
|
||||
assert bitcount(8) == 4
|
||||
assert bitcount(2**100) == 101
|
||||
assert bitcount(2**100-1) == 100
|
||||
|
||||
def test_trailing():
|
||||
assert trailing(0) == 0
|
||||
assert trailing(1) == 0
|
||||
@@ -82,22 +72,22 @@ def test_round_nearest():
|
||||
|
||||
def test_rounding_bugs():
|
||||
# 1 less than power-of-two cases
|
||||
assert from_man_exp(MPZ(72057594037927935), -56, 53, round_up) == (0, 1, 0, 1)
|
||||
assert from_man_exp(MPZ(73786976294838205979), -65, 53, round_nearest) == (0, 1, 1, 1)
|
||||
assert from_man_exp(MPZ(31), 0, 4, round_up) == (0, 1, 5, 1)
|
||||
assert from_man_exp(MPZ(-31), 0, 4, round_floor) == (1, 1, 5, 1)
|
||||
assert from_man_exp(MPZ(255), 0, 7, round_up) == (0, 1, 8, 1)
|
||||
assert from_man_exp(MPZ(-255), 0, 7, round_floor) == (1, 1, 8, 1)
|
||||
assert from_man_exp(MPZ(72057594037927935), -56, 53, round_up)[:3] == (0, 1, 0)
|
||||
assert from_man_exp(MPZ(73786976294838205979), -65, 53, round_nearest)[:3] == (0, 1, 1)
|
||||
assert from_man_exp(MPZ(31), 0, 4, round_up)[:3] == (0, 1, 5)
|
||||
assert from_man_exp(MPZ(-31), 0, 4, round_floor)[:3] == (1, 1, 5)
|
||||
assert from_man_exp(MPZ(255), 0, 7, round_up)[:3] == (0, 1, 8)
|
||||
assert from_man_exp(MPZ(-255), 0, 7, round_floor)[:3] == (1, 1, 8)
|
||||
|
||||
def test_rounding_issue_200():
|
||||
a = from_man_exp(MPZ(9867),-100)
|
||||
b = from_man_exp(MPZ(9867),-200)
|
||||
c = from_man_exp(MPZ(-1),0)
|
||||
z = (1, 1023, -10, 10)
|
||||
assert mpf_add(a, c, 10, 'd') == z
|
||||
assert mpf_add(b, c, 10, 'd') == z
|
||||
assert mpf_add(c, a, 10, 'd') == z
|
||||
assert mpf_add(c, b, 10, 'd') == z
|
||||
z = (1, 1023, -10)
|
||||
assert mpf_add(a, c, 10, 'd')[:3] == z
|
||||
assert mpf_add(b, c, 10, 'd')[:3] == z
|
||||
assert mpf_add(c, a, 10, 'd')[:3] == z
|
||||
assert mpf_add(c, b, 10, 'd')[:3] == z
|
||||
|
||||
def test_perturb():
|
||||
a = fone
|
||||
|
||||
+100
-41
@@ -1,71 +1,62 @@
|
||||
import pytest
|
||||
from hypothesis import given
|
||||
from hypothesis import strategies as st
|
||||
|
||||
from mpmath import (arange, chebyfit, cos, cosm, differint, e, euler, exp,
|
||||
expm, fourier, fourierval, inf, invertlaplace, j, limit,
|
||||
log, matrix, mp, mpf, norm, pade, pi, polyroots, polyval,
|
||||
sin, sinm, sqrt)
|
||||
expm, fft, fourier, fourierval, inf, invertlaplace, invfft,
|
||||
j, limit, log, logm, matrix, mp, mpf, norm, pade, pi,
|
||||
polyroots, polyval, sin, sinm, sqrt)
|
||||
|
||||
|
||||
def test_approximation():
|
||||
f = lambda x: cos(2-2*x)/x
|
||||
p, err = chebyfit(f, [2, 4], 8, error=True, asc=True)
|
||||
p, err = chebyfit(f, [2, 4], 8, error=True)
|
||||
assert err < 1e-5
|
||||
for i in range(10):
|
||||
x = 2 + i/5.
|
||||
assert abs(polyval(p, x, asc=True) - f(x)) < err
|
||||
assert abs(polyval(p, x) - f(x)) < err
|
||||
|
||||
def test_chebyfit_deprecated():
|
||||
def test_chebyfit():
|
||||
f = lambda x: cos(2-2*x)/x
|
||||
with pytest.deprecated_call():
|
||||
p, err = chebyfit(f, [2, 4], 8, error=True)
|
||||
p, err = chebyfit(f, [2, 4], 8, error=True, asc=False)
|
||||
assert err < 1e-5
|
||||
p = p[::-1]
|
||||
for i in range(10):
|
||||
x = 2 + i/5.
|
||||
assert abs(polyval(p, x, asc=True) - f(x)) < err
|
||||
assert abs(polyval(p, x) - f(x)) < err
|
||||
|
||||
def test_chebyfit_nonpositive_N():
|
||||
with pytest.raises(ValueError):
|
||||
chebyfit(sin, [-1, 1], 0)
|
||||
|
||||
def test_limits():
|
||||
assert limit(lambda x: (x-sin(x))/x**3, 0).ae(mpf(1)/6)
|
||||
assert limit(lambda n: (1+1/n)**n, inf).ae(e)
|
||||
|
||||
def test_polyval():
|
||||
assert polyval([], 3, asc=True) == 0
|
||||
assert polyval([0], 3, asc=True) == 0
|
||||
assert polyval([5], 3, asc=True) == 5
|
||||
assert polyval([], 3) == 0
|
||||
assert polyval([0], 3) == 0
|
||||
assert polyval([5], 3) == 5
|
||||
# 4x^3 - 2x + 5
|
||||
p = [5, -2, 0, 4]
|
||||
assert polyval(p,4,asc=True) == 253
|
||||
assert polyval(p,4,derivative=True,asc=True) == (253, 190)
|
||||
|
||||
def test_polyval_asc_false():
|
||||
assert polyval(p, 4) == 253
|
||||
assert polyval(p, 4, derivative=True) == (253, 190)
|
||||
assert polyval([1, 2, 3], 2, asc=False) == 11
|
||||
|
||||
def test_polyval_deprecated():
|
||||
with pytest.deprecated_call():
|
||||
p = [4, 0, -2, 5]
|
||||
assert polyval(p,4) == 253
|
||||
assert polyval(list(reversed(p)), 4, asc=False) == 253
|
||||
|
||||
def test_polyroots():
|
||||
p = polyroots([-4,1], asc=True)
|
||||
p = polyroots([-4,1])
|
||||
assert p[0].ae(4)
|
||||
p, q = polyroots([3,2,1], asc=True)
|
||||
p, q = polyroots([3,2,1])
|
||||
assert p.ae(-1 - sqrt(2)*j)
|
||||
assert q.ae(-1 + sqrt(2)*j)
|
||||
#this is not a real test, it only tests a specific case
|
||||
assert polyroots([1], asc=True) == []
|
||||
pytest.raises(ValueError, lambda: polyroots([0], asc=True))
|
||||
|
||||
def test_polyroots_asc_false():
|
||||
assert polyroots([1]) == []
|
||||
pytest.raises(ValueError, lambda: polyroots([0]))
|
||||
p, q = polyroots([1,2,3], asc=False)
|
||||
assert p.ae(-1 - sqrt(2)*j)
|
||||
assert q.ae(-1 + sqrt(2)*j)
|
||||
|
||||
def test_polyroots_deprecated():
|
||||
with pytest.deprecated_call():
|
||||
p, q = polyroots([1,2,3])
|
||||
assert p.ae(-1 - sqrt(2)*j)
|
||||
assert q.ae(-1 + sqrt(2)*j)
|
||||
|
||||
def test_polyroots_legendre():
|
||||
n = 64
|
||||
coeffs = [916312070471295267, 0, -1905929106580294155360, 0,
|
||||
@@ -102,10 +93,10 @@ def test_polyroots_legendre():
|
||||
with mp.workdps(3):
|
||||
with pytest.raises(mp.NoConvergence):
|
||||
polyroots(coeffs, maxsteps=5, cleanup=True, error=False,
|
||||
extraprec=n*10, asc=True)
|
||||
extraprec=n*10)
|
||||
|
||||
roots = polyroots(coeffs, maxsteps=50, cleanup=True, error=False,
|
||||
extraprec=n*10, asc=True)
|
||||
extraprec=n*10)
|
||||
roots = [str(r) for r in roots]
|
||||
assert roots == \
|
||||
['-0.999', '-0.996', '-0.991', '-0.983', '-0.973', '-0.961',
|
||||
@@ -167,16 +158,15 @@ def test_polyroots_legendre_init():
|
||||
'0.983', '0.991', '0.996', '0.999', '1.0'])
|
||||
with mp.workdps(2*mp.dps):
|
||||
roots_exact = polyroots(coeffs, maxsteps=50, cleanup=True, error=False,
|
||||
extraprec=2*extra_prec, asc=True)
|
||||
extraprec=2*extra_prec)
|
||||
with pytest.raises(mp.NoConvergence):
|
||||
polyroots(coeffs, maxsteps=5, cleanup=True, error=False,
|
||||
extraprec=extra_prec, asc=True)
|
||||
extraprec=extra_prec)
|
||||
roots,err = polyroots(coeffs, maxsteps=5, cleanup=True, error=True,
|
||||
extraprec=extra_prec,roots_init=roots_init, asc=True)
|
||||
extraprec=extra_prec,roots_init=roots_init)
|
||||
assert max(matrix(roots_exact)-matrix(roots).apply(abs)) < err
|
||||
roots1,err1 = polyroots(coeffs, maxsteps=25, cleanup=True, error=True,
|
||||
extraprec=extra_prec,roots_init=roots_init[:60],
|
||||
asc=True)
|
||||
extraprec=extra_prec,roots_init=roots_init[:60])
|
||||
assert max(matrix(roots_exact)-matrix(roots1).apply(abs)) < err1
|
||||
|
||||
def test_pade():
|
||||
@@ -190,7 +180,7 @@ def test_pade():
|
||||
a.append(one/k)
|
||||
p, q = pade(a, N//2, N//2)
|
||||
for x in arange(0, 1, 0.1):
|
||||
r = polyval(p, x, asc=True)/polyval(q, x, asc=True)
|
||||
r = polyval(p, x)/polyval(q, x)
|
||||
assert r.ae(exp(x), 1.0e-10)
|
||||
|
||||
def test_fourier():
|
||||
@@ -213,9 +203,17 @@ def test_invlap():
|
||||
ft = lambda t: t*exp(-t)
|
||||
ftt = ft(t)
|
||||
assert invertlaplace(fp,t,method='talbot').ae(ftt)
|
||||
assert mp.invlaptalbot(fp, t).ae(ftt)
|
||||
assert invertlaplace(fp,t,method='talbot', degree=35).ae(ftt)
|
||||
assert invertlaplace(fp,t,method='stehfest').ae(ftt)
|
||||
assert mp.invlapstehfest(fp, t).ae(ftt)
|
||||
assert invertlaplace(fp,t,method='stehfest', degree=45).ae(ftt)
|
||||
assert invertlaplace(fp,t,method='dehoog').ae(ftt)
|
||||
assert mp.invlapdehoog(fp, t).ae(ftt)
|
||||
assert invertlaplace(fp,t,method='dehoog', degree=20).ae(ftt)
|
||||
assert invertlaplace(fp,t,method='cohen').ae(ftt)
|
||||
assert mp.invlapcohen(fp, t).ae(ftt)
|
||||
assert invertlaplace(fp,t,method='cohen', degree=50).ae(ftt)
|
||||
t = 1.0
|
||||
ftt = ft(t)
|
||||
assert invertlaplace(fp,t,method='talbot').ae(ftt)
|
||||
@@ -283,3 +281,64 @@ def test_cosm_sinm():
|
||||
A = [[1, 0], [0, 1], [0, 0]]
|
||||
pytest.raises(ValueError, lambda: cosm(A))
|
||||
pytest.raises(ValueError, lambda: sinm(A))
|
||||
|
||||
def test_logm():
|
||||
# Test for zero matrix
|
||||
A = [[0, 0], [0, 0]]
|
||||
pytest.raises(ValueError, lambda: logm(A))
|
||||
|
||||
def test_fft():
|
||||
assert fft([]) == []
|
||||
assert fft([1]) == [1]
|
||||
pytest.raises(NotImplementedError, lambda: fft([1, 2, 3]))
|
||||
assert fft([1, 0, 0, 0]) == [1, 1, 1, 1]
|
||||
|
||||
spectrum = fft([0, 1, 0, 0])
|
||||
expected = [1, -1j, -1, 1j]
|
||||
assert all(a.ae(b) for a, b in zip(spectrum, expected))
|
||||
|
||||
spectrum = fft([1, 2, 3, 4])
|
||||
expected = [10, -2 + 2j, -2, -2 - 2j]
|
||||
assert all(a.ae(b) for a, b in zip(spectrum, expected))
|
||||
assert mp.chop(invfft(spectrum)) == [1, 2, 3, 4]
|
||||
|
||||
spectrum = fft([1, j, -1, -j])
|
||||
expected = [0, 4, 0, 0]
|
||||
assert all(a.ae(b) for a, b in zip(spectrum, expected))
|
||||
|
||||
x = invfft([4, 1 - 1j, 0, 1 + 1j])
|
||||
expected = [1.5, 1.5, 0.5, 0.5]
|
||||
assert all(a.ae(b) for a, b in zip(x, expected))
|
||||
|
||||
assert invfft([]) == []
|
||||
pytest.raises(NotImplementedError, lambda: invfft([1, 2, 3]))
|
||||
|
||||
# test parseval's theorem
|
||||
x = [0.25 + 2.0j, -0.5, 0.75 - 1.0j, -1.0 - 8.0j, 0.5, 0.125 + 0.65j, -0.75, 1.25 + 2.5j]
|
||||
X = fft(x)
|
||||
time_energy = sum(abs(complex(v)) ** 2 for v in x)
|
||||
freq_energy = sum(abs(complex(v)) ** 2 for v in X) / 8
|
||||
assert abs(time_energy - freq_energy) < 1e-12
|
||||
|
||||
@st.composite
|
||||
def power_of_two_signals(draw):
|
||||
size = draw(st.sampled_from([1, 2, 4, 8, 16]))
|
||||
return draw(st.lists(
|
||||
st.complex_numbers(
|
||||
min_magnitude=0,
|
||||
max_magnitude=10,
|
||||
allow_nan=False,
|
||||
allow_infinity=False,
|
||||
),
|
||||
min_size=size,
|
||||
max_size=size,
|
||||
))
|
||||
|
||||
@given(x=power_of_two_signals())
|
||||
def test_fft_randomized_complex(x):
|
||||
# test that fft and invfft are inverses of each other for random complex inputs
|
||||
recovered = invfft(fft(x))
|
||||
assert all(a.ae(b) for a, b in zip(recovered, x))
|
||||
|
||||
recovered = fft(invfft(x))
|
||||
assert all(a.ae(b) for a, b in zip(recovered, x))
|
||||
|
||||
+14
-20
@@ -1,40 +1,26 @@
|
||||
"""Tests for the Command-Line Interface."""
|
||||
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import time
|
||||
|
||||
import pexpect
|
||||
import pytest
|
||||
|
||||
from mpmath.tests.test_demos import Console
|
||||
|
||||
|
||||
if platform.python_implementation() == 'PyPy':
|
||||
pytest.skip("Don't run CLI tests on PyPy.",
|
||||
allow_module_level=True)
|
||||
|
||||
|
||||
class Console(pexpect.spawn):
|
||||
"""Spawned console for testing."""
|
||||
|
||||
def __init__(self, command, timeout=60, _dumb=True):
|
||||
env = os.environ.copy()
|
||||
if _dumb:
|
||||
env['TERM'] = 'dumb'
|
||||
else:
|
||||
env['TERM'] = 'xterm'
|
||||
env['NO_COLOR'] = '1'
|
||||
super().__init__(command, timeout=timeout, encoding='utf-8', env=env)
|
||||
|
||||
def __del__(self):
|
||||
self.send('exit()\r\n')
|
||||
time.sleep(10) # a delay to allow coverage finish work
|
||||
if self.isalive():
|
||||
self.terminate(force=True)
|
||||
if sys.version_info >= (3, 15):
|
||||
pytestmark = pytest.mark.filterwarnings("ignore:.*:DeprecationWarning")
|
||||
|
||||
|
||||
def test_bare_console_no_bare_division():
|
||||
c = Console(f'{sys.executable} -m mpmath --no-ipython --no-wrap-floats')
|
||||
c = Console(f'{sys.executable} -m mpmath --no-ipython '
|
||||
'--no-wrap-floats --int-limits') # for coverage
|
||||
|
||||
assert c.expect_exact('>>> ') == 0
|
||||
assert c.send('1 + 2\r\n') == 7
|
||||
@@ -60,6 +46,14 @@ def test_bare_console_bare_division():
|
||||
assert c.expect_exact('0.5\r\n>>> ') == 0
|
||||
|
||||
|
||||
def test_bare_console_shortest_str():
|
||||
c = Console(f'{sys.executable} -m mpmath --no-ipython --shortest-str')
|
||||
|
||||
assert c.expect_exact('>>> ') == 0
|
||||
assert c.send('0.1\r\n') == 5
|
||||
assert c.expect_exact('0.1\r\n>>> ') == 0
|
||||
|
||||
|
||||
def test_bare_console_without_ipython():
|
||||
try:
|
||||
import IPython
|
||||
|
||||
@@ -61,6 +61,7 @@ def test_from_str():
|
||||
assert mpf(from_str('0b1101.100101')) == mpf('13.578125')
|
||||
assert mpf(from_str('0o1101.100101')) == mpf('577.12524795532227')
|
||||
assert mpf(from_str('1.99999999', prec=0)) == mpf('1.9999999901046976')
|
||||
pytest.raises(ValueError, lambda: from_str('1e400e2', 6))
|
||||
|
||||
def test_eps_repr():
|
||||
mp.dps = 24
|
||||
@@ -79,7 +80,6 @@ def test_to_str():
|
||||
x = mpf('1234.567891')._mpf_
|
||||
pytest.raises(ValueError, lambda: to_str(x, 6, binary_exp=True))
|
||||
pytest.raises(ValueError, lambda: to_str(x, 6, rnd='Y'))
|
||||
pytest.raises(ValueError, lambda: to_str('1e400e2', 6))
|
||||
assert to_str(x, 5, rnd='n') == '1234.6'
|
||||
assert to_str(x, 5, rnd='d') == '1234.5'
|
||||
assert to_str(x, 5, rnd='u') == '1234.6'
|
||||
@@ -137,10 +137,10 @@ def test_str_prec0():
|
||||
assert to_str(from_float(-1e+15), 0) == '-.0e+15'
|
||||
|
||||
def test_convert_rational():
|
||||
assert from_rational(30, 5, 53, round_nearest) == (0, 3, 1, 2)
|
||||
assert from_rational(-7, 4, 53, round_nearest) == (1, 7, -2, 3)
|
||||
assert to_rational((0, 1, -1, 1)) == (1, 2)
|
||||
assert to_rational((0, 1, 0, 1)) == (1, 1)
|
||||
assert from_rational(30, 5, 53, round_nearest)[:3] == (0, 3, 1)
|
||||
assert from_rational(-7, 4, 53, round_nearest)[:3] == (1, 7, -2)
|
||||
assert to_rational(mpf('0.5')._mpf_) == (1, 2)
|
||||
assert to_rational(mpf('1')._mpf_) == (1, 1)
|
||||
pytest.raises(ValueError, lambda: to_rational(mpf('nan')._mpf_))
|
||||
pytest.raises(OverflowError, lambda: to_rational(mpf('inf')._mpf_))
|
||||
pytest.raises(OverflowError, lambda: to_rational(mpf('-inf')._mpf_))
|
||||
@@ -250,12 +250,11 @@ def test_issue548():
|
||||
def test_compatibility():
|
||||
from packaging.version import Version, parse
|
||||
np = pytest.importorskip("numpy")
|
||||
if parse(np.__version__) < Version('2.0.0b1'):
|
||||
npcore = np.core
|
||||
else:
|
||||
npcore = np._core
|
||||
# numpy types
|
||||
for nptype in npcore.numerictypes.typeDict.values():
|
||||
for typecode in (np.typecodes['AllInteger']
|
||||
+ np.typecodes['Float']
|
||||
+ np.typecodes['Complex']):
|
||||
nptype = np.dtype(typecode).type
|
||||
if issubclass(nptype, np.complexfloating):
|
||||
x = nptype(complex(0.5, -0.5))
|
||||
elif issubclass(nptype, np.floating):
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
"""Tests for demo scripts."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
import pexpect
|
||||
import pytest
|
||||
|
||||
|
||||
class Console(pexpect.spawn):
|
||||
"""Spawned console for testing."""
|
||||
|
||||
def __init__(self, command, timeout=60, _dumb=True):
|
||||
env = os.environ.copy()
|
||||
if _dumb:
|
||||
env['TERM'] = 'dumb'
|
||||
else:
|
||||
env['TERM'] = 'xterm'
|
||||
env['NO_COLOR'] = '1'
|
||||
super().__init__(command, timeout=timeout, encoding='utf-8', env=env)
|
||||
|
||||
def __del__(self):
|
||||
self.send('exit()\r\n')
|
||||
time.sleep(10) # a delay to allow coverage finish work
|
||||
if self.isalive():
|
||||
self.terminate(force=True)
|
||||
|
||||
|
||||
# TODO: how to test plots? // mandelbrot.py and plotting.py
|
||||
|
||||
|
||||
def test_manydigits():
|
||||
expected = r"""
|
||||
This script prints answers to a selection of the "Many Digits"
|
||||
competition problems: http://www.cs.ru.nl/~milad/manydigits/problems.php
|
||||
|
||||
The output for each problem is the first 100 digits after the
|
||||
decimal point in the result.
|
||||
|
||||
C01: sin(tan(cos(1)))
|
||||
56451092986195980582768640645029648577648661582588
|
||||
56955552147245934844803576138875921296745208522197
|
||||
|
||||
C02: sqrt(e/pi)
|
||||
93019136710263285866812462363333155602971092070428
|
||||
87264450006489855422345460234483872155723942699765
|
||||
|
||||
C03: sin((e+1)^3)
|
||||
90949524105726624718554721945217426889396524221380
|
||||
80108799599078079083693175099387713504636663839042
|
||||
|
||||
C04: exp(pi*sqrt(2011))
|
||||
08911292681099318912549002226654964403231616008375
|
||||
14260187657441716605755144354088871641544234358651
|
||||
|
||||
C05: exp(exp(exp(1/2)))
|
||||
33130360854569351505757451265398380886369247851475
|
||||
92794392700131812592190818654155341658216570329325
|
||||
|
||||
C06: arctanh(1-arctanh(1-arctanh(1-arctanh(1/pi))))
|
||||
12376761044118329658639748452701440281087636723733
|
||||
55412845934779398491016984592299074199915669907895
|
||||
|
||||
C07: pi^1000
|
||||
96790874439619754260235142488458363174182234378720
|
||||
67532446047250097144332075967536835025898399733192
|
||||
|
||||
C08: sin(6^(6^6))
|
||||
95395374345732063524921114340552534258118576365118
|
||||
22065161716596988369691845451204872928519972839961
|
||||
|
||||
C09: sin(10*arctan(tanh(pi*(2011^(1/2))/3)))
|
||||
99999999999999999999999999999999999999999999999999
|
||||
99999999999999999999999999999868216408727535391618
|
||||
|
||||
C10: (7+2^(1/5)-5*(8^(1/5)))^(1/3) + 4^(1/5)-2^(1/5)
|
||||
00000000000000000000000000000000000000000000000000
|
||||
00000000000000000000000000000000000000000000000000
|
||||
|
||||
C11: tan(2^(1/2))+arctanh(sin(1))
|
||||
56031033792570862486989423169964262718414115287379
|
||||
65510969436882273871745968195963502918253580384966
|
||||
|
||||
C12: arcsin(1/e^2) + arcsinh(e^2)
|
||||
83344680806041761874543293615785770019293386147122
|
||||
63906848335142800750122119140978807925425237483497
|
||||
|
||||
C17: S= -4*Zeta(2) - 2*Zeta(3) + 4*Zeta(2)*Zeta(3) + 2*Zeta(5)
|
||||
99922283776383000876193574924756988603699551613617
|
||||
09442048984358627610229735501242221963535035597647
|
||||
|
||||
C18: Catalan G = Sum{i=0}{\infty}(-1)^i/(2i+1)^2
|
||||
91596559417721901505460351493238411077414937428167
|
||||
21342664981196217630197762547694793565129261151062
|
||||
|
||||
C21: Equation exp(cos(x)) = x
|
||||
30296400121601255253211430697335802538621997810467
|
||||
85962942111799929657676507417868401302803638230948
|
||||
|
||||
C22: J = integral(sin(sin(sin(x)))), x=0..1
|
||||
40783902635001567262733691845249456720742376991339
|
||||
01533400692321748591761662552762179981626145798049
|
||||
|
||||
"""
|
||||
result = subprocess.run([f'{sys.executable}',
|
||||
'demo/manydigits.py'],
|
||||
capture_output=True, text=True)
|
||||
assert result.stdout == expected
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:.*:DeprecationWarning")
|
||||
def test_pidigits():
|
||||
c = Console(f'{sys.executable} demo/pidigits.py')
|
||||
assert c.expect_exact('> ') == 0
|
||||
assert c.send('10\n') == 3
|
||||
assert c.expect_exact('> ') == 0
|
||||
assert c.send('100\n') == 4
|
||||
assert c.expect_exact('> ') == 0
|
||||
assert c.send('\n') == 1
|
||||
assert c.expect('5820974944 5923078164 0628620899 '
|
||||
'8628034825 3421170679 : 100') == 0
|
||||
|
||||
|
||||
def test_sofa():
|
||||
result = subprocess.run([f'{sys.executable}',
|
||||
'demo/sofa.py'],
|
||||
capture_output=True, text=True)
|
||||
assert result.stdout == '2.2195316688719674255462841007968\n'
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:.*:DeprecationWarning")
|
||||
def test_taylor():
|
||||
c = Console(f'{sys.executable} demo/taylor.py')
|
||||
assert c.expect_exact('Enter the value of x (e.g. 3.5): ') == 0
|
||||
assert c.send('1\n') == 2
|
||||
assert c.expect_exact('Enter the number of terms n (e.g. 10): ') == 0
|
||||
assert c.send('10\n') == 3
|
||||
assert c.expect_exact('[2.7182818011463827368, 2.7182818011463862895]') == 0
|
||||
@@ -1,9 +1,10 @@
|
||||
from random import choice, randint, seed
|
||||
|
||||
from mpmath import mpf
|
||||
from mpmath.libmp import (from_int, from_str, mpf_div, mpf_mul, mpf_rdiv_int,
|
||||
round_ceiling, round_down, round_floor,
|
||||
round_nearest, round_up, trailing)
|
||||
from mpmath.libmp import (from_int, from_str, mpf_div, mpf_mul, round_ceiling,
|
||||
round_down, round_floor, round_nearest, round_up)
|
||||
from mpmath.libmp.libintmath import trailing
|
||||
from mpmath.libmp.libmpf import mpf_rdiv_int
|
||||
|
||||
|
||||
def test_div_1_3():
|
||||
@@ -83,7 +84,7 @@ def test_tight_integer_division():
|
||||
a = from_int(a); b = from_int(b); p = from_int(p)
|
||||
for mode in [round_floor, round_ceiling, round_down,
|
||||
round_up, round_nearest]:
|
||||
assert mpf_div(p, a, width, mode) == b
|
||||
assert mpf_div(p, a, int(width), mode) == b
|
||||
|
||||
|
||||
def test_epsilon_rounding():
|
||||
|
||||
+815
-55
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,15 @@ from mpmath import fp, zetazero
|
||||
(1048449116, 388858886.690745053),
|
||||
(3570918901, 1239587702.54745031),
|
||||
(3570918902, 1239587702.54752387),
|
||||
# issue 1147, see
|
||||
# https://www.lmfdb.org/zeros/zeta/?limit=100&N=325890640
|
||||
# and https://www.lmfdb.org/zeros/zeta/?limit=100&N=325890640
|
||||
(325890640, 129273228.66142665),
|
||||
(325890641, 129273228.76005181),
|
||||
(325890642, 129273228.79754069),
|
||||
(357738764, 141125096.01260684),
|
||||
(357738765, 141125096.18511831),
|
||||
(357738766, 141125096.28064566),
|
||||
# Huge zeros (this may take hours):
|
||||
# (8637740722917, 2124447368584.39296466152),
|
||||
# (8637740722918, 2124447368584.39298170604),
|
||||
|
||||
+115
-14
@@ -11,9 +11,6 @@ from mpmath import fp, inf, mp, nan, ninf, workdps
|
||||
from mpmath.libmp.libmpf import read_format_spec
|
||||
|
||||
|
||||
vinfo = sys.version_info
|
||||
|
||||
|
||||
@st.composite
|
||||
def fmt_str(draw, types='fFeE', for_complex=False):
|
||||
res = ''
|
||||
@@ -41,7 +38,7 @@ def fmt_str(draw, types='fFeE', for_complex=False):
|
||||
res += draw(st.sampled_from([''] + list('-+ ')))
|
||||
|
||||
# no_neg_0 (not used yet.)
|
||||
if vinfo >= (3, 11):
|
||||
if sys.version_info >= (3, 11):
|
||||
res += draw(st.sampled_from([''] + ['z']))
|
||||
|
||||
# alternate mode
|
||||
@@ -71,7 +68,7 @@ def fmt_str(draw, types='fFeE', for_complex=False):
|
||||
+ ['0' + str(_) for _ in range(40)]))
|
||||
if prec:
|
||||
res += '.' + prec
|
||||
if vinfo >= (3, 14):
|
||||
if sys.version_info >= (3, 14):
|
||||
gchar = draw(st.sampled_from([''] + list(',_')))
|
||||
res += gchar
|
||||
|
||||
@@ -475,9 +472,10 @@ def test_mpf_fmt_cpython():
|
||||
assert f'{mp.pi}' == '3.14159265358979'
|
||||
mp.pretty_dps = 'repr'
|
||||
assert f'{mp.pi}' == '3.1415926535897931'
|
||||
mp.shortest_str = True
|
||||
assert f'{mp.mpf("1e100000")}' == '1e+100000'
|
||||
|
||||
|
||||
@settings(max_examples=20000)
|
||||
@given(fmt_str(types=list('fFeEgG%') + ['']),
|
||||
st.floats(allow_nan=True,
|
||||
allow_infinity=True,
|
||||
@@ -501,7 +499,7 @@ def test_mpf_floats_bulk(fmt, x):
|
||||
if not x and math.copysign(1, x) == -1:
|
||||
return # skip negative zero
|
||||
spec = read_format_spec(fmt)
|
||||
if spec['frac_separators'] and vinfo < (3, 14):
|
||||
if spec['frac_separators'] and sys.version_info < (3, 14):
|
||||
mp.pretty_dps = "str"
|
||||
return # see also python/cpython#130860
|
||||
if not spec['type'] and spec['precision'] < 0 and math.isfinite(x):
|
||||
@@ -515,19 +513,29 @@ def test_mpf_floats_bulk(fmt, x):
|
||||
assert format(x, fmt) == format(mp.mpf(x), fmt)
|
||||
|
||||
|
||||
@settings(max_examples=20000)
|
||||
@given(fmt_str(types=['']),
|
||||
st.floats(allow_nan=True,
|
||||
allow_infinity=True,
|
||||
allow_subnormal=False))
|
||||
@example('', 1000000000000000.0)
|
||||
def test_mpf_floats_default_bulk(fmt, x):
|
||||
mp.shortest_str = True
|
||||
if not x and math.copysign(1, x) == -1:
|
||||
return # skip negative zero
|
||||
spec = read_format_spec(fmt)
|
||||
assert format(x, fmt) == format(mp.mpf(x), fmt)
|
||||
|
||||
|
||||
@given(fmt_str(types=list('gGfFeE') + [''], for_complex=True),
|
||||
st.complex_numbers(allow_nan=True,
|
||||
allow_infinity=True,
|
||||
allow_subnormal=True))
|
||||
def test_mpc_complexes(fmt, z):
|
||||
def test_mpc_complexes_bulk(fmt, z):
|
||||
mp.pretty_dps = "repr"
|
||||
if ((not z.real and math.copysign(1, z.real) == -1)
|
||||
or (not z.imag and math.copysign(1, z.imag) == -1)):
|
||||
return # skip negative zero
|
||||
spec = read_format_spec(fmt)
|
||||
if spec['frac_separators'] and vinfo < (3, 14):
|
||||
return # see also python/cpython#130860
|
||||
if spec['precision'] < 0 and any(math.isfinite(_) for _ in [z.real, z.imag]):
|
||||
# The mpmath could choose a different decimal
|
||||
# representative (wrt CPython) for same binary
|
||||
@@ -540,6 +548,21 @@ def test_mpc_complexes(fmt, z):
|
||||
assert format(z, fmt) == format(mp.mpc(z), fmt)
|
||||
|
||||
|
||||
@given(fmt_str(types=[''], for_complex=True),
|
||||
st.complex_numbers(allow_nan=True,
|
||||
allow_infinity=True,
|
||||
allow_subnormal=False))
|
||||
@example(fmt='', z=complex(0))
|
||||
@example(fmt='#', z=complex(0))
|
||||
def test_mpc_complexes_default_bulk(fmt, z):
|
||||
mp.shortest_str = True
|
||||
if ((not z.real and math.copysign(1, z.real) == -1)
|
||||
or (not z.imag and math.copysign(1, z.imag) == -1)):
|
||||
return # skip negative zero
|
||||
spec = read_format_spec(fmt)
|
||||
assert format(z, fmt) == format(mp.mpc(z), fmt)
|
||||
|
||||
|
||||
def test_mpc_fmt():
|
||||
pytest.raises(ValueError, lambda: f'{mp.mpc(1j):=10f}')
|
||||
pytest.raises(ValueError, lambda: f'{mp.mpc(1j):010f}')
|
||||
@@ -843,7 +866,6 @@ def test_errors():
|
||||
f"{mp.mpf(1):._6f}"
|
||||
|
||||
|
||||
@settings(max_examples=10000)
|
||||
@given(st.floats(allow_nan=True, allow_infinity=True,
|
||||
allow_subnormal=False))
|
||||
@example(float('nan'))
|
||||
@@ -864,12 +886,12 @@ except OSError:
|
||||
def float_print(d, i):
|
||||
fmt = "%." + str(i) + "a\n"
|
||||
a = ctypes.create_string_buffer(256)
|
||||
libc.sprintf.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
||||
libc.sprintf(a, bytes(fmt, 'utf-8'), ctypes.c_double(d))
|
||||
return a.raw.decode('utf-8').split("\n")[0]
|
||||
|
||||
|
||||
@pytest.mark.skipif(libc is None, reason='requires libc')
|
||||
@settings(max_examples=10000)
|
||||
@given(st.floats(allow_nan=False, allow_infinity=False,
|
||||
allow_subnormal=False),
|
||||
st.integers(min_value=0, max_value=15))
|
||||
@@ -880,7 +902,6 @@ def test_hexadecimal_with_libc_bulk(x, p):
|
||||
assert mp.mpf(m_hex) == mp.mpf(x_hex)
|
||||
|
||||
|
||||
@settings(max_examples=10000)
|
||||
@given(st.floats(allow_nan=False, allow_infinity=False,
|
||||
allow_subnormal=False),
|
||||
st.integers(min_value=-3, max_value=15))
|
||||
@@ -919,3 +940,83 @@ def test_hexadecimal_fmt():
|
||||
assert f'{x:.0a}' == '0x1p+0'
|
||||
assert f'{x:#.0a}' == '0x1.p+0'
|
||||
assert f"{mp.mpf('1.234567890123456789'):+.0a}" == '+0x1p+0'
|
||||
|
||||
|
||||
@given(st.floats(allow_nan=False, allow_infinity=False,
|
||||
allow_subnormal=False),
|
||||
st.integers(min_value=1, max_value=40),
|
||||
st.sampled_from(list('UDNYZ')))
|
||||
def test_fixed_with_gmpy2_bulk(x, dps, mode):
|
||||
gmpy2 = pytest.importorskip('gmpy2')
|
||||
if not x and math.copysign(1, x) == -1:
|
||||
return # skip negative zero
|
||||
fmt = f'.{dps}{mode}f'
|
||||
gx = gmpy2.mpfr(x)
|
||||
mx = mp.mpf(x)
|
||||
assert format(mx, fmt) == format(gx, fmt)
|
||||
|
||||
|
||||
def test_issue_1131():
|
||||
# 'f' formatting must round the last digit like the 'e' path and MPFR do.
|
||||
# below 0.1 unit in the last place at .1f:
|
||||
tiny = 0.0004641126344492319
|
||||
cases = [
|
||||
# nonzero remainder hidden past the extracted guard digits
|
||||
(0.688196003332049, '.15Uf', '0.688196003332050'),
|
||||
(0.688196003332049, '.15Yf', '0.688196003332050'),
|
||||
(0.6619127364342315, '.21Nf', '0.661912736434231541161'),
|
||||
(0.6297105422352101, '.21Uf', '0.629710542235210057883'),
|
||||
(0.6297105422352101, '.21Yf', '0.629710542235210057883'),
|
||||
(0.09235788595039773, '.21Uf', '0.092357885950397733411'),
|
||||
(0.15440508559046828, '.30Uf',
|
||||
'0.154405085590468282852327774891'),
|
||||
# value below the last requested place: away from zero rounds it up,
|
||||
# toward zero truncates it (.0f drops the trailing '.0', as in CPython)
|
||||
(tiny, '.1Nf', '0.0'), (tiny, '.1Zf', '0.0'), (tiny, '.1Df', '0.0'),
|
||||
(tiny, '.1Uf', '0.1'), (tiny, '.1Yf', '0.1'),
|
||||
(tiny, '.3Uf', '0.001'), (tiny, '.0Uf', '1'),
|
||||
(-tiny, '.1Nf', '-0.0'), (-tiny, '.1Uf', '-0.0'), (-tiny, '.1Zf', '-0.0'),
|
||||
(-tiny, '.1Df', '-0.1'), (-tiny, '.1Yf', '-0.1'),
|
||||
]
|
||||
for x, fmt, expected in cases:
|
||||
assert format(mp.mpf(x), fmt) == expected, (x.hex(), fmt)
|
||||
|
||||
# the expansion terminates with a 5 at the rounding position: round-half-even
|
||||
assert format(mp.mpf('0.125'), '.2Nf') == '0.12'
|
||||
assert format(mp.mpf('0.375'), '.2Nf') == '0.38'
|
||||
assert format(mp.mpf('2.5'), '.0Nf') == '2'
|
||||
assert format(mp.mpf('3.5'), '.0Nf') == '4'
|
||||
# carry propagation
|
||||
assert format(mp.mpf('0.6999999999'), '.4Uf') == '0.7000'
|
||||
|
||||
|
||||
def test_str_rounding_near_boundary():
|
||||
# to_str extracted only dps+10 digits, narrower than format_scientific /
|
||||
# format_fixed which cover the whole mantissa. A value sitting just above
|
||||
# a decimal boundary is then extracted as "...99999" one ULP low, so
|
||||
# directed rounding through str/nstr fell one ULP short of the 'e' format
|
||||
# and the exact value. These exact dyadics are just above such boundaries.
|
||||
with mp.workprec(200):
|
||||
b = mp.mpf(1058187881481430099485) / mp.mpf(2)**74 # 0.056020000...16941...
|
||||
d = mp.mpf(136826224263983729993245) / mp.mpf(2)**81 # 0.056590000...
|
||||
e = -mp.mpf(2218543292904312125153593) / mp.mpf(2)**86 # -0.028674000...
|
||||
|
||||
# public nstr(rnd=...) API: only directed-away modes were affected
|
||||
assert mp.nstr(b, 6, rnd='n') == '0.05602'
|
||||
assert mp.nstr(b, 6, rnd='c') == '0.0560201'
|
||||
assert mp.nstr(b, 6, rnd='u') == '0.0560201'
|
||||
assert mp.nstr(b, 6, rnd='f') == '0.05602'
|
||||
assert mp.nstr(b, 6, rnd='d') == '0.05602'
|
||||
assert mp.nstr(d, 6, rnd='n') == '0.05659'
|
||||
assert mp.nstr(d, 6, rnd='c') == '0.0565901'
|
||||
assert mp.nstr(d, 6, rnd='u') == '0.0565901'
|
||||
# negative: ceiling truncates the magnitude, floor/away rounds it up
|
||||
assert mp.nstr(e, 6, rnd='c') == '-0.028674'
|
||||
assert mp.nstr(e, 6, rnd='d') == '-0.028674'
|
||||
assert mp.nstr(e, 6, rnd='u') == '-0.0286741'
|
||||
assert mp.nstr(e, 6, rnd='f') == '-0.0286741'
|
||||
|
||||
# str/nstr now agrees with the already-correct 'e' format for the same value
|
||||
assert format(b, '.5Ne') == '5.60200e-02'
|
||||
assert format(b, '.5Ue') == '5.60201e-02'
|
||||
assert format(b, '.5Ye') == '5.60201e-02'
|
||||
|
||||
@@ -1811,3 +1811,12 @@ def test_issue_491():
|
||||
def test_issue_521():
|
||||
assert fp.ff(1, -fp.inf) == 0.0
|
||||
assert fp.isnan(fp.ff(1, fp.inf))
|
||||
|
||||
def test_issue_493():
|
||||
assert ae(fp.binomial(1100, 1), 1100.0)
|
||||
assert ae(fp.binomial(1100, 1099), 1100.0)
|
||||
assert fp.binomial(1100, 0) == 1.0
|
||||
assert ae(fp.rf(1100, 1), 1100.0)
|
||||
assert ae(fp.beta(1100, 1), 1/1100)
|
||||
assert ae(fp.binomial(5, 2), 10.0)
|
||||
pytest.raises(OverflowError, lambda: fp.binomial(1100, 550))
|
||||
|
||||
@@ -16,8 +16,9 @@ from mpmath import (acos, acosh, acot, acoth, acsc, acsch, arange, arg, asec,
|
||||
sec, sech, sign, sin, sinc, sincpi, sinh, sinpi, sqrt, tan,
|
||||
tanh, twinprime, unitroots)
|
||||
from mpmath.libmp import (MPZ, ComplexResult, from_int, mpf_gt, mpf_lt,
|
||||
mpf_mul, mpf_pow_int, mpf_rand, mpf_sqrt,
|
||||
round_ceiling, round_down, round_nearest, round_up)
|
||||
mpf_mul, mpf_pow_int, mpf_sqrt, round_ceiling,
|
||||
round_down, round_nearest, round_up)
|
||||
from mpmath.libmp.libmpf import mpf_rand
|
||||
|
||||
|
||||
def mpc_ae(a, b, eps=eps):
|
||||
@@ -156,6 +157,9 @@ def test_hypot():
|
||||
assert hypot(0.33, 0) == mpf(0.33)
|
||||
assert hypot(-0.33, 0) == mpf(0.33)
|
||||
assert hypot(3, 4) == mpf(5)
|
||||
# issue 1011
|
||||
assert hypot(1.0000044432326138,
|
||||
1.0068578402095993) == mpf('1.4190742041473763')
|
||||
|
||||
def test_exact_cbrt():
|
||||
for i in range(0, 20000, 200):
|
||||
@@ -546,6 +550,10 @@ def test_frexp():
|
||||
assert frexp(1) == (0.5, 1)
|
||||
assert frexp(0.2) == (0.8, -2)
|
||||
assert frexp(1000) == (0.9765625, 10)
|
||||
assert frexp(inf) == (inf, 0)
|
||||
assert frexp(-inf) == (-inf, 0)
|
||||
r = frexp(nan)
|
||||
assert isnan(r[0]) and r[1] == 0
|
||||
|
||||
def test_aliases():
|
||||
assert ln(7) == log(7)
|
||||
@@ -1086,3 +1094,13 @@ def test_issue_749():
|
||||
assert mp.asinh(mp.inf) == mp.inf
|
||||
assert mp.asinh(mp.mpc(mp.inf, 0)) == mp.mpc(mp.inf, 0)
|
||||
assert fp.asinh(fp.mpc(fp.inf, 0)) == fp.mpc(fp.inf, 0)
|
||||
|
||||
def test_issue_1035():
|
||||
assert mp.acos(1e-50j).ae(1.5707963267948966)
|
||||
|
||||
def test_wrap_libmp_api():
|
||||
assert sin(1) != sin(1, prec=1000)
|
||||
assert sin(1) != sin(1, dps=100)
|
||||
assert sin(1, rounding='d') < sin(1, rounding='u')
|
||||
pytest.raises(ValueError, lambda: sin(1, prec=123, dps=321))
|
||||
pytest.raises(TypeError, lambda: sin(1, 2))
|
||||
|
||||
+106
-13
@@ -1,17 +1,21 @@
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from mpmath import (agm, airyai, airybi, appellf1, bei, ber, besseli, besselj,
|
||||
besseljzero, besselk, bessely, besselyzero, betainc,
|
||||
chebyt, chebyu, chi, ci, convert, coulombg, e, e1, ei,
|
||||
ellipe, ellipk, eps, erf, erfc, erfi, erfinv, exp, expint,
|
||||
fadd, fmul, foxh, fp, fraction, fresnelc, fresnels, fsub, fsum,
|
||||
gamma, gammainc, gegenbauer, hankel1, hankel2, hermite,
|
||||
hyp0f1, hyp1f1, hyp1f2, hyp2f0, hyp2f1, hyp2f2, hyp2f3,
|
||||
hyper, hypercomb, hyperu, inf, isnan, j, j0, j1, jacobi,
|
||||
kei, ker, laguerre, lambertw, ldexp, legendre, legenp,
|
||||
legenq, lerchphi, li, log, lower_gamma, meijerg, mp, mpc,
|
||||
mpf, nan, ncdf, npdf, nthroot, pi, qp, quadts, shi, si,
|
||||
spherharm, spherical_jn, spherical_yn, sqrt, struveh,
|
||||
chebyt, chebyu, chi, ci, clsin, convert, coulombg, e, e1,
|
||||
ei, ellipe, ellipk, eps, erf, erfc, erfi, erfinv, exp,
|
||||
expint, extradps, fadd, fmul, foxh, fp, fraction, fresnelc,
|
||||
fresnels, fsub, fsum, gamma, gammainc, gegenbauer, hankel1,
|
||||
hankel2, hermite, hyp0f1, hyp1f1, hyp1f2, hyp2f0, hyp2f1,
|
||||
hyp2f2, hyp2f3, hyper, hypercomb, hyperu, inf, isnan, j,
|
||||
j0, j1, jacobi, kei, ker, laguerre, lambertw, ldexp,
|
||||
legendre, legenp, legenq, lerchphi, li, log, lower_gamma,
|
||||
meijerg, mp, mpc, mpf, nan, ncdf, npdf, nthroot, pi,
|
||||
polylog, qp, quadts, shi, si, spherharm, spherical_in,
|
||||
spherical_jn, spherical_kn, spherical_yn, sqrt, struveh,
|
||||
struvel, upper_gamma, whitm, whitw, zeta)
|
||||
from mpmath.libmp import BACKEND, NoConvergence
|
||||
|
||||
@@ -86,6 +90,18 @@ def test_bessel():
|
||||
assert besselk(0,j).ae(-0.13863371520405399968-1.20196971531720649914j)
|
||||
assert (besselk(3, 10**10) * mpf(10)**4342944824).ae(1.1628981033356187851)
|
||||
assert besselk(1,inf) == 0
|
||||
|
||||
# Reference values for spherical_in(n, z) and spherical_kn(n, z) were
|
||||
# computed with Wolfram Engine 15:
|
||||
# SphericalIn[n_, z_] := BesselI[n + 1/2, z] * Sqrt[Pi / (2*z)]
|
||||
# SphericalKn[n_, z_] := BesselK[n + 1/2, z] * Sqrt[Pi / (2*z)]
|
||||
assert spherical_in(0, 1).ae(1.1752011936438014)
|
||||
ref = 0.0014838823109673326 + 0.0008458614117247069j
|
||||
assert spherical_in(6, -1.5 + 2j).ae(ref)
|
||||
assert spherical_kn(0, 1).ae(0.5778636748954609)
|
||||
ref = -25.42791007767947 - 13.388885300250143j
|
||||
assert spherical_kn(6, -1.5 + 2j).ae(ref)
|
||||
|
||||
assert spherical_jn(0, 1).ae(0.841470984807896)
|
||||
assert spherical_yn(0, 1).ae(-0.54030230586814)
|
||||
# test for issue 331, bug reported by Michael Hartmann
|
||||
@@ -532,7 +548,6 @@ def test_hyper_2f1():
|
||||
def test_hyper_2f1_hard():
|
||||
# Singular cases
|
||||
assert hyp2f1(2,-1,-1,3).ae(7)
|
||||
pytest.raises(NotImplementedError, lambda: fp.hyp2f1(2,-1,-1,3))
|
||||
assert hyp2f1(2,-1,-1,3,eliminate_all=True).ae(0.25)
|
||||
assert hyp2f1(2,-2,-2,3).ae(34)
|
||||
assert hyp2f1(2,-2,-2,3,eliminate_all=True).ae(0.25)
|
||||
@@ -771,6 +786,15 @@ def test_gegenbauer():
|
||||
assert gegenbauer(0, 4, 2.2) == 1
|
||||
assert gegenbauer(0, 0, 1.8) == 0
|
||||
assert gegenbauer(0, 1, 1.8) == 1
|
||||
# issue 1077: odd integer n at z=0 vanishes
|
||||
assert gegenbauer(1, 1, 0) == 0
|
||||
assert gegenbauer(5, 1.5, 0) == 0
|
||||
assert gegenbauer(3, 2, 0) == 0
|
||||
assert gegenbauer(3, 1, mpc(0)) == 0
|
||||
# adjacent cases must keep going through the general path
|
||||
assert gegenbauer(2, 1, 0).ae(-1)
|
||||
assert gegenbauer(4, 1.5, 0).ae(1.875)
|
||||
assert gegenbauer(2.5, 1, 0).ae(-0.70710678118654752440)
|
||||
mp.dps = 200
|
||||
assert gegenbauer(2,-1.0, 27397079.00297188) == 0 # issue 461
|
||||
|
||||
@@ -1470,7 +1494,13 @@ def test_issue_239():
|
||||
x = ldexp(2476979795053773,-52)
|
||||
assert betainc(206, 385, 0, 0.55, 1).ae('0.99999999999999999999996570910644857895771110649954')
|
||||
mp.dps = 15
|
||||
pytest.raises(ValueError, lambda: hyp2f1(-5,5,0.5,0.5))
|
||||
expected_exc = ValueError
|
||||
if platform.machine() == 's390x' and sys.version_info < (3, 14):
|
||||
# This case has recursion depth beyond platform capabilities, that
|
||||
# could be controlled with sys.setrecursionlimit(). See issue #1046
|
||||
# for details.
|
||||
expected_exc = RecursionError
|
||||
pytest.raises(expected_exc, lambda: hyp2f1(-5,5,0.5,0.5))
|
||||
|
||||
# Extra stress testing for Bessel functions
|
||||
# Reference zeros generated with the aid of scipy.special
|
||||
@@ -2391,7 +2421,6 @@ ynp_small_zeros = \
|
||||
def test_bessel_zeros_extra():
|
||||
for v in range(V):
|
||||
for m in range(1,M+1):
|
||||
print(v, m, "of", V, M)
|
||||
# Twice to test cache (if used)
|
||||
assert besseljzero(v,m).ae(jn_small_zeros[v][m-1])
|
||||
assert besseljzero(v,m).ae(jn_small_zeros[v][m-1])
|
||||
@@ -2445,6 +2474,12 @@ def test_issue_473():
|
||||
assert mp.polylog(4, -mp.inf) == -mp.inf
|
||||
assert mp.polylog(5, -mp.inf) == -mp.inf
|
||||
|
||||
def test_issue_1033():
|
||||
assert isnan(mp.polylog(2, mp.inf))
|
||||
assert isnan(mp.polylog(3, mp.inf))
|
||||
assert mp.polylog(2, mp.inf).real == -mp.inf
|
||||
assert mp.polylog(3, mp.inf).real == -mp.inf
|
||||
|
||||
def test_issue_634():
|
||||
assert mp.polylog(1+1e-15, -2).ae(mp.mpf('-1.09861228866811'))
|
||||
|
||||
@@ -2458,3 +2493,61 @@ def test_issue_637():
|
||||
def test_issue_991():
|
||||
assert spherical_jn(0, 1.3).ae(0.74119860416707)
|
||||
assert spherical_yn(0, 1.3).ae(-0.20576832971122)
|
||||
|
||||
def test_issue_545():
|
||||
x = 100+j
|
||||
assert erfc(x).ae(mpc('8.634691205220881e-4346',
|
||||
'1.5120569745187501e-4345'))
|
||||
assert erfc(-x).ae(mpc(2, '-1.5120569745187501e-4345'),
|
||||
rel_eps=mpf('1e-4346'))
|
||||
assert erf(x).ae(mpc(1, '-1.5120569745187501e-4345'),
|
||||
rel_eps=mpf('1e-4346'))
|
||||
assert erf(-x).ae(mpc(-1, '1.5120569745187501e-4345'),
|
||||
rel_eps=mpf('1e-4346'))
|
||||
|
||||
def test_issue_459():
|
||||
assert isnan(clsin(1, mp.inf))
|
||||
assert isnan(clsin(2, mp.inf))
|
||||
assert isnan(clsin(2, mp.nan))
|
||||
assert isnan(polylog(-2, mp.nan))
|
||||
|
||||
def test_issue_1099():
|
||||
mp.dps = 200
|
||||
z = mpf(1)/2809
|
||||
a = mpc(mpf(1)/4, pi*32/log(53))
|
||||
r1 = lerchphi(z, 2, a)
|
||||
r2 = extradps(100)(lerchphi)(z, 2, a)
|
||||
assert r1.ae(r2)
|
||||
|
||||
def test_issue_252():
|
||||
z, s, a = 2.5, 1.5, 4
|
||||
e = 1/mpf(10**10)
|
||||
# N[LerchPhi[5/2, 3/2, 4-10^-10], 17]
|
||||
assert lerchphi(z, s,
|
||||
a - e).ae(mpc('-0.16723817353102306-0.08686834435129020j'))
|
||||
# N[LerchPhi[5/2, 3/2, 4+10^-10], 17]
|
||||
assert lerchphi(z, s,
|
||||
a + e).ae(mpc('-0.16723817351940769-0.08686834433537087j'))
|
||||
# N[LerchPhi[5/2, 3/2, 4], 17]
|
||||
assert lerchphi(z, s,
|
||||
a).ae(mpc('-0.16723817352521537-0.08686834434333054j'))
|
||||
# N[LerchPhi[5/2+I/4, 2, 4], 17]
|
||||
assert lerchphi(2.5+0.25j, 2,
|
||||
4).ae(mpc('-0.066397419699793568+0.076201248010951803j'))
|
||||
# N[LerchPhi[1/4+I/2, 5/2, 4], 17]
|
||||
assert lerchphi(0.25+0.5j, 2.5,
|
||||
4).ae(mpc('0.032357329026949928+0.010945877309574764j'))
|
||||
# N[LerchPhi[3/4, 5/2, 4], 17]
|
||||
assert lerchphi(0.75, 2.5, 4).ae(mpf('0.058457869546642472'))
|
||||
|
||||
def test_issue_496():
|
||||
assert fp.hyper([0], [0], 0.25) == 1
|
||||
assert fp.hyper([0], [0], 0.5) == 1
|
||||
assert fp.hyper([0], [0], 1.5) == 1
|
||||
assert fp.hyper([2, 0], [0, 1], 2.5) == 1
|
||||
assert fp.hyper([1, -1], [-2], 3) == 2.5
|
||||
assert fp.hyp2f1(2, -1, -1, 3) == 7
|
||||
|
||||
def test_issue_1142():
|
||||
assert spherical_jn(8, 5).ae(+spherical_jn(8, -5))
|
||||
assert spherical_jn(9, 5).ae(-spherical_jn(9, -5))
|
||||
|
||||
@@ -7,7 +7,8 @@ from mpmath import (altzeta, apery, barnesg, bell, bernfrac, bernoulli,
|
||||
j, log, loggamma, mp, mpc, mpf, mpmathify, nan, pi,
|
||||
polyexp, polylog, primezeta, psi, rf, rgamma, sech,
|
||||
secondzeta, siegelz, sinc, sqrt, stieltjes, superfac, zeta)
|
||||
from mpmath.libmp import from_float, mpf_zeta_int, round_up
|
||||
from mpmath.libmp import from_float, round_up
|
||||
from mpmath.libmp.gammazeta import mpf_zeta_int
|
||||
|
||||
|
||||
def test_zeta_int_bug():
|
||||
|
||||
@@ -221,7 +221,7 @@ def last_digits(a):
|
||||
b = float(int(r))/10**(len(r) - m)
|
||||
if b >= 10**m - 0.5: # pragma: no cover
|
||||
raise NotImplementedError
|
||||
n = int(round(b))
|
||||
n = round(b)
|
||||
sn = str(n)
|
||||
s = s[:-m] + '0'*num0 + sn
|
||||
return s[-20:]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import pytest
|
||||
|
||||
from mpmath import e, exp, findpoly, identify, log, mp, pi, pslq, sqrt, zeta
|
||||
|
||||
|
||||
@@ -21,5 +19,4 @@ def test_identify():
|
||||
assert identify(pi+1, {'a':+pi}) == '(1 + 1*a)'
|
||||
|
||||
def test_findpoly_deprecated():
|
||||
with pytest.deprecated_call():
|
||||
assert findpoly(1+sqrt(2), 2) == [1, -2, -1]
|
||||
assert findpoly(1+sqrt(2), 2, asc=False) == [1, -2, -1]
|
||||
|
||||
@@ -382,6 +382,7 @@ def test_interval_nstr():
|
||||
assert iv.nstr(mpi('1e123', '1e129'), n, mode='diff') == '[1.0e+123, 1.0e+129]'
|
||||
exp = iv.exp
|
||||
assert iv.nstr(iv.exp(mpi('5000.1')), n, mode='diff') == '3.2797365856787867069110487[0926, 1191]e+2171'
|
||||
assert iv.nstr(iv.mpc(3, 4)) == '([3.0, 3.0] + [4.0, 4.0]*j)'
|
||||
|
||||
def test_mpi_from_str():
|
||||
assert iv.convert('1.5 +- 0.5') == mpi(mpf('1.0'), mpf('2.0'))
|
||||
@@ -447,3 +448,6 @@ def test_issue_258():
|
||||
b = 0.5
|
||||
pytest.raises(ValueError, lambda: min(a, b))
|
||||
pytest.raises(ValueError, lambda: max(a, b))
|
||||
|
||||
def test_mpi_mag():
|
||||
assert iv.mag(iv.mpc(3, 4)) == 4
|
||||
|
||||
+27
-12
@@ -4,7 +4,7 @@ import pytest
|
||||
|
||||
from mpmath import (cond, det, diag, exp, expm, extend, extradps, eye, fp,
|
||||
hilbert, inf, inverse, iv, j, lu, lu_solve, matrix, mnorm,
|
||||
mp, mpc, mpf, nint, norm, pi, qr, qr_solve, rand, rank,
|
||||
mp, mpc, mpf, nint, norm, pi, pinv, qr, qr_solve, rand, rank,
|
||||
randmatrix, residual, zeros, absmin, eps)
|
||||
|
||||
|
||||
@@ -113,6 +113,20 @@ def test_inverse():
|
||||
inv = inverse(A)
|
||||
assert mnorm(A*inv - eye(A.rows), 1) < 1.e-14
|
||||
|
||||
def test_pinv():
|
||||
# Test the Moore Penrose pseudoinverse for square matrices.
|
||||
for A in [A1, A2, A5]:
|
||||
inv = pinv(A)
|
||||
assert mnorm(A*inv - eye(A.rows), 1) < 1.e-13
|
||||
|
||||
# Test the Moore Penrose pseudoinverse for non-square matrices.
|
||||
A = matrix([[1, 0], [0, 1], [0, 1]])
|
||||
Aplus = matrix([[1, 0, 0], [0, 0.5, 0.5]])
|
||||
assert mnorm(pinv(A) - Aplus, 1) < 1.e-14
|
||||
|
||||
# Check with non-default tolerance.
|
||||
assert mnorm(pinv(A, rtol=1e-20) - Aplus, 1) < 1.e-14
|
||||
|
||||
def test_householder():
|
||||
A, b = A8, b8
|
||||
H, p, x, r = householder(extend(A, b))
|
||||
@@ -187,6 +201,18 @@ def test_solve_overdet_complex():
|
||||
b = matrix([1 + j, 2, -j])
|
||||
assert norm(residual(A, lu_solve(A, b), b)) < 1.0208
|
||||
|
||||
def test_qr_solve_issue_983():
|
||||
A = matrix([[1, -pi/20, (-pi/20)**2, (-pi/20)**3],
|
||||
[1, 0, 0, 0],
|
||||
[1, pi / 20, (pi/20)**2, (pi/20)**3],
|
||||
[1, pi/10, (pi/10)**2, (pi/10)**3]])
|
||||
b = matrix([[mp.sin(-pi/20)],
|
||||
[0],
|
||||
[mp.sin(pi/20)],
|
||||
[mp.sin(pi/20)]])
|
||||
x, _ = qr_solve(A, b)
|
||||
assert norm(residual(A, x, b), inf) < 1e-14
|
||||
|
||||
def test_singular():
|
||||
A = [[5.6, 1.2], [7./15, .1]]
|
||||
B = repr(zeros(2))
|
||||
@@ -276,7 +302,6 @@ def test_exp_pade():
|
||||
e1 = expm(a1, method='pade')
|
||||
mp.dps = dps + extra
|
||||
d = e2 - e1
|
||||
#print d
|
||||
mp.dps = dps
|
||||
assert norm(d, inf).ae(0)
|
||||
|
||||
@@ -321,32 +346,22 @@ def test_qr():
|
||||
# perform A -> QR decomposition
|
||||
Q, R = qr(A, mode, edps = exdps)
|
||||
|
||||
#print('\n\n A = \n', nstr(A, 4))
|
||||
#print('\n Q = \n', nstr(Q, 4))
|
||||
#print('\n R = \n', nstr(R, 4))
|
||||
#print('\n Q*R = \n', nstr(Q*R, 4))
|
||||
|
||||
maxnorm = mpf('1.0E-11')
|
||||
n1 = norm(A - Q * R)
|
||||
#print '\n Norm of A - Q * R = ', n1
|
||||
assert n1 <= maxnorm
|
||||
|
||||
if dtype == 'real':
|
||||
n1 = norm(eye(m) - Q.T * Q)
|
||||
#print ' Norm of I - Q.T * Q = ', n1
|
||||
assert n1 <= maxnorm
|
||||
|
||||
n1 = norm(eye(m) - Q * Q.T)
|
||||
#print ' Norm of I - Q * Q.T = ', n1
|
||||
assert n1 <= maxnorm
|
||||
|
||||
if dtype == 'complex':
|
||||
n1 = norm(eye(m) - Q.T * Q.conjugate())
|
||||
#print ' Norm of I - Q.T * Q.conjugate() = ', n1
|
||||
assert n1 <= maxnorm
|
||||
|
||||
n1 = norm(eye(m) - Q.conjugate() * Q.T)
|
||||
#print ' Norm of I - Q.conjugate() * Q.T = ', n1
|
||||
assert n1 <= maxnorm
|
||||
|
||||
def test_rank():
|
||||
|
||||
@@ -304,7 +304,3 @@ def test_interval_matrix_mult_bug():
|
||||
assert mp.mpf('1.00000000000001998401444325291756783368705994138804689654') in C[0, 0]
|
||||
# the following caused an error before the bug was fixed
|
||||
assert iv.matrix(mp.eye(2)) * (iv.ones(2) + mpi(1, 2)) == iv.matrix([[mpi(2, 3), mpi(2, 3)], [mpi(2, 3), mpi(2, 3)]])
|
||||
|
||||
def test_issue_156():
|
||||
with pytest.deprecated_call():
|
||||
matrix([[1, 2], [3, 4]], force_type=float)
|
||||
|
||||
@@ -5,7 +5,7 @@ from mpmath import (cos, eps, findroot, fp, inf, iv, jacobian, matrix, mnorm,
|
||||
workprec)
|
||||
from mpmath.calculus.optimization import (Anderson, ANewton, Bisection,
|
||||
Illinois, MDNewton, MNewton, Muller,
|
||||
Newton, Pegasus, Ridder, Secant)
|
||||
Newton, Pegasus, Ridder, Secant, ModAB, Brent)
|
||||
|
||||
|
||||
def test_findroot():
|
||||
@@ -21,7 +21,7 @@ def test_findroot():
|
||||
assert abs(f(x)) < eps
|
||||
# test all solvers with interval of 2 points
|
||||
for solver in [Secant, Muller, Bisection, Illinois, Pegasus, Anderson,
|
||||
Ridder]:
|
||||
Ridder, ModAB, Brent]:
|
||||
x = findroot(f, (1., 2.), solver=solver)
|
||||
assert abs(f(x)) < eps
|
||||
# test types
|
||||
@@ -50,10 +50,38 @@ def test_bisection():
|
||||
# issue 273
|
||||
assert findroot(lambda x: x**2-1,(0,2),solver='bisect') == 1
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
findroot(lambda x: x**2-1, (4, 2), solver='bisect') == 1
|
||||
|
||||
# issue 285
|
||||
mp.dps = 240
|
||||
sol = -mp.ceil(mp.log(abs(findroot(lambda x: mp.sign(x - 3), (1, 4),
|
||||
solver='bisect', verify=False,
|
||||
tol=1e-200) - 3))/mp.log(10))
|
||||
assert sol.ae(200)
|
||||
|
||||
# issue 339
|
||||
mp.dps = 15
|
||||
res = mpf('0.73908513321516064')
|
||||
for dps in [100, 200, 300, 1000]:
|
||||
with mp.workdps(dps):
|
||||
sol = findroot(lambda x: cos(x) - x, [0, 1], solver='bisect')
|
||||
assert (+sol).ae(res)
|
||||
|
||||
def test_mnewton():
|
||||
f = lambda x: polyval([1,3,3,1],x,asc=True)
|
||||
f = lambda x: polyval([1, 3, 3, 1], x)
|
||||
x = findroot(f, -0.9, solver='mnewton')
|
||||
assert abs(f(x)) < eps
|
||||
x = findroot(f, -0.9, solver='mnewton',
|
||||
df=lambda x: polyval([3, 6, 3], x))
|
||||
assert abs(f(x)) < eps
|
||||
x = findroot(f, -0.9, solver='mnewton',
|
||||
d1f=lambda x: polyval([3, 6, 3], x))
|
||||
assert abs(f(x)) < eps
|
||||
x = findroot(f, -0.9, solver='mnewton',
|
||||
d1f=lambda x: polyval([3, 6, 3], x),
|
||||
d2f=lambda x: polyval([6, 6], x))
|
||||
assert abs(f(x)) < 1000*eps
|
||||
|
||||
def test_anewton():
|
||||
f = lambda x: (x - 2)**100
|
||||
@@ -65,6 +93,59 @@ def test_muller():
|
||||
x = findroot(f, 1., solver=Muller)
|
||||
assert abs(f(x)) < eps
|
||||
|
||||
def test_ridder():
|
||||
f = lambda x: cos(x)/x
|
||||
x = findroot(f, (1, 2), solver='ridder')
|
||||
assert abs(f(x)) < eps
|
||||
|
||||
def test_brent():
|
||||
f = lambda x: cos(x)/x
|
||||
x = findroot(f, (1, 2), solver='brent')
|
||||
assert abs(f(x)) < eps
|
||||
|
||||
with pytest.raises(ValueError, match="expected interval of 2 points"):
|
||||
findroot(lambda x: x**2 - 1, (0,), solver='brent')
|
||||
|
||||
with pytest.raises(ValueError, match="Function must have opposite signs"):
|
||||
findroot(lambda x: x**2 - 1, (2, 4), solver='brent')
|
||||
|
||||
assert findroot(lambda x: x, (-1, 2), solver='brent') == 0.0
|
||||
|
||||
assert findroot(lambda x: x, (-1, 1), solver='brent') == 0.0
|
||||
|
||||
def test_modAB():
|
||||
assert findroot(lambda x: x**2 - 1, (0, 2), solver='modAB') == 1
|
||||
|
||||
# test ordering
|
||||
assert findroot(lambda x: x**2 - 1, (2, 0), solver='modAB') == 1
|
||||
|
||||
with pytest.raises(ValueError, match="expected interval of 2 points"):
|
||||
findroot(lambda x: x**2 - 1, (0,), solver='modAB')
|
||||
|
||||
with pytest.raises(ValueError, match="Function must have opposite signs"):
|
||||
findroot(lambda x: x**2 - 1, (2, 4), solver='modAB')
|
||||
|
||||
# test exact zero hit
|
||||
assert findroot(lambda x: x, (-1, 1), solver='modAB') == 0.0
|
||||
|
||||
# test bisection to secant switch for a purely linear function
|
||||
f_linear = lambda x: 2*x - 4
|
||||
assert mp.almosteq(findroot(f_linear, (0, 5), solver='modAB'), 2.0)
|
||||
|
||||
f_convex = lambda x: x**10 - 1
|
||||
assert mp.almosteq(findroot(f_convex, (0.1, 2.0), solver='modAB'), 1.0)
|
||||
|
||||
f_concave = lambda x: 1 - x**10
|
||||
assert mp.almosteq(findroot(f_concave, (2.0, 0.1), solver='modAB'), 1.0)
|
||||
|
||||
f_cubic_inflection = lambda x: x**3 - 3*x + 3
|
||||
root = findroot(f_cubic_inflection, (-3, 2), solver='modAB')
|
||||
assert abs(f_cubic_inflection(root)) < eps
|
||||
|
||||
# test reset to Bisection if the interval width exceeds the threshold
|
||||
f_step = lambda x: mp.sin(x) if x > 1 else x - 1
|
||||
assert mp.almosteq(findroot(f_step, (0.4, 3.0), solver='modAB'), 1.0)
|
||||
|
||||
def test_multiplicity():
|
||||
for i in range(1, 5):
|
||||
assert multiplicity(lambda x: (x - 1)**i, 1) == i
|
||||
@@ -93,7 +174,11 @@ def test_multidimensional(capsys):
|
||||
f1x = f1(x, y)
|
||||
return (f2(x, y) - f1x, f3(x, y) - f1x)
|
||||
x = findroot(f, (10, 10))
|
||||
assert [int(round(i)) for i in x] == [3, 4]
|
||||
assert [round(i) for i in x] == [3, 4]
|
||||
x = findroot(f, (10, 10), multidimensional=True)
|
||||
assert [round(i) for i in x] == [3, 4]
|
||||
x = findroot(f, (10, 10), J=lambda *x: mp.jacobian(f, x))
|
||||
assert [round(i) for i in x] == [3, 4]
|
||||
|
||||
def test_trivial():
|
||||
assert findroot(lambda x: 0, 1) == 1
|
||||
|
||||
+108
-1
@@ -1,4 +1,10 @@
|
||||
from mpmath import inf, matrix, mpc, nstr
|
||||
import math
|
||||
import random
|
||||
|
||||
import hypothesis.strategies as st
|
||||
from hypothesis import example, given
|
||||
|
||||
from mpmath import inf, matrix, mp, mpc, mpf, nstr, rand
|
||||
|
||||
|
||||
A1 = matrix([])
|
||||
@@ -50,3 +56,104 @@ def test_matrix_str():
|
||||
'''[1.0]
|
||||
[2.0]
|
||||
[3.0]'''
|
||||
|
||||
|
||||
@given(st.floats(allow_subnormal=True,
|
||||
allow_nan=False,
|
||||
allow_infinity=False),
|
||||
st.sampled_from(list('nfcud')))
|
||||
@example(x=6.170920920537087e+17, rnd='f')
|
||||
def test_eval_repr_roundtrip(x, rnd):
|
||||
mp.rounding = rnd
|
||||
mp.shortest_str = False
|
||||
mp.pretty = True
|
||||
mp.pretty_dps = 'repr'
|
||||
mx = mp.mpf(x)
|
||||
smx = repr(mx)
|
||||
assert mx == mp.mpf(smx)
|
||||
mp.pretty_dps = 'str'
|
||||
mp.shortest_str = True
|
||||
smx = repr(mx)
|
||||
assert mx == mp.mpf(smx)
|
||||
|
||||
|
||||
@given(st.floats(allow_subnormal=False,
|
||||
allow_nan=False,
|
||||
allow_infinity=False))
|
||||
@example(1.0)
|
||||
@example(-10.0)
|
||||
@example(3.411330784663857e+16)
|
||||
@example(5.960464477539063e-08)
|
||||
@example(562949953421312.2)
|
||||
def test_float_short_repr(f):
|
||||
mp.shortest_str = True
|
||||
if not f and math.copysign(1, f) == -1:
|
||||
return
|
||||
s = str(f)
|
||||
m = mpf(f)
|
||||
sm = str(m)
|
||||
assert s == sm
|
||||
assert f"mpf('{s}')" == repr(m)
|
||||
assert m == mpf(sm)
|
||||
|
||||
|
||||
@given(st.complex_numbers(allow_subnormal=False,
|
||||
allow_nan=False,
|
||||
allow_infinity=False))
|
||||
@example(1+0.1j)
|
||||
def test_complex_short_repr(z):
|
||||
mp.shortest_str = True
|
||||
mp.pretty = False
|
||||
if ((not z.real and math.copysign(1, z.real) == -1)
|
||||
or (not z.imag and math.copysign(1, z.imag) == -1)):
|
||||
return # skip negative zero
|
||||
s = str(z)
|
||||
mz = mpc(z)
|
||||
smz = str(mz)
|
||||
assert s == smz
|
||||
assert f"mpc(real='{mz.real!s}', imag='{mz.imag!s}')" == repr(mz)
|
||||
assert mz == mpc(smz)
|
||||
mp.pretty = True
|
||||
assert smz == repr(mz)
|
||||
|
||||
|
||||
def test_short_repr_specials():
|
||||
mp.shortest_str = True
|
||||
assert str(mpf(0)) == '0.0'
|
||||
assert str(mpf('inf')) == 'inf'
|
||||
assert str(mpf('-inf')) == '-inf'
|
||||
assert str(mpf('nan')) == 'nan'
|
||||
|
||||
|
||||
def test_short_repr_roundtrip():
|
||||
mp.shortest_str = True
|
||||
for dps in [15, 20, 30, 50, 100, 300]:
|
||||
with mp.workdps(dps):
|
||||
for _ in range(1000):
|
||||
f = random.choice([(rand()-0.5)*2 for _ in range(10)]
|
||||
+ [(rand()-0.5)*2*10**5 for _ in range(5)]
|
||||
+ [(rand()-0.5)*2/10**5 for _ in range(5)]
|
||||
+ [(rand()-0.5)*2*10**100 for _ in range(2)]
|
||||
+ [(rand()-0.5)*2*10**10000 for _ in range(2)]
|
||||
+ [(rand()-0.5)*2/10**10000 for _ in range(2)])
|
||||
s = str(f)
|
||||
b = mpf(s)
|
||||
assert f == b # round-trip
|
||||
|
||||
integer, *frac = s.split('.')
|
||||
if not frac:
|
||||
continue
|
||||
frac = frac[0]
|
||||
if len(frac) < 2:
|
||||
continue
|
||||
frac, *exponent = frac.split('e')
|
||||
exponent = 'e' + exponent[0] if exponent else ''
|
||||
|
||||
# round-trip:
|
||||
assert f == mpf(str(integer + '.' + frac + exponent))
|
||||
|
||||
# test that short repr is really minimal
|
||||
frac = frac[:-1]
|
||||
for d in range(10):
|
||||
frac = frac[:-1] + str(d)
|
||||
assert f != mpf(str(integer + '.' + frac + exponent))
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test that the version number is provided correctly in a frozen (bundled)
|
||||
# executable (see #1044).
|
||||
|
||||
set -e
|
||||
|
||||
# Get the directory of the current repository
|
||||
MPMATH_DIR="$(realpath "$(dirname "$0")/../../")"
|
||||
echo "Repo mpmath directory: $MPMATH_DIR"
|
||||
|
||||
echo "Install requirements..."
|
||||
python3 -m pip install build pyinstaller
|
||||
|
||||
echo "Building the source distribution from the local repo..."
|
||||
python3 -m build --sdist
|
||||
|
||||
# Find and install the generated tarball
|
||||
TARBALL=$(ls -t dist/*.tar.gz | head -1)
|
||||
echo "Generated tarball: $TARBALL"
|
||||
|
||||
echo "Installing mpmath from tarball..."
|
||||
pip install dist/"$(basename $TARBALL)"
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
echo "Created temporary directory: $TEMP_DIR"
|
||||
cd "$TEMP_DIR"
|
||||
|
||||
# Create version_script.py that prints the package version
|
||||
cat << EOF > version_script.py
|
||||
import mpmath
|
||||
print(mpmath.__version__)
|
||||
EOF
|
||||
|
||||
# Save local version for later comparison
|
||||
DIRECT_VERSION="$(python3 -m mpmath --version)"
|
||||
|
||||
echo "Building version_script with PyInstaller..."
|
||||
pyinstaller --onefile --clean version_script.py
|
||||
|
||||
echo "Run frozen executable and extract the version from the output..."
|
||||
FROZEN_VERSION="$(./dist/version_script 2>&1)"
|
||||
|
||||
if [ "$DIRECT_VERSION" == "$FROZEN_VERSION" ]; then
|
||||
echo "Test passed: Version matches in frozen (bundled) executable."
|
||||
else
|
||||
echo "Test failed: Version mismatch."
|
||||
echo "Direct version: $DIRECT_VERSION"
|
||||
echo "Frozen version: $FROZEN_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
@@ -9,7 +9,6 @@ import pytest
|
||||
from mpmath import fp, mp
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:.*:DeprecationWarning")
|
||||
def test_axes():
|
||||
try:
|
||||
import matplotlib
|
||||
@@ -19,8 +18,7 @@ def test_axes():
|
||||
raise ImportError
|
||||
import pylab
|
||||
except ImportError:
|
||||
print("\nSkipping test (pylab not available or too old version)\n")
|
||||
return
|
||||
pytest.skip("\nSkipping test (pylab not available or too old version)\n")
|
||||
fig = pylab.figure()
|
||||
axes = fig.add_subplot(111)
|
||||
for ctx in [mp, fp]:
|
||||
@@ -34,3 +32,61 @@ def test_axes():
|
||||
ctx.cplot(lambda z: z, [-2, 2], [-10, 10], axes=axes)
|
||||
assert axes.get_xlabel() == 'Re(z)'
|
||||
assert axes.get_ylabel() == 'Im(z)'
|
||||
|
||||
|
||||
def test_issue_379():
|
||||
try:
|
||||
import pylab
|
||||
except ImportError:
|
||||
pytest.skip("\nSkipping test (pylab not available)\n")
|
||||
|
||||
for ctx in [mp, fp]:
|
||||
for points in [8, 9]:
|
||||
fig = pylab.figure()
|
||||
axes = fig.add_subplot(111)
|
||||
evaluated = []
|
||||
|
||||
def f(z):
|
||||
evaluated.append(z)
|
||||
return z
|
||||
|
||||
ctx.cplot(f, points=points, axes=axes)
|
||||
assert len(evaluated) == 9
|
||||
assert axes.images[0].get_array().shape == (3, 3, 3)
|
||||
pylab.close(fig)
|
||||
|
||||
|
||||
def test_issue_1007():
|
||||
# plot(), cplot() and splot() must not leave a stale figure open
|
||||
# when the user-supplied function raises an unexpected exception;
|
||||
# otherwise that blank figure lingers and is shown on the next call.
|
||||
try:
|
||||
import matplotlib
|
||||
version = matplotlib.__version__.split("-")[0]
|
||||
version = version.split(".")[:2]
|
||||
if [int(_) for _ in version] < [0,99]:
|
||||
raise ImportError
|
||||
import pylab
|
||||
except ImportError:
|
||||
pytest.skip("\nSkipping test (pylab not available or too old version)\n")
|
||||
|
||||
class Boom(Exception):
|
||||
pass
|
||||
|
||||
def bad(*args):
|
||||
# An error that is not in plot_ignore, so it propagates out of
|
||||
# plot()/cplot()/splot() instead of being silently skipped.
|
||||
raise Boom
|
||||
|
||||
for ctx in [mp, fp]:
|
||||
pylab.close("all")
|
||||
pytest.raises(Boom, lambda: ctx.plot(bad, [0, 2]))
|
||||
assert pylab.get_fignums() == []
|
||||
|
||||
pylab.close("all")
|
||||
pytest.raises(Boom, lambda: ctx.cplot(bad, [-2, 2], [-2, 2]))
|
||||
assert pylab.get_fignums() == []
|
||||
|
||||
pylab.close("all")
|
||||
pytest.raises(Boom, lambda: ctx.splot(bad, [-1, 1], [-1, 1]))
|
||||
assert pylab.get_fignums() == []
|
||||
|
||||
+21
-7
@@ -81,6 +81,9 @@ def plot(ctx, f, xlim=[-5,5], ylim=None, points=200, file=None, dpi=None,
|
||||
if segment:
|
||||
segments.append(segment)
|
||||
segment = []
|
||||
except Exception:
|
||||
pylab.close(fig)
|
||||
raise
|
||||
if segment:
|
||||
segments.append(segment)
|
||||
for segment in segments:
|
||||
@@ -171,10 +174,14 @@ def cplot(ctx, f, re=[-5,5], im=[-5,5], points=2000, color=None,
|
||||
with white for positive reals, black for negative reals, gold in the
|
||||
upper half plane, and blue in the lower half plane.
|
||||
|
||||
To obtain a sharp image, the number of points may need to be
|
||||
increased to 100,000 or thereabout. Since evaluating the
|
||||
function that many times is likely to be slow, the 'verbose'
|
||||
option is useful to display progress.
|
||||
The *points* argument specifies approximately the total number of
|
||||
evaluation points in the rectangular grid, not the number per axis.
|
||||
The number of points on each axis is rounded upward independently,
|
||||
so the actual number of evaluations may be slightly larger.
|
||||
|
||||
To obtain a sharp image, *points* may need to be increased to 100,000
|
||||
or thereabout. Since evaluating the function that many times is likely
|
||||
to be slow, the 'verbose' option is useful to display progress.
|
||||
|
||||
.. note :: This function requires matplotlib (pylab).
|
||||
"""
|
||||
@@ -193,8 +200,8 @@ def cplot(ctx, f, re=[-5,5], im=[-5,5], points=2000, color=None,
|
||||
ima, imb = im
|
||||
dre = reb - rea
|
||||
dim = imb - ima
|
||||
M = int(ctx.sqrt(points*dre/dim)+1)
|
||||
N = int(ctx.sqrt(points*dim/dre)+1)
|
||||
M = int(ctx.ceil(ctx.sqrt(points*dre/dim)))
|
||||
N = int(ctx.ceil(ctx.sqrt(points*dim/dre)))
|
||||
x = pylab.linspace(rea, reb, M)
|
||||
y = pylab.linspace(ima, imb, N)
|
||||
# Note: we have to be careful to get the right rotation.
|
||||
@@ -209,6 +216,9 @@ def cplot(ctx, f, re=[-5,5], im=[-5,5], points=2000, color=None,
|
||||
v = color(f(z))
|
||||
except ctx.plot_ignore:
|
||||
v = (0.5, 0.5, 0.5)
|
||||
except Exception:
|
||||
pylab.close(fig)
|
||||
raise
|
||||
w[n,m] = v
|
||||
if verbose:
|
||||
print(str(n) + ' of ' + str(N))
|
||||
@@ -268,7 +278,11 @@ def splot(ctx, f, u=[-5,5], v=[-5,5], points=100, keep_aspect=True,
|
||||
xab, yab, zab = [[0, 0] for i in range(3)]
|
||||
for n in range(N):
|
||||
for m in range(M):
|
||||
fdata = f(ctx.convert(u[m]), ctx.convert(v[n]))
|
||||
try:
|
||||
fdata = f(ctx.convert(u[m]), ctx.convert(v[n]))
|
||||
except Exception:
|
||||
plt.close(fig)
|
||||
raise
|
||||
try:
|
||||
x[m,n], y[m,n], z[m,n] = fdata
|
||||
except TypeError:
|
||||
|
||||
+9
-9
@@ -1,5 +1,5 @@
|
||||
[build-system]
|
||||
requires = ['setuptools>=77', 'setuptools_scm[toml]>=6.0']
|
||||
requires = ['setuptools>=77', 'setuptools-scm>=8']
|
||||
build-backend = 'setuptools.build_meta'
|
||||
|
||||
[project]
|
||||
@@ -12,17 +12,17 @@ classifiers = ['Topic :: Scientific/Engineering :: Mathematics',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3 :: Only',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Programming Language :: Python :: 3.13',
|
||||
'Programming Language :: Python :: 3.14',
|
||||
'Programming Language :: Python :: 3.15',
|
||||
'Programming Language :: Python :: Free Threading :: 2 - Beta',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Programming Language :: Python :: Implementation :: PyPy']
|
||||
dynamic = ['version']
|
||||
requires-python = '>=3.9'
|
||||
requires-python = '>=3.10'
|
||||
readme = 'README.rst'
|
||||
|
||||
[project.urls]
|
||||
@@ -32,13 +32,12 @@ Homepage = 'https://mpmath.org/'
|
||||
Documentation = 'http://mpmath.org/doc/current/'
|
||||
|
||||
[project.optional-dependencies]
|
||||
tests = ['pytest>=6', 'numpy', 'packaging', 'pytest-timeout',
|
||||
'matplotlib', 'pexpect', 'ipython', 'hypothesis']
|
||||
tests = ['pytest>=6', 'numpy; python_version<"3.15"', 'packaging', 'pytest-timeout',
|
||||
'matplotlib; python_version<"3.15"', 'pexpect', 'ipython', 'hypothesis']
|
||||
develop = ['mpmath[tests]', 'flake518>=1.5', 'pytest-cov>=7', 'wheel', 'build']
|
||||
gmpy2 = ['gmpy2>=2.3']
|
||||
gmpy = ['mpmath[gmpy2]']
|
||||
gmp = ['python-gmp>=0.5; python_version>="3.11"',
|
||||
'python-gmp>=0.4; python_version<"3.11"']
|
||||
gmp = ['python-gmp']
|
||||
docs = ['sphinx', 'matplotlib', 'sphinxcontrib-autoprogram']
|
||||
ci = ['pytest-xdist', 'diff_cover']
|
||||
|
||||
@@ -55,11 +54,12 @@ exclude = ['.eggs', '.git']
|
||||
max_line_length = 200
|
||||
|
||||
[tool.setuptools_scm]
|
||||
version_file = "mpmath/_version.py"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ['mpmath', 'docs']
|
||||
doctest_optionflags = ['IGNORE_EXCEPTION_DETAIL', 'ELLIPSIS']
|
||||
addopts = "--durations=20 --doctest-modules --doctest-glob='*.rst'"
|
||||
addopts = "--doctest-modules --doctest-glob='*.rst'"
|
||||
norecursedirs = ['docs/plots', 'demo', '.eggs', '.git', '.hypothesis']
|
||||
filterwarnings = ['error::DeprecationWarning']
|
||||
xfail_strict = true
|
||||
@@ -67,7 +67,7 @@ timeout = 600
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = true
|
||||
omit = ['mpmath/tests/*']
|
||||
omit = ['mpmath/tests/*', 'mpmath/_version.py']
|
||||
patch = ["subprocess"]
|
||||
|
||||
[tool.coverage.html]
|
||||
|
||||
Reference in New Issue
Block a user