mpmath 0.3__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.
@@ -1,3 +1,18 @@
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
+
1
16
  --0.3--
2
17
  Released October 5, 2007
3
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: mpmath
3
- Version: 0.3
3
+ Version: 0.4
4
4
  Summary: Python library for arbitrary-precision floating-point arithmetic
5
5
  Home-page: http://mpmath.googlecode.com
6
6
  Author: Fredrik Johansson
@@ -12,6 +12,7 @@ LICENSE file for details)
12
12
  0. History
13
13
  ----------
14
14
 
15
+ * Version 0.4 released on November 3, 2007
15
16
  * Version 0.3 released on October 5, 2007
16
17
  * Version 0.2 released on October 2, 2007
17
18
  * Version 0.1 released on September 27, 2007
@@ -40,6 +41,9 @@ http://mpmath.googlecode.com/svn/trunk/
40
41
  2. Documentation and usage
41
42
  --------------------------
42
43
 
44
+ Note: some additional documentation is available on the mpmath website.
45
+ The unit tests and internal docstrings may also be helpful.
46
+
43
47
  Import mpmath with
44
48
 
45
49
  from mpmath import *
@@ -51,7 +55,7 @@ Python's float and complex types:
51
55
  mpf('0.66666666666666663')
52
56
 
53
57
  >>> mpc(0, -1)
54
- mpc(real='0', imag='-1')
58
+ mpc(real='0.0', imag='-1.0')
55
59
 
56
60
  >>> mpf(-0.6) ** mpf(-0.2)
57
61
  mpc(real='0.89603999408558288', imag='-0.65101116249684809')
@@ -158,15 +162,12 @@ and verifies a 15-digit approximation interval for pi:
158
162
  singularities, and/or with the precision set to (say) several hundred
159
163
  digits, the last few digits in a result may be wrong.
160
164
 
161
- * Directed rounding works for addition and multiplication. It is implemented
165
+ * Directed rounding works for arithmetic operations. It is implemented
162
166
  heuristically for other operations, and their results may be off by one
163
167
  or two bits in the last place (if otherwise accurate).
164
168
 
165
- * binary <-> decimal conversion currently uses the decimal module, which
166
- is very slow and fails for extremely large and small numbers
167
-
168
169
  * Some IEEE 754 features such as infinities, NaN and denormal rounding
169
- are not implemented
170
+ are not supported
170
171
 
171
172
 
172
173
  4. Help and bug reports
@@ -1,9 +1,9 @@
1
1
  import sys
2
2
  import math
3
3
  from time import clock
4
- from mpmath import mpf
5
- from mpmath.lib import _pi_agm, small_numeral, normalize, bin_to_radix, \
6
- fixed_to_str, ROUND_FLOOR
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(approx, func, base, n, tofile):
29
-
30
- intpart = small_numeral(int(approx), base)
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 = func(prec=prec, verbose=True, verbose_base=base)
42
- a = mpf(normalize(a, -prec, prec, ROUND_FLOOR))
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.man, -a.exp, base, n)
48
- d = fixed_to_str(d, base, n)
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(3.14, _pi_agm, base, digits, tofile)
67
+ calculateit(base, digits, tofile)
74
68
  raw_input("\nPress enter to close this script.")
75
69
 
76
70
  interactive()
@@ -178,15 +178,16 @@ def gamma(x):
178
178
  else:
179
179
  x -= 1
180
180
  prec, a, c = _get_spouge_coefficients(mpf.prec + 8)
181
- if isinstance(x, mpf) and x == int(x):
182
- xs = int(x)
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)
183
184
  else:
184
- xs = x
185
- s = _spouge_sum(xs, prec, a, c)
185
+ s = _spouge_sum(x, prec, a, c)
186
186
  # TODO: higher precision may be needed here when the precision
187
187
  # and/or size of x are extremely large
188
+ xpa = x + a
188
189
  mpf.prec += 10
189
- g = exp(log(x+a)*(x+0.5)) * exp(-x-a) * s
190
+ g = exp(log(xpa) * (x + 0.5) - xpa) * s
190
191
 
191
192
  mpf.prec = oldprec
192
193
  return +g
@@ -196,7 +197,7 @@ def factorial(x):
196
197
  """Returns the factorial of x, defined in terms of the gamma
197
198
  function for non-integer x. An exception is raised if x is a
198
199
  negative integer."""
199
- return gamma(x-1)
200
+ return gamma(x+1)
200
201
 
201
202
 
202
203
  #---------------------------------------------------------------------------#
@@ -0,0 +1,7 @@
1
+ from util import *
2
+ from convert import *
3
+ from floatop import *
4
+ from squareroot import *
5
+ from constants import *
6
+ from functions import *
7
+ from complexop import *
@@ -0,0 +1,181 @@
1
+ """
2
+ Arithmetic operations and functions on complex numbers, represented on
3
+ rectangular form as tuples of real floating-point numbers.
4
+
5
+ This module is quite compact, since most of the dirty work can be
6
+ delegated to functions that work on real numbers.
7
+ """
8
+
9
+ from util import *
10
+ from floatop import *
11
+ from functions import *
12
+ from squareroot import *
13
+
14
+
15
+ # Use fastest rounding mode for intermediate calculations
16
+ RF = ROUND_FLOOR
17
+
18
+
19
+ #----------------------------------------------------------------------
20
+ # Complex parts
21
+ #
22
+
23
+ def fcabs(a, b, prec, rounding):
24
+ """
25
+ Absolute value of a complex number, |a+bi|. Returns a single
26
+ real number.
27
+ """
28
+ return fhypot(a, b, prec, rounding)
29
+
30
+
31
+ #----------------------------------------------------------------------
32
+ # Arithmetic
33
+ #
34
+
35
+ def fcmul(a, b, c, d, prec, rounding):
36
+ """
37
+ Complex multiplication.
38
+
39
+ Returns the real and imaginary part of (a+bi)*(c+di), rounded to
40
+ the specified precision. The rounding mode applies to the real and
41
+ imaginary parts separately.
42
+
43
+ Implemented the straightforward way. TODO: use a more stable
44
+ algorithm to avoid cancellation.
45
+ """
46
+
47
+ # All-real case
48
+ if b == d == fzero:
49
+ return fmul(a, c, prec, rounding), fzero
50
+
51
+ ep = prec + 10
52
+
53
+ re = fsub(fmul(a,c, ep, RF), fmul(b,d, ep, RF), prec, rounding)
54
+ im = fadd(fmul(a,d, ep, RF), fmul(b,c, ep, RF), prec, rounding)
55
+ return re, im
56
+
57
+
58
+ #----------------------------------------------------------------------
59
+ # Square root
60
+ #
61
+
62
+ def fcsqrt(a, b, prec, rounding):
63
+ """
64
+ Complex square root (principal branch).
65
+
66
+ We have sqrt(a+bi) = sqrt((r+a)/2) + b/sqrt(2*(r+a))*i where
67
+ r = abs(a+bi), when a+bi is not a negative real number.
68
+ """
69
+
70
+ if a == b == fzero:
71
+ return (a, b)
72
+
73
+ # When a+bi is a negative real number, we get a real sqrt times i
74
+ if a[0] < 0 and b == fzero:
75
+ im = fsqrt(fneg_exact(a), prec, rounding)
76
+ return (fzero, im)
77
+
78
+ ep = prec+20
79
+
80
+ t = fadd(fcabs(a, b, ep, RF), a, ep, RF) # t = abs(a+bi) + a
81
+ u = fmul(t, fhalf, ep, RF) # u = t / 2
82
+ re = fsqrt(u, prec, rounding) # re = sqrt(u)
83
+ v = fmul(t, ftwo, ep, RF) # v = t * 2
84
+ w = fsqrt(v, ep, RF) # w = sqrt(v)
85
+ im = fdiv(b, w, prec, rounding) # im = b / w
86
+
87
+ return re, im
88
+
89
+
90
+ #----------------------------------------------------------------------
91
+ # Exp and log
92
+ #
93
+
94
+ def fcexp(a, b, prec, rounding):
95
+ """
96
+ Complex exponential function.
97
+
98
+ We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i)
99
+ for the computation. This formula is very nice because it is
100
+ perfectly stable; since we just do real multiplications, the only
101
+ numerical errors that can creep in are single-ulp rounding errors.
102
+
103
+ The formula is efficient since mpmath's real exp is quite fast and
104
+ since we can compute cos and sin simultaneously.
105
+
106
+ It is no problem if a and b are large; if the implementations of
107
+ exp/cos/sin are accurate and efficient for all real numbers, then
108
+ so is this function for all complex numbers.
109
+ """
110
+
111
+ mag = fexp(a, prec+4, rounding)
112
+
113
+ c, s = cos_sin(b, prec+4, rounding)
114
+
115
+ re = fmul(mag, c, prec, rounding)
116
+ im = fmul(mag, s, prec, rounding)
117
+ return re, im
118
+
119
+ # TODO: log
120
+
121
+
122
+ #----------------------------------------------------------------------
123
+ # Trigonometric functions
124
+ #
125
+
126
+ def fccos(a, b, prec, rounding):
127
+ """
128
+ Complex cosine.
129
+
130
+ The formula used is cos(a+bi) = cos(a)*cosh(b) - sin(a)*sinh(b)*i.
131
+
132
+ The same comments apply as for the complex exp: only real
133
+ multiplications are performed, so no cancellation errors are
134
+ possible. The formula is also efficient since we can compute both
135
+ pairs (cos, sin) and (cosh, sinh) in single steps.
136
+ """
137
+ ep = prec + 6
138
+
139
+ c, s = cos_sin(a, ep, RF)
140
+ ch, sh = cosh_sinh(b, ep, RF)
141
+
142
+ re = fmul(c, ch, prec, rounding)
143
+ im = fmul(s, sh, prec, rounding)
144
+
145
+ return re, fneg_exact(im)
146
+
147
+
148
+ def fcsin(a, b, prec, rounding):
149
+ """
150
+ Complex sine.
151
+
152
+ We have sin(a+bi) = sin(a)*cosh(b) + cos(a)*sinh(b)*i.
153
+ See the docstring for fccos for additional comments.
154
+ """
155
+ ep = prec + 6
156
+
157
+ c, s = cos_sin(a, ep, RF)
158
+ ch, sh = cosh_sinh(b, ep, RF)
159
+
160
+ re = fmul(s, ch, prec, rounding)
161
+ im = fmul(c, sh, prec, rounding)
162
+
163
+ return re, im
164
+
165
+
166
+ # TODO: complex tan
167
+
168
+
169
+ #----------------------------------------------------------------------
170
+ # Hyperbolic functions
171
+ #
172
+
173
+ def fccosh(a, b, prec, rounding):
174
+ """Complex hyperbolic cosine. Computed as cosh(z) = cos(z*i)."""
175
+ return fccos(b, fneg_exact(a), prec, rounding)
176
+
177
+
178
+ def fcsinh(a, b, prec, rounding):
179
+ """Complex hyperbolic sine. Computed as sinh(z) = -i*sin(z*i)."""
180
+ b, a = fcsin(b, a, prec, rounding)
181
+ return a, b
@@ -0,0 +1,212 @@
1
+ """
2
+ Mathematical constants
3
+
4
+ """
5
+
6
+ import math
7
+
8
+ from util import *
9
+ from floatop import *
10
+ from squareroot import *
11
+
12
+
13
+ def constant_memo(f):
14
+ """
15
+ Memoization for calculation of constants using fixed-point
16
+ arithmetic. Only store the value computed with highest precision;
17
+ if a lower or equal precision is requested later, the result can
18
+ be generated directly through downshifting the cached value.
19
+ """
20
+ f.memo_prec = -1
21
+ f.memo_val = None
22
+
23
+ def g(prec):
24
+ if prec == f.memo_prec:
25
+ return f.memo_val
26
+ if prec < f.memo_prec:
27
+ return f.memo_val >> (f.memo_prec-prec)
28
+ f.memo_val = f(prec)
29
+ f.memo_prec = prec
30
+ return f.memo_val
31
+
32
+ g.__name__ = f.__name__
33
+ g.__doc__ = f.__doc__
34
+ return g
35
+
36
+ def acot(n, prec, hyperbolic):
37
+ """
38
+ Compute acot of an integer using fixed-point arithmetic. With
39
+ hyperbolic=True, compute acoth. We use the series expansion
40
+
41
+ 1 1 1
42
+ acot(n) = --- - ---- + ---- - ...,
43
+ 3 5
44
+ n 3 n 5 n
45
+
46
+ optimized for integer arguments. In the hyperbolic case, all
47
+ negative terms are changed to positive ones.
48
+ """
49
+ s = t = (1 << prec) // n # 1 / n
50
+ k = 3
51
+ while 1:
52
+ # Repeatedly divide by k * n**2, and add
53
+ t //= (n*n)
54
+ term = t // k
55
+ if not term:
56
+ break
57
+ # Alternate signs
58
+ if hyperbolic or not k & 2:
59
+ s += term
60
+ else:
61
+ s -= term
62
+ k += 2
63
+ return s
64
+
65
+ def machin(coefs, prec, hyperbolic=False):
66
+ """
67
+ Evaluate a Machin-like formula, i.e., a linear combination of
68
+ acot(n) or acoth(n) for specific integer values of n, using fixed-
69
+ point arithmetic.
70
+
71
+ The input should be a list [(c, n), ...], giving c*acot[h](n) + ...
72
+ """
73
+ extraprec = 10
74
+ s = 0
75
+ for a, b in coefs:
76
+ s += a * acot(b, prec+extraprec, hyperbolic)
77
+ return (s >> extraprec)
78
+
79
+ def agm_status(prec, step, adiff, verbose_base):
80
+ logdiff = math.log(max(1, adiff), verbose_base)
81
+ digits = int(prec/math.log(verbose_base,2) - logdiff)
82
+ print " iteration", step, ("(accuracy ~= %i base-%i digits)" % \
83
+ (digits, verbose_base))
84
+
85
+ def pi_agm(prec, verbose=False, verbose_base=10):
86
+ """
87
+ Compute floor(pi * 2**prec) as a big integer using the Brent-
88
+ Salamin algorithm based on the arithmetic-geometric mean.
89
+
90
+ See for example Wikipedia (http://en.wikipedia.org/wiki/Brent-
91
+ Salamin_algorithm) or "Pi and the AGM" by Jonathan and Peter
92
+ Borwein (Wiley, 1987). The algorithm (as stated in the Wikipedia
93
+ article) consists of setting
94
+
95
+ a_0 = 1
96
+ b_0 = 1/sqrt(2)
97
+ t_0 = 1/4
98
+ p_0 = 1
99
+
100
+ and computing
101
+
102
+ a_{n+1} = (a_n + b_n)/2
103
+ b_{n+1} = sqrt(a_n * b_n)
104
+ t_{n+1} = t_n - p_n*(a_n - a_{n+1})**2
105
+ p_{n+1} = 2*p_n
106
+
107
+ for n = 0, 1, 2, 3, ..., after which the approximation is given by
108
+ pi ~= (a_n + b_n)**2 / (4*t_n). Each step roughly doubles the
109
+ number of correct digits.
110
+ """
111
+ extraprec = 50
112
+ prec += extraprec
113
+
114
+ # Initialial values. a, b and t are fixed-point numbers
115
+ a = 1 << prec
116
+ b = sqrt_fixed2(a >> 1, prec)
117
+ t = a >> 2
118
+ p = 1
119
+
120
+ step = 1
121
+ while 1:
122
+ an = (a + b) >> 1
123
+ adiff = a - an
124
+ if verbose:
125
+ agm_status(prec, step, adiff, verbose_base)
126
+ # No change in a
127
+ if p > 16 and abs(adiff) < 1000:
128
+ break
129
+ prod = (a * b) >> prec
130
+ b = sqrt_fixed2(prod, prec)
131
+ t = t - p*((adiff**2) >> prec)
132
+ p = 2*p
133
+ a = an
134
+ step += 1
135
+ if verbose:
136
+ print " final division"
137
+ pi = ((((a+b)**2) >> 2) // t)
138
+ return pi >> extraprec
139
+
140
+ @constant_memo
141
+ def pi_fixed(prec):
142
+ """
143
+ Compute floor(pi * 2**prec) as a big integer.
144
+
145
+ For low precisions, Machin's formula pi = 16*acot(5)-4*acot(239)
146
+ is used. For high precisions, the more efficient arithmetic-
147
+ geometric mean iteration is used.
148
+ """
149
+ if prec < 10000:
150
+ return machin([(16, 5), (-4, 239)], prec)
151
+ else:
152
+ return pi_agm(prec)
153
+
154
+ def fpi(prec, rounding):
155
+ """Compute a floating-point approximation of pi"""
156
+ return normalize(pi_fixed(prec+5), -prec-5, prec, rounding)
157
+
158
+
159
+ # Logarithms of integers can be computed easily using
160
+ # Machin-like formulas
161
+
162
+ @constant_memo
163
+ def log2_fixed(prec):
164
+ return machin([(18, 26), (-2, 4801), (8, 8749)], prec, True)
165
+
166
+ def flog2(prec, rounding):
167
+ return normalize(log2_fixed(prec+5), -prec-5, prec, rounding)
168
+
169
+
170
+ @constant_memo
171
+ def log10_fixed(prec):
172
+ return machin([(46, 31), (34, 49), (20, 161)], prec, True)
173
+
174
+ def flog10(prec, rounding):
175
+ return normalize(log10_fixed(prec+5), -prec-5, prec, rounding)
176
+
177
+
178
+ """
179
+ Euler's constant (gamma) is computed using the Brent-McMillan formula,
180
+ gamma ~= A(n)/B(n) - log(n), where
181
+
182
+ A(n) = sum_{k=0,1,2,...} (n**k / k!)**2 * H(k)
183
+ B(n) = sum_{k=0,1,2,...} (n**k / k!)**2
184
+ H(k) = 1 + 1/2 + 1/3 + ... + 1/k
185
+
186
+ The error is bounded by O(exp(-4n)). Choosing n to be a power
187
+ of two, 2**p, the logarithm becomes particularly easy to calculate.
188
+
189
+ Reference:
190
+ Xavier Gourdon & Pascal Sebah, The Euler constant: gamma
191
+ http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf
192
+ """
193
+
194
+ @constant_memo
195
+ def gamma_fixed(prec):
196
+ prec += 30
197
+ # choose p such that exp(-4*(2**p)) < 2**-n
198
+ p = int(math.log((prec/4) * math.log(2), 2)) + 1
199
+ n = 1<<p
200
+ r = one = 1<<prec
201
+ H, A, B, npow, k, d = 0, 0, 0, 1, 1, 1
202
+ while r:
203
+ A += (r * H) >> prec
204
+ B += r
205
+ r = r * (n*n) // (k*k)
206
+ H += one // k
207
+ k += 1
208
+ S = ((A<<prec) // B) - p*log2_fixed(prec)
209
+ return S >> 30
210
+
211
+ def fgamma(prec, rounding):
212
+ return normalize(gamma_fixed(prec+5), -prec-5, prec, rounding)