Compare commits

...

427 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
Sergey B Kirpichev bb6cf209f4 Merge pull request #1026 from skirpichev/misc
test / coverage (push) Failing after 0s
test / docs (push) Failing after 0s
test / linter (push) Failing after 0s
test / tests (false, false, 3.14t) (push) Has been skipped
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.13t) (push) Has been skipped
test / tests (false, false, 3.14) (push) Has been skipped
test / tests (false, false, 3.9) (push) Has been skipped
test / tests (true, pypy3.11) (push) Has been skipped
test / tests (true, 3.14) (push) Has been skipped
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
Misc fixes
2026-02-08 07:02:14 +03:00
Sergey B Kirpichev c35b4d3b6d Require python-gmp>=0.5 2026-02-08 03:22:13 +03:00
Sergey B Kirpichev 012368a1d2 Update gmpy2 deps 2026-02-08 03:21:47 +03:00
Sergey B Kirpichev f3edc81705 Document python-gmp usage 2026-02-01 04:18:39 +03:00
Sergey B Kirpichev bfe0ffdb52 Change behavior of 'b' formatting type to mimic 'a'
This behavior is compatible with the gmpy2.

Closes #1027
2026-02-01 04:03:19 +03:00
Sergey B Kirpichev bd46aacf5d Correct mpc.__format__() docstring 2026-01-21 04:34:23 +03:00
Sergey B Kirpichev 6dbea12523 Merge pull request #1024 from mpmath/dependabot/github_actions/actions/checkout-6
Bump actions/checkout from 5 to 6
2026-01-02 02:19:57 +03:00
Sergey B Kirpichev 8def1a9e8e Merge pull request #1023 from mpmath/dependabot/github_actions/actions/upload-artifact-6
Bump actions/upload-artifact from 5 to 6
2026-01-02 01:48:25 +03:00
Sergey B Kirpichev 7cc4f6a022 Merge pull request #1022 from mpmath/dependabot/github_actions/actions/download-artifact-7
Bump actions/download-artifact from 6 to 7
2026-01-02 01:48:04 +03:00
dependabot[bot] bdba15bb79 Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [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/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-01 22:21:25 +00:00
dependabot[bot] b474c9f60a Bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-01 22:21:18 +00:00
dependabot[bot] efd6d4ac4f Bump actions/download-artifact from 6 to 7
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-01 22:21:13 +00:00
Sergey B Kirpichev ffda440b4d Update copyright
test / linter (push) Failing after 0s
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.13t) (push) Has been skipped
test / tests (false, false, 3.14) (push) Has been skipped
test / docs (push) Failing after 0s
test / tests (false, false, 3.14t) (push) Has been skipped
test / tests (false, false, 3.9) (push) Has been skipped
test / tests (true, pypy3.11) (push) Has been skipped
test / tests (true, 3.14) (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-01-01 09:57:52 +03:00
Sergey B Kirpichev 510d3c8617 Don't require double_fac/fib from backend 2025-12-29 11:38:41 +03:00
Sergey B Kirpichev 4b35ad317f Quickly filter warning in CI for CPython v3.9
@pytest.mark.filterwarnings("ignore:datetime.datetime.utc.*:DeprecationWarning")
    def test_axes():
        try:
>           import matplotlib

mpmath/tests/test_visualization.py:15:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/opt/hostedtoolcache/Python/3.9.25/x64/lib/python3.9/site-packages/matplotlib/__init__.py:970: in <module>
    rcParamsDefault = _rc_params_in_file(
/opt/hostedtoolcache/Python/3.9.25/x64/lib/python3.9/site-packages/matplotlib/__init__.py:904: in _rc_params_in_file
    config[key] = val  # try to convert to proper type or raise
/opt/hostedtoolcache/Python/3.9.25/x64/lib/python3.9/site-packages/matplotlib/__init__.py:739: in __setitem__
    cval = self.validate[key](val)
/opt/hostedtoolcache/Python/3.9.25/x64/lib/python3.9/site-packages/matplotlib/rcsetup.py:427: in validate_font_properties
    parse_fontconfig_pattern(s)
/opt/hostedtoolcache/Python/3.9.25/x64/lib/python3.9/site-packages/matplotlib/_fontconfig_pattern.py:83: in parse_fontconfig_pattern
    parser = _make_fontconfig_parser()
/opt/hostedtoolcache/Python/3.9.25/x64/lib/python3.9/site-packages/matplotlib/_fontconfig_pattern.py:64: in _make_fontconfig_parser
    prop = Group((name + Suppress("=") + comma_separated(value)) | oneOf(_CONSTANTS))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = ({'black': ('weight', 'black'), 'bold': ('weight', 'bold'), 'book': ('weight', 'book'), 'condensed': ('width', 'condensed'), ...},)
kwargs = {}

    @wraps(fn)
    def _inner(*args, **kwargs):
>       warnings.warn(
            f"{compat_name!r} deprecated - use {fn.__name__!r}",
            DeprecationWarning,
            stacklevel=2,
        )
E       DeprecationWarning: 'oneOf' deprecated - use 'one_of'

/opt/hostedtoolcache/Python/3.9.25/x64/lib/python3.9/site-packages/pyparsing/util.py:445: DeprecationWarning
2025-12-23 10:49:38 +03:00
Sergey B Kirpichev 4ae8195ad6 Freeze gmpy2 deps 2025-12-02 05:04:11 +03:00
Sergey B Kirpichev f444dac155 Use CPython v3.13 for coverage tests (v3.14 has no gmpy2 wheels) 2025-11-02 02:26:15 +03:00
Sergey B Kirpichev 13446c8004 Merge pull request #1018 from mpmath/dependabot/github_actions/actions/upload-artifact-5
Bump actions/upload-artifact from 4 to 5
2025-11-02 01:55:03 +03:00
Sergey B Kirpichev 2c2b8ed90b Merge pull request #1019 from mpmath/dependabot/github_actions/actions/download-artifact-6
Bump actions/download-artifact from 5 to 6
2025-11-02 01:54:40 +03:00
dependabot[bot] 2af313f354 Bump actions/download-artifact from 5 to 6
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 22:22:40 +00:00
dependabot[bot] 031616be33 Bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 22:22:37 +00:00
Sergey B Kirpichev 2fe20a3c83 Amend 6c51c9d8 (restore default context's mp.pretty behavior) 2025-10-29 09:25:05 +03:00
baranwalayush e8aa7a0bd9 Fix infinite loop in case of zero matrix in logm function 2025-10-22 15:14:30 +05:30
Sergey B Kirpichev 4a1475d7ec Amend 85fb02ab
test / tests (false, false, 3.13t) (push) Has been skipped
test / docs (push) Failing after 0s
test / linter (push) Failing after 0s
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 (true, 3.14) (push) Has been skipped
test / tests (false, false, 3.14) (push) Has been skipped
test / tests (false, false, 3.9) (push) Has been skipped
test / tests (true, pypy3.11) (push) Has been skipped
test / tests (false, false, 3.14t) (push) Has been skipped
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
Publish on PyPI / Build distributions (push) Has been cancelled
2025-10-13 08:38:31 +03:00
Sergey B Kirpichev f580874a54 Revert testing of GMP-backends for PyPy, see #1013 2025-10-13 08:36:01 +03:00
Sergey B Kirpichev be2ba908dc Merge pull request #1010 from skirpichev/misc
Misc fixes
2025-10-12 16:53:15 +03:00
Sergey B Kirpichev e274405100 Test on CPython 3.14t 2025-10-12 15:51:25 +03:00
Sergey B Kirpichev 0039506d7b Run test.yml on schedule and allow workflow dispatch for all jobs 2025-10-12 15:13:12 +03:00
Sergey B Kirpichev 0bba53b955 Amend bb1087f6 2025-10-12 11:45:01 +03:00
Sergey B Kirpichev 1d92fce111 Update CHANGES 2025-10-12 11:28:44 +03:00
Sergey B Kirpichev 6215098fda Update credits 2025-10-12 11:09:14 +03:00
Sergey B Kirpichev 85fb02ab25 Remove broken codecov integration (see mpmath/mpmath#709) 2025-10-12 11:00:39 +03:00
Ayush Baranwal 0d8b0e0b8c Fix #1003: sinm and cosm can accept list as arguments, similar to expm and other functions (#1006) 2025-10-08 11:04:34 +03:00
Sergey B Kirpichev 47917f0d90 Merge pull request #1005 from baranwalayush/fix/matrix_ValueError
Fix #1004: Matrix should raise ValueError in case of negative dimensions
2025-10-05 05:06:56 +03:00
baranwalayush de1e15d49f Fix matrix should raise ValueError and Add unit tests 2025-10-03 19:21:30 +05:30
Sergey B Kirpichev 23b8bbfd79 Unpin pytest-cov, see pytest-dev/pytest-cov#720 2025-10-03 05:07:36 +03:00
Sergey B Kirpichev 33eace1005 Merge pull request #1000 from skirpichev/wa-996
Workaround support for multiple statements in CLI
2025-10-02 09:00:59 +03:00
Sergey B Kirpichev 4247f6a7ea Pin pytest-cov (<7)
See pytest-dev/pytest-cov#720
2025-10-02 08:43:42 +03:00
Sergey B Kirpichev 57147a724a Workaround support for multiple statements in CLI
Closes #996
2025-10-02 07:44:36 +03:00
Sergey B Kirpichev 760227b06c Merge pull request #998 from WarrenWeckesser/function-names
Ensure exp, sin, tan, etc have a correct __name__ attribute
2025-09-07 07:02:31 +03:00
Warren Weckesser 82e72a72d7 Ensure exp, sin, tan, etc have a correct __name__ attribute 2025-09-06 16:32:42 -04:00
Sergey B Kirpichev df11c35fa0 Merge pull request #995 from mpmath/dependabot/github_actions/actions/setup-python-6
test / linter (push) Failing after 1s
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.13t) (push) Has been skipped
test / tests (false, false, 3.14) (push) Has been skipped
test / tests (false, false, 3.9) (push) Has been skipped
test / tests (false, false, pypy3.11) (push) Has been skipped
test / docs (push) Failing after 0s
test / tests (true, 3.14) (push) Has been skipped
test / tests (true, pypy3.11) (push) Has been skipped
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
Bump actions/setup-python from 5 to 6
2025-09-05 07:38:54 +03:00
dependabot[bot] 1984ee7780 Bump actions/setup-python from 5 to 6
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 04:21:14 +00:00
Sergey B Kirpichev 8b172a6ec4 Merge pull request #962 from skirpichev/v1.14-beta1
v1.14 beta1
2025-09-05 07:20:27 +03:00
Sergey B Kirpichev a03719f2fe Drop outdated reference to Sage's Cython-based context 2025-09-03 07:51:28 +03:00
Sergey B Kirpichev a14f851414 Unpin numpy and matplotlib for CPython 3.14 and PyPy 2025-09-03 07:50:01 +03:00
Sergey B Kirpichev aee92a48e7 Allow gmpy2/gmp for PyPy 2025-09-03 07:43:34 +03:00
Sergey B Kirpichev c3c9849651 Test no-gmpy2 backends on CPython 3.14 2025-09-03 05:56:41 +03:00
hnb22 baaccb59aa Switch logarithm implementation to taylor series when input is close to 1 (#957)
This modifies the logarithm implementation for cases where taylor series
is more optimal (cancellation >= working precision), providing a faster
path for low precision cases depending on how close input is to 1.
2025-09-03 05:31:22 +03:00
Sergey B Kirpichev 49a7d7a8b8 Test without gmp/gmpy2 on CPython 2025-09-03 05:26:32 +03:00
Sergey B Kirpichev bb1087f6f1 Runner image: ubuntu-latest -> ubuntu-24.04 2025-09-02 13:14:20 +03:00
Sergey B Kirpichev 4cf6c37a73 Setup dependabot 2025-09-02 13:11:50 +03:00
Sergey B Kirpichev 362261271a Update actions 2025-09-02 13:10:09 +03:00
Sergey B Kirpichev 5aa61a25df Clarify deprecation of using mpmath.libmp's submodules
test / linter (push) Failing after 0s
test / docs (push) Failing after 0s
test / coverage (push) Failing after 1s
test / tests (false, 3.11) (push) Has been skipped
test / tests (false, 3.12) (push) Has been skipped
test / tests (false, 3.13) (push) Has been skipped
test / tests (false, 3.10) (push) Has been skipped
test / tests (true, 3.13) (push) Has been skipped
test / tests (false, 3.13t) (push) Has been skipped
test / tests (false, 3.14) (push) Has been skipped
test / tests (false, 3.9) (push) Has been skipped
test / tests (false, pypy3.11) (push) Has been skipped
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
2025-09-01 07:43:43 +03:00
Sergey B Kirpichev 96ec4a10b8 Merge pull request #993 from skirpichev/MPContext-docs/975
Document support for free-threading builds
2025-09-01 07:34:43 +03:00
Sergey B Kirpichev 4d75799077 Test on free-threading builds 2025-09-01 07:15:19 +03:00
Sergey B Kirpichev ad91359172 Use threading.local storage for global caches 2025-09-01 06:21:07 +03:00
Sergey B Kirpichev 8042d8eb95 Document support for free-threading builds
Closes #975
2025-09-01 05:24:36 +03:00
Sergey B Kirpichev e325306bc6 Require python-gmp>=0.4 2025-08-20 05:27:29 +03:00
Ferrangelo d9587127d5 Correct implementation of spherical bessel functions (#992)
Closes #991
2025-08-17 13:11:22 +03:00
Sergey B Kirpichev 97c45f21e3 Require python-gmp>=0.4.0b3
test / coverage (push) Failing after 0s
test / linter (push) Failing after 0s
test / docs (push) Failing after 0s
test / tests (false, 3.14) (push) Has been skipped
test / tests (false, 3.9) (push) Has been skipped
test / tests (false, pypy3.11) (push) Has been skipped
test / tests (false, 3.10) (push) Has been skipped
test / tests (false, 3.11) (push) Has been skipped
test / tests (false, 3.12) (push) Has been skipped
test / tests (false, 3.13) (push) Has been skipped
test / tests (true, 3.13) (push) Has been skipped
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
2025-08-06 13:07:44 +03:00
Sergey B Kirpichev 09fa9516ad Merge pull request #989 from skirpichev/misc
Misc fixes
2025-08-06 07:48:37 +03:00
Sergey B Kirpichev f5b3e07ce4 Drop pypy3.10 from testing matrix (not supported by upstream) 2025-08-06 07:31:42 +03:00
Sergey B Kirpichev 1ad7c87381 Fix "citation not referenced" warnings
Closes #979
2025-08-06 07:15:09 +03:00
Sergey B Kirpichev 8870f1d258 Add aliases for mpf/mpc_log() as mpf/mpc_ln()
Closes #988
2025-08-06 06:02:12 +03:00
Sergey B Kirpichev 2a89521f0d Fix typo (im -> conj)
Closes #987
2025-08-06 05:26:42 +03:00
Sergey B Kirpichev b6af936582 Make mpc_hash() compatible with complex hash
Closes #985
2025-08-06 05:26:42 +03:00
Sergey B Kirpichev 657ef46356 Cleanup docs/general.rst (use signatures from autofunction)
Closes #986
2025-08-06 04:30:39 +03:00
Hongren Zheng 9ac078ca7c Add Fox H-function with rational A and B parameters (#982)
Closes #980
2025-07-27 19:03:23 +03:00
Sergey B Kirpichev 890b7a6a5e Merge pull request #981 from skirpichev/misc
Misc fixes
2025-07-25 08:09:47 +03:00
Sergey B Kirpichev bf11729f64 Amend 5c719770a 2025-07-23 06:43:05 +03:00
Sergey B Kirpichev 2a91ca3c6f Cleanup pyproject.toml 2025-07-23 06:41:31 +03:00
Sergey B Kirpichev 5b68356a26 Require python-gmp>=0.4.0b2 2025-07-23 06:31:40 +03:00
Sergey B Kirpichev 3788ac7320 Merge pull request #978 from d-torrance/citation-warnings
Fix citation warning due to typo
2025-07-20 05:54:40 +03:00
Doug Torrance a28bcd5eec Fix citation warning due to typo
WARNING: citation not found: Homeier [ref.ref]
2025-07-19 22:21:17 -04:00
Sergey B Kirpichev 5d66b10f53 Use Fraction's with the python-gmp 2025-07-03 22:16:18 +03:00
David Walker 9b98a71476 Switch to anderson method to speed up zetazero (#959)
test / docs (push) Failing after 0s
test / coverage (push) Failing after 0s
test / linter (push) Failing after 0s
test / tests (false, 3.9) (push) Has been skipped
test / tests (false, pypy3.10) (push) Has been skipped
test / tests (false, pypy3.11) (push) Has been skipped
test / tests (false, 3.10) (push) Has been skipped
test / tests (false, 3.11) (push) Has been skipped
test / tests (false, 3.12) (push) Has been skipped
test / tests (false, 3.13) (push) Has been skipped
test / tests (false, 3.14) (push) Has been skipped
test / tests (true, 3.13) (push) Has been skipped
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
Closes #958
2025-06-20 05:44:34 +03:00
Sergey B Kirpichev 027bfd36ed Merge pull request #973 from skirpichev/references/224
Move references to docs/references.rst
2025-06-19 19:10:24 +03:00
Sergey B Kirpichev 57d341cf7d Merge pull request #972 from skirpichev/misc
Misc fixes
2025-06-19 06:36:19 +03:00
Sergey B Kirpichev 2cad89428e Move references to docs/references.rst
Closes #224
2025-06-18 18:03:46 +03:00
That's ¬a problem d5f370a68a Fix typo in mpf_gamma() docstring 2025-06-18 07:53:03 +03:00
Sergey B Kirpichev cd52b6da6b Cleanup ctx_mp_python.py (consistent argument naming) 2025-06-18 07:53:01 +03:00
Sergey B Kirpichev 1485493711 Merge pull request #969 from skirpichev/misc
Misc fixes
2025-06-10 05:16:29 +03:00
Sergey B Kirpichev 9ad6a13925 Fix from_man_exp to correctly reject non-integral mantissa
This partially reverts 25506567
2025-06-09 05:21:57 +03:00
Sergey B Kirpichev bad54feab6 Use context's rounding mode per default in the format() 2025-06-08 18:27:16 +03:00
Sergey B Kirpichev e8bc7ea1a8 More tests for zetazeros()
Taken from #959
2025-06-08 16:48:16 +03:00
Sergey B Kirpichev 2b72f9f3a0 Rename to_str() argument: rounding -> rnd 2025-06-08 15:28:06 +03:00
Sergey B Kirpichev 936039cd6c precision -> dps in formatting helpers 2025-06-08 13:42:27 +03:00
Sergey B Kirpichev ed1ad32051 Rename kwarg rounding -> rnd in new helpers 2025-06-08 12:59:50 +03:00
Sergey B Kirpichev 4b26ebf276 Rename argument rounding -> rnd in private *_besseljn() functions 2025-06-08 12:59:45 +03:00
Sergey B Kirpichev 8a2bed58a5 Merge pull request #966 from skirpichev/misc
Misc fixes
2025-06-04 15:33:55 +03:00
Sergey B Kirpichev cf22ea86be Amend 4582b137
Use mp._prec_rounding[1] instead of _rounding attribute
for compatibility with the custom MPContext context in
Diofant/SymPy (polys).
2025-06-03 22:00:37 +03:00
Sergey B Kirpichev 82e202a4f1 Clarify deprecation of direct access to _mpf_ tuples 2025-06-03 15:25:17 +03:00
Sergey B Kirpichev 08a7a2f92a Revert "Revert 849e185e and (partially) f4cef9c8 (same for with field)"
This reverts commit 5ad43bd837.

Closes #915
2025-06-03 14:59:47 +03:00
Sergey B Kirpichev 8b48864418 Fix mpf.__round__() to return int type per default (ndigits=None) 2025-06-03 11:24:13 +03:00
Sergey B Kirpichev 3e516d4fe3 Fix exception type for int(mp.inf) to OverflowError 2025-06-03 11:23:46 +03:00
David Walker 75b52a3aea Fix two small bugs. (#964)
The method could not be properly set for zeta().
kwargs didn't work fully for nstr() when applied to mpc types.

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
2025-06-03 09:41:45 +03:00
Sergey B Kirpichev 8d487d15c8 Merge pull request #963 from skirpichev/rounding-prop
Support rounding property for the mp context
2025-06-02 17:30:57 +03:00
Sergey B Kirpichev e403511878 Clear other obsoleted XXX/FIXME marks 2025-06-01 13:33:02 +03:00
Sergey B Kirpichev 4582b13795 Support rounding property for the mp context 2025-06-01 13:28:51 +03:00
Sergey B Kirpichev 1c4bf6db68 Merge pull request #961 from skirpichev/misc
Misc fixes
2025-06-01 07:13:27 +03:00
Sergey B Kirpichev 9989ce5c1c Drop verify=False (True per default) in zetazeros.py/bessel.py 2025-05-31 07:04:11 +03:00
Sergey B Kirpichev 454ce15028 Cleanup findroot() calls in zetazeros.py 2025-05-31 05:27:20 +03:00
Sergey B Kirpichev c61d0345b3 Drop useless verify=False from doctest 2025-05-31 03:55:10 +03:00
Sergey B Kirpichev 065746d7de Cleanup optimization.rst 2025-05-30 06:05:03 +03:00
Sergey B Kirpichev 58859b9acf Fix Anderson docs 2025-05-30 06:04:23 +03:00
Sergey B Kirpichev 5e17d5611f Merge pull request #954 from skirpichev/bin/hex-literals-in-cli
Support bin/hex float literals in the CLI
2025-05-14 05:22:23 +03:00
Sergey B Kirpichev 84bddafd07 Support bin/hex float literals in the CLI
See skirpichev/peps#4 and python/cpython#114667.
2025-05-11 08:58:06 +03:00
Sergey B Kirpichev 2dc1682b98 Merge pull request #950 from skirpichev/changes/937
Update CHANGES
2025-05-10 08:40:04 +03:00
Sergey B Kirpichev b7276c1235 Update CHANGES
Closes #937
2025-05-09 13:29:57 +03:00
Sergey B Kirpichev 785a5288ff Merge pull request #955 from skirpichev/deprecate-bitcount
Deprecate bitcount()
2025-05-09 13:28:01 +03:00
Sergey B Kirpichev dd54732850 Deprecate bitcount() 2025-05-09 13:02:15 +03:00
Sergey B Kirpichev 8f87833c39 Workaround CLI testing on CPython 3.14 2025-05-09 10:08:09 +03:00
Sergey B Kirpichev fdcda9cef7 Merge pull request #952 from skirpichev/misc
Misc fixes
2025-05-02 08:17:28 +03:00
Sergey B Kirpichev c3c76f4d28 Don't use python-gmp on PyPy (lack of cache) 2025-05-01 12:51:26 +03:00
Sergey B Kirpichev 8778568951 Avoid using ";" in doctests, esp with side effects
The new CPython repl hide all output, except for the last statement.
See https://github.com/python/cpython/issues/131217.
2025-05-01 10:41:48 +03:00
Sergey B Kirpichev dbd8976e3f Merge pull request #951 from skirpichev/gmp-backend
test / docs (push) Failing after 0s
test / coverage (push) Failing after 0s
test / linter (push) Failing after 0s
test / tests (false, 3.10) (push) Has been skipped
test / tests (false, 3.11) (push) Has been skipped
test / tests (false, 3.12) (push) Has been skipped
test / tests (false, 3.13) (push) Has been skipped
test / tests (false, 3.14) (push) Has been skipped
test / tests (false, 3.9) (push) Has been skipped
test / tests (false, pypy3.10) (push) Has been skipped
test / tests (false, pypy3.11) (push) Has been skipped
test / tests (true, 3.13) (push) Has been skipped
Publish on PyPI / Build distributions (push) Has been cancelled
Publish on PyPI / Publish distributions to PyPI (push) Has been cancelled
Add python-gmp as optional backend (gmpy2 alternative)
2025-04-30 10:33:56 +03:00
Sergey B Kirpichev 0b523c0e7a Merge pull request #949 from skirpichev/isspecial/946
Add isspecial() method for mp/fp contexts, deprecate isnormal()
2025-04-29 07:56:56 +03:00
Sergey B Kirpichev 33eb8b787b Add python-gmp as optional backend (gmpy2 alternative)
Closes #895
2025-04-28 20:52:16 +03:00
Sergey B Kirpichev e6aa3b46b1 Add isspecial() method for mp/fp contexts, deprecate isnormal()
Closes #946
2025-04-28 08:36:15 +03:00
Sergey B Kirpichev 7a0b3f48b6 Merge pull request #948 from skirpichev/misc
Misc fixes
2025-04-28 07:00:24 +03:00
Sergey B Kirpichev beb5029ac5 Use mpf_log1p in acos_asin() helper (implementing Hull et al algorithm)
Closes #787
2025-04-26 19:10:44 +03:00
Sergey B Kirpichev 4edff338a2 Add exp2() and log2()
Closes #947
2025-04-26 19:10:40 +03:00
Sergey B Kirpichev cb74f50321 Use SPDX license value 2025-04-26 09:05:44 +03:00
Sergey B Kirpichev 7a8b98c6b3 Merge pull request #944 from skirpichev/fix-nospec-fmt/943
Fix new-style formatting without type specifier (repr_dps vs dps)
2025-04-24 13:48:32 +03:00
Sergey B Kirpichev 6c51c9d898 Fix new-style formatting without type specifier (repr_dps vs dps)
Closes #943
2025-04-23 07:04:24 +03:00
Sergey B Kirpichev fcf6e9650c Merge pull request #941 from skirpichev/use-pyrepl/938
Use PyREPL, as fallback (no IPython)
2025-04-17 07:35:07 +03:00
Sergey B Kirpichev c945b4d2b0 Use PyREPL, as fallback (no IPython)
Closes #938
2025-04-16 08:27:22 +03:00
Sergey B Kirpichev a0ba3a00c1 Merge pull request #940 from RonnyPfannschmidt/patch-1
use the intended setuptools_scm integration pattern
2025-04-15 06:43:28 +03:00
Sergey B Kirpichev 608cd99115 Merge pull request #939 from skirpichev/misc
Misc fixes
2025-04-15 06:43:04 +03:00
Ronny Pfannschmidt 499b44115f use the intended setuptools_scm integration pattern
as per https://setuptools-scm.readthedocs.io/en/latest/#with-setuptools

i'd appreciate details on how the usage of dynamic and get-version came to be
2025-04-14 16:38:07 +02:00
Sergey B Kirpichev 9d42919136 Use ast transformation in wrap_float_literals() 2025-04-13 12:37:30 +03:00
Sergey B Kirpichev 2c82152738 Fix source transformation in IPython, add tests 2025-04-13 12:37:27 +03:00
Sergey B Kirpichev 788f3a7fca Ensure mpf_bernoulli() returns normalized answer
Closes #928
2025-04-09 09:31:51 +03:00
Sergey B Kirpichev 609d09bb74 Merge pull request #936 from skirpichev/misc
Misc fixes
2025-04-08 07:50:01 +03:00
Sergey B Kirpichev 210a6a77da Fix wrap_float_literals() to detect all float/complex literals 2025-04-08 06:01:58 +03:00
Sergey B Kirpichev 798d929ace Move source transformations up in runsource() 2025-04-08 05:52:14 +03:00
Sergey B Kirpichev 91416abc82 Amend 950d654a (support '._f'-like cases, i.e. with default precision) 2025-04-07 11:17:29 +03:00
Sergey B Kirpichev c465c17c2b Revert rest of f4cef9c8
Python docs doesn't specify 0-padding behavior, when alignment is
specified.  Lets be more strict here as Fraction/Decimal's: just reject
0 flag in this case.

float's behavior is just odd:
>>> format(1.123, '<020f')
'1.123000000000000000'
2025-04-07 10:56:11 +03:00
Sergey B Kirpichev 17a3f61928 Test formatting with digit separators in the fractional part 2025-04-07 10:56:09 +03:00
Sergey B Kirpichev 5ad43bd837 Revert 849e185e and (partially) f4cef9c8 (same for with field)
While python/cpython#130662 being discussed, lets keep previous (more
strict) formatting specification.
2025-04-07 10:49:01 +03:00
Sergey B Kirpichev 551ab3224e CLI fixes for multi-line input
Processing in wrap_float_literals() can't work line-by-line, as the
tokenize.unparse() doesn't preserve indentation in this case.  E.g.:

$ python -m mpmath --no-ipython
>>> def f():
...     return 1.2
...
Traceback (most recent call last):
  ...
  File "<unknown>", line 2
    return mpf('1.2')
    ^^^^^^
IndentationError: expected an indented block after function definition on line 1
2025-04-07 10:49:01 +03:00
Sergey B Kirpichev f4af440eb3 Use itertools.product() in calculus 2025-04-07 10:49:01 +03:00
Sergey B Kirpichev 33bf8a48f4 Merge pull request #933 from skirpichev/repr_dps
Add pretty_dps context property to control number of printed digits
2025-04-05 06:53:18 +03:00
Sergey B Kirpichev d8277f07d2 Merge pull request #935 from skirpichev/sph-bessel/319
Support spherical Bessel functions (jn/yn)
2025-04-03 07:21:09 +03:00
Sergey B Kirpichev 5294dd9ddd Support spherical Bessel functions (jn/yn)
Naming scheme follows to SciPy.  Closes #319
2025-04-02 07:12:47 +03:00
Sergey B Kirpichev a14901da43 Add pretty_dps context property to control number of printed digits
Closes #921
2025-03-30 09:13:43 +03:00
Sergey B Kirpichev 8c6f6603ad Merge pull request #931 from skirpichev/fix-repl
Fix runsource() for incomplete input
2025-03-19 04:34:14 +03:00
Sergey B Kirpichev 23b9054b3f Fix runsource() for incomplete input 2025-03-18 16:26:22 +03:00
Sergey B Kirpichev 9ea52503dd Merge pull request #926 from skirpichev/fix-637
Use sum_accurately() in hankel1/2()
2025-03-09 02:24:50 +03:00
Sergey B Kirpichev 1a2bd24e5f Use sum_accurately() in hankel1/2()
Closes #637
2025-03-08 02:58:48 +03:00
Sergey B Kirpichev 18b83d144d Merge pull request #925 from skirpichev/frac-sep/918
Support thousands separators for formatting of fractional part
2025-03-06 05:37:24 +03:00
Sergey B Kirpichev 0661c36724 Cleanup handling of separators 2025-03-05 06:25:10 +03:00
Sergey B Kirpichev 950d654a22 Support thousands separators for formatting of fractional part
Closes #918
2025-03-05 03:34:06 +03:00
Sergey B Kirpichev 8f68766f75 Simplify handling of thousands_separators 2025-03-04 12:50:21 +03:00
Sergey B Kirpichev 1a0e1aea07 Simplify format_scientific/binary/hexadecimal() 2025-03-04 09:58:22 +03:00
Sergey B Kirpichev a06c90b884 Simplify format_fixed() 2025-03-04 08:51:41 +03:00
Sergey B Kirpichev ddd8c9d698 Drop base argument for format_scientific/fixed() 2025-03-04 07:36:44 +03:00
Sergey B Kirpichev de66a1cac7 Simplify sign handling in format() helpers 2025-03-04 07:12:02 +03:00
Sergey B Kirpichev 6f698709e1 Merge pull request #924 from skirpichev/sofa
Add moving sofa demo
2025-03-04 03:49:31 +03:00
Sergey B Kirpichev e6c5041498 Merge pull request #923 from skirpichev/misc
Misc fixes
2025-03-04 03:19:10 +03:00
Sergey B Kirpichev 8d2c851247 Add moving sofa demo
Closes #96
2025-03-03 12:12:17 +03:00
Sergey B Kirpichev d66b8f2536 Enable pretty-printing in CLI per default 2025-03-03 04:37:10 +03:00
Sergey B Kirpichev a0bd397484 Test on PyPy3.11 2025-03-03 04:37:10 +03:00
Sergey B Kirpichev 198fe541fc Test on stable PyPy3.10 2025-03-03 04:37:10 +03:00
Sergey B Kirpichev ac5dc65288 Merge pull request #922 from skirpichev/fix-917
Fix handling of zeropad flag
2025-03-03 04:22:03 +03:00
Sergey B Kirpichev 8a42c3f6a0 Fix handling of zeropad flag
Closes #917
2025-03-03 03:59:13 +03:00
Sergey B Kirpichev f816f92923 Merge pull request #920 from mpmath/revert-896-gmp-backend
Revert "Add python-gmp as optional backend (gmpy2 alternative)"
2025-03-01 05:30:05 +03:00
Sergey B Kirpichev 23b5d390bc Revert "Add python-gmp as optional backend (gmpy2 alternative)" 2025-03-01 05:29:44 +03:00
Sergey B Kirpichev 0805720944 Merge pull request #896 from skirpichev/gmp-backend
Add python-gmp as optional backend (gmpy2 alternative)
2025-03-01 05:29:38 +03:00
Sergey B Kirpichev 7c55d87ca9 Merge pull request #916 from skirpichev/misc
Misc fixes
2025-03-01 04:55:09 +03:00
Sergey B Kirpichev 4d166d8853 Add python-gmp as optional backend (gmpy2 alternative)
Closes #895
2025-02-28 08:48:32 +03:00
Sergey B Kirpichev f4cef9c83e Correct handling of zeropad and fill_char options 2025-02-28 08:48:11 +03:00
Sergey B Kirpichev 849e185ede Relax requirements on precision format, allow 0-prefixes
Closes #915
2025-02-28 08:48:11 +03:00
Sergey B Kirpichev 4c9a2ef9cd Simplify backend handling in libintmath.py/libmpf.py 2025-02-28 08:47:50 +03:00
Sergey B Kirpichev 5e57cb8039 Merge pull request #913 from dimbleby/build-publish-wheel
build and publish wheel
2025-01-31 06:46:58 +03:00
David Hotham e254e50428 build and publish wheel 2025-01-30 21:12:35 +00:00
Sergey B Kirpichev 861c1cd0d4 Add aliases for pure-python integer functions
test / linter (push) Failing after 0s
test / coverage (push) Failing after 0s
test / tests (3.10) (push) Has been skipped
test / tests (3.11) (push) Has been skipped
test / tests (3.12) (push) Has been skipped
test / tests (3.13) (push) Has been skipped
test / tests (3.14) (push) Has been skipped
test / tests (3.9) (push) Has been skipped
test / tests (pypy3.10-nightly) (push) Has been skipped
test / docs (push) Failing after 1s
Publish on PyPI / Build distribution (push) Has been cancelled
Publish on PyPI / Publish distribution to PyPI (push) Has been cancelled
2025-01-30 06:06:19 +03:00
Sergey B Kirpichev 65247c70df Merge pull request #911 from skirpichev/drop-3.8
Drop support for CPython 3.8
2025-01-30 05:24:54 +03:00
Sergey B Kirpichev 5a52ee2867 Update copyright years 2025-01-30 05:19:47 +03:00
Sergey B Kirpichev 085182d60c Merge pull request #912 from skirpichev/speedup-coverage
Speedup coverage tests (run less without gmpy2)
2025-01-30 05:15:00 +03:00
Sergey B Kirpichev bd1584671c Speedup coverage tests (run less without gmpy2) 2025-01-30 04:48:20 +03:00
Sergey B Kirpichev 5c719770ae Drop support for CPython 3.8
Closes #910
2025-01-29 10:35:48 +03:00
Sergey B Kirpichev 049321c80e Merge pull request #909 from skirpichev/misc
Misc fixes
2025-01-29 05:09:50 +03:00
Sergey B Kirpichev e0f88b3495 Use gmpy2.mpz.digits() method 2025-01-29 04:02:02 +03:00
Sergey B Kirpichev 2550656706 Make sure we pass MPZ mantissa to from_man_exp() 2025-01-29 04:00:25 +03:00
Sergey B Kirpichev 4fb7367100 Ignore missing ~/.python_history for default console
Closes #907
2025-01-29 03:43:08 +03:00
Sergey B Kirpichev 5d501de9e8 Correct case for integer n in besselj/besseli
Closes #908
2025-01-29 03:35:24 +03:00
Sergey B Kirpichev fa51d4c822 Merge pull request #898 from ricor07/fix_polylog
Increase working precision in polylog_general() for negative s
2025-01-22 15:26:00 +03:00
Sergey B Kirpichev 441fa3ee8d add test 2025-01-22 13:57:45 +03:00
Sergey B Kirpichev bfb4711bd0 restrict precision adjustment for finite numbers 2025-01-22 13:54:40 +03:00
Sergey B Kirpichev 09bdbbb28f Merge branch 'master' into fix_polylog 2025-01-22 13:50:36 +03:00
Sergey B Kirpichev 859eb36c6e Cleanup publish.yml 2025-01-22 11:01:11 +03:00
Riccardo Orsi af72f59566 Update zeta.py 2025-01-21 16:26:45 +01:00
Riccardo Orsi 3ac143b2e1 Update zeta.py
removed trailing whitespace
2025-01-21 16:21:10 +01:00
Riccardo Orsi 18ad9aed8c Update zeta.py 2025-01-21 16:04:50 +01:00
Riccardo Orsi 9f9a1ae39b Update zeta.py
Tries to address error in polylog precision when abs(z) > 1 and s is close to an integer
2025-01-18 16:20:05 +01:00
109 changed files with 8129 additions and 3457 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/
+11
View File
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: "monthly"
rebase-strategy: "disabled"
groups:
actions-deps:
patterns:
- "*"
+15 -14
View File
@@ -1,44 +1,45 @@
name: Run coverage tests
on: workflow_call
on: [workflow_dispatch, workflow_call]
jobs:
coverage:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
strategy:
fail-fast: true
env:
PYTEST_ADDOPTS: --cov mpmath --cov-append -n auto
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v5
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- name: Install dependencies
run: |
pip install --upgrade setuptools pip
pip install --upgrade .[develop,gmpy2,ci]
- run: touch ~/.python_history
pip install --upgrade .[develop,gmpy2,gmp,ci]
- name: Run coverage tests
run: |
pytest
pip uninstall -y ipython
pytest mpmath/tests/test_cli.py
pip uninstall -y gmpy2
pytest
pytest mpmath/tests/test_basic_ops.py mpmath/tests/test_convert.py \
mpmath/tests/test_functions.py mpmath/tests/test_gammazeta.py \
mpmath/tests/test_bitwise.py
pip uninstall -y python-gmp
pytest mpmath/tests/test_basic_ops.py mpmath/tests/test_convert.py \
mpmath/tests/test_functions.py mpmath/tests/test_gammazeta.py \
mpmath/tests/test_bitwise.py
- name: Generate coverage reports
run: |
coverage xml
coverage html
diff-cover coverage.xml --fail-under=100 --compare-branch=origin/master
- uses: actions/upload-artifact@v4
diff-cover coverage.xml --fail-under=100 \
--compare-branch=origin/master
- uses: actions/upload-artifact@v7
with:
name: coverage
path: |
coverage.xml
build/coverage/html/
- name: Upload coverage data
uses: codecov/codecov-action@v4
with:
files: ./coverage.xml
fail_ci_if_error: false
+10 -8
View File
@@ -1,13 +1,13 @@
name: Build & test docs
on: workflow_call
on: [workflow_dispatch, workflow_call]
jobs:
docs:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v5
- 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@v4
env:
NO_COLOR: 1 # workaround for sphinx-contrib/autoprogram#76
COLUMNS: 80 # also enable line wrapping for argparse
- uses: actions/upload-artifact@v7
with:
name: docs
path: |
+4 -4
View File
@@ -1,13 +1,13 @@
name: Linting with flake8, etc
on: workflow_call
on: [workflow_dispatch, workflow_call]
jobs:
linter:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v5
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- run: pip install --upgrade .[develop]
+14 -16
View File
@@ -1,36 +1,34 @@
name: Publish on PyPI
on: push
on: [push, workflow_dispatch, workflow_call]
jobs:
sdist:
name: Build distribution
runs-on: ubuntu-latest
build:
name: Build distributions
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v5
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- run: pip install build
- run: python -m build -s
- uses: actions/upload-artifact@v4
- run: python -m build
- uses: actions/upload-artifact@v7
with:
name: sdist
name: build
path: dist/
publish-to-pypi:
name: Publish distribution to PyPI
name: Publish distributions to PyPI
if: startsWith(github.ref, 'refs/tags/')
needs:
- sdist
runs-on: ubuntu-latest
- build
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
pattern: sdist
pattern: build
path: dist/
merge-multiple: true
- run: ls -l
- run: ls -l dist/
- uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
+40 -11
View File
@@ -1,5 +1,13 @@
name: test
on: [push, pull_request]
on:
push:
pull_request:
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
@@ -7,32 +15,53 @@ 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
runs-on: ubuntu-latest
- frozen-version
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
python-version: [3.8, 3.9, '3.10', 3.11, 3.12, 3.13, 3.14, pypy3.10-nightly]
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.x"
nogmpy: true
- 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@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install gmpy2 deps
if: matrix.python-version == 3.14
run: sudo apt install libmpc-dev
- name: Install dependencies
run: |
pip install --upgrade setuptools pip
pip install --upgrade .[develop,gmpy2,ci]
- run: touch ~/.python_history
pip install --upgrade .[develop,gmpy2,gmp,ci]
- run: pip uninstall -y gmpy2
if: matrix.nogmpy
- run: pip uninstall -y gmpy2 python-gmp
if: matrix.purepy
- run: pytest
+3
View File
@@ -33,6 +33,9 @@ my/
dist/
build/
# Generated by setuptools_scm
mpmath/_version.py
# Tox files
.tox/
+332
View File
@@ -1,3 +1,335 @@
--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)
* Rewrite bernpoly/eulerpoly to avoid dependency on bernoulli(1) convention,
see #700 (Sergey B Kirpichev)
* Support base kwarg for from_str(), see #703 (Jonathan Warner, Sergey B
Kirpichev)
* Support randmatrix() for mp.iv and mp contexts, see #527 (Maximilian
Gaukler)
* 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
objects, see #731 (Sergey B Kirpichev)
* Expose lower/upper_gamma functions, see #740 (Sergey B Kirpichev)
* Support mpc initialization from string, see #743 (Sergey B Kirpichev)
* Support asinh/acosh/atanh in the fp context, see #750 (Sergey B Kirpichev)
* Support binary/octal/hexadecimal string output, see #711 (Jonathan Warner,
Sergey B Kirpichev)
* Support pickling for matrices and mpi, see #761 (Sergey B Kirpichev)
* Support matrix.__array__() dunder method, see #767 (Sergey B Kirpichev)
* Support more number syntaxes, see #778 (Sergey B Kirpichev)
* Run mpmath as a module for interactive work, see #773, #923, #931, #936,
#939 and #954 (Sergey B Kirpichev)
* Add signed option to to_man_exp(), see #783 (Sergey B Kirpichev)
* Add fp.hypot, see #798 (Sergey B Kirpichev)
* Support inf/nan's in ctx.almosteq(), #802 (Sergey B Kirpichev)
* Implement mpf.__format__(), see #819, #831, #850, #859, #857, #862, #881,
#944 and #966 (Javier Garcia, Sergey B Kirpichev)
* Support conversion from scalar ndarray's, see #821 (Sergey B Kirpichev)
* Support rounding modes in mpf.__format__, see #823, #831, #834
and #969 (Javier Garcia, Sergey B Kirpichev)
* Support '%' presentation type for mpf, see #847 (Sergey B Kirpichev)
* Support gmpy2-like rounding modes in to_str(), see #830 (Javier Garcia)
* Implement 'a'/'A' formating types for mpf.__format__, see #841 and #870
(Sergey B Kirpichev)
* Add mpc.__format__(), see #855 (Sergey B Kirpichev)
* Now mpf.__round__() returns mpf, see #826 and #966 (Sergey B Kirpichev)
* Support 'b' (binary) format type for mpf/mpc, see #867 (Sergey B Kirpichev)
* Implement mpf.__floordiv__() and mpf.__divmod__(), see #873 (Sergey B
Kirpichev)
* Add parameters for MPContext constructor, see #876 and #963 (Sergey B Kirpichev)
* Add MPFR-compatible aliases for rounding modes, see #892 (Sergey B
Kirpichev)
* Support negative indexes in matrix, see #897 (Riccardo Orsi)
* Better introspection support for decorated functions, see #900 (Sergey B
Kirpichev)
* Add moving sofa demo, see #924 (Sergey B Kirpichev)
* Support spherical Bessel functions (jn/yn), #935 (Sergey B Kirpichev)
* Add pretty_dps context property to control number of printed digits, see
#933 (Sergey B Kirpichev)
* Support thousands separators for formatting of fractional part, see #925 and
#936 (Sergey B Kirpichev)
* Use PyREPL, as fallback (no IPython), see #941 (Sergey B Kirpichev)
* Add exp2() and log2(), see #948 (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.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
Kirpichev)
* Direct access to _mpf_ tuples now deprecated, please use from/to_man_exp()
functions and special constants (finf, fninf and fnan), see
#783 (Sergey B Kirpichev)
* Removed sage backend, see #732 (Sergey B Kirpichev)
* Drop MPMATH_STRICT environment variable, see #759 (Sergey B Kirpichev)
* 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)
* 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:
* sum_accurately(), betainc() and power() fixes, see #664 (Sergey B Kirpichev)
* Warn users about Python's true division, see #670 (Sergey B Kirpichev)
* Propagate nan's in ei_asympt(), see #672 (Sergey B Kirpichev)
* Fix matrix.__eq__, fix string parsing with underscores, see #679 (Sergey B
Kirpichev)
* Raise IndexError if matrix index out of bounds, see #689 (Sergey B
Kirpichev)
* Fix nan handling in fp.mag() and hyper(), see #688 (Sergey B Kirpichev)
* Optimize sparse matrix dot product, see #450 (Tyler Chen)
* Correct pow() for mpf's to be consistent with mpfr/float's, see #690 (Sergey
B Kirpichev)
* Improve hypsum non-convergence behaviour, see #703 (Benjamin Fischer)
* Fixed TypeError in LU_comp, see #610 (Jan-Philipp Hoffmann)
* Fix disagreement fp.mod vs mp.mod, see #710 (Sergey B Kirpichev)
* Skip eigenvectors if left=right=False for one-dimentional matrix, see #713
(Sergey B Kirpichev)
* Fix mpc() constructor to be compatible with complex(), fix disagreement
fp.pow vs mp.pow, see #731 (Sergey B Kirpichev)
* Add derivative keyword argument for besselk(), see #735 (Sergey B Kirpichev)
* Fix quadosc(), fast case in _hyp2f1(), correct fp.gamma() and fp.isnprint(),
see #740 (Sergey B Kirpichev)
* Pevent erroneous setting of dps/prec on mpmath module, see #678
(Colin B. Macdonald)
* Update splot() for recent matplotlib, see #747 (Sergey B Kirpichev)
* acos_asin(): don't try to normalize special numbers, fix repr(mp.eps), see
#750 (Sergey B Kirpichev)
* Fix choleksy_solve for complex matrix, see #755 (Qiming Sun)
* Fix hang in polylog_general(), fix demos, use cholesky_solve() for
overdetermined complex linear systems, fix several issues with empty
matrices, see #759
* sqrt(z): special case for infinite z.imag, see #777 (Sergey B Kirpichev)
* Correct atan2(±inf, ±inf), see #775 (Sergey B Kirpichev)
* Handle infinite arguments in tan/tanh, see #785 (Sergey B Kirpichev)
* Drop mpf.__complex__(), workaround 1/z division for acot/asec/acsc/acoth,
see #797 (Sergey B Kirpichev)
* Handle more special cases in besselk and hyp1f1, see #792, #800 and #801
(Sergey B Kirpichev)
* Correct asin/acos for infinite arguments, see #795 (Sergey B Kirpichev)
* Reduce memory usage for QuadratureRule cache, see #812 (Sergey B Kirpichev)
* Drop sign from nstr(mpf('inf')) output, see #828 (Sergey B Kirpichev)
* Improve accuracy of log1p(), see #803 and #854 (Tim Peters, Sergey B
Kirpichev)
* Normalize mpf in mp.npconvert(), fix special cases in bernpoly(), see #839
(Sergey B Kirpichev)
* Fix mpf_div() for prec=0, see #849 (Sergey B Kirpichev)
* Raise ValueError for complex infininity condition in zeta(s, a), see #864
(Sergey B Kirpichev)
* Enable trap_complex for MDNewton, see #870 (Sergey B Kirpichev)
* Use correct mixed-mode functions in fsub/fdiv, add special cases for
infinities in mpc_*div(), see #873 (Sergey B Kirpichev)
* Revert "fix ellippi to return an inf instead of raising an exception", see
#875 (Sergey B Kirpichev)
* Reject invalid strings in from_str(), see #886 (Sergey B Kirpichev)
* Use parity formula for besseli, see #889 (Sergey B Kirpichev)
* 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, #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)
* Use sum_accurately() in hankel1/2(), see #926 (Sergey B Kirpichev)
* 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 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
(Warren Weckesser)
* 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:
* Use codecov/coverage-action, see #674 (Sergey B Kirpichev)
* Run CI tests with pytest-xdist, see #685 (Sergey B Kirpichev)
* Add pyproject.toml, depend on flake518, see #684 (Sergey B Kirpichev)
* Port torture.py/extratest_zeta.py/extratest_gamma.py to the pytest
framework, see #687 (Sergey B Kirpichev)
* Create CITATION.bib, see #681 (Devharsh Trivedi)
* Avoid using star imports in tests and documentation, see #698 (Sergey B
Kirpichev)
* Fix some py2's remnants and remove old gmpy workarounds, see #699 (Sergey B
Kirpichev)
* Use math.isqrt in isqrt_python calculations, see #695 (Daiki Takahashi)
* Use gcd() and other bigint's functions from the backend, see #697 (Sergey B
Kirpichev)
* Drop legacy and redundant code, see #701 (Sergey B Kirpichev)
* Change FPContext to use more functions from the stdlib, see #692 (Sergey B
Kirpichev)
* Avoid dynamic method creation in _mpf, see #702 (Sergey B Kirpichev)
* Enable testing on CPython 3.12-dev, see #706 (Sergey B Kirpichev)
* Use bit_length() method instead of bitcount(), see #721 (Sergey B Kirpichev)
* Use lru_cache() in ifib() and eulernum(), isprime() alternatives from
backends, see #722 (Sergey B Kirpichev)
* Use setuptools_scm to update __version__, see #694 (Sergey B Kirpichev)
* Run tests on 3.13, see #759 (Sergey B Kirpichev)
* Do not build depend on pip and wheel, see #758 (Gonzalo Tornaría)
* Add CONTRIBUTING.rst, see #763 (Sergey B Kirpichev)
* 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)
* 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.
--1.3.0--
Released March 7, 2023
+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},
}
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2005-2024 Fredrik Johansson and mpmath contributors
Copyright (c) 2005-2026 Fredrik Johansson and mpmath contributors
All rights reserved.
+10 -5
View File
@@ -1,14 +1,12 @@
mpmath
======
|pypi version| |Build status| |Code coverage status| |Zenodo Badge|
|pypi version| |Build status| |Zenodo Badge|
.. |pypi version| image:: https://img.shields.io/pypi/v/mpmath.svg
:target: https://pypi.python.org/pypi/mpmath
.. |Build status| image:: https://github.com/mpmath/mpmath/workflows/test/badge.svg
:target: https://github.com/mpmath/mpmath/actions?workflow=test
.. |Code coverage status| image:: https://codecov.io/gh/mpmath/mpmath/branch/master/graph/badge.svg
:target: https://codecov.io/gh/mpmath/mpmath
.. |Zenodo Badge| image:: https://zenodo.org/badge/2934512.svg
:target: https://zenodo.org/badge/latestdoi/2934512
@@ -49,6 +47,8 @@ or new features to mpmath:
* Max Gaukler <max.gaukler@fau.de>
* Guillermo Navas-Palencia <g.navas.palencia@gmail.com>
* Nike Dattani <nike@hpqc.org>
* Tim Peters <tim.peters@gmail.com>
* Javier Garcia <javier.garcia.tw@hotmail.com>
Numerous other people have contributed by reporting bugs,
requesting new features, or suggesting improvements to the
@@ -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.8 or later versions. It has been tested with CPython
3.8 through 3.14 and for PyPy 3.10.
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 -10
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,11 +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.dps = 15
mp.pretty = False
iv.dps = 15
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
+63
View File
@@ -0,0 +1,63 @@
'''
This script calculates the constant in Gerver's solution to the moving sofa
problem.
See Finch, S. R. "Moving Sofa Constant." §8.12 in Mathematical Constants.
Cambridge, England: Cambridge University Press, pp. 519-523, 2003.
'''
from mpmath import cos, sin, pi, quad, findroot, mp
mp.prec = 113
eqs = [lambda A, B, φ, θ: (A*(cos(θ) - cos(φ)) - 2*B*sin(φ)
+ (θ - φ - 1)*cos(θ) - sin(θ) + cos(φ) + sin(φ)),
lambda A, B, φ, θ: (A*(3*sin(θ) + sin(φ)) - 2*B*cos(φ)
+ 3*(θ - φ - 1)*sin(θ) + 3*cos(θ) - sin(φ) + cos(φ)),
lambda A, B, φ, θ: A*cos(φ) - (sin(φ) + 0.5 - 0.5*cos(φ) + B*sin(φ)),
lambda A, B, φ, θ: ((A + pi/2 - φ - θ) - (B - (θ - φ)*(1 + A)/2
- 0.25*(θ - φ)**2))]
A, B, φ, θ = findroot(eqs, (0, 0, 0, 0))
def r(α):
if 0 <= α < φ:
return 0.5
if φ <= α < θ:
return (1 + A + α - φ)/2
if θ <= α < pi/2 - θ:
return A + α - φ
return B - (pi/2 - α - φ)*(1 + A)/2 - (pi/2 - α - φ)**2/4
s = lambda α: 1 - r(α)
def u(α):
if φ <= α < θ:
return B - (α - φ)*(1 + A)/2 - (α - φ)**2/4
return A + pi/2 - φ - α
def du(α):
if φ <= α < θ:
return -(1 + A)/2 - (α - φ)/2
return -1
def y(α, f):
if α > pi/2 - θ:
i = [0, φ, θ, pi/2 - θ, α]
elif α > θ:
i = [0, φ, θ, α]
elif α > φ:
i = [0, φ, α]
else:
i = i = [0, α]
return 1 - quad(lambda x: f(x)*sin(x), i)
y1 = lambda α: y(α, r)
y2 = lambda α: y(α, s)
y3 = lambda α: y2(α) - u(α)*sin(α)
S1 = quad(lambda x: y1(x)*r(x)*cos(x), [0, φ, θ, pi/2 - θ, pi/2 - φ])
S2 = quad(lambda x: y2(x)*s(x)*cos(x), [0, φ, θ])
S3 = quad(lambda x: y3(x)*(u(x)*sin(x) - du(x)*cos(x) - s(x)*cos(x)),
[φ, θ, pi/4])
print(2*(S1 + S2 + S3))
+49 -2
View File
@@ -80,7 +80,10 @@ Mpmath uses a global working precision; it does not keep track of the precision
Mpmath settings:
mp.prec = 53 [default: 53]
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).
@@ -96,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:
@@ -120,6 +123,17 @@ Or why not 1 googolplex:
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 is the default
'n'
>>> sin(1)
mpf('0.8414709848078965')
>>> mp.rounding = 'u' # round up
>>> sin(1)
mpf('0.84147098480789662')
>>> mp.rounding = 'n'
Temporarily changing the precision
..................................
@@ -221,6 +235,39 @@ 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, 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)))``.
>>> 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
changing the working precision, use :func:`format syntax support
+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 -7
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,9 +21,5 @@ Solvers
.. autoclass:: mpmath.calculus.optimization.Ridder
.. autoclass:: mpmath.calculus.optimization.ANewton
.. autoclass:: mpmath.calculus.optimization.MDNewton
.. Minimization and maximization (``findmin``, ``findmax``)
.. ........................................................
.. (To be added.)
.. 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-2024, 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().
+17 -1
View File
@@ -4,10 +4,14 @@ Contexts
High-level code in mpmath is implemented as methods on a "context object". The context implements arithmetic, type conversions and other fundamental operations. The context also holds settings such as precision, and stores cache data. A few different contexts (with a mostly compatible interface) are provided so that the high-level algorithms can be used with different implementations of the underlying arithmetic, allowing different features and speed-accuracy tradeoffs. Currently, mpmath provides the following contexts:
* Arbitrary-precision arithmetic (``mp``)
* A faster Cython-based version of ``mp`` (used by default in Sage, and currently only available there)
* Arbitrary-precision interval arithmetic (``iv``)
* Double-precision arithmetic using Python's builtin ``float`` and ``complex`` types (``fp``)
.. note::
Using global context is not thread-safe, create instead
local contexts with e.g. :class:`~mpmath.MPContext`.
Most global functions in the global mpmath namespace are actually methods of the ``mp``
context. This fact is usually transparent to the user, but sometimes shows up in the
form of an initial parameter called "ctx" visible in the help for the function::
@@ -108,6 +112,18 @@ The ``mp`` context is what most users probably want to use most of the time, as
See :doc:`basics` for a description of basic usage.
.. autoclass:: mpmath.MPContext
Local contexts, created on demand, could be used just as the global ``mp``:
>>> from mpmath import MPContext
>>> ctx = MPContext()
>>> ctx.sin(1)
mpf('0.8414709848078965')
>>> ctx.prec = 113
>>> ctx.sin(1)
mpf('0.841470984807896506652502321630298954')
Arbitrary-precision interval arithmetic (``iv``)
------------------------------------------------
+12 -3
View File
@@ -33,6 +33,15 @@ Hankel functions
.. autofunction:: mpmath.hankel2
Spherical Bessel functions
..........................
.. autofunction:: mpmath.spherical_jn
.. autofunction:: mpmath.spherical_yn
.. autofunction:: mpmath.spherical_in
.. autofunction:: mpmath.spherical_kn
Kelvin functions
................
@@ -85,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
+2 -1
View File
@@ -13,7 +13,8 @@ Results are numerical approximations, so to compute *exact* values a high
enough precision must be set manually::
>>> from mpmath import mp, fac
>>> mp.dps = 15; mp.pretty = True
>>> mp.dps = 15
>>> mp.pretty = True
>>> fac(100)
9.33262154439442e+157
>>> print(int(_)) # most digits are wrong
+5
View File
@@ -52,6 +52,11 @@ Meijer G-function
.. autofunction:: mpmath.meijerg
Fox H-function
.................
.. autofunction:: mpmath.foxh
Bilateral hypergeometric series
...............................
+5 -3
View File
@@ -15,11 +15,12 @@ Exponentiation
..............
.. autofunction:: mpmath.exp
.. autofunction:: mpmath.exp2
.. 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
@@ -27,8 +28,9 @@ Logarithms
.. autofunction:: mpmath.log
.. autofunction:: mpmath.ln
.. autofunction:: mpmath.log2
.. autofunction:: mpmath.log10
.. autofunction:: mpmath.log1p(x)
.. autofunction:: mpmath.log1p
Lambert W function
+43 -40
View File
@@ -9,23 +9,23 @@ Conversion and printing
:func:`~mpmath.mpmathify` / ``convert()``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.mpmathify(x, strings=True)
.. autofunction:: mpmath.mpmathify
:func:`~mpmath.nstr`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.nstr(x, n=6, **kwargs)
.. autofunction:: mpmath.nstr
:func:`~mpmath.nprint`
^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.nprint(x, n=6, **kwargs)
.. autofunction:: mpmath.nprint
:func:`mpmath.mpf.__format__`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.mpf.__format__(s, format_spec)
.. autofunction:: mpmath.mpf.__format__
:func:`mpmath.mpc.__format__`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.mpc.__format__(s, format_spec)
.. autofunction:: mpmath.mpc.__format__
Arithmetic operations
---------------------
@@ -55,147 +55,150 @@ in :doc:`functions/powers`
:func:`~mpmath.fmod`
^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.fmod(x, y)
.. autofunction:: mpmath.fmod
:func:`~mpmath.fsum`
^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.fsum(terms, absolute=False, squared=False)
.. autofunction:: mpmath.fsum
:func:`~mpmath.fprod`
^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.fprod(factors)
.. autofunction:: mpmath.fprod
:func:`~mpmath.fdot`
^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.fdot(A, B=None, conjugate=False)
.. autofunction:: mpmath.fdot
Complex components
------------------
:func:`~mpmath.fabs`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.fabs(x)
.. autofunction:: mpmath.fabs
:func:`~mpmath.sign`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.sign(x)
.. autofunction:: mpmath.sign
:func:`~mpmath.re`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.re(x)
.. autofunction:: mpmath.re
:func:`~mpmath.im`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.im(x)
.. autofunction:: mpmath.im
:func:`~mpmath.arg`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.arg(x)
.. autofunction:: mpmath.arg
:func:`~mpmath.conj`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.conj(x)
.. autofunction:: mpmath.conj
:func:`~mpmath.polar`
^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.polar(x)
.. autofunction:: mpmath.polar
:func:`~mpmath.rect`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.rect(x)
.. autofunction:: mpmath.rect
Integer and fractional parts
-----------------------------
:func:`~mpmath.floor`
^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.floor(x)
.. autofunction:: mpmath.floor
:func:`~mpmath.ceil`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.ceil(x)
.. autofunction:: mpmath.ceil
:func:`~mpmath.nint`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.nint(x)
.. autofunction:: mpmath.nint
:func:`~mpmath.frac`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.frac(x)
.. autofunction:: mpmath.frac
Tolerances and approximate comparisons
--------------------------------------
:func:`~mpmath.chop`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.chop(x, tol=None)
.. autofunction:: mpmath.chop
:func:`~mpmath.almosteq`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.almosteq(s, t, rel_eps=None, abs_eps=None)
.. autofunction:: mpmath.almosteq
Properties of numbers
-------------------------------------
:func:`~mpmath.isinf`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.isinf(x)
.. autofunction:: mpmath.isinf
:func:`~mpmath.isnan`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.isnan(x)
.. autofunction:: mpmath.isnan
:func:`~mpmath.isnormal`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.isnormal(x)
.. autofunction:: mpmath.isnormal
:func:`~mpmath.isfinite`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.isfinite(x)
.. autofunction:: mpmath.isfinite
:func:`~mpmath.isint`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.isint(x, gaussian=False)
.. autofunction:: mpmath.isint
:func:`~mpmath.ldexp`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.ldexp(x, n)
.. autofunction:: mpmath.ldexp
:func:`~mpmath.frexp`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.frexp(x, n)
.. autofunction:: mpmath.frexp
:func:`~mpmath.mag`
^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.mag(x)
.. autofunction:: mpmath.mag
:func:`~mpmath.nint_distance`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.nint_distance(x)
.. 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
-----------------
:func:`~mpmath.fraction`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.fraction(p,q)
.. autofunction:: mpmath.fraction
:func:`~mpmath.rand`
^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.rand()
.. autofunction:: mpmath.rand
:func:`~mpmath.arange`
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.arange(*args)
.. autofunction:: mpmath.arange
:func:`~mpmath.linspace`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autofunction:: mpmath.linspace(*args, **kwargs)
.. autofunction:: mpmath.linspace
Precision management
--------------------
+9 -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]
@@ -471,6 +471,9 @@ Examples::
[0.0 0.0 0.0]
See also [Stoer]_ and [Kresser]_.
The symmetric eigenvalue problem
................................
@@ -516,6 +519,9 @@ Examples::
[0.0]
See also [Golub]_, [GolubWelsch]_, [Stoer]_ and [Stroud]_.
Determinant
...........
+183 -20
View File
@@ -5,46 +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/)
.. [Bailey] D H Bailey. "Tanh-Sinh High-Precision Quadrature", http://crd.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf
.. [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
.. [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.
.. [BenderOrszag] C M Bender & S A Orszag. *Advanced Mathematical Methods for
Scientists and Engineers*, Springer 1999
Scientists and Engineers*, Springer 1999
.. [BorweinBailey] J Borwein, D H Bailey & R Girgensohn. *Experimentation in Mathematics - Computational Paths to Discovery*, A K Peters, 2003
.. [Bernoulli] The Bernoulli Number Page: http://www.bernoulli.org/
.. [BorweinBorwein] J Borwein & P B Borwein. *Pi and the AGM: A Study in Analytic Number Theory and Computational Complexity*, Wiley 1987
.. [BorweinBailey] J Borwein, D H Bailey & R Girgensohn. *Experimentation in
Mathematics - Computational Paths to Discovery*,
A K Peters, 2003
.. [BorweinZeta] P Borwein. "An Efficient Algorithm for the Riemann Zeta Function", http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf
.. [BorweinBorwein] J Borwein & P B Borwein. *Pi and the AGM: A Study in
Analytic Number Theory and Computational Complexity*,
Wiley 1987
.. [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
.. [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
.. [Carlson] B C Carlson. "Numerical computation of real or complex elliptic integrals". http://arxiv.org/abs/math/9409227v1
.. [BorweinZeta] P Borwein. "An Efficient Algorithm for the Riemann Zeta
Function", http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.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
.. [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.
.. [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
.. [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
.. [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
.. [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.
.. [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
.. [Duffy98] Duffy, D.G. (1998). Advanced Engineering Mathematics, CRC Press.
.. [DLMF] NIST Digital Library of Mathematical Functions. http://dlmf.nist.gov/
.. [GradshteynRyzhik] I S Gradshteyn & I M Ryzhik, A Jeffrey & D Zwillinger (eds.), *Table of Integrals, Series and Products*, Seventh edition (2007), Elsevier
.. [Froberg] Carl-Erik Froberg, "On the prime zeta function", BIT 8 (1968),
pp. 187-202.
.. [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)
.. [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
.. [MPFR] The MPFR team. "The MPFR Library: Algorithms and Proofs", http://www.mpfr.org/algorithms.pdf
.. [Golub] golub, "some modified matrix eigenvalue problems", siam review
15, p. 318-334 (1973)
.. [Slater] L J Slater. *Generalized Hypergeometric Functions*. Cambridge University Press, 1966
.. [GolubWelsch] golub and welsch, "calculations of gaussian quadrature
rules", mathematics of computation 23, p. 221-230 (1969)
.. [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.
.. [Gourdon] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma
http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf
.. [SrivastavaKarlsson] H M Srivastava & P W Karlsson. *Multiple Gaussian Hypergeometric Series*. Ellis Horwood, 1985.
.. [GradshteynRyzhik] I S Gradshteyn & I M Ryzhik, A Jeffrey & D Zwillinger
(eds.), *Table of Integrals, Series and Products*,
Seventh edition (2007), Elsevier
.. [Vidunas] R Vidunas. "Identities between Appell's and hypergeometric functions". http://arxiv.org/abs/0804.0655
.. [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
.. [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
.. [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.
.. [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
.. [OEIS] The On-Line Encyclopedia of Integer Sequences (OEIS).
.. [Sidi] A. Sidi - "Pratical Extrapolation
Methods".
.. [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.
.. [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
.. [Stoer] Stoer, Bulirsch - Introduction to Numerical Analysis.
.. [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
.. [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.
.. [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.
.. [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/
.. [WhittakerWatson] E T Whittaker & G N Watson. *A Course of Modern Analysis*. 4th Ed. 1946
Cambridge University Press
.. [Weniger] E.J. Weniger - "Nonlinear Sequence Transformations for the
Acceleration of Convergence and the Summation of Divergent
Series" arXiv:math/0306302
.. [Wikipedia] *Wikipedia, the free encyclopedia*. http://en.wikipedia.org/wiki/Main_Page
.. [WhittakerWatson] E T Whittaker & G N Watson. *A Course of Modern Analysis*.
4th Ed. 1946 Cambridge University Press
.. [WolframFunctions] Wolfram Research, Inc. *The Wolfram Functions Site*. http://functions.wolfram.com/
.. [Widder] Widder, D. (1941). *The Laplace Transform*. Princeton.
.. [Wikipedia] *Wikipedia, the free encyclopedia*.
http://en.wikipedia.org/wiki/Main_Page
.. [WolframFunctions] Wolfram Research, Inc. *The Wolfram Functions Site*.
http://functions.wolfram.com/
+9 -15
View File
@@ -1,8 +1,8 @@
Setting up mpmath
=================
Mpmath requires at least Python 3.8. It has been tested with CPython 3.8
through 3.14 and for PyPy 3.10
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]
@@ -84,7 +73,7 @@ Python interpreter and do the following::
Using gmpy2 (optional)
----------------------
If `gmpy2 <https://github.com/aleaxit/gmpy>`_ version 2.2.0 or later is
If `gmpy2 <https://github.com/gmpy2/gmpy2>`_ version 2.3.0 or later is
installed on your system, mpmath will automatically detect it and transparently
use gmpy2 integers instead of Python integers. This makes mpmath much faster,
especially at high precision (approximately above 100 digits).
@@ -96,6 +85,11 @@ Using the gmpy2 backend can be disabled by setting the ``MPMATH_NOGMPY``
environment variable. Note that the mode cannot be switched during runtime;
mpmath must be re-imported for this change to take effect.
Alternatively, you can use `python-gmp
<https://github.com/diofant/python-gmp>`_ extension. The ``BACKEND`` value
will be equal to 'gmp' in this case. If both extensions are installed on your
system, the gmpy2 will be preferred.
Running tests
-------------
+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):
+22 -8
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
@@ -161,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
@@ -203,6 +207,8 @@ eps = mp.eps
pi = mp.pi
ln2 = mp.ln2
ln10 = mp.ln10
exp2 = mp.exp2
log2 = mp.log2
phi = mp.phi
e = mp.e
euler = mp.euler
@@ -216,6 +222,7 @@ mertens = mp.mertens
ldexp = mp.ldexp
frexp = mp.frexp
ulp = mp.ulp
fsum = mp.fsum
fdot = mp.fdot
@@ -332,6 +339,7 @@ hyp3f2 = mp.hyp3f2
hyperu = mp.hyperu
hypercomb = mp.hypercomb
meijerg = mp.meijerg
foxh = mp.foxh
appellf1 = mp.appellf1
appellf2 = mp.appellf2
appellf3 = mp.appellf3
@@ -392,6 +400,10 @@ bessely = mp.bessely
besselk = mp.besselk
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
@@ -440,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
+76 -31
View File
@@ -9,15 +9,15 @@ some initialization code.
import argparse
import ast
import atexit
import code
import os
import readline
import rlcompleter
import sys
import tokenize
from mpmath import __version__
from mpmath._interactive import (IntegerDivisionWrapper,
wrap_float_literals)
from mpmath._interactive import (IntegerDivisionWrapper, wrap_float_literals,
wrap_hexbinfloats)
__all__ = ()
@@ -38,7 +38,12 @@ parser.add_argument('-V', '--version',
action='store_true')
parser.add_argument('--prec', type=int,
help='Set default mpmath precision')
parser.add_argument('--pretty', help='Enable pretty-printing',
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')
@@ -49,13 +54,20 @@ 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:
lines.append(f'mp.prec = {args.prec}')
if args.pretty:
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
@@ -79,10 +91,14 @@ def main():
for l in lines:
shell.run_cell(l, silent=True)
if not args.no_wrap_floats:
shell.run_cell('from mpmath._interactive import wrap_float_literals')
shell.run_cell('ip = get_ipython()')
shell.run_cell('ip.input_transformers_post.append(wrap_float_literals)')
shell.run_cell('del ip')
source = """
from mpmath._interactive import wrap_float_literals, wrap_hexbinfloats
ip = get_ipython()
ip.input_transformers_post.append(wrap_float_literals)
ip.input_transformers_post.append(wrap_float_literals)
del ip
"""
shell.run_cell(source)
app.start()
else:
ast_transformers = []
@@ -92,53 +108,82 @@ def main():
if not args.no_wrap_division:
ast_transformers.append(IntegerDivisionWrapper())
if not args.no_wrap_floats:
source_transformers.append(wrap_hexbinfloats)
source_transformers.append(wrap_float_literals)
class MpmathConsole(code.InteractiveConsole):
try:
from _pyrepl.main import CAN_USE_PYREPL
if CAN_USE_PYREPL: # pragma: no cover
from _pyrepl.console import \
InteractiveColoredConsole as InteractiveConsole
else:
raise ImportError
except ImportError: # pragma: no cover
from code import InteractiveConsole
class MpmathConsole(InteractiveConsole):
"""An interactive console with readline support."""
def __init__(self, ast_transformers=[],
source_transformers=[], **kwargs):
super().__init__(**kwargs)
readline.set_completer(rlcompleter.Completer(ns).complete)
readline.parse_and_bind('tab: complete')
history = os.path.expanduser('~/.python_history')
readline.read_history_file(history)
atexit.register(readline.write_history_file, history)
self.ast_transformers = ast_transformers
self.source_transformers = source_transformers
def runsource(self, source, filename='<input>', symbol='single'):
if not source:
if self.source_transformers:
last_line = source.endswith("\n") # signals the end of a block
try:
for t in self.source_transformers:
source = ''.join(t(source.splitlines(keepends=True)))
except SyntaxError:
pass # XXX: emit warning?
if last_line:
source += "\n"
try:
code = self.compile(source, filename, 'exec')
except (OverflowError, SyntaxError, ValueError):
if sys.version_info >= (3, 13):
self.showsyntaxerror(filename, source=source)
else: # pragma: no cover
self.showsyntaxerror(filename)
return False
if code is None:
return True
for t in self.source_transformers:
source = '\n'.join(t(source.splitlines()))
if self.ast_transformers:
tree = ast.parse(source, mode=symbol)
tree = ast.parse(source)
for t in self.ast_transformers:
tree = t.visit(tree)
ast.fix_missing_locations(tree)
code_obj = compile(tree, filename, mode=symbol)
try:
self.runcode(code_obj)
except SystemExit:
os.exit(0)
return False
source = ast.unparse(tree)
source += "\n"
return super().runsource(source, filename=filename, symbol=symbol)
c = MpmathConsole(ast_transformers=ast_transformers,
source_transformers=source_transformers, locals=ns)
interactive_hook = getattr(sys, "__interactivehook__", None)
if interactive_hook is not None: # pragma: no branch
sys.audit("cpython.run_interactivehook", interactive_hook)
interactive_hook()
for l in lines:
c.push(l)
c.interact('', '')
try:
from _pyrepl.main import CAN_USE_PYREPL
if CAN_USE_PYREPL: # pragma: no cover
from _pyrepl.simple_interact import \
run_multiline_interactive_console
run_multiline_interactive_console(c)
else:
raise ImportError
except Exception: # pragma: no cover
c.interact('', '')
if __name__ == '__main__':
+56 -10
View File
@@ -1,5 +1,6 @@
import ast
import io
import re
import tokenize
@@ -27,18 +28,63 @@ class IntegerDivisionWrapper(ast.NodeTransformer):
return self.generic_visit(node)
class _WrapFloats(ast.NodeTransformer):
"""Wrap float literals by calls to specified type."""
def __init__(self, lines, type):
super().__init__()
self.lines = lines
self.type = type
def visit_Constant(self, node):
if isinstance(node.value, (float, complex)):
line = self.lines[node.lineno - 1]
value = line[node.col_offset:node.end_col_offset]
is_complex = value.endswith(('j', 'J'))
if is_complex:
value = value[:-1]
value = ast.Constant(value)
value = ast.Call(ast.Name(self.type, ast.Load()), [value], [])
if is_complex:
value = ast.BinOp(left=value, op=ast.Mult(),
right=ast.Constant(1j))
return value
return node
def wrap_float_literals(lines):
"""Wraps all float/complex literals with mpmath classes."""
source = ''.join(lines)
tree = ast.parse(source)
tree = _WrapFloats(lines, 'mpf').visit(tree)
ast.fix_missing_locations(tree)
source = ast.unparse(tree)
return source.splitlines(keepends=True)
_HEXFLT_MATCHER = re.compile(r"""
(?: [^"' ]|^)[ ]*(?P<hexflt>
0x
[0-9a-z]+
(?: \.[0-9a-z]*)?
p(?:[+-])?[0-9]+
)
""", re.VERBOSE | re.IGNORECASE)
_BINFLT_MATCHER = re.compile(r"""
(?: [^"' ]|^)[ ]*(?P<binflt>
0b
[01]+
(?: \.[01]*)?
p(?:[+-])?[0-9]+
)
""", re.VERBOSE | re.IGNORECASE)
def wrap_hexbinfloats(lines):
new_lines = []
for line in lines:
result = []
g = tokenize.tokenize(io.BytesIO(line.encode()).readline)
for toknum, tokval, _, _, _ in g:
if toknum == tokenize.NUMBER:
if 'j' in tokval:
tokval = f"mpc(0, mpf('{tokval[:-1]}'))"
elif '.' in tokval:
tokval = f"mpf('{tokval}')"
result.append((toknum, tokval))
new_lines.append(tokenize.untokenize(result).decode())
for r in _HEXFLT_MATCHER.findall(line):
line = line.replace(r, 'mpf("' + r + '", base=16)')
for r in _BINFLT_MATCHER.findall(line):
line = line.replace(r, 'mpf("' + r + '", base=2)')
new_lines.append(line)
return new_lines
+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:
+47 -28
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:
@@ -324,17 +325,21 @@ def diffs_prod(ctx, factors):
>>> f = lambda x: exp(x)*cos(x)*sin(x)
>>> u = diffs(f, 1)
>>> v = mp.diffs_prod([diffs(exp,1), diffs(cos,1), diffs(sin,1)])
>>> next(u); next(v)
>>> next(u)
1.23586333600241
>>> next(v)
1.23586333600241
>>> next(u); next(v)
>>> next(u)
0.104658952245596
>>> next(v)
0.104658952245596
>>> next(u); next(v)
>>> next(u)
-5.96999877552086
>>> next(v)
-5.96999877552086
>>> next(u); next(v)
>>> next(u)
-12.4632923122697
>>> next(v)
-12.4632923122697
"""
@@ -419,17 +424,21 @@ def diffs_exp(ctx, fdiffs):
...
>>> u = diffs_exp(diffs_loggamma(3))
>>> v = diffs(gamma, 3)
>>> next(u); next(v)
>>> next(u)
2.0
>>> next(v)
2.0
>>> next(u); next(v)
>>> next(u)
1.84556867019693
>>> next(v)
1.84556867019693
>>> next(u); next(v)
>>> next(u)
2.49292999190269
>>> next(v)
2.49292999190269
>>> next(u); next(v)
>>> next(u)
3.44996501352367
>>> next(v)
3.44996501352367
"""
@@ -478,7 +487,9 @@ def differint(ctx, f, x, n=1, x0=0):
>>> from mpmath import (mp, mpf, differint, gamma, inf, exp, pi,
... j, lower_gamma)
>>> mp.pretty = True
>>> x = mpf(3); p = 2; n = 0.5
>>> x = mpf(3)
>>> p = 2
>>> n = 0.5
>>> differint(lambda t: t**p, x, n)
7.81764019044672
>>> gamma(p+1)/gamma(p-n+1) * x**(p-n)
@@ -519,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)`::
@@ -537,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):
@@ -544,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.
@@ -564,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]
@@ -608,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
+56 -37
View File
@@ -1,3 +1,5 @@
import itertools
from .calculus import defun
@@ -47,7 +49,8 @@ def richardson(ctx, seq):
Applying Richardson extrapolation to the Leibniz series for `\pi`::
>>> from mpmath import mp, mpf, richardson, nprint, pi
>>> mp.dps = 30; mp.pretty = True
>>> mp.dps = 30
>>> mp.pretty = True
>>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m))
... for m in range(1,30)]
>>> v, c = richardson(S[:10])
@@ -210,7 +213,6 @@ def shanks(ctx, seq, table=None, randomized=False):
**References**
1. [GravesMorris]_
2. [BenderOrszag]_ pp. 368-375
"""
@@ -477,12 +479,9 @@ class levin_class:
References:
[1] E.J. Weniger - "Nonlinear Sequence Transformations for the Acceleration of
Convergence and the Summation of Divergent Series" arXiv:math/0306302
[2] A. Sidi - "Pratical Extrapolation Methods"
[3] H.H.H. Homeier - "Scalar Levin-Type Sequence Transformations" arXiv:math/0005209
1. [Weniger]_
2. [Sidi]_
3. [Homeier]_
"""
@@ -907,7 +906,8 @@ def sumap(ctx, f, interval, integral=None, error=False):
>>> from mpmath import (mp, sumap, zeta, inf, chop, expint, log,
... polylog)
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> sumap(lambda k: 1/k**2.5, [1,inf])
1.34148725725091717975677
>>> zeta(2.5)
@@ -921,7 +921,8 @@ def sumap(ctx, f, interval, integral=None, error=False):
line is likely to give poor results, so it is better to evaluate
the first term symbolically whenever possible:
>>> n=3; z=-0.75
>>> n=3
>>> z=-0.75
>>> I = expint(n,-log(z))
>>> chop(sumap(lambda k: z**k / k**n, [1,inf], integral=I))
-0.6917036036904594510141448
@@ -1006,7 +1007,8 @@ def sumem(ctx, f, interval, tol=None, reject=10, integral=None,
integral and derivative values (the second should be much faster)::
>>> from mpmath import mp, sumem, inf, fac, mpf
>>> mp.dps = 50; mp.pretty = True
>>> mp.dps = 50
>>> mp.pretty = True
>>> sumem(lambda n: 1/n**2, [32, inf])
0.03174336652030209012658168043874142714132886413417
>>> I = mpf(1)/32
@@ -1082,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)))
@@ -1134,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
@@ -1236,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
@@ -1684,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()
@@ -1744,15 +1754,6 @@ def standardize(ctx, f, intervals, options):
return f(*args)
return True, g
# backwards compatible itertools.product
def cartesian_product(args):
pools = map(tuple, args)
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
def fold_finite(ctx, f, intervals):
if not intervals:
return f
@@ -1762,7 +1763,7 @@ def fold_finite(ctx, f, intervals):
def g(*args):
args = list(args)
s = ctx.zero
for xs in cartesian_product(ranges):
for xs in itertools.product(*ranges):
for dim, x in zip(indices, xs):
args[dim] = ctx.mpf(x)
s += f(*args)
@@ -1825,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
@@ -1856,7 +1861,8 @@ def nprod(ctx, f, interval, nsum=False, **kwargs):
>>> from mpmath import (mp, nprod, inf, csch, cosh, exp, pi, sinh,
... sqrt, exp, euler, cos, tanh, log, jtheta)
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> nprod(lambda k: k, [1, 4])
24.0
@@ -1878,12 +1884,14 @@ def nprod(ctx, f, interval, nsum=False, **kwargs):
Next, several more infinite products with more complicated
values::
>>> nprod(lambda k: exp(1/k**2), [1, inf]); exp(pi**2/6)
>>> nprod(lambda k: exp(1/k**2), [1, inf])
5.180668317897115748416626
>>> exp(pi**2/6)
5.180668317897115748416626
>>> nprod(lambda k: (k**2-1)/(k**2+1), [2, inf]); pi*csch(pi)
>>> nprod(lambda k: (k**2-1)/(k**2+1), [2, inf])
0.2720290549821331629502366
>>> pi*csch(pi)
0.2720290549821331629502366
>>> nprod(lambda k: (k**4-1)/(k**4+1), [2, inf])
@@ -1896,8 +1904,9 @@ def nprod(ctx, f, interval, nsum=False, **kwargs):
>>> 3*sqrt(2)*cosh(pi*sqrt(3)/2)**2*csch(pi*sqrt(2))/pi
1.848936182858244485224927
>>> nprod(lambda k: (1-1/k**4), [2, inf]); sinh(pi)/(4*pi)
>>> nprod(lambda k: (1-1/k**4), [2, inf])
0.9190194775937444301739244
>>> sinh(pi)/(4*pi)
0.9190194775937444301739244
>>> nprod(lambda k: (1-1/k**6), [2, inf])
@@ -1905,8 +1914,9 @@ def nprod(ctx, f, interval, nsum=False, **kwargs):
>>> (1+cosh(pi*sqrt(3)))/(12*pi**2)
0.9826842777421925183244759
>>> nprod(lambda k: (1+1/k**2), [2, inf]); sinh(pi)/(2*pi)
>>> nprod(lambda k: (1+1/k**2), [2, inf])
1.838038955187488860347849
>>> sinh(pi)/(2*pi)
1.838038955187488860347849
>>> nprod(lambda n: (1+1/n)**n * exp(1/(2*n)-1), [1, inf])
@@ -1955,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:
@@ -1991,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
@@ -2036,7 +2053,8 @@ def limit(ctx, f, x, direction=1, exp=False, **kwargs):
>>> from mpmath import (limit, mp, sin, inf, exp, fac, sqrt, pi, e,
... mpf, log, euler)
>>> mp.dps = 30; mp.pretty = True
>>> mp.dps = 30
>>> mp.pretty = True
>>> limit(lambda x: (x-sin(x))/x**3, 0)
0.166666666666666666666666666667
@@ -2101,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]
+84 -76
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)
@@ -172,13 +176,9 @@ class FixedTalbot(InverseLaplaceTransform):
**References**
1. 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
2. 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
1. [Abate]_
2. [Talbot]_
"""
# required
@@ -219,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
@@ -253,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)
@@ -325,10 +325,8 @@ class Stehfest(InverseLaplaceTransform):
**References**
1. Widder, D. (1941). *The Laplace Transform*. Princeton.
2. 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
1. [Widder]_
2. [Stehfest]_
"""
@@ -353,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.
@@ -391,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)
@@ -410,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
@@ -423,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)
@@ -456,12 +463,8 @@ class deHoog(InverseLaplaceTransform):
**References**
1. Davies, B. (2005). *Integral Transforms and their
Applications*, Third Edition. Springer.
2. 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
1. [Davies]_
2. [Hoog]_
"""
@@ -541,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.
@@ -577,15 +580,13 @@ class Cohen(InverseLaplaceTransform):
**References**
1. 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
1. [Glasserman]_
"""
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)
@@ -602,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
@@ -671,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.
@@ -718,7 +722,8 @@ class LaplaceTransformInversionMethods:
The methods also work for higher precision:
>>> mp.dps = 100; mp.pretty = True
>>> mp.dps = 100
>>> mp.pretty = True
>>> nstr(ft(tt[0]),15),nstr(ft(tt[0])-invertlaplace(fp,tt[0],method='talbot'),15)
('0.000999000499833375', '-4.96868310693356e-105')
>>> nstr(ft(tt[1]),15),nstr(ft(tt[1])-invertlaplace(fp,tt[1],method='talbot'),15)
@@ -732,7 +737,8 @@ class LaplaceTransformInversionMethods:
f(t) = \mathrm{J}_0(t)
>>> mp.dps = 15; mp.pretty = True
>>> mp.dps = 15
>>> mp.pretty = True
>>> fp = lambda p: 1/sqrt(p*p + 1)
>>> ft = lambda t: besselj(0,t)
>>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0],method='dehoog')
@@ -748,7 +754,8 @@ class LaplaceTransformInversionMethods:
f(t) = -\gamma -\log t
>>> mp.dps = 15; mp.pretty = True
>>> mp.dps = 15
>>> mp.pretty = True
>>> fp = lambda p: log(p)/p
>>> ft = lambda t: -euler-log(t)
>>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0],method='stehfest')
@@ -881,7 +888,8 @@ class LaplaceTransformInversionMethods:
f(t)=\frac{1}{3}\sinh 3t
>>> mp.dps = 15; mp.pretty = True
>>> mp.dps = 15
>>> mp.pretty = True
>>> fp = lambda p: 1/(p*p-9)
>>> ft = lambda t: sinh(3*t)/3
>>> tt = [0.01,0.1,1.0,10.0]
@@ -897,31 +905,19 @@ class LaplaceTransformInversionMethods:
**References**
1. [DLMF]_ section 1.14 (http://dlmf.nist.gov/1.14T4)
2. Cohen, A.M. (2007). Numerical Methods for Laplace Transform
Inversion, Springer.
3. Duffy, D.G. (1998). Advanced Engineering Mathematics, CRC Press.
2. [Cohen]_
3. [Duffy98]_
**Numerical Inverse Laplace Transform Reviews**
1. Bellman, R., R.E. Kalaba, J.A. Lockett (1966). *Numerical
inversion of the Laplace transform: Applications to Biology,
Economics, Engineering, and Physics*. Elsevier.
2. 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
3. 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
4. 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
1. [Bellman]_
2. [Davies79]_
3. [Duffy93]_
4. [Kuhlman]_
"""
rule = kwargs.get('method', 'cohen')
rule = method
if type(rule) is str:
lrule = rule.lower()
if lrule == 'talbot':
@@ -937,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)
@@ -950,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:
+256 -35
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)
@@ -446,7 +451,7 @@ def Anderson(*args, **kwargs):
1d-solver generating pairs of approximative root and error.
Uses Anderson-Bjoerk method to find a root of f in [a, b].
Wrapper for illinois to use method='pegasus'.
Wrapper for illinois to use method='anderson'.
"""
kwargs['method'] = 'anderson'
return Illinois(*args, **kwargs)
@@ -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.
@@ -751,7 +962,8 @@ def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
>>> from mpmath import (diff, gamma, findroot, sin, zeta, exp, log,
... lambertw, mp, j)
>>> mp.dps = 30; mp.pretty = True
>>> mp.dps = 30
>>> mp.pretty = True
>>> findroot(sin, 3)
3.14159265358979323846264338328
@@ -788,11 +1000,13 @@ def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
... return findroot(lambda w: w*exp(w) - x, log(1+x))
...
>>> mp.dps = 15
>>> lambert(1); lambertw(1)
>>> lambert(1)
0.567143290409784
>>> lambertw(1)
0.567143290409784
>>> lambert(1000); lambert(1000)
>>> lambert(1000)
5.2496028524016
>>> lambertw(1000)
5.2496028524016
Multidimensional functions are also supported::
@@ -824,7 +1038,7 @@ def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True,
converge slowly. Consider this example::
>>> f = lambda x: (x - 1)**99
>>> findroot(f, 0.9, verify=False)
>>> findroot(f, 0.9)
0.918073542444929
Even for a very close starting point the secant method converges very
@@ -876,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::
@@ -901,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:
@@ -935,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:
+12 -27
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
@@ -164,7 +156,7 @@ def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10,
**References**
1. http://en.wikipedia.org/wiki/Durand-Kerner_method
1. [Wikipedia]_ https://en.wikipedia.org/wiki/Durand-Kerner_method
"""
if len(coeffs) <= 1:
@@ -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:
+27 -20
View File
@@ -282,7 +282,7 @@ class TanhSinh(QuadratureRule):
**References**
* [Bailey]_
* http://users.cs.dal.ca/~jborwein/tanh-sinh.pdf
* [BorweinTanhSinh]_
"""
@@ -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::
@@ -682,11 +683,13 @@ class QuadratureMethods:
`\sin(x)` accurately over an interval of length 100 but not over
length 1000::
>>> quad(sin, [0, 100]); 1-cos(100) # Good
>>> quad(sin, [0, 100]) # Good
0.137681127712316
>>> 1-cos(100)
0.137681127712316
>>> quad(sin, [0, 1000]); 1-cos(1000) # Bad
>>> quad(sin, [0, 1000]) # Bad
-37.8587612408485
>>> 1-cos(1000)
0.437620923709297
One solution is to break the integration into 10 intervals of
@@ -715,10 +718,10 @@ class QuadratureMethods:
**References**
1. http://mathworld.wolfram.com/DoubleIntegral.html
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
@@ -728,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
@@ -754,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
@@ -779,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
@@ -803,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"""
@@ -1003,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
@@ -1070,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:
@@ -1081,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
@@ -1102,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
+20 -13
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
@@ -414,7 +420,8 @@ class StandardBaseContext(Context,
the principal value of `\exp(y \log(x))`::
>>> from mpmath import mp, power
>>> mp.dps = 30; mp.pretty = True
>>> mp.dps = 30
>>> mp.pretty = True
>>> power(2, 0.5)
1.41421356237309504880168872421
+11 -9
View File
@@ -87,9 +87,6 @@ class FPContext(StandardBaseContext):
absmin = absmax = abs
def is_special(ctx, x):
return x - x != 0.0
def isnan(ctx, x):
return x != x
@@ -102,9 +99,10 @@ class FPContext(StandardBaseContext):
return math.isfinite(x)
def isnormal(ctx, x):
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:
@@ -163,6 +161,7 @@ class FPContext(StandardBaseContext):
ldexp = math.ldexp
frexp = math.frexp
hypot = math.hypot
ulp = math.ulp
def mag(ctx, z):
if z:
@@ -190,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)
@@ -235,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):
+169 -117
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, 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,27 +33,30 @@ 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 multiprecision arithmetic with a global precision.
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, trap_complex=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)
ctx._set_rounding(rounding)
ctx.trap_complex = trap_complex
StandardBaseContext.__init__(ctx)
@@ -65,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
@@ -98,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_log, libmp.mpc_log)
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)
@@ -152,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))
@@ -161,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:
@@ -174,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:
@@ -183,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
@@ -211,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)
@@ -228,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)
@@ -341,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_'):
@@ -359,7 +370,10 @@ class MPContext(BaseMPContext, StandardBaseContext):
lines = ["Mpmath settings:",
(" mp.prec = %s" % ctx.prec).ljust(30) + f"[default: {sys.float_info.mant_dig}]",
(" 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)
@@ -461,14 +475,18 @@ class MPContext(BaseMPContext, StandardBaseContext):
cancellation, but is fooled by too severe cancellation::
>>> x = 1e-10
>>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x)
>>> exp(x)-1
1.00000008274037e-10
>>> expm1(x)
1.00000000005e-10
>>> autoprec(lambda t: exp(t)-1)(x)
1.00000000005e-10
>>> x = 1e-50
>>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x)
>>> exp(x)-1
0.0
>>> expm1(x)
1.0e-50
>>> autoprec(lambda t: exp(t)-1)(x)
0.0
With *catch*, an exception or list of exceptions to intercept
@@ -526,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
@@ -566,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):
@@ -618,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'
@@ -670,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))
@@ -767,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.
@@ -815,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))
@@ -823,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.
@@ -838,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)
@@ -882,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:
@@ -900,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.
@@ -947,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:
@@ -965,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.
@@ -1015,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:
@@ -1033,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.
@@ -1081,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)
@@ -1106,28 +1150,34 @@ maxterms, or set zeroprec."""
>>> from mpmath import nint_distance, mpf, mpc
>>> n, d = nint_distance(5)
>>> print(n); print(d)
>>> print(n)
5
>>> print(d)
-inf
>>> n, d = nint_distance(mpf(5))
>>> print(n); print(d)
>>> print(n)
5
>>> print(d)
-inf
>>> n, d = nint_distance(mpf(5.00000001))
>>> print(n); print(d)
>>> print(n)
5
>>> print(d)
-26
>>> n, d = nint_distance(mpf(4.99999999))
>>> print(n); print(d)
>>> print(n)
5
>>> print(d)
-26
>>> n, d = nint_distance(mpc(5,10))
>>> print(n); print(d)
>>> print(n)
5
>>> print(d)
4
>>> n, d = nint_distance(mpc(5,0.000001))
>>> print(n); print(d)
>>> print(n)
5
>>> print(d)
-19
"""
@@ -1149,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:
@@ -1160,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:
@@ -1230,12 +1280,14 @@ maxterms, or set zeroprec."""
>>> from mpmath import fraction, mpf, mp
>>> a = fraction(1,100)
>>> b = mpf(1)/100
>>> print(a); print(b)
>>> print(a)
0.01
>>> print(b)
0.01
>>> mp.dps = 30
>>> print(a); print(b) # a will be accurate
>>> print(a) # a will be accurate
0.01
>>> print(b)
0.0100000000000000002081668171172
"""
return ctx.constant(lambda prec, rnd: from_rational(p, q, prec, rnd),
@@ -1265,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
'''
@@ -1276,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
+399 -292
View File
File diff suppressed because it is too large Load Diff
+668 -300
View File
File diff suppressed because it is too large Load Diff
+349 -55
View File
@@ -12,12 +12,17 @@ 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:
n = ctx.convert(n)
n_isint = ctx._is_real_type(n) and ctx.isint(n)
n_isint = ctx.isint(n)
if n_isint:
n = int(ctx._re(n))
if n_isint and n < 0:
@@ -79,10 +84,15 @@ 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 ctx._is_real_type(n) and ctx.isint(n) and n < 0:
if n and ctx.isnpint(n):
return ctx.besseli(-n, z, derivative, **kwargs)
if not z:
if derivative:
@@ -117,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
@@ -153,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:
@@ -179,15 +199,157 @@ def besselk(ctx, n, z, derivative=0, **kwargs):
return ctx.hypercomb(h, [n], **kwargs)
@defun_wrapped
def hankel1(ctx,n,x,**kwargs):
return ctx.besselj(n,x,**kwargs) + ctx.j*ctx.bessely(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,derivative,**kwargs),
ctx.j*ctx.bessely(n,x,derivative,**kwargs)]
return ctx.sum_accurately(terms)
@defun_wrapped
def hankel2(ctx,n,x,**kwargs):
return ctx.besselj(n,x,**kwargs) - ctx.j*ctx.bessely(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,derivative,**kwargs),
-ctx.j*ctx.bessely(n,x,derivative,**kwargs)]
return ctx.sum_accurately(terms)
@defun
def spherical_jn(ctx, n, z):
r"""
Spherical Bessel function of the first kind.
This function is a solution to the spherical Bessel equation
.. 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 ::
j_\nu(z) = \sqrt{\frac{\pi}{2z}} J_{\nu + \frac{1}{2}}(z),
where `J_\nu(z)` is the Bessel function of the first kind.
**Examples**
>>> from mpmath import spherical_jn
>>> spherical_jn(0, 1)
mpf('0.8414709848078965')
"""
return ctx.besselj(n + ctx.one/2, z) / ctx.sqrt(2*z/ctx.pi)
@defun
def spherical_yn(ctx, n, z):
r"""
Spherical Bessel function of the second kind.
This function is another solution to the spherical Bessel equation, and
linearly independent from `j_n`. It can be defined as
.. math ::
j_\nu(z) = \sqrt{\frac{\pi}{2z}} Y_{\nu + \frac{1}{2}}(z),
where `Y_\nu(z)` is the Bessel function of the second kind.
**Examples**
>>> from mpmath import spherical_yn
>>> spherical_yn(0, 1)
mpf('-0.54030230586813977')
"""
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:
@@ -201,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:
@@ -215,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)
@@ -244,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/
@@ -253,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/
@@ -281,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)
@@ -301,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)
@@ -330,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/
@@ -343,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/
@@ -356,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/
@@ -372,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/
@@ -450,7 +672,12 @@ 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)
@@ -542,7 +769,12 @@ 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)
@@ -653,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:
@@ -666,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
@@ -697,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={}):
@@ -718,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
@@ -757,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
@@ -853,7 +1107,7 @@ def generalized_bisection(ctx,f,a,b,n):
N = N*2
def find_in_interval(ctx, f, ab):
return ctx.findroot(f, ab, solver='illinois', verify=False)
return ctx.findroot(f, ab, solver='illinois')
def bessel_zero(ctx, kind, prime, v, m, isoltol=0.01, _interval_cache={}):
prec = ctx.prec
@@ -937,18 +1191,25 @@ def besseljzero(ctx, v, m, derivative=0):
Initial zeros of the Bessel functions `J_0(z), J_1(z), J_2(z)`::
>>> from mpmath import mp, besseljzero, mpf, gamma, nprod, inf, besselj
>>> mp.dps = 25; mp.pretty = True
>>> besseljzero(0,1); besseljzero(0,2); besseljzero(0,3)
>>> mp.dps = 25
>>> mp.pretty = True
>>> besseljzero(0,1)
2.404825557695772768621632
>>> besseljzero(0,2)
5.520078110286310649596604
>>> besseljzero(0,3)
8.653727912911012216954199
>>> besseljzero(1,1); besseljzero(1,2); besseljzero(1,3)
>>> besseljzero(1,1)
3.831705970207512315614436
>>> besseljzero(1,2)
7.01558666981561875353705
>>> besseljzero(1,3)
10.17346813506272207718571
>>> besseljzero(2,1); besseljzero(2,2); besseljzero(2,3)
>>> besseljzero(2,1)
5.135622301840682556301402
>>> besseljzero(2,2)
8.417244140399864857783614
>>> besseljzero(2,3)
11.61984117214905942709415
Initial zeros of `J'_0(z), J'_1(z), J'_2(z)`::
@@ -956,28 +1217,38 @@ def besseljzero(ctx, v, m, derivative=0):
0.0
3.831705970207512315614436
7.01558666981561875353705
>>> besseljzero(1,1,1); besseljzero(1,2,1); besseljzero(1,3,1)
>>> besseljzero(1,1,1)
1.84118378134065930264363
>>> besseljzero(1,2,1)
5.331442773525032636884016
>>> besseljzero(1,3,1)
8.536316366346285834358961
>>> besseljzero(2,1,1); besseljzero(2,2,1); besseljzero(2,3,1)
>>> besseljzero(2,1,1)
3.054236928227140322755932
>>> besseljzero(2,2,1)
6.706133194158459146634394
>>> besseljzero(2,3,1)
9.969467823087595793179143
Zeros with large index::
>>> besseljzero(0,100); besseljzero(0,1000); besseljzero(0,10000)
>>> besseljzero(0,100)
313.3742660775278447196902
>>> besseljzero(0,1000)
3140.807295225078628895545
>>> besseljzero(0,10000)
31415.14114171350798533666
>>> besseljzero(5,100); besseljzero(5,1000); besseljzero(5,10000)
>>> besseljzero(5,100)
321.1893195676003157339222
>>> besseljzero(5,1000)
3148.657306813047523500494
>>> besseljzero(5,10000)
31422.9947255486291798943
>>> besseljzero(0,100,1); besseljzero(0,1000,1); besseljzero(0,10000,1)
>>> besseljzero(0,100,1)
311.8018681873704508125112
>>> besseljzero(0,1000,1)
3139.236339643802482833973
>>> besseljzero(0,10000,1)
31413.57032947022399485808
Zeros of functions with large order::
@@ -997,9 +1268,11 @@ def besseljzero(ctx, v, m, derivative=0):
Zeros of functions with fractional order::
>>> besseljzero(0.5,1); besseljzero(1.5,1); besseljzero(2.25,4)
>>> besseljzero(0.5,1)
3.141592653589793238462643
>>> besseljzero(1.5,1)
4.493409457909064175307881
>>> besseljzero(2.25,4)
15.15657692957458622921634
Both `J_{\nu}(z)` and `J'_{\nu}(z)` can be expressed as infinite
@@ -1044,48 +1317,67 @@ def besselyzero(ctx, v, m, derivative=0):
Initial zeros of the Bessel functions `Y_0(z), Y_1(z), Y_2(z)`::
>>> from mpmath import mp, besselyzero
>>> mp.dps = 25; mp.pretty = True
>>> besselyzero(0,1); besselyzero(0,2); besselyzero(0,3)
>>> mp.dps = 25
>>> mp.pretty = True
>>> besselyzero(0,1)
0.8935769662791675215848871
>>> besselyzero(0,2)
3.957678419314857868375677
>>> besselyzero(0,3)
7.086051060301772697623625
>>> besselyzero(1,1); besselyzero(1,2); besselyzero(1,3)
>>> besselyzero(1,1)
2.197141326031017035149034
>>> besselyzero(1,2)
5.429681040794135132772005
>>> besselyzero(1,3)
8.596005868331168926429606
>>> besselyzero(2,1); besselyzero(2,2); besselyzero(2,3)
>>> besselyzero(2,1)
3.384241767149593472701426
>>> besselyzero(2,2)
6.793807513268267538291167
>>> besselyzero(2,3)
10.02347797936003797850539
Initial zeros of `Y'_0(z), Y'_1(z), Y'_2(z)`::
>>> besselyzero(0,1,1); besselyzero(0,2,1); besselyzero(0,3,1)
>>> besselyzero(0,1,1)
2.197141326031017035149034
>>> besselyzero(0,2,1)
5.429681040794135132772005
>>> besselyzero(0,3,1)
8.596005868331168926429606
>>> besselyzero(1,1,1); besselyzero(1,2,1); besselyzero(1,3,1)
>>> besselyzero(1,1,1)
3.683022856585177699898967
>>> besselyzero(1,2,1)
6.941499953654175655751944
>>> besselyzero(1,3,1)
10.12340465543661307978775
>>> besselyzero(2,1,1); besselyzero(2,2,1); besselyzero(2,3,1)
>>> besselyzero(2,1,1)
5.002582931446063945200176
>>> besselyzero(2,2,1)
8.350724701413079526349714
>>> besselyzero(2,3,1)
11.57419546521764654624265
Zeros with large index::
>>> besselyzero(0,100); besselyzero(0,1000); besselyzero(0,10000)
>>> besselyzero(0,100)
311.8034717601871549333419
>>> besselyzero(0,1000)
3139.236498918198006794026
>>> besselyzero(0,10000)
31413.57034538691205229188
>>> besselyzero(5,100); besselyzero(5,1000); besselyzero(5,10000)
>>> besselyzero(5,100)
319.6183338562782156235062
>>> besselyzero(5,1000)
3147.086508524556404473186
>>> besselyzero(5,10000)
31421.42392920214673402828
>>> besselyzero(0,100,1); besselyzero(0,1000,1); besselyzero(0,10000,1)
>>> besselyzero(0,100,1)
313.3726705426359345050449
>>> besselyzero(0,1000,1)
3140.807136030340213610065
>>> besselyzero(0,10000,1)
31415.14112579761578220175
Zeros of functions with large order::
@@ -1105,9 +1397,11 @@ def besselyzero(ctx, v, m, derivative=0):
Zeros of functions with fractional order::
>>> besselyzero(0.5,1); besselyzero(1.5,1); besselyzero(2.25,4)
>>> besselyzero(0.5,1)
1.570796326794896619231322
>>> besselyzero(1.5,1)
2.798386045783887136720249
>>> besselyzero(2.25,4)
13.56721208770735123376018
"""
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:
+17 -13
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):
@@ -359,6 +354,14 @@ def log(ctx, x, b=None):
def log10(ctx, x):
return ctx.log(x, 10)
@defun
def log2(ctx, x):
return ctx.log(x, 2)
@defun
def exp2(ctx, x):
return ctx.power(2, x)
@defun
def fmod(ctx, x, y):
return ctx.convert(x) % ctx.convert(y)
@@ -619,7 +622,8 @@ def mangoldt(ctx, n):
**Examples**
>>> from mpmath import mp, mangoldt, fsum
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> [mangoldt(n) for n in range(-2,3)]
[0.0, 0.0, 0.0, 0.0, 0.6931471805599453094172321]
>>> mangoldt(6)
+228 -38
View File
@@ -1,5 +1,6 @@
from ..libmp.backend import MPQ
from .functions import defun, defun_wrapped
import math
def _check_need_perturb(ctx, terms, prec, discard_known_zeros):
perturb = recompute = False
@@ -48,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
@@ -56,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
@@ -192,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
@@ -204,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
@@ -231,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
@@ -315,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):
@@ -405,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:
@@ -428,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
@@ -441,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:
@@ -495,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
@@ -506,9 +576,7 @@ def _hypq1fq(ctx, p, q, a_s, b_s, z, **kwargs):
if absz > 1.1 or ispoly:
raise
# Use expansion at |z-1| -> 0.
# Reference: Wolfgang Buhring, "Generalized Hypergeometric Functions at
# Unit Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992),
# pp.145-153
# Reference: [Buhring]_
# The current implementation has several problems:
# 1. We only implement it for 3F2. The expansion coefficients are
# given by extremely messy nested sums in the higher degree cases
@@ -1003,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)
@@ -1023,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,8 +1138,93 @@ def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs):
return terms
return ctx.hypercomb(h, a+b, **kwargs)
@defun
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)
p = n + len(aAp)
m = len(bBm)
q = m + len(bBq)
aA = aAn+aAp
bB = bBm+bBq
a = [ctx.convert(a) for a, _ in aA]
b = [ctx.convert(b) for b, _ in bB]
A = [A for _, A in aA]
B = [B for _, B in bB]
z = ctx.convert(z)
r = ctx.convert(r)
A = [ctx._convert_param(Ai) for Ai in A]
B = [ctx._convert_param(Bj) for Bj in B]
if not all(Ai > 0 and (AiType == 'Z' or AiType == 'Q') for Ai, AiType in A + B):
raise NotImplementedError("All A and B must be positive rationals")
# Find L.C.M. of denominators
D = math.lcm(*[Ai.denominator if AiType == 'Q' else 1 for Ai, AiType in A + B])
# Convert rationals to integers using common denominator
A = [Ai.numerator * (D // Ai.denominator) if AiType == 'Q' else Ai * D for Ai, AiType in A]
B = [Bi.numerator * (D // Bi.denominator) if BiType == 'Q' else Bi * D for Bi, BiType in B]
r = r / D
prefactor = ctx.convert(D)
# Expand using Gauss Multiplication Formula
a_tilde = []
for ai, Ai in zip(a, A):
for k in range(Ai):
a_tilde.append((ai + k) / Ai)
b_tilde = []
for bj, Bj in zip(b, B):
for k in range(Bj):
b_tilde.append((bj + k) / Bj)
m_tilde = sum(B[:m])
n_tilde = sum(A[:n])
a_star = ctx.convert(sum(A[:n]) - sum(A[n:]) + sum(B[:m]) - sum(B[m:]))
c_star = m + n - (ctx.convert(p) + q) / 2
beta = ctx.one
for Ai in A:
beta /= Ai**Ai
for Bj in B:
beta *= Bj**Bj
# Compute M factor = prod(B_j^(b_j - 1/2)) / prod(A_i^(a_i - 1/2))
M = ctx.one
for bj, Bj in zip(b, B):
M *= Bj**(bj - ctx.one/2)
for ai, Ai in zip(a, A):
M /= Ai**(ai - ctx.one/2)
prefactor *= (2 * ctx.pi)**(c_star - a_star/2) * M
return prefactor * meijerg(
ctx,
[a_tilde[:n_tilde], a_tilde[n_tilde:]],
[b_tilde[:m_tilde], b_tilde[m_tilde:]],
z / (beta ** r),
r,
series=series,
**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):
@@ -1094,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:
@@ -1110,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
@@ -1188,7 +1367,8 @@ def hyper2d(ctx, a, b, x, y, **kwargs):
product of two Gaussian hypergeometric functions::
>>> from mpmath import mp, mpf, hyper2d, hyp2f1, exp
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> x, y = mpf(0.25), mpf(0.5)
>>> hyper2d({'m':1,'n':1}, {}, x,y)
2.666666666666666666666667
@@ -1209,7 +1389,8 @@ def hyper2d(ctx, a, b, x, y, **kwargs):
Six of the 34 Horn functions, G1-G3 and H1-H3::
>>> from mpmath import mp, hyper2d, nsum, fac, inf, rf
>>> mp.dps = 10; mp.pretty = True
>>> mp.dps = 10
>>> mp.pretty = True
>>> x, y = 0.0625, 0.125
>>> a1,a2,b1,b2,c1,c2,d = 1.1,-1.2,-1.3,-1.4,1.5,-1.6,1.7
>>> hyper2d({'m+n':a1,'n-m':b1,'m-n':b2},{},x,y) # G1
@@ -1250,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):
@@ -1353,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
@@ -1375,7 +1561,8 @@ def bihyper(ctx, a_s, b_s, z, **kwargs):
The value of `\,_2H_2` at `z = 1` is given by Dougall's formula::
>>> from mpmath import mp, bihyper, mpf, hyper, gammaprod
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> a,b,c,d = 0.5, 1.5, 2.25, 3.25
>>> bihyper([a,b],[c,d],1)
-14.49118026212345786148847
@@ -1400,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)
+103 -27
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`).
@@ -88,14 +95,19 @@ def pcfd(ctx, n, z, **kwargs):
**Examples**
>>> from mpmath import mp, pcfd, mpf, chop, diff, taylor
>>> mp.dps = 25; mp.pretty = True
>>> pcfd(0,0); pcfd(1,0); pcfd(2,0); pcfd(3,0)
>>> mp.dps = 25
>>> mp.pretty = True
>>> pcfd(0,0)
1.0
>>> pcfd(1,0)
0.0
>>> pcfd(2,0)
-1.0
>>> pcfd(3,0)
0.0
>>> pcfd(4,0); pcfd(-3,0)
>>> pcfd(4,0)
3.0
>>> pcfd(-3,0)
0.6266570686577501256039413
>>> pcfd('1/2', 2+3j)
(-5.363331161232920734849056 - 3.858877821790010714163487j)
@@ -116,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
@@ -146,7 +163,8 @@ def pcfu(ctx, a, z, **kwargs):
Connection to other functions::
>>> from mpmath import mp, mpf, pcfu, sqrt, pi, exp, erfc
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> z = mpf(3)
>>> pcfu(0.5,z)
0.03210358129311151450551963
@@ -162,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
@@ -180,7 +203,8 @@ def pcfv(ctx, a, z, **kwargs):
Wronskian relation between `U` and `V`::
>>> from mpmath import mp, pcfu, diff, pcfv, sqrt, pi, chop
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> a, z = 2, 3
>>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)
0.7978845608028653558798921
@@ -197,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)
@@ -239,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).
@@ -248,7 +277,8 @@ def pcfw(ctx, a, z, **kwargs):
Value at the origin::
>>> from mpmath import mp, mpf, pcfw, power, gamma, sqrt, diff
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> a = mpf(0.25)
>>> pcfw(a,0)
0.9722833245718180765617104
@@ -260,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():
@@ -269,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)
@@ -306,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
@@ -327,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),)
@@ -340,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)
@@ -349,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
@@ -364,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)
@@ -387,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)
@@ -441,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)
+20 -14
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)
@@ -27,7 +27,8 @@ def qp(ctx, a, q=None, n=None, **kwargs):
>>> from mpmath import (mp, qp, fprod, limit, rf, taylor, findroot,
... diffun, mpf, jtheta, pi, root)
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> qp(2,3,5)
-725305.0
>>> fprod(1-2*3**k for k in range(5))
@@ -97,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():
@@ -130,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
@@ -144,7 +146,8 @@ def qgamma(ctx, z, q, **kwargs):
Evaluation for real and complex arguments::
>>> from mpmath import mp, qgamma, mpf
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> qgamma(4,0.75)
4.046875
>>> qgamma(6,6)
@@ -165,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,
@@ -186,7 +189,8 @@ def qfac(ctx, z, q, **kwargs):
**Examples**
>>> from mpmath import mp, qfac
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> qfac(0,0)
1.0
>>> qfac(4,3)
@@ -199,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
@@ -226,7 +230,8 @@ def qhyper(ctx, a_s, b_s, q, z, **kwargs):
Evaluation works for real and complex arguments::
>>> from mpmath import qhyper, mp, nsum, qp, inf, j
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> qhyper([0.5], [2.25], 0.25, 4)
-0.1975849091263356009534385
>>> qhyper([0.5], [2.25], 0.25-0.25j, 4)
@@ -254,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
+89 -44
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:
@@ -458,6 +458,10 @@ def polylog_general(ctx, s, z):
return ctx.gamma(v)*(j**v*ctx.zeta(v,0.5+y) + j**-v*ctx.zeta(v,0.5-y))/(2*ctx.pi)**v
t = 1
k = 0
prec = ctx.prec
if ctx.isfinite(s):
ctx.prec += max(0, -ctx.nint_distance(s)[1])
while 1:
term = ctx.zeta(s-k) * t
if not abs(term) >= ctx.eps:
@@ -466,7 +470,10 @@ def polylog_general(ctx, s, z):
k += 1
t *= u
t /= k
return ctx.gamma(1-s)*(-u)**(s-1) + v
r = ctx.gamma(1-s)*(-u)**(s-1) + v
ctx.prec = prec
return r
@defun_wrapped
def polylog(ctx, s, z):
@@ -486,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)
@@ -517,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)
@@ -530,17 +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
method = kwargs.get('method')
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':
@@ -560,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")
@@ -579,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
@@ -617,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
@@ -865,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
@@ -920,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
@@ -1005,19 +1013,17 @@ def secondzeta(ctx, s, a = 0.015, **kwargs):
**References**
A. Voros, Zeta functions for the Riemann zeros, Ann. Institute Fourier,
53, (2003) 665--699.
* [Voros2003]_
* [Voros2009]_
A. Voros, Zeta functions over Zeros of Zeta Functions, Lecture Notes
of the Unione Matematica Italiana, Springer, 2009.
"""
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:
@@ -1034,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)
@@ -1043,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
@@ -1080,18 +1086,23 @@ def lerchphi(ctx, z, s, a):
>>> from mpmath import (mp, lerchphi, catalan, diff, zeta, pi, log,
... atanh, sqrt, j, polylog)
>>> mp.dps = 25; mp.pretty = True
>>> lerchphi(-1,2,0.5); 4*catalan
>>> mp.dps = 25
>>> mp.pretty = True
>>> lerchphi(-1,2,0.5)
3.663862376708876060218414
>>> 4*catalan
3.663862376708876060218414
>>> diff(lerchphi, (-1,-2,1), (0,1,0)); 7*zeta(3)/(4*pi**2)
>>> diff(lerchphi, (-1,-2,1), (0,1,0))
0.2131391994087528954617607
>>> 7*zeta(3)/(4*pi**2)
0.2131391994087528954617607
>>> lerchphi(-4,1,1); log(5)/4
>>> lerchphi(-4,1,1)
0.4023594781085250936501898
>>> log(5)/4
0.4023594781085250936501898
>>> lerchphi(-3+2j,1,0.5); 2*atanh(sqrt(-3+2j))/sqrt(-3+2j)
>>> lerchphi(-3+2j,1,0.5)
(1.142423447120257137774002 + 0.2118232380980201350495795j)
>>> 2*atanh(sqrt(-3+2j))/sqrt(-3+2j)
(1.142423447120257137774002 + 0.2118232380980201350495795j)
Evaluation works for complex arguments and `|z| \ge 1`::
@@ -1149,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
+21 -20
View File
@@ -121,7 +121,7 @@ def separate_zeros_in_block(ctx, zero_number_block, T, V, limitloop=None,
f = lambda x: ctx.rs_z(x,derivative=1)
t0=T[kMax-1]
t1 = T[kMax]
t=ctx.findroot(f, (t0,t1), solver ='illinois',verify=False, verbose=False)
t=ctx.findroot(f, (t0,t1), solver ='anderson')
v = ctx.siegelz(t)
if (t0<t) and (t<t1) and (v*V[kMax]<0):
T.insert(kMax,t)
@@ -159,7 +159,7 @@ def separate_my_zero(ctx, my_zero_number, zero_number_block, T, V, prec):
index +=1
precs = [precs[0] // 2 +3+2*index] + precs
ctx.prec = precs[0] + guard
r = ctx.findroot(lambda x:ctx.siegelz(x), (t0,t1), solver ='illinois', verbose=False)
r = ctx.findroot(ctx.siegelz, (t0,t1), solver ='anderson')
#print "first step at", ctx.dps, "digits"
z=ctx.mpc(0.5,r)
for prec in precs[1:]:
@@ -174,9 +174,9 @@ def sure_number_block(ctx, n):
"""The number of good Rosser blocks needed to apply
Turing method
References:
R. P. Brent, On the Zeros of the Riemann Zeta Function
in the Critical Strip, Math. Comp. 33 (1979) 1361--1372
T. Trudgian, Improvements to Turing Method, Math. Comp."""
* [Brent79]_
* [Trudgian]_
"""
if n < 9*10**5:
return(2)
g = ctx.grampoint(n-100)
@@ -350,7 +350,8 @@ def zetazero(ctx, n, info=False, round=True):
The first few zeros::
>>> from mpmath import mp, zetazero, chop, zeta, siegelz
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> zetazero(1)
(0.5 + 14.13472514173469379045725j)
>>> zetazero(2)
@@ -396,6 +397,15 @@ def zetazero(ctx, n, info=False, round=True):
of zeros in this interval. The numbers indicate the number of zeros
in each Gram interval (Rosser blocks between parenthesis). In this case
there is only one Rosser block of length nine.
**References**
* [Brent79]_
* [Trudgian]_
* [Brent86]_
* [Lune86]_
* [Lune84]_
"""
n = int(n)
if n < 0:
@@ -602,21 +612,10 @@ that are noted in the literature by some reason.
The list is composed from the data published in the references:
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.
See also Corrigenda in Math. Comp. 46 (1986) 771.
* [Brent86]_
* [Lune86]_
* [Lune84]_
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.
See also Corrigenda in Math. Comp. 46 (1986) 771.
J. van de Lune,
'Sums of Equal Powers of Positive Integers',
Dissertation,
Vrije Universiteit te Amsterdam, Centrum voor Wiskunde en Informatica,
Amsterdam, 1984.
Thanks to the authors all this papers and those others that have
contributed to make this possible.
@@ -894,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)',
@@ -943,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',
+26 -29
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**
@@ -40,9 +39,11 @@ def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False):
[22, 7]
>>> pslq([-1, pi], tol=0.001)
[355, 113]
>>> mpf(22)/7; mpf(355)/113; +pi
>>> mpf(22)/7
3.14285714285714
>>> mpf(355)/113
3.14159292035398
>>> +pi
3.14159265358979
Pi is not a rational number with denominator less than 1000::
@@ -54,8 +55,9 @@ def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False):
by at least one rational number with denominator less than `10^{12}`::
>>> p, q = pslq([-1, pi], maxcoeff=10**12)
>>> print(p); print(q)
>>> print(p)
238410049439
>>> print(q)
75888275702
>>> mpf(p)/q
3.14159265358979
@@ -310,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`.
@@ -338,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
@@ -356,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
@@ -382,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
@@ -394,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
@@ -407,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
@@ -423,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]
@@ -831,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_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_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)
+8 -2
View File
@@ -26,10 +26,16 @@ if 'MPMATH_NOGMPY' not in os.environ:
try:
import gmpy2 as gmpy
BACKEND = 'gmpy'
MPZ = gmpy.mpz
MPQ = gmpy.mpq
except ImportError:
pass
try:
import gmp as gmpy
BACKEND = 'gmp'
except ImportError:
pass
if gmpy:
MPZ = gmpy.mpz
MPZ_ZERO = MPZ(0)
MPZ_ONE = MPZ(1)
+74 -74
View File
@@ -15,16 +15,17 @@ This module implements gamma- and zeta-related functions:
import math
import sys
import threading
from .backend import MPZ, MPZ_ONE, MPZ_THREE, MPZ_ZERO
from .libelefun import (constant_memo, cos_sin_fixed, def_mpf_constant,
exp_fixed, ln2_fixed, ln_sqrt2pi_fixed, log_int_fixed,
mpf_cos_sin_pi, mpf_exp, mpf_ln2, mpf_log, mpf_pi,
mpf_cos_sin_pi, mpf_exp, mpf_ln, mpf_ln2, mpf_pi,
mpf_pow, mpf_sin_pi, mpf_sqrtpi, pi_fixed,
sqrtpi_fixed)
from .libintmath import ifac, ifac2, isqrt_fast, list_primes, lshift, moebius
from .libmpc import (mpc_abs, mpc_add, mpc_add_mpf, mpc_cos_pi, mpc_div,
mpc_div_mpf, mpc_exp, mpc_half, mpc_log, mpc_mpf_div,
mpc_div_mpf, mpc_exp, mpc_half, mpc_ln, mpc_mpf_div,
mpc_mul, mpc_mul_int, mpc_mul_mpf, mpc_neg, mpc_one,
mpc_pos, mpc_pow, mpc_pow_int, mpc_reciprocal, mpc_shift,
mpc_sin_pi, mpc_square, mpc_sub, mpc_sub_mpf, mpc_two,
@@ -34,10 +35,13 @@ 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)
local = threading.local()
# Catalan's constant is computed using Lupas's rapidly convergent series
# (listed on http://mathworld.wolfram.com/CatalansConstant.html)
# oo
@@ -164,7 +168,7 @@ def glaisher_fixed(prec):
#print k, N
s += log_int_fixed(k, wp) // k**2
logN = log_int_fixed(N, wp)
#logN = to_fixed(mpf_log(from_int(N), wp+20), wp)
#logN = to_fixed(mpf_ln(from_int(N), wp+20), wp)
# E-M step 2: integral of log(x)/x**2 from N to inf
s += (ONE + logN) // N
# E-M step 3: endpoint correction term f(N)/2
@@ -199,7 +203,7 @@ def glaisher_fixed(prec):
s *= 6
s = (s << wp) // (pi**2 >> wp)
s += euler_fixed(wp)
s += to_fixed(mpf_log(from_man_exp(2*pi, -wp), wp), wp)
s += to_fixed(mpf_ln(from_man_exp(2*pi, -wp), wp), wp)
s //= 12
A = mpf_exp(from_man_exp(s, -wp), wp)
return to_fixed(A, prec)
@@ -242,9 +246,8 @@ of two, 2**p, the logarithm becomes particularly easy to calculate.[1]
We use the formulation of Algorithm 3.9 in [2] to make the summation
more efficient.
Reference:
[1] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma
http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf
References:
[1] [Gourdon]_
[2] [BorweinBailey]_
"""
@@ -283,7 +286,7 @@ def mertens_fixed(prec):
t = mpf_zeta_int(m, wp)
if t == fone:
break
t = mpf_log(t, wp)
t = mpf_ln(t, wp)
t = mpf_mul_int(t, moebius(m), wp)
t = mpf_div(t, from_int(m), wp)
s = mpf_add(s, t)
@@ -366,7 +369,7 @@ can then be used to optionally find the exact value of the
numerator and denominator.
"""
bernoulli_cache = {}
bernoulli_cache = local.bernoulli_cache = {}
f3 = from_int(3)
f6 = from_int(6)
@@ -377,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:
@@ -421,7 +424,7 @@ def mpf_bernoulli(n, prec, rnd=round_fast, plus=False):
# Accurately estimate size of B_m so we can use
# fixed point math without using too much precision
szbm = bernoulli_size(m)
s = 0
s = MPZ(0)
sexp = max(0, szbm) - wp
if m < 6:
a = MPZ_ZERO
@@ -448,9 +451,9 @@ def mpf_bernoulli(n, prec, rnd=round_fast, plus=False):
if m > 6:
bin1 = bin1 * ((2+m)*(3+m)) // ((m-7)*(m-6))
state[:] = [m, bin, bin1]
return numbers[n]
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)
@@ -532,13 +535,11 @@ def bernfrac(n, plus=False):
**References**
1. MathWorld, von Staudt-Clausen Theorem:
http://mathworld.wolfram.com/vonStaudt-ClausenTheorem.html
1. [Weisstein]_ http://mathworld.wolfram.com/vonStaudt-ClausenTheorem.html
2. The Bernoulli Number Page:
http://www.bernoulli.org/
2. [Bernoulli]_
3. https://en.wikipedia.org/wiki/Bernoulli_number
3. [Wikipedia]_ https://en.wikipedia.org/wiki/Bernoulli_number
"""
n = int(n)
@@ -639,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.
@@ -663,7 +664,7 @@ def mpf_psi0(x, prec, rnd=round_fast):
return mpf_sub(p, q, prec, rnd)
# The logarithmic term is accurate enough
if (not sign) and bc + exp > wp:
return mpf_log(mpf_sub(x, fone, wp), prec, rnd)
return mpf_ln(mpf_sub(x, fone, wp), prec, rnd)
# Initial recurrence to obtain a large enough x
m = to_int(x)
n = int(0.11*wp) + 2
@@ -676,7 +677,7 @@ def mpf_psi0(x, prec, rnd=round_fast):
x += one
x -= one
# Logarithmic term
s += to_fixed(mpf_log(from_man_exp(x, -wp, wp), wp), wp)
s += to_fixed(mpf_ln(from_man_exp(x, -wp, wp), wp), wp)
# Endpoint term in Euler-Maclaurin expansion
s += (one << wp) // (2*x)
# Euler-Maclaurin remainder sum
@@ -698,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.
@@ -718,7 +719,7 @@ def mpc_psi0(z, prec, rnd=round_fast):
return mpc_sub(p, q, prec, rnd)
# Just the logarithmic term
if (not sign) and bc + exp > wp:
return mpc_log(mpc_sub(z, mpc_one, wp), prec, rnd)
return mpc_ln(mpc_sub(z, mpc_one, wp), prec, rnd)
# Initial recurrence to obtain a large enough z
w = to_int(re)
n = int(0.11*wp) + 2
@@ -729,7 +730,7 @@ def mpc_psi0(z, prec, rnd=round_fast):
z = mpc_add_mpf(z, fone, wp)
z = mpc_sub(z, mpc_one, wp)
# Logarithmic and endpoint term
s = mpc_add(s, mpc_log(z, wp), wp)
s = mpc_add(s, mpc_ln(z, wp), wp)
s = mpc_add(s, mpc_div(mpc_half, z, wp), wp)
# Euler-Maclaurin remainder sum
z2 = mpc_square(z, wp)
@@ -752,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.
@@ -866,13 +867,12 @@ So for p bits, we should choose n > (p + 2.28*|b|) / 2.54.
References:
-----------
Peter Borwein, "An Efficient Algorithm for the Riemann Zeta Function"
http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P117.ps
* [BorweinZeta]_
http://en.wikipedia.org/wiki/Dirichlet_eta_function
* [Wikipedia]_ http://en.wikipedia.org/wiki/Dirichlet_eta_function
"""
borwein_cache = {}
borwein_cache = local.borwein_cache = {}
def borwein_coefficients(n):
if n in borwein_cache:
@@ -889,9 +889,9 @@ def borwein_coefficients(n):
return ds
ZETA_INT_CACHE_MAX_PREC = 1000
zeta_int_cache = {}
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
@@ -1144,9 +1144,9 @@ def pow_fixed(x, n, wp):
return y
# TODO: optimize / cleanup interface / unify with list_primes
sieve_cache = []
primes_cache = []
mult_cache = []
sieve_cache = local.sieve_cache = []
primes_cache = local.primes_cache = []
mult_cache = local.mult_cache = []
def primesieve(n):
global sieve_cache, primes_cache, mult_cache
@@ -1352,8 +1352,8 @@ GAMMA_STIRLING_BETA = 0.2
SMALL_FACTORIAL_CACHE_SIZE = 150
gamma_taylor_cache = {}
gamma_stirling_cache = {}
gamma_taylor_cache = local.gamma_taylor_cache = {}
gamma_stirling_cache = local.gamma_stirling_cache = {}
small_factorial_cache = [from_int(ifac(n)) for \
n in range(SMALL_FACTORIAL_CACHE_SIZE+1)]
@@ -1518,7 +1518,7 @@ def gamma_fixed_taylor(xmpf, x, wp, prec, rnd, type):
if type == 2:
return mpf_shift(from_rational(p, (r<<wp), prec, rnd), wp)
if type == 3:
return mpf_log(mpf_abs(from_man_exp((r<<wp)//p, -wp)), prec, rnd)
return mpf_ln(mpf_abs(from_man_exp((r<<wp)//p, -wp)), prec, rnd)
else:
r = one
for i in range(-nearest_int):
@@ -1538,12 +1538,12 @@ def gamma_fixed_taylor(xmpf, x, wp, prec, rnd, type):
if type == 2:
return mpf_pos(r, prec, rnd)
if type == 3:
return mpf_log(mpf_abs(mpf_div(fone, r, wp)), prec, rnd)
return mpf_ln(mpf_abs(mpf_div(fone, r, wp)), prec, rnd)
else:
r = from_man_exp(x*p*r,-3*wp)
if type == 0: return mpf_div(fone, r, prec, rnd)
if type == 2: return mpf_pos(r, prec, rnd)
if type == 3: return mpf_neg(mpf_log(mpf_abs(r), prec, rnd))
if type == 3: return mpf_neg(mpf_ln(mpf_abs(r), prec, rnd))
def stirling_coefficient(n):
if n in gamma_stirling_cache:
@@ -1678,13 +1678,13 @@ 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:
type = 0 -- G(x) [standard gamma function]
type = 1 -- G(x+1) = x*G(x+1) = x! [factorial]
type = 1 -- G(x+1) = x*G(x) = x! [factorial]
type = 2 -- 1/G(x) [reciprocal gamma function]
type = 3 -- log(|G(x)|) [log-gamma function, real part]
"""
@@ -1707,7 +1707,7 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
if type == 3:
wp = prec+20
if exp+bc > wp and not sign:
return mpf_sub(mpf_mul(x, mpf_log(x, wp), wp), x, prec, rnd)
return mpf_sub(mpf_mul(x, mpf_ln(x, wp), wp), x, prec, rnd)
# We strongly want to special-case small integers
is_integer = exp >= 0
@@ -1727,7 +1727,7 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
if type == 2:
return mpf_div(fone, small_factorial_cache[n-1], prec, rnd)
if type == 3:
return mpf_log(small_factorial_cache[n-1], prec, rnd)
return mpf_ln(small_factorial_cache[n-1], prec, rnd)
else:
# floor(abs(x))
n = int(man >> (-exp))
@@ -1748,7 +1748,7 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
return mpf_sub(mpf_div(fone,x, wp),mpf_shift(fone,-wp),prec,rnd)
if type == 1: return mpf_sub(fone, x, prec, rnd)
if type == 2: return mpf_add(x, mpf_shift(fone,mag-wp), prec, rnd)
if type == 3: return mpf_neg(mpf_log(mpf_abs(x), prec, rnd))
if type == 3: return mpf_neg(mpf_ln(mpf_abs(x), prec, rnd))
# From now on, we assume having a gamma function
if type == 1:
@@ -1765,7 +1765,7 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
if type == 2:
return from_rational(MPZ_ONE, ifac(n-1), prec, rnd)
if type == 3:
return mpf_log(from_int(ifac(n-1)), prec, rnd)
return mpf_ln(from_int(ifac(n-1)), prec, rnd)
# half-integer
if n < 100 or gamma_size < 10*wp:
if sign:
@@ -1777,18 +1777,18 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
if type == 2:
return mpf_shift(from_rational(f, w, prec, rnd), wp-n-1)
if type == 3:
return mpf_log(mpf_shift(from_rational(w, abs(f),
return mpf_ln(mpf_shift(from_rational(w, abs(f),
prec, rnd), -wp+n+1), prec, rnd)
elif n == 0:
if type == 0: return mpf_sqrtpi(prec, rnd)
if type == 2: return mpf_div(fone, mpf_sqrtpi(wp), prec, rnd)
if type == 3: return mpf_log(mpf_sqrtpi(wp), prec, rnd)
if type == 3: return mpf_ln(mpf_sqrtpi(wp), prec, rnd)
else:
w = sqrtpi_fixed(wp)
w = from_man_exp(w * ifac2(2*n-1), -wp-n)
if type == 0: return mpf_pos(w, prec, rnd)
if type == 2: return mpf_div(fone, w, prec, rnd)
if type == 3: return mpf_log(mpf_abs(w), prec, rnd)
if type == 3: return mpf_ln(mpf_abs(w), prec, rnd)
# Convert to fixed point
offset = exp + wp
@@ -1844,7 +1844,7 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
# Asymptotic series
y = real_stirling_series(absxman, wp)
u = to_fixed(mpf_log(xabs, wp), wp)
u = to_fixed(mpf_ln(xabs, wp), wp)
u = ((absxman - (MPZ_ONE<<(wp-1))) * u) >> wp
y += u
w = from_man_exp(y, -wp)
@@ -1865,8 +1865,8 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
if type == 3:
if r:
B = mpf_mul(B, from_man_exp(r, -wp), wp)
A = mpf_add(mpf_log(mpf_abs(A), wp), w, wp)
return mpf_sub(mpf_log(mpf_abs(B), wp), A, prec, rnd)
A = mpf_add(mpf_ln(mpf_abs(A), wp), w, wp)
return mpf_sub(mpf_ln(mpf_abs(B), wp), A, prec, rnd)
else:
if type == 0:
if r:
@@ -1880,11 +1880,11 @@ def mpf_gamma(x, prec, rnd=round_fast, type=0):
return mpf_exp(mpf_neg(w), prec, rnd)
if type == 3:
if r:
return mpf_sub(w, mpf_log(from_man_exp(r,-wp), wp), prec, rnd)
return mpf_sub(w, mpf_ln(from_man_exp(r,-wp), wp), prec, rnd)
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
@@ -1920,7 +1920,7 @@ def mpc_gamma(z, prec, rnd=round_fast, type=0):
if type == 0: return mpc_reciprocal(v, prec, rnd)
if type == 1: return mpc_div(z, v, prec, rnd)
if type == 2: return mpc_pos(v, prec, rnd)
if type == 3: return mpc_log(mpc_reciprocal(v, prec), prec, rnd)
if type == 3: return mpc_ln(mpc_reciprocal(v, prec), prec, rnd)
elif type != 1:
wp += (-mag)
@@ -1928,7 +1928,7 @@ def mpc_gamma(z, prec, rnd=round_fast, type=0):
# a fixed-point value. TODO: determine a precise cutoff of validity
# depending on amag and bmag
if type == 3 and mag > wp and ((not asign) or (bmag >= amag)):
return mpc_sub(mpc_mul(z, mpc_log(z, wp), wp), z, prec, rnd)
return mpc_sub(mpc_mul(z, mpc_ln(z, wp), wp), z, prec, rnd)
# From now on, we assume having a gamma function
if type == 1:
@@ -2035,7 +2035,7 @@ def mpc_gamma(z, prec, rnd=round_fast, type=0):
yre, yim = complex_stirling_series(afix, bfix, wp)
# (z-1/2)*log(z) + S
lre, lim = mpc_log(z, wp)
lre, lim = mpc_ln(z, wp)
lre = to_fixed(lre, wp)
lim = to_fixed(lim, wp)
yre = ((lre*afix - lim*bfix)>>wp) - (lre>>1) + yre
@@ -2046,7 +2046,7 @@ def mpc_gamma(z, prec, rnd=round_fast, type=0):
# If re(z) > 0 and abs(z) <= 4, the branches of loggamma(z)
# and log(gamma(z)) coincide. Otherwise, use the zeroth order
# Stirling expansion to compute the correct imaginary part.
y = mpc_sub(y, mpc_log(r, wp), wp)
y = mpc_sub(y, mpc_ln(r, wp), wp)
zfa = to_float(zprered[0])
zfb = to_float(zprered[1])
zfabs = math.hypot(zfa,zfb)
@@ -2088,7 +2088,7 @@ def mpc_gamma(z, prec, rnd=round_fast, type=0):
else:
s1 = mpc_neg(y)
# s -= log(-z)
s1 = mpc_sub(s1, mpc_log(mpc_neg(zorig), wp), wp)
s1 = mpc_sub(s1, mpc_ln(mpc_neg(zorig), wp), wp)
# floor(re(z))
rezfloor = mpf_floor(zorig[0])
imzsign = mpf_sign(zorig[1])
@@ -2096,9 +2096,9 @@ def mpc_gamma(z, prec, rnd=round_fast, type=0):
t = mpf_mul(pi, rezfloor)
t = mpf_mul_int(t, imzsign, wp)
s1 = (s1[0], mpf_add(s1[1], t, wp))
s1 = mpc_add_mpf(s1, mpf_log(pi, wp), wp)
s1 = mpc_add_mpf(s1, mpf_ln(pi, wp), wp)
t = mpc_sin_pi(mpc_sub_mpf(zorig, rezfloor), wp)
t = mpc_log(t, wp)
t = mpc_ln(t, wp)
s1 = mpc_sub(s1, t, wp)
# Note: may actually be unused, because we fall back
# to the mpf_ function for real arguments
@@ -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)
+78 -49
View File
@@ -10,6 +10,8 @@ see libmpc and libmpi.
"""
import math
import threading
import warnings
from .backend import BACKEND, MPZ, MPZ_FIVE, MPZ_ONE, MPZ_TWO, MPZ_ZERO
from .libintmath import (giant_steps, ifib, isqrt_fast, lshift, rshift,
@@ -19,10 +21,13 @@ 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)
local = threading.local()
#-------------------------------------------------------------------------------
# Tuning parameters
#-------------------------------------------------------------------------------
@@ -43,21 +48,21 @@ if BACKEND == 'python':
else:
COS_SIN_CACHE_PREC = 200
COS_SIN_CACHE_STEP = 8
cos_sin_cache = {}
cos_sin_cache = local.cos_sin_cache = {}
# Number of integer logarithms to cache (for zeta sums)
MAX_LOG_INT_CACHE = 2000
log_int_cache = {}
log_int_cache = local.log_int_cache = {}
LOG_TAYLOR_PREC = 2500 # Use Taylor series with caching up to this prec
LOG_TAYLOR_SHIFT = 9 # Cache log values in steps of size 2^-N
log_taylor_cache = {}
log_taylor_cache = local.log_taylor_cache = {}
# prec/size ratio of x for fastest convergence in AGM formula
LOG_AGM_MAG_PREC_RATIO = 20
ATAN_TAYLOR_PREC = 3000 # Same as for log
ATAN_TAYLOR_SHIFT = 7 # steps of size 2^-N
atan_taylor_cache = {}
atan_taylor_cache = local.atan_taylor_cache = {}
# ~= next power of two + 20
@@ -72,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
@@ -79,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
@@ -101,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):
@@ -289,7 +294,7 @@ mpf_ln10 = def_mpf_constant(ln10_fixed)
def ln_sqrt2pi_fixed(prec):
wp = prec + 10
# ln(sqrt(2*pi)) = ln(2*pi)/2
return to_fixed(mpf_log(mpf_shift(mpf_pi(wp), 1), wp), prec-1)
return to_fixed(mpf_ln(mpf_shift(mpf_pi(wp), 1), wp), prec-1)
@constant_memo
def sqrtpi_fixed(prec):
@@ -305,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.
@@ -332,7 +337,7 @@ def mpf_pow(s, t, prec, rnd=round_fast):
return fone
# General formula: s**t = exp(t*log(s))
# TODO: handle rnd direction of the logarithm carefully
c = mpf_log(s, prec+10, rnd)
c = mpf_ln(s, prec+10, rnd)
return mpf_exp(mpf_mul(t, c), prec, rnd)
def int_pow_fixed(y, n, prec):
@@ -411,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)
@@ -494,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)
@@ -516,7 +521,7 @@ def log_int_fixed(n, prec, ln2=None):
return value >> (vprec - prec)
wp = prec + 10
assert wp > LOG_TAYLOR_SHIFT
v = to_fixed(mpf_log(from_int(n), wp+5), wp)
v = to_fixed(mpf_ln(from_int(n), wp+5), wp)
if n < MAX_LOG_INT_CACHE:
log_int_cache[n] = (v, wp)
return v >> (wp-prec)
@@ -651,7 +656,7 @@ def log_taylor_cached(x, prec):
s = (s0+s1) << 1
return log_a + s
def mpf_log(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.
@@ -694,9 +699,18 @@ def mpf_log(x, prec, rnd=round_fast):
return mpf_perturb(t, tsign, prec, rnd)
else:
wp += cancellation
# TODO: if close enough to 1, we could use Taylor series
# If close enough to 1, use Taylor series
# even in the AGM precision range, since the Taylor series
# converges rapidly
# converges rapidly.
# Taylor = AGM when O~(prec) = O~(prec^2/cancellation) where cancellation
# is greater than or equal to precision
wpb = wp.bit_length()
if wpb <= cancellation: # possibly include constant (big integer operations)
a = to_fixed(x, wp)
s = log_taylor(a, wp)
return from_man_exp(s, -wp, prec, rnd)
#------------------------------------------------------------------
# Another special case:
# n*log(2) is a good enough approximation
@@ -723,6 +737,21 @@ def mpf_log(x, prec, rnd=round_fast):
m -= n*ln2_fixed(wp)
return from_man_exp(m, -wp, prec, rnd)
mpf_log = mpf_ln # deprecated alias
def mpf_log1p(x, prec, rnd=round_down):
"""
Computes log(1+x) accurately.
"""
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):
"""
Computes log(sqrt(a^2+b^2)) accurately.
@@ -743,7 +772,7 @@ def mpf_log_hypot(a, b, prec, rnd):
# only a is inf/nan/0
if a == fzero:
# log(sqrt(0+b^2)) = log(|b|)
return mpf_log(mpf_abs(b), prec, rnd)
return mpf_ln(mpf_abs(b), prec, rnd)
if a == fnan:
return fnan
return finf
@@ -760,7 +789,7 @@ def mpf_log_hypot(a, b, prec, rnd):
# and the other is tiny...)
if cancelled == fzero or mag_cancelled < -extra//2:
h2 = mpf_add(a2, b2, prec+extra-min(a2[2],b2[2]))
return mpf_shift(mpf_log(h2, prec, rnd), -1)
return mpf_shift(mpf_ln(h2, prec, rnd), -1)
#----------------------------------------------------------------------
@@ -825,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
@@ -860,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:
@@ -905,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")
@@ -916,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:
@@ -930,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
@@ -943,19 +972,19 @@ def mpf_asinh(x, prec, rnd=round_fast):
q = mpf_sqrt(mpf_add(mpf_mul(x, x), fone, wp), wp)
q = mpf_add(mpf_abs(x), q, wp)
if sign:
return mpf_neg(mpf_log(q, prec, negative_rnd[rnd]))
return mpf_neg(mpf_ln(q, prec, negative_rnd[rnd]))
else:
return mpf_log(q, prec, rnd)
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:
raise ComplexResult("acosh(x) is real only for x >= 1")
q = mpf_sqrt(mpf_add(mpf_mul(x,x), fnone, wp), wp)
return mpf_log(mpf_add(x, q, wp), prec, rnd)
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:
@@ -974,9 +1003,9 @@ def mpf_atanh(x, prec, rnd=round_fast):
wp += (-mag)
a = mpf_add(x, fone, wp)
b = mpf_sub(fone, x, wp)
return mpf_shift(mpf_log(mpf_div(a, b, wp), prec, rnd), -1)
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:
@@ -1144,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
@@ -1189,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:
@@ -1292,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)
@@ -1371,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
+36 -36
View File
@@ -7,20 +7,20 @@ cases are also provided.
import math
from .backend import MPZ_ONE, MPZ_ZERO
from .backend import MPZ, MPZ_ONE, MPZ_ZERO
from .gammazeta import euler_fixed, mpf_euler, mpf_gamma_int
from .libelefun import (agm_fixed, mpf_cos_sin, mpf_exp, mpf_log, mpf_pi,
from .libelefun import (agm_fixed, mpf_cos_sin, mpf_exp, mpf_ln, mpf_pi,
mpf_sin, mpf_sqrt, pi_fixed)
from .libintmath import ifac, sqrt_fixed
from .libmpc import (complex_int_pow, mpc_abs, mpc_add, mpc_add_mpf, mpc_div,
mpc_exp, mpc_is_infnan, mpc_log, mpc_mpf_div, mpc_mul,
mpc_exp, mpc_is_infnan, mpc_ln, mpc_mpf_div, mpc_mul,
mpc_neg, mpc_one, mpc_pos, mpc_shift, mpc_sqrt, mpc_sub,
mpc_zero)
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
@@ -480,7 +480,7 @@ def mpf_ei(x, prec, rnd=round_fast, e1=False):
u = to_fixed(x, wp)
v = ei_taylor(u, wp) + euler_fixed(wp)
t1 = from_man_exp(v,-wp)
t2 = mpf_log(xabs,wp)
t2 = mpf_ln(xabs,wp)
v = mpf_add(t1, t2, prec, rnd)
else:
if x == fzero: v = fninf
@@ -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
@@ -548,21 +548,21 @@ def mpc_ei(z, prec, rnd=round_fast, e1=False):
vre += euler_fixed(wp)
v = from_man_exp(vre,-wp), from_man_exp(vim,-wp)
if e1:
u = mpc_log(mpc_neg(z),wp)
u = mpc_ln(mpc_neg(z),wp)
else:
u = mpc_log(z,wp)
u = mpc_ln(z,wp)
v = mpc_add(v, u, prec, rnd)
if e1:
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
@@ -653,7 +653,7 @@ def mpf_expint(n, x, prec, rnd=round_fast, gamma=False):
for k in range(1,n-1):
facs[k] = facs[k-1] * k
facs = facs[::-1]
s = facs[0] << wp
s = MPZ(facs[0]) << wp
for k in range(1, n-1):
if k & 1:
s -= facs[k] * t
@@ -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.
@@ -761,7 +761,7 @@ def mpf_ci_si(x, prec, rnd=round_fast, which=2):
if which != 1:
y = mpf_euler(wp)
xabs = mpf_abs(x)
ci = mpf_add(y, mpf_log(xabs, wp), prec, rnd)
ci = mpf_add(y, mpf_ln(xabs, wp), prec, rnd)
return ci, si
# For huge x: Ci(x) ~ sin(x)/x, Si(x) ~ pi/2
elif mag > wp:
@@ -786,7 +786,7 @@ def mpf_ci_si(x, prec, rnd=round_fast, which=2):
if which != 1:
ci = mpf_ci_si_taylor(x, wp, 0)
ci = mpf_add(ci, mpf_euler(wp), wp)
ci = mpf_add(ci, mpf_log(mpf_abs(x), wp), prec, rnd)
ci = mpf_add(ci, mpf_ln(mpf_abs(x), wp), prec, rnd)
return ci, si
x = mpf_abs(x)
# Case 2: asymptotic series for x >> 1
@@ -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]
@@ -839,10 +839,10 @@ def mpc_ci(z, prec, rnd=round_fast):
wp = prec + 20
cre, cim = mpc_ci_si_taylor(re, im, wp, 0)
cre = mpf_add(cre, mpf_euler(wp), wp)
ci = mpc_add((cre, cim), mpc_log(z, wp), prec, rnd)
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, rounding=round_fast):
def mpf_besseljn(n, x, prec, rnd=round_down):
prec += 50
negate = n < 0 and n & 1
mag = x[2]+x[3]
@@ -903,9 +903,9 @@ def mpf_besseljn(n, x, prec, rounding=round_fast):
k += 1
if negate:
s = -s
return from_man_exp(s, -wp, prec, rounding)
return from_man_exp(s, -wp, prec, rnd)
def mpc_besseljn(n, z, prec, rounding=round_fast):
def mpc_besseljn(n, z, prec, rnd=round_down):
negate = n < 0 and n & 1
n = abs(n)
origprec = prec
@@ -937,11 +937,11 @@ def mpc_besseljn(n, z, prec, rounding=round_fast):
if negate:
sre = -sre
sim = -sim
re = from_man_exp(sre, -prec, origprec, rounding)
im = from_man_exp(sim, -prec, origprec, rounding)
re = from_man_exp(sre, -prec, origprec, rnd)
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:
+58 -46
View File
@@ -10,13 +10,9 @@ import math
import sys
from functools import lru_cache
from .backend import BACKEND, MPZ, MPZ_ONE, MPZ_ZERO, gmpy
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 ~=
@@ -57,29 +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]
return MPZ((n & (-n)).bit_length() - 1 if n else 0)
def bitcount(n):
"""Calculate bit size of abs(n)."""
return MPZ(n).bit_length()
if BACKEND == 'gmpy':
bitcount = gmpy.bit_length
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, ...
@@ -138,7 +118,7 @@ def numeral_gmpy(n, base=10, size=0, digits=stddigits):
# extremely large values to a string. The size limit may need to be
# adjusted on some platforms, but 1500000 works on Windows and Linux.
if size < 1500000:
return gmpy.digits(n, base)
return MPZ(n).digits(base)
# Divide in half
half = (size // 2) + (size & 1)
A, B = divmod(n, MPZ(base)**half)
@@ -146,10 +126,10 @@ def numeral_gmpy(n, base=10, size=0, digits=stddigits):
bd = numeral(B, base, half, digits).rjust(half, "0")
return ad + bd
if BACKEND == "gmpy":
numeral = numeral_python
if gmpy:
numeral = numeral_gmpy
else:
numeral = numeral_python
_1_800 = 1<<800
_1_600 = 1<<600
@@ -257,7 +237,7 @@ def sqrt_fixed(x, prec):
sqrt_fixed2 = sqrt_fixed
if BACKEND == 'gmpy':
if gmpy:
isqrt_small = isqrt_fast = isqrt = gmpy.isqrt
sqrtrem = gmpy.isqrt_rem
else:
@@ -270,19 +250,9 @@ else:
sqrtrem = sqrtrem_python
_gcd2 = math.gcd
if sys.version_info >= (3, 9) and BACKEND == 'python':
gcd = math.gcd
elif BACKEND == 'gmpy':
gcd = math.gcd
if gmpy:
gcd = gmpy.gcd
else:
def gcd(*args):
res = MPZ_ZERO
for a in args:
a = MPZ(a)
if res != MPZ_ONE:
res = _gcd2(res, a)
return res
@lru_cache(maxsize=250)
@@ -306,6 +276,7 @@ def ifib(n):
p, q = p*p+qq, qq+2*p*q
n >>= 1
return b
ifib_python = ifib
MAX_FACTORIAL_CACHE = 1000
@@ -324,13 +295,15 @@ def ifac2(n, memo_pair=[{0:1}, {1:1}]):
if k <= MAX:
memo[k] = p
return p
ifac2_python = ifac2
ifac = math.factorial
if BACKEND == 'gmpy':
if gmpy:
ifac = gmpy.fac
ifac2 = gmpy.double_fac
ifib = gmpy.fib
else:
ifac = math.factorial
if hasattr(gmpy, 'double_fac'):
ifac2 = gmpy.double_fac
if hasattr(gmpy, 'fib'):
ifib = gmpy.fib
ifac = lru_cache(maxsize=1024)(ifac)
@@ -390,8 +363,9 @@ def isprime(n):
if not test(a):
return False
return True
isprime_python = isprime
if BACKEND == 'gmpy':
if gmpy and hasattr(gmpy, 'is_prime'):
isprime = gmpy.is_prime
def moebius(n):
@@ -509,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)
+72 -70
View File
@@ -8,16 +8,16 @@ from .backend import MPZ
from .libelefun import (mpf_acos, mpf_acosh, mpf_asin, mpf_atan, mpf_atan2,
mpf_cos, mpf_cos_pi, mpf_cos_sin, mpf_cos_sin_pi,
mpf_cosh, mpf_cosh_sinh, mpf_exp, mpf_fibonacci,
mpf_log, mpf_log_hypot, mpf_nthroot, mpf_phi, mpf_pi,
mpf_pow_int, mpf_sin, mpf_sin_pi, mpf_sinh, mpf_tan,
mpf_tanh)
mpf_ln, mpf_log1p, mpf_log_hypot, mpf_nthroot, mpf_phi,
mpf_pi, mpf_pow_int, mpf_sin, mpf_sin_pi, mpf_sinh,
mpf_tan, mpf_tanh)
from .libintmath import giant_steps, lshift, rshift
from .libmpf import (ComplexResult, fhalf, finf, fnan, fninf, fnone, fone,
from_float, from_int, from_man_exp, ftwo, fzero, mpf_abs,
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)
@@ -46,57 +46,57 @@ def mpc_is_infnan(z):
def mpc_to_str(z, dps, **kwargs):
re, im = z
rs = to_str(re, dps)
rs = to_str(re, dps, **kwargs)
if im[0]:
return rs + " - " + to_str(mpf_neg(im), dps, **kwargs) + "j"
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))
def mpc_hash(z):
re, im = z
h = mpf_hash(re) + sys.hash_info.imag * mpf_hash(im)
# Need to reduce either module 2^32 or 2^64
h = h % (2**sys.hash_info.width)
if h == -1:
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)
@@ -104,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.
@@ -150,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)
@@ -160,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
@@ -185,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
@@ -202,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
@@ -225,21 +225,21 @@ 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_log(z, prec+10), w, prec+10), 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)
if pexp == -1:
sqrtz = mpc_sqrt(z, prec+10)
return mpc_pow_int(sqrtz, (-1)**psign * pman, prec, rnd)
return mpc_exp(mpc_mul_mpf(mpc_log(z, prec+10), p, prec+10), 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
@@ -266,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
@@ -277,9 +277,9 @@ def mpc_pow_int(z, n, prec, rnd=round_fast):
re = from_man_exp(re, int(n*aexp), prec, rnd)
im = from_man_exp(im, int(n*bexp), prec, rnd)
return re, im
return mpc_exp(mpc_mul_int(mpc_log(z, prec+10), n, prec+10), prec, rnd)
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
@@ -356,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.
@@ -397,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.
@@ -430,12 +430,14 @@ def mpc_exp(z, prec, rnd=round_fast):
im = mpf_mul(mag, s, prec, rnd)
return re, im
def mpc_log(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_cos(z, prec, rnd=round_fast):
mpc_log = mpc_ln # deprecated alias
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.
@@ -455,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."""
@@ -471,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
@@ -498,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
@@ -512,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
@@ -526,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)
@@ -543,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)
@@ -561,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
@@ -587,8 +589,8 @@ def mpc_atan(z, prec, rnd=round_fast):
wp = prec + 15
x = mpf_add(fone, b, wp), mpf_neg(a)
y = mpf_sub(fone, b, wp), a
l1 = mpc_log(x, wp)
l2 = mpc_log(y, wp)
l1 = mpc_ln(x, wp)
l2 = mpc_ln(y, wp)
a, b = mpc_sub(l1, l2, prec, rnd)
# (I/2) * (a+b*I) = (-b/2 + a/2*I)
v = mpf_neg(mpf_shift(b,-1)), mpf_shift(a,-1)
@@ -719,11 +721,11 @@ def acos_asin(z, prec, rnd, n):
Am1 = mpf_shift(mpf_add(c1, c2, wp), -1)
# im = log(1 + Am1 + sqrt(Am1*(alpha+1)))
im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp)
im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp)
im = mpf_log1p(mpf_add(Am1, mpf_sqrt(im, wp), wp), wp)
else:
# im = log(alpha + sqrt(alpha*alpha - 1))
im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp)
im = mpf_log(mpf_add(alpha, im, wp), wp)
im = mpf_ln(mpf_add(alpha, im, wp), wp)
if asign:
if n == 0:
re = mpf_sub(mpf_pi(wp), re, wp)
@@ -764,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)
@@ -785,13 +787,13 @@ 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)
b = mpc_sub(mpc_one, z, wp)
a = mpc_log(a, wp)
b = mpc_log(b, wp)
a = mpc_ln(a, wp)
b = mpc_ln(b, wp)
v = mpc_shift(mpc_sub(a, b, wp), -1)
# Subtraction at infinity gives correct imaginary part but
# wrong real part (should be zero)
@@ -799,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)
@@ -814,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)
@@ -829,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)
+464 -410
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -5,7 +5,7 @@ Computational functions for interval arithmetic.
from .backend import MPZ_ONE
from .gammazeta import mpc_loggamma, mpf_gamma, mpf_loggamma, mpf_rgamma
from .libelefun import (mod_pi2, mpf_atan, mpf_atan2, mpf_cos_sin, mpf_exp,
mpf_log, mpf_pi, mpf_sqrt)
mpf_ln, mpf_pi, mpf_sqrt)
from .libmpf import (dps_to_prec, fhalf, finf, fnan, fninf, fnone, fone,
from_float, from_int, from_man_exp, from_str, fzero,
mpf_abs, mpf_add, mpf_div, mpf_ge, mpf_gt, mpf_le, mpf_lt,
@@ -273,8 +273,8 @@ def mpi_exp(s, prec):
def mpi_log(s, prec):
sa, sb = s
# log is monotonic
a = mpf_log(sa, prec, round_floor)
b = mpf_log(sb, prec, round_ceiling)
a = mpf_ln(sa, prec, round_floor)
b = mpf_ln(sb, prec, round_ceiling)
return a, b
def mpi_sqrt(s, prec):
-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)
+7 -2
View File
@@ -110,16 +110,17 @@ class MatrixCalculusMethods:
42.0927851137247
"""
A = ctx.matrix(A)
if method == 'pade':
prec = ctx.prec
try:
A = ctx.matrix(A)
ctx.prec += 2*A.rows
res = ctx._exp_pade(A)
finally:
ctx.prec = prec
return res
A = ctx.matrix(A)
prec = ctx.prec
j = int(max(1, ctx.mag(ctx.mnorm(A,'inf'))))
j += int(0.5*prec**0.5)
@@ -167,6 +168,7 @@ class MatrixCalculusMethods:
[(0.833730025131149 - 0.988897705762865j) (1.07485840848393 - 0.17192140544213j)]
[ 0.0 (1.54308063481524 + 0.0j)]
"""
A = ctx.matrix(A)
B = 0.5 * (ctx.expm(A*ctx.j) + ctx.expm(A*(-ctx.j)))
if not sum(A.apply(ctx.im).apply(abs)):
B = B.apply(ctx.re)
@@ -196,6 +198,7 @@ class MatrixCalculusMethods:
[(1.29845758141598 + 0.634963914784736j) (-1.96751511930922 + 0.314700021761367j)]
[ 0.0 (0.0 - 1.1752011936438j)]
"""
A = ctx.matrix(A)
B = (-0.5j) * (ctx.expm(A*ctx.j) - ctx.expm(A*(-ctx.j)))
if not sum(A.apply(ctx.im).apply(abs)):
B = B.apply(ctx.re)
@@ -430,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
+5 -5
View File
@@ -267,8 +267,8 @@ def qr_step(ctx, n0, n1, A, Q, shift):
eigenvalue of the bottemmost part of the submatrix A[n0:n1,n0:n1].
references:
Stoer, Bulirsch - Introduction to Numerical Analysis.
Kresser : Numerical Methods for General and Structured Eigenvalue Problems
[Stoer]_
[Kresser]_
"""
# implicitly shifted and bulge chasing is explained at p.398/399 in "Stoer, Bulirsch - Introduction to Numerical Analysis"
@@ -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]
+3 -4
View File
@@ -798,10 +798,9 @@ def gauss_quadrature(ctx, n, qtype = "legendre", alpha = 0, beta = 0):
0.0
references:
- golub and welsch, "calculations of gaussian quadrature rules", mathematics of
computation 23, p. 221-230 (1969)
- golub, "some modified matrix eigenvalue problems", siam review 15, p. 318-334 (1973)
- stroud and secrest, "gaussian quadrature formulas", prentice-hall (1966)
- [GolubWelsch]_
- [Golub]_
- [Stroud]_
See also the routine gaussq.f in netlog.org or ACM Transactions on
Mathematical Software algorithm 726.
+69 -24
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
@@ -426,7 +468,8 @@ class LinearAlgebraMethods:
>>> from mpmath import (mp, eye, hilbert, nprint, cholesky,
... chop, matrix)
>>> mp.dps = 25; mp.pretty = True
>>> mp.dps = 25
>>> mp.pretty = True
>>> A = eye(3) + hilbert(3)
>>> nprint(A)
[ 2.0 0.5 0.333333]
@@ -466,7 +509,7 @@ class LinearAlgebraMethods:
**References**
1. [Wikipedia]_ http://en.wikipedia.org/wiki/Cholesky_decomposition
1. [Wikipedia]_ https://en.wikipedia.org/wiki/Cholesky_decomposition
"""
if not isinstance(A, ctx.matrix):
@@ -493,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
@@ -509,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
@@ -538,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.
@@ -572,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
@@ -582,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
+15 -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
@@ -320,10 +311,14 @@ class _matrix:
elif isinstance(args[0], int):
# create empty matrix of given dimensions
if len(args) == 1:
if args[0] < 0:
raise ValueError("expected non-negative int")
self._rows = self._cols = args[0]
else:
if not isinstance(args[1], int):
raise TypeError("expected int")
if args[0] < 0 or args[1] < 0:
raise ValueError("expected non-negative int")
self._rows = args[0]
self._cols = args[1]
elif isinstance(args[0], _matrix):
@@ -825,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.
@@ -846,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.
@@ -870,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.
@@ -895,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
@@ -917,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.
@@ -933,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}")
+200 -35
View File
@@ -1,7 +1,11 @@
import collections
import decimal
import math
import operator
import random
import sys
import threading
from concurrent.futures import ThreadPoolExecutor
import pytest
from hypothesis import example, given, settings
@@ -10,11 +14,14 @@ 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, iv, monitor, mp, mpc, mpf,
mpi, nan, ninf, nint, nint_distance, nstr, pi, workprec)
from mpmath.libmp import (MPQ, finf, fnan, fninf, fnone, fone, from_float,
from_int, from_pickable, from_str, mpf_add, mpf_mul,
mpf_sub, round_down, round_nearest, round_up, to_int,
to_man_exp, to_pickable)
mpi, nan, ninf, nint, nint_distance, nstr, pi, rand,
workprec)
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():
@@ -118,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
@@ -149,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))
@@ -177,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
@@ -287,11 +300,11 @@ def test_arithmetic_functions():
assert fneg(z1) == -(+z1)
def test_exact_integer_arithmetic():
# XXX: re-fix this so that all operations are tested with all rounding modes
random.seed(0)
for prec in [6, 10, 25, 40, 100, 250, 725]:
for rounding in ['d', 'u', 'f', 'c', 'n']:
mp.dps = prec
mp.rounding = rounding
M = 10**(prec-2)
M2 = 10**(prec//2-2)
for i in range(10):
@@ -489,6 +502,20 @@ def test_isnan_etc():
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
@@ -539,6 +566,11 @@ def test_isnan_etc():
assert mp.isnpint(inf) is False
def test_isprime():
assert isprime(MPZ(2))
assert not isprime(MPZ(4))
def test_issue_438():
assert mpf(finf) == mpf('inf')
assert mpf(fninf) == mpf('-inf')
@@ -550,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():
"""
@@ -633,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)
@@ -648,3 +655,161 @@ def test_round_bulk(x, n):
return
assert nstr(mr, n=14, base=16, strip_zeros=False,
show_zero_exponent=True, binary_exp=True) == xr.hex()
try:
xr = round(x)
except ValueError:
pytest.raises(ValueError, lambda: round(m))
except OverflowError:
pytest.raises(OverflowError, lambda: round(m))
else:
mr = round(m)
assert type(mr) is int
assert mr == xr
def test_rounding_prop():
assert mp.rounding == 'n'
assert mp.sin(1) == mpf('0x1.aed548f090ceep-1')
mp.rounding = 'u'
assert mp.rounding == 'u'
assert mp.sin(1) == mpf('0x1.aed548f090cefp-1')
with pytest.raises(ValueError):
mp.rounding = 'x'
def test_from_man_exp():
with pytest.raises(TypeError):
mp.mpf(("!", 1))
def test_issue_985():
assert hash(mpc(-1)) == -2
assert hash(mpmath.mpc(-1000004, 1)) == -2
assert mpc(-1) in {1, -1}
def test_issue_975():
def worker():
mp = mpmath.MPContext()
mp.quad(lambda x: mp.exp(-x**2), [-mp.inf, mp.inf]) ** 2
sz = 100
tpe = ThreadPoolExecutor(max_workers=4)
futures = [None]*sz
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
+60 -67
View File
@@ -3,20 +3,13 @@ Test bit-level integer and mpf operations
"""
from mpmath import eps, fadd, ldexp, mp, mpc, mpf
from mpmath.libmp import (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():
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
@@ -27,74 +20,74 @@ def test_trailing():
assert trailing(2**100-1) == 0
def test_round_down():
assert from_man_exp(0, -4, 4, round_down)[:3] == (0, 0, 0)
assert from_man_exp(0xf0, -4, 4, round_down)[:3] == (0, 15, 0)
assert from_man_exp(0xf1, -4, 4, round_down)[:3] == (0, 15, 0)
assert from_man_exp(0xff, -4, 4, round_down)[:3] == (0, 15, 0)
assert from_man_exp(-0xf0, -4, 4, round_down)[:3] == (1, 15, 0)
assert from_man_exp(-0xf1, -4, 4, round_down)[:3] == (1, 15, 0)
assert from_man_exp(-0xff, -4, 4, round_down)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(0), -4, 4, round_down)[:3] == (0, 0, 0)
assert from_man_exp(MPZ(0xf0), -4, 4, round_down)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xf1), -4, 4, round_down)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xff), -4, 4, round_down)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(-0xf0), -4, 4, round_down)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xf1), -4, 4, round_down)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xff), -4, 4, round_down)[:3] == (1, 15, 0)
def test_round_up():
assert from_man_exp(0, -4, 4, round_up)[:3] == (0, 0, 0)
assert from_man_exp(0xf0, -4, 4, round_up)[:3] == (0, 15, 0)
assert from_man_exp(0xf1, -4, 4, round_up)[:3] == (0, 1, 4)
assert from_man_exp(0xff, -4, 4, round_up)[:3] == (0, 1, 4)
assert from_man_exp(-0xf0, -4, 4, round_up)[:3] == (1, 15, 0)
assert from_man_exp(-0xf1, -4, 4, round_up)[:3] == (1, 1, 4)
assert from_man_exp(-0xff, -4, 4, round_up)[:3] == (1, 1, 4)
assert from_man_exp(MPZ(0), -4, 4, round_up)[:3] == (0, 0, 0)
assert from_man_exp(MPZ(0xf0), -4, 4, round_up)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xf1), -4, 4, round_up)[:3] == (0, 1, 4)
assert from_man_exp(MPZ(0xff), -4, 4, round_up)[:3] == (0, 1, 4)
assert from_man_exp(MPZ(-0xf0), -4, 4, round_up)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xf1), -4, 4, round_up)[:3] == (1, 1, 4)
assert from_man_exp(MPZ(-0xff), -4, 4, round_up)[:3] == (1, 1, 4)
def test_round_floor():
assert from_man_exp(0, -4, 4, round_floor)[:3] == (0, 0, 0)
assert from_man_exp(0xf0, -4, 4, round_floor)[:3] == (0, 15, 0)
assert from_man_exp(0xf1, -4, 4, round_floor)[:3] == (0, 15, 0)
assert from_man_exp(0xff, -4, 4, round_floor)[:3] == (0, 15, 0)
assert from_man_exp(-0xf0, -4, 4, round_floor)[:3] == (1, 15, 0)
assert from_man_exp(-0xf1, -4, 4, round_floor)[:3] == (1, 1, 4)
assert from_man_exp(-0xff, -4, 4, round_floor)[:3] == (1, 1, 4)
assert from_man_exp(MPZ(0), -4, 4, round_floor)[:3] == (0, 0, 0)
assert from_man_exp(MPZ(0xf0), -4, 4, round_floor)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xf1), -4, 4, round_floor)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xff), -4, 4, round_floor)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(-0xf0), -4, 4, round_floor)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xf1), -4, 4, round_floor)[:3] == (1, 1, 4)
assert from_man_exp(MPZ(-0xff), -4, 4, round_floor)[:3] == (1, 1, 4)
def test_round_ceiling():
assert from_man_exp(0, -4, 4, round_ceiling)[:3] == (0, 0, 0)
assert from_man_exp(0xf0, -4, 4, round_ceiling)[:3] == (0, 15, 0)
assert from_man_exp(0xf1, -4, 4, round_ceiling)[:3] == (0, 1, 4)
assert from_man_exp(0xff, -4, 4, round_ceiling)[:3] == (0, 1, 4)
assert from_man_exp(-0xf0, -4, 4, round_ceiling)[:3] == (1, 15, 0)
assert from_man_exp(-0xf1, -4, 4, round_ceiling)[:3] == (1, 15, 0)
assert from_man_exp(-0xff, -4, 4, round_ceiling)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(0), -4, 4, round_ceiling)[:3] == (0, 0, 0)
assert from_man_exp(MPZ(0xf0), -4, 4, round_ceiling)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xf1), -4, 4, round_ceiling)[:3] == (0, 1, 4)
assert from_man_exp(MPZ(0xff), -4, 4, round_ceiling)[:3] == (0, 1, 4)
assert from_man_exp(MPZ(-0xf0), -4, 4, round_ceiling)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xf1), -4, 4, round_ceiling)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xff), -4, 4, round_ceiling)[:3] == (1, 15, 0)
def test_round_nearest():
assert from_man_exp(0, -4, 4, round_nearest)[:3] == (0, 0, 0)
assert from_man_exp(0xf0, -4, 4, round_nearest)[:3] == (0, 15, 0)
assert from_man_exp(0xf7, -4, 4, round_nearest)[:3] == (0, 15, 0)
assert from_man_exp(0xf8, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1000 -> 10000.0
assert from_man_exp(0xf9, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1001 -> 10000.0
assert from_man_exp(0xe8, -4, 4, round_nearest)[:3] == (0, 7, 1) # 1110.1000 -> 1110.0
assert from_man_exp(0xe9, -4, 4, round_nearest)[:3] == (0, 15, 0) # 1110.1001 -> 1111.0
assert from_man_exp(-0xf0, -4, 4, round_nearest)[:3] == (1, 15, 0)
assert from_man_exp(-0xf7, -4, 4, round_nearest)[:3] == (1, 15, 0)
assert from_man_exp(-0xf8, -4, 4, round_nearest)[:3] == (1, 1, 4)
assert from_man_exp(-0xf9, -4, 4, round_nearest)[:3] == (1, 1, 4)
assert from_man_exp(-0xe8, -4, 4, round_nearest)[:3] == (1, 7, 1)
assert from_man_exp(-0xe9, -4, 4, round_nearest)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(0), -4, 4, round_nearest)[:3] == (0, 0, 0)
assert from_man_exp(MPZ(0xf0), -4, 4, round_nearest)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xf7), -4, 4, round_nearest)[:3] == (0, 15, 0)
assert from_man_exp(MPZ(0xf8), -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1000 -> 10000.0
assert from_man_exp(MPZ(0xf9), -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1001 -> 10000.0
assert from_man_exp(MPZ(0xe8), -4, 4, round_nearest)[:3] == (0, 7, 1) # 1110.1000 -> 1110.0
assert from_man_exp(MPZ(0xe9), -4, 4, round_nearest)[:3] == (0, 15, 0) # 1110.1001 -> 1111.0
assert from_man_exp(MPZ(-0xf0), -4, 4, round_nearest)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xf7), -4, 4, round_nearest)[:3] == (1, 15, 0)
assert from_man_exp(MPZ(-0xf8), -4, 4, round_nearest)[:3] == (1, 1, 4)
assert from_man_exp(MPZ(-0xf9), -4, 4, round_nearest)[:3] == (1, 1, 4)
assert from_man_exp(MPZ(-0xe8), -4, 4, round_nearest)[:3] == (1, 7, 1)
assert from_man_exp(MPZ(-0xe9), -4, 4, round_nearest)[:3] == (1, 15, 0)
def test_rounding_bugs():
# 1 less than power-of-two cases
assert from_man_exp(72057594037927935, -56, 53, round_up) == (0, 1, 0, 1)
assert from_man_exp(73786976294838205979, -65, 53, round_nearest) == (0, 1, 1, 1)
assert from_man_exp(31, 0, 4, round_up) == (0, 1, 5, 1)
assert from_man_exp(-31, 0, 4, round_floor) == (1, 1, 5, 1)
assert from_man_exp(255, 0, 7, round_up) == (0, 1, 8, 1)
assert from_man_exp(-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(9867,-100)
b = from_man_exp(9867,-200)
c = from_man_exp(-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
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)
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
+147 -41
View File
@@ -1,70 +1,62 @@
import pytest
from hypothesis import given
from hypothesis import strategies as st
from mpmath import (arange, chebyfit, cos, differint, e, euler, exp, fourier,
fourierval, inf, invertlaplace, j, limit, log, matrix, mp,
mpf, pade, pi, polyroots, polyval, sin, sqrt)
from mpmath import (arange, chebyfit, cos, cosm, differint, e, euler, exp,
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,
@@ -101,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',
@@ -166,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():
@@ -189,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():
@@ -212,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)
@@ -236,3 +235,110 @@ def test_invlap():
assert invertlaplace(fp,t,method='stehfest').ae(ftt)
assert invertlaplace(fp,t,method='dehoog').ae(ftt)
assert invertlaplace(fp,t,method='cohen').ae(ftt)
def test_expm():
# Simple tests with known exact results
A = matrix([[2, 0], [0, 1]])
A = expm(A)
B = matrix([[e**2, 0], [0, e]])
assert norm(A-B, inf) < 1e-15
A = matrix([[0, -pi], [pi, 0]])
A = expm(A)
B = matrix([[-1, 0], [0, -1]])
assert norm(A-B, inf) < 1e-15
# Test with input as list of lists
A = [[1, 0], [0, 2]]
A = expm(A)
B = matrix([[e, 0], [0, e**2]])
assert norm(A-B, inf) < 1e-15
# Test non-square matrix input
A = [[1, 0], [0, 1], [0, 0]]
pytest.raises(ValueError, lambda: expm(A))
def test_cosm_sinm():
# Simple test with known exact result
A = matrix([[-pi, 0], [0, pi]])
C = cosm(A)
S = sinm(A)
C_exact = matrix([[cos(-pi), 0], [0, cos(pi)]])
S_exact = matrix([[0, 0], [0, 0]])
assert norm(C-C_exact, inf) < 1e-15
assert norm(S-S_exact, inf) < 1e-15
# Test with input as list of lists
A = [[-pi, 0], [0, pi]]
C = cosm(A)
S = sinm(A)
C_exact = matrix([[cos(-pi), 0], [0, cos(pi)]])
S_exact = matrix([[0, 0], [0, 0]])
assert norm(C-C_exact, inf) < 1e-15
assert norm(S-S_exact, inf) < 1e-15
# Test non-square matrix input
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))
+43 -18
View File
@@ -2,32 +2,25 @@
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):
super().__init__(command, timeout=timeout, encoding='utf-8')
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
@@ -53,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
@@ -85,33 +86,57 @@ def test_ipython_console_wrap_floats():
pytest.importorskip('IPython')
c = Console(f'{sys.executable} -m mpmath --simple-prompt --prec 100 '
"--colors 'NoColor'")
"--colors 'NoColor' --no-pretty")
assert c.expect_exact('\r\nIn [1]: ') == 0
assert c.send('10.9\r\n') == 6
assert c.expect_exact("\r\nOut[1]: mpf('10.899999999999999999999999999995')\r\n\r\nIn [2]: ") == 0
assert c.send('def f():\r\n x = 1.1\n return x + 1\n\r\n\n') == 42
assert c.expect_exact("\r\n\r\nIn [3]: ") == 0
assert c.send('f()\r\n') == 5
assert c.expect_exact("\r\nOut[3]: mpf('2.0999999999999999999999999999987')\r\n\r\nIn [4]: ") == 0
def test_bare_console_wrap_floats():
c = Console(f'{sys.executable} -m mpmath --simple-prompt --no-ipython --prec 100 '
"--colors 'NoColor'")
"--colors 'NoColor' --no-pretty")
assert c.expect_exact('>>> ') == 0
assert c.send("10.9\r\n") == 6
assert c.expect_exact("mpf('10.899999999999999999999999999995')\r\n>>> ") == 0
assert c.send("1e100\r\n") == 7
assert c.expect_exact("mpf('9.9999999999999999999999999999997e+99')\r\n>>> ") == 0
assert c.send("1E100\r\n") == 7
assert c.expect_exact("mpf('9.9999999999999999999999999999997e+99')\r\n>>> ") == 0
assert c.send("1+10.9j\r\n") == 9
assert c.expect_exact("mpc(real='1.0', imag='10.899999999999999999999999999995')\r\n>>> ") == 0
assert c.send("1+10.9J\r\n") == 9
assert c.expect_exact("mpc(real='1.0', imag='10.899999999999999999999999999995')\r\n>>> ") == 0
assert c.send('mpf(10.9)\r\n') == 11
assert c.expect_exact("mpf('10.899999999999999999999999999995')\r\n>>> ") == 0
assert c.send('0x1p-1\r\n') == 8
assert c.expect_exact("mpf('0.5')\r\n>>> ") == 0
assert c.send('0b1p+1\r\n') == 8
assert c.expect_exact("mpf('2.0')\r\n>>> ") == 0
@pytest.mark.skipif(sys.version_info < (3, 13),
reason="XXX: uses new REPL")
def test_bare_console_pretty():
c = Console(f'{sys.executable} -m mpmath --simple-prompt --no-ipython --prec 100 '
"--colors 'NoColor' --pretty")
"--colors 'NoColor'", _dumb=False)
assert c.expect_exact('>>> ') == 0
assert c.expect('>>> ') == 0
assert c.send("10.9\r\n") == 6
assert c.expect_exact("10.9\r\n>>> ") == 0
assert c.expect("10.899999999999999999999999999995") == 0
assert c.send("def f():\r\n x = ?\r\n\r\n") == 21
assert c.expect('SyntaxError:') == 0
assert c.send('def f():\r\n return 1.1\r\n\r\n') == 26
assert c.expect('>>> ') == 0
assert c.send("f()\r\n") == 5
assert c.expect('1.1000000000000000000000000000003') == 0
assert c.send("a = 2.1; a\r\n") == 12
assert c.expect('2.0999999999999999999999999999987') == 0
def test_mpmath_version():
+13 -14
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
@@ -78,11 +79,10 @@ def test_to_str():
assert to_str(from_str('1', base=16), 6, base=16, binary_exp=True) == '0x1.0'
x = mpf('1234.567891')._mpf_
pytest.raises(ValueError, lambda: to_str(x, 6, binary_exp=True))
pytest.raises(ValueError, lambda: to_str(x, 6, rounding='Y'))
pytest.raises(ValueError, lambda: to_str('1e400e2', 6))
assert to_str(x, 5, rounding='n') == '1234.6'
assert to_str(x, 5, rounding='d') == '1234.5'
assert to_str(x, 5, rounding='u') == '1234.6'
pytest.raises(ValueError, lambda: to_str(x, 6, rnd='Y'))
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'
def test_pretty():
mp.pretty = True
@@ -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
+17 -1
View File
@@ -1,6 +1,6 @@
import pytest
from mpmath import zetazero
from mpmath import fp, zetazero
@pytest.mark.parametrize("n,v",
@@ -13,9 +13,25 @@ from mpmath import zetazero
(1048449114, 388858886.002285122),
(1048449115, 388858886.00239369),
(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),
])
def test_zetazero(n, v):
assert zetazero(n).ae(complex(0.5,v))
def test_zeta_param(capsys):
fp.zeta(0.5+100j, method="riemann-siegel", verbose=True)
captured = capsys.readouterr()
assert "Attempting to use the Riemann-Siegel algorithm" in captured.out
+157 -18
View File
@@ -38,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 sys.version_info[:3] > (3, 11):
if sys.version_info >= (3, 11):
res += draw(st.sampled_from([''] + ['z']))
# alternate mode
@@ -54,7 +54,9 @@ def fmt_str(draw, types='fFeE', for_complex=False):
skip_thousand_separators = True
# Width
res += draw(st.sampled_from(['']*7 + list(map(str, range(1, 40)))))
res += draw(st.sampled_from(['']*7 + list(map(str, range(1, 40)))
+ ([] if for_complex else ['0' + str(_)
for _ in range(40)])))
# grouping character (thousand_separators)
gchar = draw(st.sampled_from([''] + list(',_')))
@@ -62,9 +64,13 @@ def fmt_str(draw, types='fFeE', for_complex=False):
res += gchar
# Precision
prec = draw(st.sampled_from(['']*7 + list(map(str, range(40)))))
prec = draw(st.sampled_from(['']*7 + list(map(str, range(40)))
+ ['0' + str(_) for _ in range(40)]))
if prec:
res += '.' + prec
if sys.version_info >= (3, 14):
gchar = draw(st.sampled_from([''] + list(',_')))
res += gchar
# Type
res += draw(st.sampled_from(types))
@@ -175,6 +181,9 @@ def test_mpf_fmt_cpython():
assert f'{mp.mpf(0.0000005001):f}' == '0.000001'
assert f'{mp.mpf(0.0000004999):f}' == '0.000000'
# grouping in fractional part
assert f'{mp.mpf(0.0000004999):.9_f}' == '0.000_000_500'
# 'e' code formatting with explicit precision (>= 0). Output should
# always have exactly the number of places after the point that were
# requested.
@@ -460,25 +469,39 @@ def test_mpf_fmt_cpython():
# No formatting code.
assert f'{mp.mpf(0.0):.0}' == '0e+00'
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,
allow_subnormal=True))
@example(fmt='.0g', x=9.995074823339339e-05) # issue 880
@example(fmt='.016f', x=0.1) # issue 915
@example(fmt='0030f', x=0.3)
@example(fmt='0=13,f', x=1.1) # issue 917
@example(fmt='013,f', x=1.1)
@example(fmt='013,.0%', x=1.1)
@example(fmt='010.6,f', x=0.1234567891)
@example(fmt='010.7,f', x=0.1234567891)
@example(fmt='010._f', x=0.1234567891)
def test_mpf_floats_bulk(fmt, x):
'''
These are additional random tests that check that mp.mpf and fp.mpf yield
the same results for default precision.
'''
mp.pretty_dps = "repr"
if not x and math.copysign(1, x) == -1:
return # skip negative zero
spec = read_format_spec(fmt)
if sys.version_info < (3, 9) and spec['type'] in ('g', 'G'):
pytest.skip("CPython has incompatible g/G formatting in 3.8 and 3.9+")
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):
# The mpmath could choose a different decimal
# representative (wrt CPython) for same binary
@@ -490,18 +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 sys.version_info < (3, 9) and spec['type'] in ('g', 'G'):
pytest.skip("CPython has incompatible g/G formatting in 3.8 and 3.9+")
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
@@ -514,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}')
@@ -776,6 +825,16 @@ def test_mpf_fmt():
assert f"{mp.mpf('-0.1'):=.4Df}" == "-0.1000"
def test_default_rounding():
x = mp.mpf(mp.pi)
assert f"{x:.3f}" == '3.142'
mp.rounding = 'd'
assert f"{x:.3f}" == '3.141'
mp.rounding = 'u'
assert f"{x:.3f}" == '3.142'
def test_issue_858():
for n in range(2, 15):
str_num = '0.' + (n)*'9'
@@ -801,13 +860,12 @@ def test_errors():
with pytest.raises(ValueError, match="Invalid format specifier '12.3 E '"):
f"{mp.mpf('4'):12.3 E }"
with pytest.raises(ValueError, match="Cannot specify both 0-padding "
"and a fill character"):
f"{mp.mpf('4'):q<03f}"
with pytest.raises(ValueError):
f"{mp.mpf(1):.f}"
with pytest.raises(ValueError):
f"{mp.mpf(1):._6f}"
@settings(max_examples=10000)
@given(st.floats(allow_nan=True, allow_infinity=True,
allow_subnormal=False))
@example(float('nan'))
@@ -828,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))
@@ -844,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))
@@ -866,7 +923,9 @@ def test_binary_fmt():
assert f'{x:b}' == '1.1p+1'
assert f'{x:.2b}' == '1.10p+1'
assert f'{x:+.2b}' == '+1.10p+1'
assert f'{x:#.2b}' == '0b1.10p+1'
assert f'{x:#.2b}' == '1.10p+1'
assert f'{x:.0b}' == '1p+2'
assert f'{x:#.0b}' == '1.p+2'
x = mp.mpf(0)
assert f'{x:.2b}' == '0.00p+0'
@@ -881,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))
+39 -9
View File
@@ -8,16 +8,17 @@ from mpmath import (acos, acosh, acot, acoth, acsc, acsch, arange, arg, asec,
asech, asin, asinh, atan, atan2, atanh, catalan, cbrt,
ceil, conj, cos, cos_sin, cosh, cospi, cospi_sinpi, cot,
coth, csc, csch, cyclotomic, degree, degrees, e, eps,
euler, exp, expj, expjpi, expm1, fabs, fadd, fib,
euler, exp, exp2, expj, expjpi, expm1, fabs, fadd, fib,
fibonacci, floor, fmod, fp, frexp, glaisher, hypot, im,
inf, isnan, j, khinchin, ldexp, linspace, ln, ln2, ln10,
log, log1p, log10, mertens, mp, mpc, mpf, nan, nthroot,
phi, pi, power, powm1, radians, rand, re, root, sec, sech,
sign, sin, sinc, sincpi, sinh, sinpi, sqrt, tan, tanh,
twinprime, unitroots)
log, log1p, log2, log10, mertens, mp, mpc, mpf, nan,
nthroot, phi, pi, power, powm1, radians, rand, re, root,
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):
@@ -209,6 +213,10 @@ def test_log():
assert (log(-1j-1e-8).real*10**16).ae(0.5)
assert (log(1+1e-40j).real*10**80).ae(0.5)
assert (log(1j+1e-40).real*10**80).ae(0.5)
# Taylor series
assert log(0.99999).ae(-1.0000050000287824e-5)
assert log(1.00001).ae(9.9999500003988414e-6)
# Huge
assert log(ldexp(1.234,10**20)).ae(log(2)*1e20)
assert log(ldexp(1.234,10**200)).ae(log(2)*1e200)
@@ -326,6 +334,12 @@ def test_asin():
assert asin(mpc(+2, 0)).ae(mpc(+pi2, -log(2 + sqrt(3))))
assert asin(mpc(0.5, 0)).ae(pi/6)
# issue 787
assert asin(mpc(0, 1e-22)).ae(1e-22j)
mp.prec = 700
assert asin(mpc(0, 1e-220)).ae(1e-220j)
mp.prec = 53
def test_acos():
pi4 = pi/4
assert acos(mpc(+inf, +inf)) == mpc(+pi4, -inf)
@@ -536,10 +550,16 @@ 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)
assert log10(3.75) == log(3.75,10)
assert log2(1.25) == log(1.25,2)
assert exp2(-0.5) == power(2, -0.5)
assert degrees(5.6) == 5.6 / degree
assert radians(5.6) == 5.6 * degree
assert power(-1,0.5) == j
@@ -645,9 +665,8 @@ def test_root():
r = nthroot(a, -n)
r1 = pow(a, -mpf(1)/n)
assert r.ae(r1)
# XXX: this is broken right now
# tests for nthroot rounding
for rnd in ['nearest', 'up', 'down']:
for rnd in ['n', 'u', 'd']:
mp.rounding = rnd
for n in [-5, -3, 3, 5]:
prec = 50
@@ -659,6 +678,7 @@ def test_root():
mp.prec = prec
r = nthroot(b, n)
assert r == a
mp.rounding = 'n'
mp.dps = 30
for n in range(3, 21):
a = (random.random() + j*random.random())
@@ -1074,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))
+142 -18
View File
@@ -1,18 +1,22 @@
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, 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, sqrt, struveh, struvel, upper_gamma, whitm,
whitw, zeta)
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,20 @@ 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
for n in range(10,100,10):
mp.dps = n
@@ -530,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)
@@ -615,10 +632,7 @@ def test_hyper_u():
assert hyperu(2,6,pi).ae(0.55804439825913399130)
assert (hyperu((3,2),8,100+201j)*10**4).ae(-0.3797318333856738798 - 2.9974928453561707782j)
assert (hyperu((5,2),(-1,2),-5000)*10**10).ae(-5.6681877926881664678j)
# XXX: fails because of undetected cancellation in low level series code
# Alternatively: could use asymptotic series here, if convergence test
# tweaked back to recognize this one
#assert (hyperu((5,2),(-1,2),-500)*10**7).ae(-1.82526906001593252847j)
assert (hyperu((5,2),(-1,2),-500)*10**7).ae(-1.82526906001593252847j)
def test_hyper_2f0():
assert hyper([1,2],[],3) == hyp2f0(1,2,3)
@@ -772,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
@@ -1359,6 +1382,24 @@ def test_meijerg():
assert x1.ae(x2)
assert x1.ae(x3)
def test_foxh():
# from Mathematica, https://reference.wolfram.com/language/ref/FoxH.html
assert foxh([[(mpf('1/2'),1)],[(mpf('1/3'),2)]],[[(mpf('1/4'),3)],[(pi,4)]],mpf('0.2')).ae(0.014549867809356231)
assert foxh([[(mpf('1/10'),(6,5)), (mpf('13/10'),1)],[(mpf('17/5'),2)]],[[(mpf('7/5'),2)],[(mpf('1/5'),1)]],mpf('0.2')).ae(0.27964621202572)
# Equivalent by definition
b = 1
B = 2
z = mpf('0.2')
x1 = mpf(1)/B * (z ** (mpf(b)/B)) * exp(-z ** (mpf(1)/B))
x2 = foxh([[],[]],[[(b,B)],[]],z)
x3 = meijerg([[],[]],[[b],[]],z,r=B)/B
assert x1.ae(x2)
assert x1.ae(x3)
# Test foxh with r != 1
x2 = foxh([[],[]],[[(b,B)],[]],z,r=3)
x3 = meijerg([[],[]],[[b],[]],z,r=(3*B))/B
assert x2.ae(x3)
def test_appellf1():
assert appellf1(2,-2,1,1,2,3).ae(-1.75)
assert appellf1(2,1,-2,1,2,3).ae(-8)
@@ -1453,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
@@ -2374,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])
@@ -2427,3 +2473,81 @@ def test_issue_473():
assert mp.polylog(3, -mp.inf) == -mp.inf
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'))
def test_issue_908():
assert mp.besselj(-10+0j, 0+0j) == 0
def test_issue_637():
assert hankel1(1, 1 + 30j).ae(-7.25495e-15 - 1.17346e-14j)
assert hankel2(1, 1 - 30j).ae(-7.25495e-15 + 1.17346e-14j)
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))
+9 -2
View File
@@ -6,8 +6,9 @@ from mpmath import (altzeta, apery, barnesg, bell, bernfrac, bernoulli,
fraction, gamma, gammaprod, harmonic, hyperfac, inf, isnan,
j, log, loggamma, mp, mpc, mpf, mpmathify, nan, pi,
polyexp, polylog, primezeta, psi, rf, rgamma, sech,
siegelz, sinc, sqrt, stieltjes, superfac, zeta)
from mpmath.libmp import from_float, mpf_zeta_int, round_up
secondzeta, siegelz, sinc, sqrt, stieltjes, superfac, zeta)
from mpmath.libmp import from_float, round_up
from mpmath.libmp.gammazeta import mpf_zeta_int
def test_zeta_int_bug():
@@ -41,6 +42,7 @@ def test_bernoulli(plus):
assert bernoulli(5, plus) == 0
assert bernoulli(6, plus).ae(1./42)
assert str(bernoulli(10, plus)) == '0.0757575757575758'
assert repr(bernoulli(10, plus)) == "mpf('0.07575757575757576')"
assert str(bernoulli(234, plus)) == '7.62772793964344e+267'
assert str(bernoulli(10**5, plus)) == '-5.82229431461335e+376755'
assert str(bernoulli(10**8+2, plus)) == '1.19570355039953e+676752584'
@@ -386,6 +388,8 @@ def test_polygamma():
# issue #647
mp.prec = 42
assert digamma(-0.5+0.5j).ae(mpc('0.131892637354523', '2.44065951997751'))
mp.prec = 53
assert digamma(1e300+1j).ae(690.77552789821368)
def test_polygamma_high_prec():
mp.dps = 100
@@ -631,6 +635,9 @@ def test_primezeta():
assert primezeta(inf) == 0
assert isnan(primezeta(nan))
def test_secondzeta():
assert secondzeta(2, 0.6).ae(0.022849870007492626)
def test_rs_zeta():
assert zeta(0.5+100000j).ae(1.0730320148577531321 + 5.7808485443635039843j)
assert zeta(0.75+100000j).ae(1.837852337251873704 + 1.9988492668661145358j)
+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]
+6 -3
View File
@@ -371,9 +371,8 @@ def test_interval_ae():
def test_interval_nstr():
iv.dps = n = 30
x = mpi(1, 2)
# FIXME: error_dps should not be necessary
assert iv.nstr(x, n, mode='plusminus', error_dps=6) == '1.5 +- 0.5'
assert iv.nstr(x, n, mode='plusminus', use_spaces=False, error_dps=6) == '1.5+-0.5'
assert iv.nstr(x, n, mode='plusminus') == '1.5 +- 0.5'
assert iv.nstr(x, n, mode='plusminus', use_spaces=False) == '1.5+-0.5'
assert iv.nstr(x, n, mode='percent') == '1.5 (33.33%)'
assert iv.nstr(x, n, mode='brackets', use_spaces=False) == '[1.0,2.0]'
assert iv.nstr(x, n, mode='brackets' , brackets=('<', '>')) == '<1.0, 2.0>'
@@ -383,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'))
@@ -448,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

Some files were not shown because too many files have changed in this diff Show More