Compare commits

...

231 Commits

Author SHA1 Message Date
Sergey B Kirpichev 5fa2b9c793 Merge pull request #1160 from skirpichev/misc
Misc fixes
2026-08-22 03:23:58 +03:00
Sergey B Kirpichev d3812b1194 Amend bc96a889 2026-08-21 08:25:46 +03:00
Sergey B Kirpichev d8c4597a7d Merge pull request #1159 from skirpichev/misc
v1.5.0a1
2026-08-21 05:28:28 +03:00
Sergey B Kirpichev f3751eb87f Update CHANGES 2026-08-21 05:15:07 +03:00
Sergey B Kirpichev bcc67d3e55 Enable cancel-in-progress in CI 2026-08-21 04:37:44 +03:00
Sergey B Kirpichev 27a1fe7f6d Merge pull request #1115 from skirpichev/shortest-repr/1028
Use shortest decimal representations for str/repr
2026-08-21 04:34:47 +03:00
Ayush Baranwal ffcaef924a Add fft()/invfft() functions for DFT calculation (#1152)
Addresses #707

Implements the Radix-2 Cooley-Tukey Fast Fourier Transform (FFT) algorithm to
compute the discrete fourier transform and inverse discrete fourier transform of a signal.

Inputs are currently restricted to lengths of powers of 2.
2026-08-21 04:23:58 +03:00
Sergey B Kirpichev bc96a88926 Apply naive algorithm for huge exponents 2026-08-21 04:14:33 +03:00
Sergey B Kirpichev 0d2af59d5d Speedup exponent computation 2026-08-20 12:46:40 +03:00
Sergey B Kirpichev 70b722800f Use shortest decimal representations for str/repr
Was:
```
$ python -m mpmath
>>> 0.1
0.10000000000000001
```

Now:
```
$ python -m mpmath --shortest-str
>>> 0.1
0.1
```

Closes #1028
2026-08-20 12:46:40 +03:00
Sergey B Kirpichev d271b8312a Merge pull request #1157 from HeskethGD/omega-period-optimize-pr
Optimize conversion from Weierstrass invariants to half-periods
2026-08-19 04:31:41 +03:00
HeskethGD 49c7eeb43c Normalize Weierstrass parameter conversions 2026-08-16 16:01:54 +03:00
Sergey B Kirpichev bf6459d6be Merge pull request #1155 from userfypp/codex/fix-cplot-points-379
Fix cplot points rounding (#379)
2026-08-16 02:30:58 +03:00
HeskethGD f72b86ca17 Optimize Weierstrass period conversion 2026-08-15 20:09:07 +03:00
alexfyp 0abd5de3f0 Fix cplot points rounding (#379) 2026-08-11 15:10:35 +02:00
Sergey B Kirpichev 017cc473fb Merge pull request #1154 from skirpichev/document-pretty_dps
Document pretty_dps context's option
2026-08-06 06:32:14 +03:00
Sergey B Kirpichev bfaf6a7a31 Merge pull request #1153 from skirpichev/use-n-rounding-in-repr/1129
Use round_nearest in repr/str and as default mpf's rounding mode
2026-08-06 06:31:37 +03:00
Sergey B Kirpichev 24538751aa Document pretty_dps context's option
This amends a14901da.
2026-08-06 05:07:36 +03:00
Sergey B Kirpichev 4a3af8ef2b Use round_nearest in repr/str and as default mpf's rounding mode
Better alternative to #1137 (which was partially reverted).

Closes #1129
2026-08-05 09:47:43 +03:00
Sergey B Kirpichev e5142170db Merge pull request #1127 from skirpichev/use-explicit-kwargs/1056
Use explicit kwargs in public API, where possible
2026-08-05 04:32:15 +03:00
Sergey B Kirpichev fb69943530 Use explicit kwargs in public API, where possible
Following functions kept:
```pycon
>>> import inspect
... with_args = []
... with_kwargs = []
... for n in dir(mpmath):
...     m = getattr(mpmath, n)
...     try:
...         s = inspect.signature(m)
...     except:
...         continue
...     if any(_.kind == inspect._ParameterKind.VAR_POSITIONAL for _ in s.parameters.values()):
...         for name in s.parameters:
...             if s.parameters[name].kind == inspect._ParameterKind.VAR_POSITIONAL and name == 'args':
...                 with_args.append(n)
...                 break
...     if any(_.kind == inspect._ParameterKind.VAR_KEYWORD for _ in s.parameters.values()):
...         with_kwargs.append(n)
... print(with_args)
... print(with_kwargs)
...
['arange', 'ellipe', 'ellippi', 'linspace', 'matrix', 'ones', 'timing', 'zeros']
['multiplicity', 'timing']
```

We need support for multiple signatures in the first case.  In the
second - it's impossible to implement these functions without kwargs.

Closes #1056
2026-08-04 06:40:53 +03:00
Sanjay Santhanam c90e242741 Avoid spurious overflow in fp gammaprod (#1150)
* Avoid spurious overflow in fp gammaprod

fp.binomial(1100, 1) raised OverflowError even though the result is
exactly 1100: gammaprod multiplies the individual gamma values, and
gamma(1101) exceeds the double range although the quotient does not.

Fall back to evaluating the regular numerator/denominator in log space
when a term overflows, so the quotient is computed whenever it is
representable. Genuinely out-of-range results, such as
fp.binomial(1100, 550), still raise OverflowError.

Closes #493
2026-08-03 07:09:31 +03:00
Sergey B Kirpichev 255d98ff98 Merge pull request #1151 from mpmath/dependabot/github_actions/actions-deps-696b649f44
Bump actions/setup-python from 6 to 7 in the actions-deps group
2026-08-02 03:08:49 +03:00
dependabot[bot] da14fb3a42 Bump actions/setup-python from 6 to 7 in the actions-deps group
Bumps the actions-deps group with 1 update: [actions/setup-python](https://github.com/actions/setup-python).


Updates `actions/setup-python` from 6 to 7
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 22:55:26 +00:00
catalin-hanga 4f58dcd947 Added 2 new expceptions to Rosser's Rule (#1148)
closes #1147
2026-07-31 11:21:22 +03:00
Sergey B Kirpichev 3e73240699 Merge pull request #1146 from HeskethGD/g2-g3-omega-from
test / linter (push) Failing after 1s
test / coverage (push) Failing after 0s
test / docs (push) Failing after 0s
Publish on PyPI / Build distributions (push) Has been cancelled
test / frozen-version (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
test / tests (false, false, 3.10) (push) Has been cancelled
test / tests (false, false, 3.11) (push) Has been cancelled
test / tests (false, false, 3.12) (push) Has been cancelled
test / tests (false, false, 3.13) (push) Has been cancelled
test / tests (false, false, 3.14) (push) Has been cancelled
test / tests (false, false, 3.14t) (push) Has been cancelled
test / tests (false, false, 3.15) (push) Has been cancelled
test / tests (false, false, 3.15t) (push) Has been cancelled
test / tests (true, pypy3.11) (push) Has been cancelled
test / tests (true, 3.x) (push) Has been cancelled
Standardise Weierstrass from param functions
2026-07-27 03:54:00 +03:00
Sergey B Kirpichev 460c289f90 Update repo link (git -> https)
Publish on PyPI / Build distributions (push) Has been cancelled
test / frozen-version (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
test / tests (false, false, 3.10) (push) Has been cancelled
test / tests (false, false, 3.11) (push) Has been cancelled
test / tests (false, false, 3.12) (push) Has been cancelled
test / tests (false, false, 3.13) (push) Has been cancelled
test / tests (false, false, 3.14) (push) Has been cancelled
test / tests (false, false, 3.14t) (push) Has been cancelled
test / tests (false, false, 3.15) (push) Has been cancelled
test / tests (false, false, 3.15t) (push) Has been cancelled
test / tests (true, pypy3.11) (push) Has been cancelled
test / coverage (push) Failing after 1s
test / docs (push) Failing after 0s
test / linter (push) Failing after 1s
test / tests (true, 3.x) (push) Has been cancelled
Closes #1145
2026-07-26 03:07:07 +03:00
HeskethGD 1f3e9bf593 Rename Weierstrass parameter converters 2026-07-25 17:32:21 +01:00
HeskethGD 534dd479f0 Expand Weierstrass parameter conversions 2026-07-25 17:32:06 +01:00
Sergey B Kirpichev 3481440ce5 Merge pull request #1141 from HeskethGD/simplified-omega
test / coverage (push) Failing after 0s
test / docs (push) Failing after 0s
test / linter (push) Failing after 1s
Publish on PyPI / Build distributions (push) Has been cancelled
test / frozen-version (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
test / tests (false, false, 3.10) (push) Has been cancelled
test / tests (false, false, 3.11) (push) Has been cancelled
test / tests (false, false, 3.12) (push) Has been cancelled
test / tests (false, false, 3.13) (push) Has been cancelled
test / tests (false, false, 3.14) (push) Has been cancelled
test / tests (false, false, 3.14t) (push) Has been cancelled
test / tests (false, false, 3.15) (push) Has been cancelled
test / tests (false, false, 3.15t) (push) Has been cancelled
test / tests (true, pypy3.11) (push) Has been cancelled
test / tests (true, 3.x) (push) Has been cancelled
Simplify and standardise weierhalfperiods
2026-07-25 06:04:39 +03:00
Sergey B Kirpichev 28e36ca652 Merge pull request #1144 from skirpichev/ulp
test / linter (push) Failing after 0s
test / coverage (push) Failing after 0s
test / docs (push) Failing after 1s
Publish on PyPI / Build distributions (push) Has been cancelled
test / frozen-version (push) Has been cancelled
test / tests (true, 3.x) (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
test / tests (false, false, 3.14t) (push) Has been cancelled
test / tests (false, false, 3.15) (push) Has been cancelled
test / tests (false, false, 3.10) (push) Has been cancelled
test / tests (false, false, 3.11) (push) Has been cancelled
test / tests (false, false, 3.12) (push) Has been cancelled
test / tests (false, false, 3.13) (push) Has been cancelled
test / tests (false, false, 3.14) (push) Has been cancelled
test / tests (false, false, 3.15t) (push) Has been cancelled
test / tests (true, pypy3.11) (push) Has been cancelled
Add ulp(), see #1009
2026-07-24 13:37:18 +03:00
Sergey B Kirpichev b9fd43cc42 Add ulp(), see #1009 2026-07-24 01:31:54 +03:00
Sergey B Kirpichev 8d0190ad6d Merge pull request #1143 from skirpichev/fix-1142
test / docs (push) Failing after 0s
test / coverage (push) Failing after 0s
test / linter (push) Failing after 0s
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
test / tests (false, false, 3.10) (push) Has been cancelled
test / tests (false, false, 3.11) (push) Has been cancelled
test / tests (false, false, 3.12) (push) Has been cancelled
test / tests (false, false, 3.13) (push) Has been cancelled
test / tests (false, false, 3.14) (push) Has been cancelled
test / tests (false, false, 3.14t) (push) Has been cancelled
test / tests (false, false, 3.15) (push) Has been cancelled
test / tests (false, false, 3.15t) (push) Has been cancelled
test / tests (true, pypy3.11) (push) Has been cancelled
test / frozen-version (push) Has been cancelled
test / tests (true, 3.x) (push) Has been cancelled
Correct definitions for spherical Bessel functions
2026-07-23 04:04:44 +03:00
Sergey B Kirpichev afdb228884 Correct definitions for spherical Bessel functions
Closes #1142
2026-07-23 03:24:33 +03:00
Vincent Gao 2de2d509ec Widen to_str's digit window so str/nstr rounds near-boundary values correctly (#1139)
to_str extracted only dps+10 digits, narrower than format_scientific and
format_fixed which cover the whole mantissa.  A value just above a decimal
boundary was 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.
Widen the base-10 window to match the sibling formatters.

Also fixed test for from_str(), coming from 30e8001e
2026-07-22 00:05:32 +03:00
HeskethGD 55aefa6f82 Simplified and standardised weierhalfperiods 2026-07-21 16:45:46 +01:00
Sergey B Kirpichev 7eb6f23063 Merge pull request #1138 from skirpichev/fix-1135
Use CoW pattern to manage constant_memo() cache
2026-07-19 15:06:33 +03:00
Sergey B Kirpichev 65dca57ca7 Use CoW pattern to manage constant_memo() cache
Closes #1135
2026-07-19 10:28:41 +03:00
Sergey B Kirpichev 6309ea0550 Merge pull request #1137 from skirpichev/fix-repr-roundtrip/1129
Use reversed rounding mode for repr
2026-07-19 07:16:33 +03:00
Sergey B Kirpichev d30d632c39 Use reversed rounding mode for repr
Closes #1129
2026-07-19 04:59:27 +03:00
Sergey B Kirpichev d8c9fd2ea3 Refactor round_digits()
* add exponent argument
* pass original number
* make inexact flag local
2026-07-19 04:07:42 +03:00
Vincent Gao 101a8379a8 Fix last-digit rounding of the "f" format type (#1132)
format_fixed() extracts a handful of guard digits and lets round_digits()
decide, so a nonzero remainder lying past them was invisible: directed and
nearest rounding truncated the last digit instead of rounding it up.  The
value is dyadic, so its exactness at the extracted digits is decidable;
pass that as a inexact flag and round on it.  The "e" path and MPFR already
round these correctly.

Values whose leading digit falls past the last requested place skipped
round_digits() altogether and always printed zeros, even under rounding
away from zero; round them to one unit in the last place in that case.

Closes #1131

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
2026-07-18 07:33:23 +03:00
Sergey B Kirpichev ead1a61d09 Merge pull request #1134 from skirpichev/fix-1130
Better document None return value of the pslq()
2026-07-16 10:25:38 +03:00
Sergey B Kirpichev 9e85fabc1d Better document None return value of the pslq()
Closes #1130
2026-07-16 09:15:25 +03:00
Sergey B Kirpichev 6d356df80f Merge pull request #1117 from HeskethGD/enhance-kleinj-tau
Add kleinjinv and support g and omega in taufrom
2026-07-13 20:41:50 +03:00
HeskethGD b747a30db1 Support Weierstrass data in elliptic parameter helpers 2026-07-13 16:11:02 +01:00
HeskethGD 6f2a553551 Add inverse Klein j-function 2026-07-13 16:08:46 +01:00
Sergey B Kirpichev 52c1546a0c Merge pull request #1124 from skirpichev/explicit-kwargs-for-libmp-funcs
Change signature for wrapped libmp's functions
2026-07-11 09:30:56 +03:00
Sergey B Kirpichev 573549a39f Merge pull request #1126 from skirpichev/fix-docs-ci
Fix docs CI job
2026-07-11 09:26:26 +03:00
Sergey B Kirpichev ed63e50a8a Fix weierp/weierpinv() docstrings
Closes #1125
2026-07-11 09:18:32 +03:00
Sergey B Kirpichev a2333847a8 Fix docs CI job 2026-07-11 09:16:26 +03:00
Sergey B Kirpichev c328efcd37 Change signature for wrapped libmp's functions
See #1056
2026-07-11 07:17:59 +03:00
Apoorv Darshan 86978167e1 Close leftover figure in plot/cplot/splot on error (#1123)
Fixes #1007
2026-07-10 04:57:23 +03:00
Warren Weckesser 8d21910bba Implement the modified spherical bessel functions spherical_in() and spherical_kn(). (#1121) 2026-07-08 04:13:53 +03:00
Sergey B Kirpichev 76d91a4fa5 Merge pull request #1120 from skirpichev/theta-apply-qperiodicy
Use quasi-periodicity property (DLMF, §20.2(ii)) to compute jtheta()
2026-07-07 07:44:49 +03:00
Sergey B Kirpichev cc198d4ecd Use quasi-periodicity property (DLMF, §20.2(ii)) to compute jtheta()
*_theta2/3a() helpers now not needed
2026-07-05 09:24:26 +03:00
Sergey B Kirpichev 5688722737 Merge pull request #1119 from mpmath/dependabot/github_actions/actions-deps-640176b5ab
Bump actions/checkout from 6 to 7 in the actions-deps group
2026-07-02 04:10:57 +03:00
Sergey B Kirpichev 40cf9ba085 Merge pull request #1118 from skirpichev/fix-repr_dps/1116
Fix repr_dps() to produce a correct estimate
2026-07-02 04:07:27 +03:00
dependabot[bot] b20d7c530a Bump actions/checkout from 6 to 7 in the actions-deps group
Bumps the actions-deps group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 22:55:58 +00:00
Sergey B Kirpichev f12743f60f Remove dead code in secondzeta_exp_term() 2026-07-01 08:31:40 +03:00
Sergey B Kirpichev c44efa2ca2 Remove py2 remnants (cast round() to int) 2026-07-01 08:08:19 +03:00
Sergey B Kirpichev 6f1fbd275d Fix repr_dps() to produce a correct estimate
That should work at lest for 32-bit integers.  Though, I think that
the repr/str representation loses all sence for precisions much
less than that ;-)

Closes #1116
2026-07-01 05:36:13 +03:00
Graham Hesketh aa205a2821 Add Weierstrass elliptic functions (#1113)
* Weierstrass $\wp$: `weierp`
* inverse Weierstrass $\wp$: `weierpinv`
* derivative of Weierstrass $\wp$: `weierpprime`
* Weierstrass zeta: `weierzeta`
* Weierstrass sigma: `weiersigma`

Also adds parameter conversion functions:

* Weierstrass invariants $g_2, g_3$: `weierinvariants`
* half-periods $\omega_1, \omega_2$: `weierhalfperiods`

The idea for this PR was inspired by the `pyweierstrass` package by @stla / Stéphane Laurent:

* https://pyweierstrass.readthedocs.io/en/latest/
* https://github.com/stla/pyweierstrass

The code implementation in this PR is different to that in `pyweierstrass` although both packages derive from similar well known mathematical formulas. Differences between `pyweierstrass` and this `mpmath` implementation include but are not limited to:

* function names adapted to `mpmath` conventions;
* tau normalization: `tau` specifies the normalized period lattice `(1, tau)`, corresponding to half-periods `(1/2, tau/2)`; this differs from `pyweierstrass`, where `tau` denotes half-periods `(1, tau)`;
* support for disambiguating inverse values of Weierstrass $\wp$ by optionally passing the corresponding derivative value;
* internal refactoring of helper functions for integration with `mpmath`.

Dedicated to Stéphane Laurent who opened the original PR to request this functionality in `mpmath` and whose `pyweierstrass` package provided great utility to the author of this PR in the years since.

Co-authored-by: Stéphane Laurent <stla@users.noreply.github.com>

Closes #612
2026-06-30 03:43:58 +03:00
Sergey B Kirpichev a686b903ae Merge pull request #1111 from skirpichev/jtheta-modular/930
Use generic modular transformations to compute jtheta() with |q| ~ 1
2026-06-29 14:12:08 +03:00
Sergey B Kirpichev 2a2de4e7ca Drop mp.THETA_Q_LIM 2026-06-29 03:19:33 +03:00
Sergey B Kirpichev 8e31d10120 Use generic modular transformations to compute jtheta() with |q| ~ 1
See https://fungrim.org/topic/Lattice_transformations_for_Jacobi_theta_functions/#General_modular_transformations

Closes #930

Co-Authored-By: Jam Balaya <jambalaya.pyoncafe@outlook.jp>
2026-06-29 03:19:33 +03:00
Sergey B Kirpichev 90e1cff769 Reapply "Revert testing of GMP-backends for PyPy, see #1013"
This reverts commit 390972e250.
2026-06-26 13:38:01 +03:00
Vincent Gao ba2a0da3a1 Fix chebyfit IndexError when N <= 0 (#1114)
chebyfit(f, interval, N) support only positive integer N.  Add an explicit ValueError.
2026-06-26 05:13:49 +03:00
Sergey B Kirpichev c4f9187805 Merge pull request #1021 from skirpichev/test-pypy-on-gmpy/1013
Re-enable testing with GMP backends on PyPy
2026-06-26 04:50:05 +03:00
Sergey B Kirpichev 390972e250 Revert "Revert testing of GMP-backends for PyPy, see #1013"
This reverts commit f580874a54.

Closes #1013
2026-06-25 03:13:21 +03:00
Sergey B Kirpichev 1626936cca Merge pull request #1112 from skirpichev/misc
Misc fixes
2026-06-25 03:06:45 +03:00
Sergey B Kirpichev 1aff951d93 Fix fp.hypsum() to exit if ZeroDivisionError occurs and t==0
Closes #496
2026-06-24 16:19:06 +03:00
Sergey B Kirpichev 92f61841bf Merge pull request #1110 from skirpichev/prepare-v1.5.0a0
test / linter (push) Failing after 1s
test / coverage (push) Failing after 0s
test / docs (push) Failing after 0s
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
test / frozen-version (push) Has been cancelled
test / tests (false, false, 3.10) (push) Has been cancelled
test / tests (false, false, 3.11) (push) Has been cancelled
test / tests (false, false, 3.12) (push) Has been cancelled
test / tests (false, false, 3.13) (push) Has been cancelled
test / tests (false, false, 3.14) (push) Has been cancelled
test / tests (false, false, 3.14t) (push) Has been cancelled
test / tests (false, false, 3.15) (push) Has been cancelled
test / tests (false, false, 3.15t) (push) Has been cancelled
test / tests (true, pypy3.11) (push) Has been cancelled
test / tests (true, 3.x) (push) Has been cancelled
Prepare v1.5.0a0
2026-06-24 04:43:20 +03:00
Sergey B Kirpichev bf99017677 Allow gmpy2 for CPython v3.15 2026-06-24 04:27:00 +03:00
Sergey B Kirpichev 5a4116a13e Update CHANGES 2026-06-24 04:18:33 +03:00
Sergey B Kirpichev b5e38772b1 Merge pull request #1109 from skirpichev/fix-integral-path-for-lerchphi/252
Change integral path of the lerchphi() to use Laplace transform integral
2026-06-24 03:43:34 +03:00
Sergey B Kirpichev 085f181126 Change integral path of the lerchphi() to use Laplace transform integral
Current formula ignores some constraints: https://dlmf.nist.gov/25.14#E6

Implementation follows to lerch_hankel():
https://fredrikj.net/blog/2022/02/computing-the-lerch-transcendent/#section6

Closes #252
2026-06-23 13:17:01 +03:00
Sergey B Kirpichev 60cf796714 Merge pull request #1107 from skirpichev/fix-1104
Improve fixed-precision computation paths for jtheta()
2026-06-23 08:11:34 +03:00
Sergey B Kirpichev 3f3d3ec9a3 Merge pull request #1108 from skirpichev/add-jacobi_symbol
Add jacobi_symbol() to libintmath
2026-06-23 08:01:22 +03:00
Sergey B Kirpichev 1d89343973 Add jacobi_symbol() to libintmath 2026-06-23 07:16:42 +03:00
Sergey B Kirpichev 9e890f67cd Drop _jacobi_theta2() 2026-06-23 04:58:49 +03:00
Sergey B Kirpichev 1cfd36f49e Drop _jacobi_theta3() 2026-06-23 04:58:49 +03:00
Sergey B Kirpichev 0fe9033f18 Use prec kwarg for cos_sin in theta helpers 2026-06-23 04:58:49 +03:00
Sergey B Kirpichev c8a17b495d Amend dca2abd6 2026-06-23 04:58:49 +03:00
Sergey B Kirpichev 3e6e7967ad Reorganize fixed-precision computations for theta3
θ₃(z) ~ 1 + 2q¹cos(2z) + 2q⁴cos(4z) + ..., and we
are in |Im(z)| < |Re(log(q))|/2 domain.

To avoid severe cancellation we compute in fixed-point
only s ~ 2(cos(2z) + q³ cos(4z) + ...), then
return 1 + s*q.

Closes #1104
2026-06-23 04:58:48 +03:00
Sergey B Kirpichev 5e683847c7 Fix test_compatibility()
Avoid using of private numpy API
2026-06-23 03:42:46 +03:00
Sergey B Kirpichev 6dfd359ed6 Merge pull request #1106 from skirpichev/misc
Misc fixes
2026-06-21 12:39:33 +03:00
Sergey B Kirpichev 7a1aaff95a Add debug info to random tests in mpmath/tests/test_elliptic.py
Also some cleanup for assert statements and comments
2026-06-21 12:16:06 +03:00
Sergey B Kirpichev dca2abd6ca Cleanup jtheta()
* normalize result
* use extraprec context manager
* add error message
2026-06-21 12:16:06 +03:00
Sergey B Kirpichev a8a614c5f0 Drop _djtheta() helper method 2026-06-21 11:14:17 +03:00
Sergey B Kirpichev e002cb4488 Allow python-gmp for CPython v3.15 2026-06-21 10:59:32 +03:00
Sergey B Kirpichev b62c4173d1 Support CPython v3.15 2026-06-20 09:10:21 +03:00
Sergey B Kirpichev 12e7e74251 Fix test_sn_cn_dn_identities()
Closes #1105
2026-06-20 09:10:21 +03:00
Sergey B Kirpichev 11a5afe7b6 Remove --durations=20 from default pytest options 2026-06-20 07:01:58 +03:00
Ayush Baranwal a2e0351345 Add Brent root-finding algorithm (#1103)
Closes #1095
2026-06-19 03:31:16 +03:00
Vincent Gao 29c496bf14 Fix gegenbauer() failing to converge for odd integer n at z=0
Used parity identity:
https://functions.wolfram.com/Polynomials/GegenbauerC3/04/02/01/

Closes #1077.
2026-06-18 17:37:29 +03:00
Sergey B Kirpichev e45f046a02 Merge pull request #1100 from skirpichev/lerchphi-series/1099
Implement direct series for lerchphi() base case with |z| < 0.5
2026-06-18 06:12:47 +03:00
Sergey B Kirpichev d742930573 Implement direct series for lerchphi() base case with |z| < 1
Closes #1099
2026-06-18 05:55:12 +03:00
Sergey B Kirpichev 9b71ea0595 Merge pull request #1102 from skirpichev/misc
Misc fixes
2026-06-18 05:34:42 +03:00
Sergey B Kirpichev f736ab6096 Drop _jacobi_theta2a/3a() 2026-06-17 12:18:28 +03:00
Sergey B Kirpichev 41243c5438 Tune hypothesis settings (max_examples for default/ci profiles) 2026-06-17 11:22:37 +03:00
Sergey B Kirpichev b854af8d3e Unify jtheta() computations for n=1,2 and n=3,4 2026-06-17 11:22:27 +03:00
Sergey B Kirpichev 90684c4405 Correct exception message for THETA_Q_LIM
Also unify entry points for jtheta/_djtheta

Co-Authored-By: Jam Balaya <jambalaya.pyoncafe@outlook.jp>
2026-06-17 11:21:33 +03:00
Sergey B Kirpichev 3d2aa3ea17 Merge pull request #1098 from skirpichev/ai-policy/1090
Add AI-related policy
2026-06-10 00:57:26 +03:00
Sergey B Kirpichev 97a091279d Add AI-related policy
Closes #1090
2026-06-09 03:49:25 +03:00
Sergey B Kirpichev 6d10972d64 Revert "Set default solver for findroot() dynamically"
This reverts commit f25a56f132.
2026-06-09 01:46:26 +03:00
Sergey B Kirpichev e3dbcd0dd5 Merge pull request #1096 from skirpichev/misc
Misc fixes
2026-06-08 05:28:47 +03:00
Sergey B Kirpichev f25a56f132 Set default solver for findroot() dynamically
Closes #1097
2026-06-07 05:31:56 +03:00
Sergey B Kirpichev 429c1d5ec2 Set dynamic maxsteps value for the bisect method
Closes #285
Closes #339
2026-06-07 05:31:56 +03:00
Sergey B Kirpichev bce4dfb5b4 Correct interval update for Ridder's method
Closes #1094
2026-06-07 05:31:54 +03:00
Ayush Baranwal 44ad9908cc Add ModAB rootfinding algorithm (#1093)
closes #1075
2026-06-07 04:30:24 +03:00
Ayush Baranwal e10416f997 Raise ValueError when same sign at interval boundaries in bisection rootfinding algorithm (#1092) 2026-06-02 11:12:23 +03:00
Sergey B Kirpichev fcb99d97a4 Make CONTRIBUTING.rst more visible 2026-06-02 10:18:37 +03:00
Sergey B Kirpichev 35a1e33f02 Fix typo and function names for sin/cospi() 2026-05-28 07:27:04 +03:00
Sergey B Kirpichev 16d5fb332c Merge pull request #1091 from skirpichev/misc
Misc fixes
2026-05-26 08:39:00 +03:00
Sergey B Kirpichev db21709e9a Amend README.rst with 1.4.1 release 2026-05-26 08:18:30 +03:00
Sergey B Kirpichev 52e0daaaeb Drop private alias round_fast 2026-05-26 07:57:20 +03:00
Sergey B Kirpichev 44859bf6cd Change default rounding for private mpf/mpc_expj/jpi() 2026-05-26 07:57:20 +03:00
Sergey B Kirpichev 34691c49f8 Change default rounding mode for private formatting helpers 2026-05-26 07:57:20 +03:00
Sergey B Kirpichev f7579c0a0d Merge pull request #1089 from skirpichev/restrict-libmp-exports/704
Restrict libmp exports to public API
2026-05-26 04:53:45 +03:00
Sergey B Kirpichev 939efcdd88 Merge pull request #1088 from skirpichev/fix-mpf_hypot/1011
Add extra precision for summation in mpf_hypot()
2026-05-25 14:51:54 +03:00
Sergey B Kirpichev aa5cd5a9bf Add coverage tests 2026-05-25 08:31:19 +03:00
Sergey B Kirpichev 0e1bd9496d Restrict libmp exports to public API
This is following namespace:
```
{'BACKEND', 'ComplexResult', 'MPZ', 'MPZ_ONE', 'NoConvergence',
 'catalan_fixed', 'dps_to_prec', 'euler_fixed', 'fhalf', 'finf',
 'fnan', 'fninf', 'fnone', 'fone', 'from_float', 'from_int', 'from_man_exp',
 'from_rational', 'from_str', 'fzero', 'giant_steps', 'ifac', 'ifib',
 'int_types', 'isqrt', 'mpc_abs', 'mpc_exp', 'mpc_pow', 'mpc_pow_int',
 'mpc_pow_mpf', 'mpc_sqrt', 'mpf_abs', 'mpf_add', 'mpf_atan', 'mpf_atan2',
 'mpf_bernoulli', 'mpf_ceil', 'mpf_cmp', 'mpf_cos', 'mpf_cosh_sinh', 'mpf_div',
 'mpf_e', 'mpf_eq', 'mpf_exp', 'mpf_floor', 'mpf_ge', 'mpf_gt', 'mpf_le',
 'mpf_log', 'mpf_lt', 'mpf_mod', 'mpf_mul', 'mpf_neg', 'mpf_pi', 'mpf_pow',
 'mpf_pow_int', 'mpf_shift', 'mpf_sign', 'mpf_sin', 'mpf_sqrt', 'mpf_sub',
 'mpf_tan', 'normalize', 'phi_fixed', 'prec_to_dps', 'repr_dps',
 'round_nearest', 'sqrtrem', 'to_float', 'to_int', 'to_man_exp', 'to_rational',
 'to_str'}
```

Closes #704
2026-05-25 08:31:19 +03:00
Sergey B Kirpichev d1e52ed28d Adapt doctests
Was:
```pycon
>>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]])
>>> Q, R = mp.schur(A)
>>> mp.norm(Q.T*A*Q-R, inf)
1.8166878507717441e-15
```

Now:
```pycon
>>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]])
>>> Q, R = mp.schur(A)
>>> mp.norm(Q.T*A*Q-R, inf)
1.3322676295501878e-15
```
2026-05-25 04:05:28 +03:00
Sergey B Kirpichev b06b1f51ce Add extra precision for summation in mpf_hypot()
Also, respect rounding option.

Closes #1011
2026-05-25 03:32:24 +03:00
Sergey B Kirpichev e90a592c7a Merge pull request #1087 from skirpichev/adjust-to_float-docs/1085
Clarify to_float() docstring
2026-05-25 02:40:43 +03:00
Sergey B Kirpichev 1eb70ed3d8 Clarify to_float() docstring
Second paragraph rephrased to:
> If the magnitude of *rounded* number is too large to represent as
> a regular float, it will be converted to infinity.

Closes #1085
2026-05-24 05:50:52 +03:00
Sergey B Kirpichev 41981f868a Merge pull request #1086 from skirpichev/update-CHANGES
Misc fixes
2026-05-23 05:16:14 +03:00
Sergey B Kirpichev 7d00f0aaa5 Fix typo in fadd() docstring 2026-05-23 04:43:14 +03:00
Sergey B Kirpichev a5edf2e3e5 Track new release cycle 2026-05-23 04:41:31 +03:00
Sergey B Kirpichev a1a6db90f2 Add v1.4.1 release notes to the master branch 2026-05-23 04:41:31 +03:00
Sergey B Kirpichev cd8722ecbb Merge pull request #1082 from skirpichev/fix-to_float-for-subnormals/1078
Correct to_float() conversion for subnormals
2026-05-21 05:35:43 +03:00
Jam Balaya b5a075fa53 Fix qr_solve() failure on well-conditioned matrices with zero pivot (#1083)
* Fix qr_solve() failure on well-conditioned matrices with zero pivot

In householder(), the sign convention
    p[j] = -sign(Re(A[j,j])) * sqrt(s)
collapses to zero when A[j,j] is exactly zero, because ctx.sign(0) == 0.
A zero p[j] makes kappa = 1/(s - p[j]*A[j,j]) = 1/s instead of the
correct 2/||v||**2, corrupting the Householder reflection. The damage
propagates to subsequent columns and eventually trips the
"matrix is numerically singular" guard.

Default sign to ctx.one when A[j,j] is zero (matching LAPACK's dlarfg
convention) so the reflection is computed correctly. The existing
singularity check is left in place to catch genuinely zero column
slices.

Fixes #983.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 03:02:53 +03:00
Sergey B Kirpichev 87cfbc42a8 Correct to_float() conversion for subnormals
Closes #1078
2026-05-18 07:46:12 +03:00
Sergey B Kirpichev fbf61435bf Merge pull request #1081 from skirpichev/fix-frexp/1080
Support special numbers in mpf_frexp() like math.frexp()
2026-05-15 03:43:59 +03:00
Sergey B Kirpichev 5393b8d55e Support special numbers in mpf_frexp() like math.frexp()
Closes #1080
2026-05-14 06:10:42 +03:00
Sergey B Kirpichev d84dc68b9d Merge pull request #1079 from skirpichev/rounding-refine-docs/1076
Improve documentation about rounding in the mp context
2026-05-13 08:31:12 +03:00
Sergey B Kirpichev eefbdcb6ad Improve documentation about rounding in the mp context
See #1076

Also (small optimization) unify _normalize and normalize.
2026-05-13 07:55:29 +03:00
Sergey B Kirpichev 3a09694b91 Merge pull request #1074 from skirpichev/opt-trailing/1073
Speedup removal trailing zeros in _normalize/from_man_exp()
2026-04-20 02:32:41 +03:00
Sergey B Kirpichev 63304aee63 Add coverage test for _normalize (man == 1 and bc != 1 case) 2026-04-19 06:27:38 +03:00
Sergey B Kirpichev 8a965fe43b Speedup removal trailing zeros in _normalize/from_man_exp()
| Benchmark      | ref     | patch                   |
|----------------|:-------:|:-----------------------:|
| 2              | 404 ns  | 398 ns: 1.01x faster    |
| 2**3           | 403 ns  | 398 ns: 1.01x faster    |
| 2**10          | 1.96 us | 1.91 us: 1.03x faster   |
| 2**100         | 6.21 us | 2.52 us: 2.47x faster   |
| 2**10000       | 1.04 ms | 5.90 us: 175.48x faster |
| Geometric mean | (ref)   | 2.78x faster            |

Benchmark hidden because not significant (1): 2**10000 + 1

$ cat bench.py
import pyperf
from mpmath.libmp import from_int
runner = pyperf.Runner()
for s in ['2', '2**3', '2**10', '2**100',
          '2**10000', '2**10000 + 1']:
    i = eval(s)
    runner.bench_func(s, from_int, i)

Closes #1073

Co-authored-by: Fredrik Johansson <fredrik.johansson@gmail.com>
2026-04-19 06:27:32 +03:00
Sergey B Kirpichev 6e3d54ef7e Merge pull request #1072 from skirpichev/fix-root-sig/1067
Fix signature of root()
2026-04-17 15:36:13 +03:00
Sergey B Kirpichev 4118cfd0db Fix signature of root()
Closes #1067
2026-04-17 05:42:03 +03:00
Sergey B Kirpichev 3cbeab5ca3 Merge pull request #1071 from skirpichev/test-on-3.15
Test on CPython 3.15 (no GMP)
2026-04-03 11:34:11 +03:00
Sergey B Kirpichev f9822f0954 Test on CPython 3.15 (no GMP)
DeprecationWarning's are filtered out in pyexpect-enabled tests,
see pexpect/pexpect#827.
2026-04-03 11:11:36 +03:00
Sergey B Kirpichev 09178c95b3 Merge pull request #1070 from skirpichev/wa-1046-v2
Amend 1108318e (restrict fix to CPython < 3.14)
2026-03-31 05:34:00 +03:00
Sergey B Kirpichev 527d9d228c Amend 1108318e (restrict fix to CPython < 3.14)
Closes #1046
2026-03-31 05:11:56 +03:00
Doug Torrance 7b40a40cc6 Add executable bit to test_version_frozen.sh (#1066)
This fixes the "script-not-executable" warning in the Debian package.
2026-03-15 23:26:08 +03:00
Sergey B Kirpichev f8a7f426e4 Merge pull request #1061 from skirpichev/wa-for-s390x/1046
Add a workaround for test on s390x
2026-03-11 15:14:50 +03:00
Tim M f98636edf6 Added Moore-Penrose pseudoinverse. (#1030) 2026-03-10 18:37:22 +03:00
Sergey B Kirpichev 73ce23d164 Merge pull request #1063 from skirpichev/revert-backport-action
Revert "Add backport action"
2026-03-09 04:11:57 +03:00
Sergey B Kirpichev 5d48d2abd9 Revert "Add backport action"
This reverts commit 319703eafd.
2026-03-08 04:52:53 +03:00
Sergey B Kirpichev 1108318e0f Add a workaround for test on s390x
Closes #1046
2026-03-07 04:33:02 +03:00
flurin4 45f5adbb37 Test frozen package version (#1055)
Add bash script to test package version in a frozen application version and a separate CI job to run it.  Closes #1044.

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
2026-03-07 04:32:29 +03:00
Sergey B Kirpichev 893bb431aa Merge pull request #1057 from skirpichev/drop-deprecated1
Remove deprecated behavior
2026-03-07 04:18:42 +03:00
Sergey B Kirpichev 8ffd139b16 Merge pull request #1059 from skirpichev/misc
Misc fixes
2026-03-07 04:17:26 +03:00
Sergey B Kirpichev c9c3946a9b Adapt test for a "wrong" tuple for mpf constructor 2026-03-07 03:39:34 +03:00
Sergey B Kirpichev 4b84174154 Fix typo in mpc_fibonacci() 2026-03-07 03:30:49 +03:00
Sergey B Kirpichev 3952417cd6 Ignore bc field of mpf's in few tests 2026-03-07 03:14:23 +03:00
Sergey B Kirpichev 22bbe9ad4c Amend 5ac8dcda 2026-03-07 03:01:50 +03:00
Sergey B Kirpichev 718a67cb8e Add 'import mpmath' to default imports in CLI 2026-03-06 06:04:10 +03:00
Sergey B Kirpichev 6665df00dd Correct fp._convert_param() to properly handle real and complex types
Remember, that the gmpy2 backend returns mpfr and mpc types
for mixed-mode arithmetics, when one operand is mpz or mpq.

Examples:

    >>> mpz(1) + 0.1
    mpfr('1.1000000000000001')
    >>> mpz(1) + 1j
    mpc('1.0+1.0j')

Also, mpfr type returned for true division of mpz's.
2026-03-06 05:58:10 +03:00
Sergey B Kirpichev 801a2e6955 Use ctx.isnpint() 2026-03-06 05:01:56 +03:00
Sergey B Kirpichev 3a454f6643 Use asc=True per default for polynomial functions 2026-03-04 07:28:50 +03:00
Sergey B Kirpichev ca0f67d9ab Use signed=True per default in to_man_exp() 2026-03-04 07:28:50 +03:00
Sergey B Kirpichev 82e0a49593 Drop DeprecationWarning for force_type kwarg for matrix()
(Note that kwargs now aren't used.)
2026-03-04 07:28:50 +03:00
Sergey B Kirpichev 81204d1105 Drop deprecated to/from_pickable() functions 2026-03-04 07:28:50 +03:00
Sergey B Kirpichev 754c073e7d Drop deprecated bitcount() function 2026-03-04 07:28:49 +03:00
Sergey B Kirpichev 3ba4132a93 Drop deprecated mp.mpnumeric 2026-03-04 07:28:49 +03:00
Sergey B Kirpichev 485fe73586 Drop deprecated fp.is_special() 2026-03-04 07:28:49 +03:00
Sergey B Kirpichev 60de9789bf Keep forever deprecated aliases for mpc/mpf_log() 2026-03-04 07:28:49 +03:00
Sergey B Kirpichev 6bc4fe86f3 Cleanup conftest.py 2026-03-04 07:28:49 +03:00
Sergey B Kirpichev cd25f9b536 Drop deprecated rational module 2026-03-04 07:28:49 +03:00
Sergey B Kirpichev 8f3e715b1e Drop deprecated math2 module 2026-03-04 07:28:49 +03:00
Sergey B Kirpichev b7728d8dbd Merge pull request #1058 from skirpichev/drop-py3.9
Drop support for CPython 3.9
2026-03-04 07:28:05 +03:00
Sergey B Kirpichev 9677a2a939 Amend docstring for round_digits() 2026-03-04 07:23:41 +03:00
Sergey B Kirpichev 2f6a5d52f3 Drop support for CPython 3.9
(Following https://github.com/sympy/sympy/pull/28816)
2026-03-04 07:14:17 +03:00
Sergey B Kirpichev 0669eb773f Merge pull request #1054 from skirpichev/update-dependabot-cfg
Improve dependabot settings
2026-03-02 13:17:31 +03:00
Sergey B Kirpichev e33d20e34b Merge pull request #1048 from skirpichev/file-version/1044
Use version_file option of setuptools-scm to keep version info
2026-03-02 12:58:28 +03:00
Sergey B Kirpichev 163096b16f Improve dependabot settings
* group multiple updates
* forbid auto rebase to reduce cost of CI (Use `@dependabot rebase` manually instead.)
2026-03-02 09:05:43 +03:00
Sergey B Kirpichev cf2ca665b4 Merge pull request #1051 from mpmath/dependabot/github_actions/actions/download-artifact-8
Bump actions/download-artifact from 7 to 8
2026-03-02 03:01:11 +03:00
Sergey B Kirpichev 14802e5d83 Merge pull request #1052 from mpmath/dependabot/github_actions/actions/upload-artifact-7
Bump actions/upload-artifact from 6 to 7
2026-03-02 03:00:47 +03:00
dependabot[bot] a0a3e580f3 Bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 22:57:12 +00:00
dependabot[bot] 3e345d62e1 Bump actions/download-artifact from 7 to 8
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 22:57:07 +00:00
Sergey B Kirpichev fee75dd985 Merge pull request #1049 from d-torrance/ctypes-sprintf
Set argtypes for sprintf from ctypes
2026-02-28 22:43:16 +03:00
Doug Torrance d74dff7e48 Set argtypes for sprintf from ctypes
Needed on some architectures (see the "Calling variadic functions" in
the ctypes documentation).
2026-02-28 14:11:34 -05:00
Sergey B Kirpichev ed57d4034d Use version_file option of setuptools-scm to keep version info
Closes #1044
2026-02-28 14:06:47 +03:00
Sergey B Kirpichev f3e9cf0706 Update CHANGES (free-threading support)
test / docs (push) Failing after 0s
test / linter (push) Failing after 0s
test / tests (false, false, 3.14t) (push) Has been skipped
test / coverage (push) Failing after 0s
test / tests (false, false, 3.10) (push) Has been skipped
test / tests (false, false, 3.11) (push) Has been skipped
test / tests (false, false, 3.12) (push) Has been skipped
test / tests (false, false, 3.13) (push) Has been skipped
test / tests (false, false, 3.9) (push) Has been skipped
test / tests (false, false, 3.13t) (push) Has been skipped
test / tests (false, false, 3.14) (push) Has been skipped
test / tests (true, pypy3.11) (push) Has been skipped
test / tests (true, 3.x) (push) Has been skipped
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
2026-02-23 06:22:10 +03:00
Sergey B Kirpichev 8a01b18638 Merge pull request #1032 from skirpichev/release-v1.4
v1.4.0
2026-02-23 05:29:18 +03:00
Sergey B Kirpichev acd4b569d4 Update CHANGES 2026-02-23 05:10:51 +03:00
Sergey B Kirpichev 2933f42ad5 Merge branch 'master' into release-v1.4 2026-02-23 04:26:21 +03:00
Sergey B Kirpichev 711d3c3681 Merge pull request #1043 from skirpichev/revert-949
Revert "Add isspecial() method for mp/fp contexts, deprecate isnormal()"
2026-02-23 04:25:43 +03:00
Sergey B Kirpichev 7cf16b7828 Revert "Add isspecial() method for mp/fp contexts, deprecate isnormal()"
This reverts commit e6aa3b46b1.

Subnormals are properly handled by fp.isnormal().  Deprecate
fp.is_special() method.

Fixes #946
2026-02-23 04:08:07 +03:00
Sergey B Kirpichev 34cc1f8147 Update CHANGES 2026-02-22 14:09:07 +03:00
Sergey B Kirpichev 1e0dc7b307 Merge branch 'master' into release-v1.4 2026-02-22 13:56:05 +03:00
Sergey B Kirpichev c7128a6ac5 Merge pull request #1041 from skirpichev/fix-459
polylog(n, nan) -> nan
2026-02-22 13:55:04 +03:00
Sergey B Kirpichev 81034e8648 Merge pull request #1042 from skirpichev/backport-action
Add backport action
2026-02-22 09:36:36 +03:00
Sergey B Kirpichev fa0da05a63 Merge pull request #1040 from skirpichev/cli-no-int-limits
Add option --int-limits for CLI
2026-02-22 09:32:51 +03:00
Sergey B Kirpichev 319703eafd Add backport action
Like for gmpy2/python-gmp
2026-02-22 08:56:41 +03:00
Sergey B Kirpichev 951a7303db And update Release history 2026-02-22 08:42:21 +03:00
Sergey B Kirpichev aed6aecdcd Fix year in conf.py 2026-02-22 08:40:58 +03:00
Sergey B Kirpichev a9f1273e68 Update year and version in CITATION.bib 2026-02-22 08:40:28 +03:00
Sergey B Kirpichev d7b9ddb23c polylog(n, nan) -> nan
closes #459
2026-02-22 08:28:04 +03:00
Sergey B Kirpichev 3c8119eeb2 Use pretty-printing in det() docstring 2026-02-22 08:08:10 +03:00
Sergey B Kirpichev 7c3252b14c Add option --int-limits for CLI
This enables *default* Python mechanism for integer string
conversion length limitation:
https://docs.python.org/3/library/stdtypes.html#integer-string-conversion-length-limitation

For the mpmath CLI it will be *off* by default.  Usually, it doesn't
matter, as these limits not affect the gmpy2/gmp backends and working
with mpmath's types (mpf/mpc).  Though, sometimes you want to play with
integers in the mpmath console and these limits are really annoying.

I consider this as a bugfix, despite it adds a new option.
2026-02-22 08:01:03 +03:00
Sergey B Kirpichev e95bf2a22a Fix some found formatting issues with docs 2026-02-22 07:42:46 +03:00
Sergey B Kirpichev 5348705040 Add workaround for sphinx-contrib/autoprogram#76 2026-02-22 06:09:51 +03:00
Sergey B Kirpichev b6b535bc72 Drop OpenSUSE note (looks outdated) 2026-02-22 05:18:47 +03:00
Sergey B Kirpichev 454ee97a83 Merge branch 'master' into release-v1.4 2026-02-22 04:44:37 +03:00
Sergey B Kirpichev e631d38cd5 Merge pull request #1039 from skirpichev/fix-545
Use erfc(z) to compute erf(z) with re(z) of large magnitude
2026-02-22 03:19:53 +03:00
Sergey B Kirpichev 0b9622a737 Polish CHANGES entry 2026-02-22 02:20:07 +03:00
Sergey B Kirpichev 932ed68c1f Use pytest.skip() in test_axes() 2026-02-21 16:19:22 +03:00
Sergey B Kirpichev 4478a0e33d Use 3.x instead of hardcoded CPython versions in CI 2026-02-21 16:19:22 +03:00
Sergey B Kirpichev 3d84b6f851 Skip installing gmpy2 deps (binary wheels are available) 2026-02-21 16:19:22 +03:00
Sergey B Kirpichev 612a34fbca Simplify gmp deps 2026-02-21 16:19:22 +03:00
Sergey B Kirpichev dcd282eb77 Require setuptools-scm>=8 2026-02-21 16:19:21 +03:00
Sergey B Kirpichev c5437d880b Get rid off debug print's 2026-02-21 16:19:17 +03:00
Sergey B Kirpichev c4176127b0 Use erfc(z) to compute erf(z) with re(z) of large magnitude
closes #545
2026-02-21 15:45:34 +03:00
Sergey B Kirpichev 3af3a60584 Update CHANGES 2026-02-21 10:40:09 +03:00
Sergey B Kirpichev ccb21aa6b4 Merge pull request #1038 from skirpichev/fix-486
Properly handle nan's elliprj()
2026-02-21 06:16:37 +03:00
Sergey B Kirpichev c8e1d276c7 Merge pull request #1017 from baranwalayush/fix/logm_zero_matrix_bug
Fix #1002: Raise ValueError when zero matrix is passed to logm()
2026-02-21 06:14:46 +03:00
Sergey B Kirpichev 69f9e55489 Properly handle nan's elliprj()
Closes #486
2026-02-21 05:40:37 +03:00
Sergey B Kirpichev 34f6e5700a Merge pull request #1037 from skirpichev/fix-1033
Fix special caze for +inf in polylog_continuation()
2026-02-21 04:01:31 +03:00
Sergey B Kirpichev d94ac5eeb3 Fix special caze for +inf in polylog_continuation()
See https://functions.wolfram.com/ZetaFunctionsandPolylogarithms/PolyLog/17/02/01/01/0008/

Closes #1033
2026-02-21 03:39:53 +03:00
Sergey B Kirpichev a1addfbc59 Merge pull request #1036 from skirpichev/fix-1035
Amend beb5029
2026-02-21 02:10:24 +03:00
Sergey B Kirpichev 90ccd8c40e Amend beb5029
Closes #1035
2026-02-21 01:50:50 +03:00
Sergey B Kirpichev dc207275a8 Merge pull request #1034 from cbm755/polylog_inf
polylog: z to positive infinity should return nan
2026-02-21 00:42:38 +03:00
Colin B. Macdonald 2aad193ce0 polylog: z to positive infinity should return nan
Fixes #1033.
2026-02-19 22:10:14 -08:00
Sergey B Kirpichev 1dfa0143f9 Merge pull request #1031 from skirpichev/demos-tests
Add tests for demo scripts
2026-02-19 22:32:36 +03:00
Sergey B Kirpichev eefdb4d0c4 Add tests for demo scripts
Closes #1029
2026-02-19 16:17:27 +03:00
baranwalayush e8aa7a0bd9 Fix infinite loop in case of zero matrix in logm function 2025-10-22 15:14:30 +05:30
99 changed files with 5493 additions and 2302 deletions
+37
View File
@@ -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/
+5
View File
@@ -4,3 +4,8 @@ updates:
directory: "/"
schedule:
interval: "monthly"
rebase-strategy: "disabled"
groups:
actions-deps:
patterns:
- "*"
+4 -4
View File
@@ -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: |
+8 -6
View File
@@ -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: |
+2 -2
View File
@@ -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]
+4 -4
View File
@@ -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/
+21 -9
View File
@@ -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
+3
View File
@@ -33,6 +33,9 @@ my/
dist/
build/
# Generated by setuptools_scm
mpmath/_version.py
# Tox files
.tox/
+137 -10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+13
View File
@@ -0,0 +1,13 @@
Fast Fourier Transform
----------------------------------
FFT
...
.. autofunction:: mpmath.fft
Inverse FFT
...........
.. autofunction:: mpmath.invfft
+1
View File
@@ -12,3 +12,4 @@ Numerical calculus
odes
approximation
inverselaplace
fft
+1 -1
View File
@@ -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.
+3 -1
View File
@@ -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
View File
@@ -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().
+5 -3
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+11
View File
@@ -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
+1
View File
@@ -4,3 +4,4 @@ from . import approximation
from . import differentiation
from . import extrapolation
from . import polynomials
from . import fft
+8 -14
View File
@@ -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 -19
View File
@@ -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
+30 -10
View File
@@ -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)
+110
View File
@@ -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]
+62 -34
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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:
+11 -26
View File
@@ -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:
+21 -16
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
File diff suppressed because it is too large Load Diff
+14 -6
View File
@@ -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
+15 -4
View File
@@ -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:
+9 -15
View File
@@ -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)
+147 -33
View File
@@ -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)
+89 -21
View File
@@ -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)
+12 -10
View File
@@ -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
+1 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+69 -33
View File
@@ -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
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
-7
View File
@@ -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)
+2
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
-11
View File
@@ -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
View File
@@ -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
+16 -26
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9 -10
View File
@@ -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):
+140
View File
@@ -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
+5 -4
View File
@@ -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():
File diff suppressed because it is too large Load Diff
+9
View File
@@ -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
View File
@@ -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'
+9
View File
@@ -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))
+20 -2
View File
@@ -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
View File
@@ -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))
+2 -1
View File
@@ -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():
+1 -1
View File
@@ -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 -4
View File
@@ -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]
+4
View File
@@ -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
View File
@@ -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():
-4
View File
@@ -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)
+89 -4
View File
@@ -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
View File
@@ -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))
+51
View File
@@ -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
+59 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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]