mpmath 0.2__zip → 0.4__zip
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mpmath-0.4/CHANGES +52 -0
- {mpmath-0.2 → mpmath-0.4}/PKG-INFO +1 -1
- {mpmath-0.2 → mpmath-0.4}/README +58 -8
- {mpmath-0.2 → mpmath-0.4}/demo/pidigits.py +10 -16
- mpmath-0.4/mpmath/__init__.py +3 -0
- mpmath-0.4/mpmath/functions2.py +384 -0
- mpmath-0.4/mpmath/lib/__init__.py +7 -0
- mpmath-0.4/mpmath/lib/complexop.py +181 -0
- mpmath-0.4/mpmath/lib/constants.py +212 -0
- mpmath-0.4/mpmath/lib/convert.py +233 -0
- mpmath-0.4/mpmath/lib/floatop.py +252 -0
- mpmath-0.4/mpmath/lib/functions.py +350 -0
- mpmath-0.4/mpmath/lib/squareroot.py +199 -0
- mpmath-0.4/mpmath/lib/util.py +268 -0
- mpmath-0.4/mpmath/mpmath.py +739 -0
- {mpmath-0.2 → mpmath-0.4}/setup.py +2 -2
- mpmath-0.4/tests/benchmark.py +139 -0
- mpmath-0.4/tests/runtests.py +5 -0
- mpmath-0.4/tests/test_bitwise.py +110 -0
- mpmath-0.4/tests/test_compatibility.py +51 -0
- mpmath-0.4/tests/test_convert.py +62 -0
- mpmath-0.4/tests/test_division.py +126 -0
- mpmath-0.4/tests/test_functions2.py +68 -0
- mpmath-0.4/tests/test_hp.py +199 -0
- {mpmath-0.2 → mpmath-0.4}/tests/test_mpmath.py +137 -166
- mpmath-0.2/CHANGES +0 -23
- mpmath-0.2/mpmath/__init__.py +0 -2
- mpmath-0.2/mpmath/lib.py +0 -1122
- mpmath-0.2/mpmath/mpmath.py +0 -515
- {mpmath-0.2 → mpmath-0.4}/LICENSE +0 -0
mpmath-0.4/CHANGES
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--0.4--
|
|
2
|
+
Released November 3, 2007
|
|
3
|
+
|
|
4
|
+
* new string conversion code (much faster; unlimited exponents)
|
|
5
|
+
* fixed bug in factorial (it gave the wrong value, though gamma worked)
|
|
6
|
+
* division now uses a rigorous algorithm for directed rounding
|
|
7
|
+
(mpmath previously used a heuristic that got the last bit wrong
|
|
8
|
+
in 1/10000 of cases)
|
|
9
|
+
* misc. performance improvements (arithmetic is 15% faster)
|
|
10
|
+
* refactored parts of the code; added many more docstrings and tests
|
|
11
|
+
* added a function rand() for generating full-precision random numbers
|
|
12
|
+
* rewrote the benchmark script to compare against Decimal and to
|
|
13
|
+
automatically generate timings with psyco both disabled and enabled
|
|
14
|
+
* rewrote unit tests to use py.test
|
|
15
|
+
|
|
16
|
+
--0.3--
|
|
17
|
+
Released October 5, 2007
|
|
18
|
+
|
|
19
|
+
* fixed high-precision accuracy problem in complex sqrt
|
|
20
|
+
* fixed high-precision accuracy problem in atan and complex log
|
|
21
|
+
* fixed directed rounding for sqrt (always rounded to nearest)
|
|
22
|
+
* implemented all hyperbolic and inverse functions (there are some
|
|
23
|
+
accuracy issues left to sort out)
|
|
24
|
+
* included gamma, factorial, erf, zeta incomplete gamma functions
|
|
25
|
+
* made sin and tan more accurate for complex input very close to 0
|
|
26
|
+
* more docstrings
|
|
27
|
+
* many more tests
|
|
28
|
+
* including a benchmark script
|
|
29
|
+
|
|
30
|
+
-- 0.2 --
|
|
31
|
+
Released October 2, 2007
|
|
32
|
+
|
|
33
|
+
* 50% faster exponential function
|
|
34
|
+
* faster mpf <-> int ops
|
|
35
|
+
* fixed import error in pidigits.py; added demos to source distribution
|
|
36
|
+
* __rmul__ was missing on mpf
|
|
37
|
+
* fixed bitcount bug also for -(2**n-1)
|
|
38
|
+
* more docstrings
|
|
39
|
+
* more tests; tests included in source distribution
|
|
40
|
+
* approximate equality testing (.ae method) supported
|
|
41
|
+
* implemented atan and atan2 functions
|
|
42
|
+
* tan works for both mpfs and mpcs
|
|
43
|
+
* complex logarithms and complex powers supported
|
|
44
|
+
* more details in README
|
|
45
|
+
|
|
46
|
+
-- 0.1 --
|
|
47
|
+
Released September 27, 2007
|
|
48
|
+
|
|
49
|
+
* imported code from SymPy's numerics module
|
|
50
|
+
* renamed functions and restructured various parts of the code
|
|
51
|
+
* fixed erroneous bitcount for 2**n-1 mantissa with directed rounding
|
|
52
|
+
* various small speed improvements and bug fixes
|
{mpmath-0.2 → mpmath-0.4}/README
RENAMED
|
@@ -12,6 +12,8 @@ LICENSE file for details)
|
|
|
12
12
|
0. History
|
|
13
13
|
----------
|
|
14
14
|
|
|
15
|
+
* Version 0.4 released on November 3, 2007
|
|
16
|
+
* Version 0.3 released on October 5, 2007
|
|
15
17
|
* Version 0.2 released on October 2, 2007
|
|
16
18
|
* Version 0.1 released on September 27, 2007
|
|
17
19
|
|
|
@@ -39,6 +41,9 @@ http://mpmath.googlecode.com/svn/trunk/
|
|
|
39
41
|
2. Documentation and usage
|
|
40
42
|
--------------------------
|
|
41
43
|
|
|
44
|
+
Note: some additional documentation is available on the mpmath website.
|
|
45
|
+
The unit tests and internal docstrings may also be helpful.
|
|
46
|
+
|
|
42
47
|
Import mpmath with
|
|
43
48
|
|
|
44
49
|
from mpmath import *
|
|
@@ -50,7 +55,7 @@ Python's float and complex types:
|
|
|
50
55
|
mpf('0.66666666666666663')
|
|
51
56
|
|
|
52
57
|
>>> mpc(0, -1)
|
|
53
|
-
mpc(real='0', imag='-1')
|
|
58
|
+
mpc(real='0.0', imag='-1.0')
|
|
54
59
|
|
|
55
60
|
>>> mpf(-0.6) ** mpf(-0.2)
|
|
56
61
|
mpc(real='0.89603999408558288', imag='-0.65101116249684809')
|
|
@@ -92,11 +97,13 @@ initialize an mpf with a full-precision value, use a string:
|
|
|
92
97
|
>>> mpf('0.1')
|
|
93
98
|
mpf('0.1000000000000000000000000000000000000000000000000001') # ok
|
|
94
99
|
|
|
95
|
-
The functions
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
The following standard functions are available and support both real and
|
|
101
|
+
complex arguments:
|
|
102
|
+
|
|
103
|
+
sqrt, exp, log, power, cos, sin, tan, cosh, sinh, tanh,
|
|
104
|
+
acos, asin, atan, acosh, asinh, atanh
|
|
105
|
+
|
|
106
|
+
Example:
|
|
100
107
|
|
|
101
108
|
>>> mpf.dps = 15
|
|
102
109
|
>>> print cos(1)
|
|
@@ -105,11 +112,22 @@ precision.
|
|
|
105
112
|
>>> print cos(1)
|
|
106
113
|
0.54030230586813971740093660744297660373231042061792
|
|
107
114
|
|
|
115
|
+
Some less-common functions are also available: gamma (gamma function),
|
|
116
|
+
factorial, erf (error function), lower_gamma/upper_gamma (incomplete
|
|
117
|
+
gamma function) and zeta (Riemann zeta function).
|
|
118
|
+
|
|
119
|
+
Finally, the convenience functions hypot and atan2 are available
|
|
120
|
+
(defined for real numbers only).
|
|
121
|
+
|
|
122
|
+
The constants pi, e, and cgamma (Euler's constant) are available as
|
|
123
|
+
special objects that behave like mpfs but whose values automatically
|
|
124
|
+
adjust to the precision.
|
|
125
|
+
|
|
108
126
|
>>> mpf.dps = 15
|
|
109
127
|
>>> print pi
|
|
110
128
|
3.14159265358979
|
|
111
129
|
>>> mpf.dps = 50
|
|
112
|
-
>>> pi
|
|
130
|
+
>>> print pi
|
|
113
131
|
3.1415926535897932384626433832795028841971693993751
|
|
114
132
|
|
|
115
133
|
>>> mpf.dps = 15
|
|
@@ -119,8 +137,40 @@ precision.
|
|
|
119
137
|
>>> e**(-pi*1j)
|
|
120
138
|
mpc(real='-1', imag='1.0106 [...] E-51')
|
|
121
139
|
|
|
140
|
+
Directed rounding is partially implemented. For example, this computes
|
|
141
|
+
and verifies a 15-digit approximation interval for pi:
|
|
142
|
+
|
|
143
|
+
>>> mpf.dps = 15
|
|
144
|
+
>>> mpf.round_down(); pi1 = +pi
|
|
145
|
+
>>> mpf.round_up(); pi2 = +pi
|
|
146
|
+
>>> pi1
|
|
147
|
+
mpf('3.1415926535897931')
|
|
148
|
+
>>> pi2
|
|
149
|
+
mpf('3.1415926535897936')
|
|
150
|
+
>>> mpf.dps = 30
|
|
151
|
+
>>> pi1 < pi < pi2
|
|
152
|
+
True
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
3. Known problems
|
|
156
|
+
-----------------
|
|
157
|
+
|
|
158
|
+
* mpmath temporarily increases the working precision during function
|
|
159
|
+
evaluations to suppress the effects of intermediate rounding errors.
|
|
160
|
+
In most functions, the precision is currently increased by a fixed amount
|
|
161
|
+
that gives accurate results for "normal" input. Close to zeros and
|
|
162
|
+
singularities, and/or with the precision set to (say) several hundred
|
|
163
|
+
digits, the last few digits in a result may be wrong.
|
|
164
|
+
|
|
165
|
+
* Directed rounding works for arithmetic operations. It is implemented
|
|
166
|
+
heuristically for other operations, and their results may be off by one
|
|
167
|
+
or two bits in the last place (if otherwise accurate).
|
|
168
|
+
|
|
169
|
+
* Some IEEE 754 features such as infinities, NaN and denormal rounding
|
|
170
|
+
are not supported
|
|
171
|
+
|
|
122
172
|
|
|
123
|
-
|
|
173
|
+
4. Help and bug reports
|
|
124
174
|
-----------------------
|
|
125
175
|
|
|
126
176
|
You can report bugs at the mpmath issue tracker,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import math
|
|
3
3
|
from time import clock
|
|
4
|
-
|
|
5
|
-
from mpmath.lib import
|
|
6
|
-
|
|
4
|
+
|
|
5
|
+
from mpmath.lib import pi_agm, bin_to_radix, numeral
|
|
6
|
+
|
|
7
7
|
|
|
8
8
|
def display_fraction(digits, skip=0, colwidth=10, columns=5):
|
|
9
9
|
perline = colwidth * columns
|
|
@@ -25,27 +25,21 @@ def display_fraction(digits, skip=0, colwidth=10, columns=5):
|
|
|
25
25
|
buf = buf[colwidth:]
|
|
26
26
|
print s + ":", printed + colwidth*columns
|
|
27
27
|
|
|
28
|
-
def calculateit(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if intpart == "0":
|
|
32
|
-
skip = 0
|
|
33
|
-
else:
|
|
34
|
-
skip = len(intpart)
|
|
28
|
+
def calculateit(base, n, tofile):
|
|
29
|
+
intpart = numeral(3, base)
|
|
30
|
+
skip = 1
|
|
35
31
|
|
|
36
32
|
prec = int(n*math.log(base,2))+10
|
|
37
|
-
mpf.prec = prec
|
|
38
33
|
|
|
39
34
|
print "Step 1 of 2: calculating binary value..."
|
|
40
35
|
t = clock()
|
|
41
|
-
a =
|
|
42
|
-
a = mpf(normalize(a, -prec, prec))
|
|
36
|
+
a = pi_agm(prec, verbose=True, verbose_base=base)
|
|
43
37
|
step1_time = clock() - t
|
|
44
38
|
|
|
45
39
|
print "Step 2 of 2: converting to specified base..."
|
|
46
40
|
t = clock()
|
|
47
|
-
d = bin_to_radix(a
|
|
48
|
-
d =
|
|
41
|
+
d = bin_to_radix(a, prec, base, n)
|
|
42
|
+
d = numeral(d, base, n)
|
|
49
43
|
step2_time = clock() - t
|
|
50
44
|
|
|
51
45
|
print "\nWriting output...\n"
|
|
@@ -70,7 +64,7 @@ def interactive():
|
|
|
70
64
|
if tofile:
|
|
71
65
|
tofile = open(tofile, "w")
|
|
72
66
|
|
|
73
|
-
calculateit(
|
|
67
|
+
calculateit(base, digits, tofile)
|
|
74
68
|
raw_input("\nPress enter to close this script.")
|
|
75
69
|
|
|
76
70
|
interactive()
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Numerical implementations of special functions (gamma, ...)
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from mpmath import mpnumeric, mpf, mpc, pi, cgamma, exp, log, sqrt, sin, power
|
|
6
|
+
from lib import make_fixed
|
|
7
|
+
|
|
8
|
+
#from sympy import Rational
|
|
9
|
+
|
|
10
|
+
def _fix(x, prec):
|
|
11
|
+
return make_fixed(x.val, prec)
|
|
12
|
+
|
|
13
|
+
def _re(s):
|
|
14
|
+
if isinstance(s, mpf):
|
|
15
|
+
return s
|
|
16
|
+
return s.real
|
|
17
|
+
|
|
18
|
+
def _im(s):
|
|
19
|
+
if isinstance(s, mpf):
|
|
20
|
+
return s
|
|
21
|
+
return s.imag
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
#---------------------------------------------------------------------------#
|
|
25
|
+
# #
|
|
26
|
+
# Gamma function #
|
|
27
|
+
# #
|
|
28
|
+
#---------------------------------------------------------------------------#
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
We compute the gamma function using Spouge's approximation
|
|
32
|
+
|
|
33
|
+
x! = (x+a)**(x+1/2) * exp(-x-a) * [c_0 + S(x) + eps]
|
|
34
|
+
|
|
35
|
+
where S(x) is the sum of c_k/(x+k) from k = 1 to a-1 and the coefficients
|
|
36
|
+
are given by
|
|
37
|
+
|
|
38
|
+
c_0 = sqrt(2*pi)
|
|
39
|
+
|
|
40
|
+
(-1)**(k-1)
|
|
41
|
+
c_k = ----------- (a-k)**(k-1/2) exp(-k+a), k = 1,2,...,a-1
|
|
42
|
+
(k - 1)!
|
|
43
|
+
|
|
44
|
+
Due to an inequality proved by Spouge, if we choose a = int(1.26*n), the
|
|
45
|
+
error eps is less than 10**-n for any x in the right complex half-plane
|
|
46
|
+
(assuming a > 2). In practice, it seems that a can be chosen quite a bit
|
|
47
|
+
lower still (30-50%); this possibility should be investigated.
|
|
48
|
+
|
|
49
|
+
Reference:
|
|
50
|
+
John L. Spouge, "Computation of the gamma, digamma, and trigamma
|
|
51
|
+
functions", SIAM Journal on Numerical Analysis 31 (1994), no. 3, 931-944.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
#----------------------------------------------------------------------
|
|
56
|
+
#
|
|
57
|
+
# We first implement a helper function for calculating the coefficients
|
|
58
|
+
# c_k and caching them so that they can be re-used for multiple gamma
|
|
59
|
+
# function evaluations
|
|
60
|
+
#
|
|
61
|
+
|
|
62
|
+
_spouge_cache = {}
|
|
63
|
+
|
|
64
|
+
def _calc_spouge_coefficients(a, prec):
|
|
65
|
+
"""
|
|
66
|
+
Calculate Spouge coefficients for approximation with parameter a.
|
|
67
|
+
Return a list of big integers representing the coefficients in
|
|
68
|
+
fixed-point form with a precision of prec bits.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# We'll store the coefficients as fixed-point numbers but calculate
|
|
72
|
+
# them as Floats for convenience. The initial terms are huge, so we
|
|
73
|
+
# need to allocate extra bits to ensure full accuracy. The integer
|
|
74
|
+
# part of the largest term has size ~= exp(a) or 2**(1.4*a)
|
|
75
|
+
floatprec = prec + int(a*1.4)
|
|
76
|
+
oldprec = mpf.prec
|
|
77
|
+
mpf.prec = floatprec
|
|
78
|
+
|
|
79
|
+
c = [0] * a
|
|
80
|
+
b = exp(a-1)
|
|
81
|
+
e = exp(1)
|
|
82
|
+
c[0] = _fix(sqrt(2*pi), prec)
|
|
83
|
+
for k in range(1, a):
|
|
84
|
+
# print "%02f" % (100.0 * k / a), "% done"
|
|
85
|
+
c[k] = _fix(((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k), prec)
|
|
86
|
+
# Divide off e and k instead of computing exp and k! from scratch
|
|
87
|
+
b = b / (e * k)
|
|
88
|
+
|
|
89
|
+
mpf.prec = oldprec
|
|
90
|
+
return c
|
|
91
|
+
|
|
92
|
+
# Cached lookup of coefficients
|
|
93
|
+
def _get_spouge_coefficients(prec):
|
|
94
|
+
|
|
95
|
+
# This exact precision has been used before
|
|
96
|
+
if prec in _spouge_cache:
|
|
97
|
+
return _spouge_cache[prec]
|
|
98
|
+
|
|
99
|
+
for p in _spouge_cache:
|
|
100
|
+
# Coefficients calculated for a slightly higher precision are ok
|
|
101
|
+
# too. But if the difference is too big, we're better off
|
|
102
|
+
# starting from scratch
|
|
103
|
+
if 0.8 <= float(p)/prec < 1:
|
|
104
|
+
return _spouge_cache[p]
|
|
105
|
+
|
|
106
|
+
# Here we estimate the value of a based on Spouge's inequality for
|
|
107
|
+
# the relative error
|
|
108
|
+
a = max(3, int(0.39*prec)) # ~= 1.26*n
|
|
109
|
+
|
|
110
|
+
# Compute and return
|
|
111
|
+
coefs = _calc_spouge_coefficients(a, prec)
|
|
112
|
+
_spouge_cache[prec] = (prec, a, coefs)
|
|
113
|
+
return _spouge_cache[prec]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# This function computes S
|
|
117
|
+
def _spouge_sum(x, prec, a, c):
|
|
118
|
+
if isinstance(x, mpf):
|
|
119
|
+
# Regular fixed-point summation
|
|
120
|
+
x = _fix(x, prec)
|
|
121
|
+
s = c[0]
|
|
122
|
+
for k in xrange(1, a):
|
|
123
|
+
s += (c[k] << prec) // (x + (k << prec))
|
|
124
|
+
return mpf((s, -prec))
|
|
125
|
+
elif isinstance(x, (int, long)):
|
|
126
|
+
# Here we can save some work
|
|
127
|
+
if isinstance(x, (int, long)):
|
|
128
|
+
p, q = x, 1
|
|
129
|
+
else: # Placeholder support for rational numbers
|
|
130
|
+
p, q = x.p, x.q
|
|
131
|
+
s = c[0]
|
|
132
|
+
for k in xrange(1, a):
|
|
133
|
+
s += c[k] * q // (p+q*k)
|
|
134
|
+
return mpf((s, -prec))
|
|
135
|
+
elif isinstance(x, mpc):
|
|
136
|
+
"""
|
|
137
|
+
For a complex number a + b*I, we have
|
|
138
|
+
|
|
139
|
+
c_k (a+k)*c_k b * c_k
|
|
140
|
+
------------- = --------- - ------- * I
|
|
141
|
+
(a + b*I) + k M M
|
|
142
|
+
|
|
143
|
+
2 2 2 2 2
|
|
144
|
+
where M = (a+k) + b = (a + b ) + (2*a*k + k )
|
|
145
|
+
"""
|
|
146
|
+
re = _fix(x.real, prec)
|
|
147
|
+
im = _fix(x.imag, prec)
|
|
148
|
+
sre, sim = c[0], 0
|
|
149
|
+
mag = ((re**2)>>prec) + ((im**2)>>prec)
|
|
150
|
+
for k in xrange(1, a):
|
|
151
|
+
M = mag + re*(2*k) + ((k**2) << prec)
|
|
152
|
+
sre += (c[k] * (re + (k << prec))) // M
|
|
153
|
+
sim -= (c[k] * im) // M
|
|
154
|
+
return mpc((sre, -prec), (sim, -prec))
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def gamma(x):
|
|
158
|
+
"""Returns the gamma function of x. Raises an exception if
|
|
159
|
+
x == 0, -1, -2, -3, ... where the gamma function has a pole."""
|
|
160
|
+
|
|
161
|
+
oldprec = mpf.prec
|
|
162
|
+
mpf.prec += 4
|
|
163
|
+
|
|
164
|
+
x = mpnumeric(x)
|
|
165
|
+
|
|
166
|
+
if isinstance(x, mpc):
|
|
167
|
+
re, im = x.real, x.imag
|
|
168
|
+
else:
|
|
169
|
+
re, im = x, 0
|
|
170
|
+
|
|
171
|
+
# For negative x (or positive x close to the pole at x = 0),
|
|
172
|
+
# we use the reflection formula
|
|
173
|
+
if re < 0.25:
|
|
174
|
+
if im == 0 and re == int(re):
|
|
175
|
+
raise ZeroDivisionError, "gamma function pole"
|
|
176
|
+
mpf.prec += 4
|
|
177
|
+
g = pi / (sin(pi*x) * gamma(1-x))
|
|
178
|
+
else:
|
|
179
|
+
x -= 1
|
|
180
|
+
prec, a, c = _get_spouge_coefficients(mpf.prec + 8)
|
|
181
|
+
# TODO: figure out when we can just use Stirling's formula
|
|
182
|
+
if isinstance(x, mpf) and x.exp >= 0:
|
|
183
|
+
s = _spouge_sum(x.man << x.exp, prec, a, c)
|
|
184
|
+
else:
|
|
185
|
+
s = _spouge_sum(x, prec, a, c)
|
|
186
|
+
# TODO: higher precision may be needed here when the precision
|
|
187
|
+
# and/or size of x are extremely large
|
|
188
|
+
xpa = x + a
|
|
189
|
+
mpf.prec += 10
|
|
190
|
+
g = exp(log(xpa) * (x + 0.5) - xpa) * s
|
|
191
|
+
|
|
192
|
+
mpf.prec = oldprec
|
|
193
|
+
return +g
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def factorial(x):
|
|
197
|
+
"""Returns the factorial of x, defined in terms of the gamma
|
|
198
|
+
function for non-integer x. An exception is raised if x is a
|
|
199
|
+
negative integer."""
|
|
200
|
+
return gamma(x+1)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
#---------------------------------------------------------------------------#
|
|
204
|
+
# #
|
|
205
|
+
# Incomplete gamma functions #
|
|
206
|
+
# #
|
|
207
|
+
#---------------------------------------------------------------------------#
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
We compute the lower incomplete gamma function g(a,z) using the formula
|
|
211
|
+
g(a,z) = z**a * exp(-z) * S(a,z) / a, where
|
|
212
|
+
oo
|
|
213
|
+
___ k
|
|
214
|
+
\ z
|
|
215
|
+
S(a,z) = 1 + ) ------------------.
|
|
216
|
+
/___ (a+1)(a+2)...(a+k)
|
|
217
|
+
k = 1
|
|
218
|
+
|
|
219
|
+
Then, in turn, various functions such as erf and exponential integrals
|
|
220
|
+
can be computed from the incomplete gamma function.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
def _lower_gamma_series(are, aim, zre, zim, prec):
|
|
224
|
+
are = _fix(are, prec)
|
|
225
|
+
aim = _fix(aim, prec)
|
|
226
|
+
zre = _fix(zre, prec)
|
|
227
|
+
zim = _fix(zim, prec)
|
|
228
|
+
one = 1 << prec
|
|
229
|
+
cre = sre = one
|
|
230
|
+
cim = sim = 0
|
|
231
|
+
while abs(cre) > 3 or abs(cim) > 3:
|
|
232
|
+
# c = (c * z) << prec
|
|
233
|
+
cre, cim = (cre*zre-cim*zim)>>prec, (cim*zre+cre*zim)>>prec
|
|
234
|
+
# c = c / (a+k)
|
|
235
|
+
are += one
|
|
236
|
+
mag = ((are**2 + aim**2) >> prec)
|
|
237
|
+
cre, cim = (cre*are + cim*aim)//mag, (cim*are - cre*aim)//mag
|
|
238
|
+
sre += cre
|
|
239
|
+
sim += cim
|
|
240
|
+
sre = mpf((sre, -prec))
|
|
241
|
+
sim = mpf((sim, -prec))
|
|
242
|
+
return mpc(sre, sim)
|
|
243
|
+
|
|
244
|
+
def lower_gamma(a, z):
|
|
245
|
+
"""Returns the lower incomplete gamma function gamma(a, z)"""
|
|
246
|
+
oldprec = mpf.prec
|
|
247
|
+
# XXX: may need more precision
|
|
248
|
+
mpf.prec += 15
|
|
249
|
+
a = mpc(a)
|
|
250
|
+
z = mpc(z)
|
|
251
|
+
s = _lower_gamma_series(a.real, a.imag, z.real, z.imag, oldprec+15)
|
|
252
|
+
y = exp(log(z)*a) * exp(-z) * s / a
|
|
253
|
+
mpf.prec = oldprec
|
|
254
|
+
return +y
|
|
255
|
+
|
|
256
|
+
def upper_gamma(a, z):
|
|
257
|
+
"""Returns the upper incomplete gamma function Gamma(a, z)"""
|
|
258
|
+
mpf.prec += 10
|
|
259
|
+
t = gamma(a) - lower_gamma(a, z)
|
|
260
|
+
mpf.prec -= 10
|
|
261
|
+
return +t
|
|
262
|
+
|
|
263
|
+
def erf(x):
|
|
264
|
+
"""Returns the error function of x."""
|
|
265
|
+
x = mpnumeric(x)
|
|
266
|
+
if x == 0:
|
|
267
|
+
return x
|
|
268
|
+
w = mpc(x)
|
|
269
|
+
if w.real < 0:
|
|
270
|
+
if isinstance(x, mpf):
|
|
271
|
+
return -erf(-x)
|
|
272
|
+
return -erf(-w)
|
|
273
|
+
oldprec = mpf.prec
|
|
274
|
+
mpf.prec += 10
|
|
275
|
+
|
|
276
|
+
y = lower_gamma(0.5, w**2) / sqrt(pi)
|
|
277
|
+
if _re(x) == 0 and _im(x) < 0:
|
|
278
|
+
y = -y
|
|
279
|
+
|
|
280
|
+
if isinstance(x, mpf):
|
|
281
|
+
y = y.real
|
|
282
|
+
|
|
283
|
+
mpf.prec = oldprec
|
|
284
|
+
return +y
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
#---------------------------------------------------------------------------#
|
|
288
|
+
# #
|
|
289
|
+
# Riemann zeta function #
|
|
290
|
+
# #
|
|
291
|
+
#---------------------------------------------------------------------------#
|
|
292
|
+
|
|
293
|
+
"""
|
|
294
|
+
We use zeta(s) = eta(s) * (1 - 2**(1-s)) and Borwein's approximation
|
|
295
|
+
n-1
|
|
296
|
+
___ k
|
|
297
|
+
-1 \ (-1) (d_k - d_n)
|
|
298
|
+
eta(s) ~= ---- ) ------------------
|
|
299
|
+
d_n /___ s
|
|
300
|
+
k = 0 (k + 1)
|
|
301
|
+
where
|
|
302
|
+
k
|
|
303
|
+
___ i
|
|
304
|
+
\ (n + i - 1)! 4
|
|
305
|
+
d_k = n ) ---------------.
|
|
306
|
+
/___ (n - i)! (2i)!
|
|
307
|
+
i = 0
|
|
308
|
+
|
|
309
|
+
If s = a + b*I, the absolute error for eta(s) is bounded by
|
|
310
|
+
|
|
311
|
+
3 (1 + 2|b|)
|
|
312
|
+
------------ * exp(|b| pi/2)
|
|
313
|
+
n
|
|
314
|
+
(3+sqrt(8))
|
|
315
|
+
|
|
316
|
+
Disregarding the linear term, we have approximately,
|
|
317
|
+
|
|
318
|
+
log(err) ~= log(exp(1.58*|b|)) - log(5.8**n)
|
|
319
|
+
log(err) ~= 1.58*|b| - log(5.8)*n
|
|
320
|
+
log(err) ~= 1.58*|b| - 1.76*n
|
|
321
|
+
log2(err) ~= 2.28*|b| - 2.54*n
|
|
322
|
+
|
|
323
|
+
So for p bits, we should choose n > (p + 2.28*|b|) / 2.54.
|
|
324
|
+
|
|
325
|
+
Reference:
|
|
326
|
+
Peter Borwein, "An Efficient Algorithm for the Riemann Zeta Function"
|
|
327
|
+
http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P117.ps
|
|
328
|
+
|
|
329
|
+
http://en.wikipedia.org/wiki/Dirichlet_eta_function
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
_d_cache = {}
|
|
333
|
+
|
|
334
|
+
def _zeta_coefs(n):
|
|
335
|
+
if n in _d_cache:
|
|
336
|
+
return _d_cache[n]
|
|
337
|
+
ds = [0] * (n+1)
|
|
338
|
+
d = 1
|
|
339
|
+
s = ds[0] = 1
|
|
340
|
+
for i in range(1, n+1):
|
|
341
|
+
d = d * 4 * (n+i-1) * (n-i+1)
|
|
342
|
+
d //= ((2*i) * ((2*i)-1))
|
|
343
|
+
s += d
|
|
344
|
+
ds[i] = s
|
|
345
|
+
_d_cache[n] = ds
|
|
346
|
+
return ds
|
|
347
|
+
|
|
348
|
+
# Integer logarithms
|
|
349
|
+
_log_cache = {}
|
|
350
|
+
|
|
351
|
+
def _logk(k):
|
|
352
|
+
p = mpf.prec
|
|
353
|
+
if k in _log_cache and _log_cache[k][0] >= p:
|
|
354
|
+
return +_log_cache[k][1]
|
|
355
|
+
else:
|
|
356
|
+
x = log(k)
|
|
357
|
+
_log_cache[k] = (p, x)
|
|
358
|
+
return x
|
|
359
|
+
|
|
360
|
+
def zeta(s):
|
|
361
|
+
"""Returns the Riemann zeta function of s."""
|
|
362
|
+
oldprec = mpf.prec
|
|
363
|
+
mpf.prec += 10
|
|
364
|
+
s = mpnumeric(s)
|
|
365
|
+
if _re(s) < 0:
|
|
366
|
+
# Reflection formula (XXX: gets bad around the zeros)
|
|
367
|
+
y = power(2, s) * power(pi, s-1) * sin(pi*s/2) * gamma(1-s) * zeta(1-s)
|
|
368
|
+
else:
|
|
369
|
+
p = mpf.prec
|
|
370
|
+
n = int((p + 2.28*abs(float(mpc(s).imag)))/2.54) + 3
|
|
371
|
+
d = _zeta_coefs(n)
|
|
372
|
+
if isinstance(s, mpf) and s == int(s):
|
|
373
|
+
sint = int(s)
|
|
374
|
+
t = 0
|
|
375
|
+
for k in range(n):
|
|
376
|
+
t += (((-1)**k * (d[k] - d[n])) << p) // (k+1)**sint
|
|
377
|
+
y = (mpf((t, -p)) / -d[n]) / (mpf(1) - mpf(2)**(1-sint))
|
|
378
|
+
else:
|
|
379
|
+
t = mpf(0)
|
|
380
|
+
for k in range(n):
|
|
381
|
+
t += (-1)**k * mpf(d[k]-d[n]) * exp(-_logk(k+1)*s)
|
|
382
|
+
y = (t / -d[n]) / (mpf(1) - exp(log(2)*(1-s)))
|
|
383
|
+
mpf.prec = oldprec
|
|
384
|
+
return +y
|