def mpc_exp(z, prec, rnd=round_fast): """ Complex exponential function. We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i) for the computation. This formula is very nice because it is pefectly stable; since we just do real multiplications, the only numerical errors that can creep in are single-ulp rounding errors. The formula is efficient since mpmath's real exp is quite fast and since we can compute cos and sin simultaneously. It is no problem if a and b are large; if the implementations of exp/cos/sin are accurate and efficient for all real numbers, then so is this function for all complex numbers. """ a, b = z if a == fzero: return mpf_cos_sin(b, prec, rnd) if b == fzero: return mpf_exp(a, prec, rnd), fzero mag = mpf_exp(a, prec + 4, rnd) c, s = mpf_cos_sin(b, prec + 4, rnd) re = mpf_mul(mag, c, prec, rnd) im = mpf_mul(mag, s, prec, rnd) return re, im
def mpc_expj(z, prec, rnd='f'): re, im = z if im == fzero: return mpf_cos_sin(re, prec, rnd) if re == fzero: return mpf_exp(mpf_neg(im), prec, rnd), fzero ey = mpf_exp(mpf_neg(im), prec + 10) c, s = mpf_cos_sin(re, prec + 10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im
def mpc_expj(z, prec, rnd="f"): re, im = z if im == fzero: return mpf_cos_sin(re, prec, rnd) if re == fzero: return mpf_exp(mpf_neg(im), prec, rnd), fzero ey = mpf_exp(mpf_neg(im), prec + 10) c, s = mpf_cos_sin(re, prec + 10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im
def mpc_expjpi(z, prec, rnd='f'): re, im = z if im == fzero: return mpf_cos_sin_pi(re, prec, rnd) sign, man, exp, bc = im wp = prec + 10 if man: wp += max(0, exp + bc) im = mpf_neg(mpf_mul(mpf_pi(wp), im, wp)) if re == fzero: return mpf_exp(im, prec, rnd), fzero ey = mpf_exp(im, prec + 10) c, s = mpf_cos_sin_pi(re, prec + 10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im
def khinchin_fixed(prec): wp = int(prec + prec**0.5 + 15) s = MPZ_ZERO fac = from_int(4) t = ONE = MPZ_ONE << wp pi = mpf_pi(wp) pipow = twopi2 = mpf_shift(mpf_mul(pi, pi, wp), 2) n = 1 while 1: zeta2n = mpf_abs(mpf_bernoulli(2*n, wp)) zeta2n = mpf_mul(zeta2n, pipow, wp) zeta2n = mpf_div(zeta2n, fac, wp) zeta2n = to_fixed(zeta2n, wp) term = (((zeta2n - ONE) * t) // n) >> wp if term < 100: break #if not n % 10: # print n, math.log(int(abs(term))) s += term t += ONE//(2*n+1) - ONE//(2*n) n += 1 fac = mpf_mul_int(fac, (2*n)*(2*n-1), wp) pipow = mpf_mul(pipow, twopi2, wp) s = (s << wp) // ln2_fixed(wp) K = mpf_exp(from_man_exp(s, -wp), wp) K = to_fixed(K, prec) return K
def mpc_zeta(s, prec, rnd=round_fast, alt=0): re, im = s if im == fzero: return mpf_zeta(re, prec, rnd, alt), fzero wp = prec + 20 # Reflection formula. To be rigorous, we should reflect to the left of # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary # slowdown for interesting values of s if mpf_lt(re, fzero): # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpc_sub(mpc_one, s, 10*wp) a = mpc_gamma(y, wp) b = mpc_zeta(y, wp) c = mpc_sin_pi(mpc_shift(s, -1), wp) rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im mag = max(rexp+rbc, iexp+ibc) wp2 = wp + mag pi = mpf_pi(wp+wp2) pi2 = (mpf_shift(pi, 1), fzero) d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) return mpc_mul(a,mpc_mul(b,mpc_mul(c,d,wp),wp),prec,rnd) n = int(wp/2.54 + 5) n += int(0.9*abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MP_ZERO tim = MP_ZERO one = MP_ONE << wp one_2wp = MP_ONE << (2*wp) critical_line = re == fhalf for k in xrange(n): log = log_int_fixed(k+1, wp) # A square root is much cheaper than an exp if critical_line: w = one_2wp // sqrt_fixed((k+1) << wp, wp) else: w = to_fixed(mpf_exp(from_man_exp(-ref*log, -2*wp), wp), wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = cos_sin(from_man_exp(-imf * log_int_fixed(k+1, wp), -2*wp), wp) tre += (w * to_fixed(wre, wp)) >> wp tim += (w * to_fixed(wim, wp)) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) if alt: return mpc_pos((tre, tim), prec, rnd) else: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_div((tre, tim), q, prec, rnd)
def mpc_zeta(s, prec, rnd): re, im = s wp = prec + 20 n = int(wp/2.54 + 5) n += int(0.9*abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MP_ZERO tim = MP_ZERO one = MP_ONE << wp one_2wp = MP_ONE << (2*wp) critical_line = re == fhalf for k in xrange(n): log = log_int_fixed(k+1, wp) # A square root is much cheaper than an exp if critical_line: w = one_2wp // sqrt_fixed((k+1) << wp, wp) else: w = to_fixed(mpf_exp(from_man_exp(-ref*log, -2*wp), wp), wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = cos_sin(from_man_exp(-imf * log_int_fixed(k+1, wp), -2*wp), wp) tre += (w * to_fixed(wre, wp)) >> wp tim += (w * to_fixed(wim, wp)) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_div((tre, tim), q, prec, rnd)
def mpc_expjpi(z, prec, rnd="f"): re, im = z if im == fzero: return mpf_cos_sin_pi(re, prec, rnd) sign, man, exp, bc = im wp = prec + 10 if man: wp += max(0, exp + bc) im = mpf_neg(mpf_mul(mpf_pi(wp), im, wp)) if re == fzero: return mpf_exp(im, prec, rnd), fzero ey = mpf_exp(im, prec + 10) c, s = mpf_cos_sin_pi(re, prec + 10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im
def khinchin_fixed(prec): wp = int(prec + prec**0.5 + 15) s = MP_ZERO fac = from_int(4) t = ONE = MP_ONE << wp pi = mpf_pi(wp) pipow = twopi2 = mpf_shift(mpf_mul(pi, pi, wp), 2) n = 1 while 1: zeta2n = mpf_abs(mpf_bernoulli(2 * n, wp)) zeta2n = mpf_mul(zeta2n, pipow, wp) zeta2n = mpf_div(zeta2n, fac, wp) zeta2n = to_fixed(zeta2n, wp) term = (((zeta2n - ONE) * t) // n) >> wp if term < 100: break #if not n % 100: # print n, nstr(ln(term)) s += term t += ONE // (2 * n + 1) - ONE // (2 * n) n += 1 fac = mpf_mul_int(fac, (2 * n) * (2 * n - 1), wp) pipow = mpf_mul(pipow, twopi2, wp) s = (s << wp) // ln2_fixed(wp) K = mpf_exp(from_man_exp(s, -wp), wp) K = to_fixed(K, prec) return K
def mpc_zeta(s, prec, rnd): re, im = s wp = prec + 20 n = int(wp / 2.54 + 5) n += int(0.9 * abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MP_ZERO tim = MP_ZERO one = MP_ONE << wp one_2wp = MP_ONE << (2 * wp) critical_line = re == fhalf for k in xrange(n): log = log_int_fixed(k + 1, wp) # A square root is much cheaper than an exp if critical_line: w = one_2wp // sqrt_fixed((k + 1) << wp, wp) else: w = to_fixed(mpf_exp(from_man_exp(-ref * log, -2 * wp), wp), wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = cos_sin( from_man_exp(-imf * log_int_fixed(k + 1, wp), -2 * wp), wp) tre += (w * to_fixed(wre, wp)) >> wp tim += (w * to_fixed(wim, wp)) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_div((tre, tim), q, prec, rnd)
def exp_fixed_prod(x, wp): u = from_man_exp(x, -2 * wp, wp) esign, eman, eexp, ebc = mpf_exp(u, wp) offset = eexp + wp if offset >= 0: return eman << offset else: return eman >> (-offset)
def exp_fixed_prod(x, wp): u = from_man_exp(x, -2*wp, wp) esign, eman, eexp, ebc = mpf_exp(u, wp) offset = eexp + wp if offset >= 0: return eman << offset else: return eman >> (-offset)
def calc_spouge_coefficients(a, prec): wp = prec + int(a * 1.4) c = [0] * a # b = exp(a-1) b = mpf_exp(from_int(a - 1), wp) # e = exp(1) e = mpf_exp(fone, wp) # sqrt(2*pi) sq2pi = mpf_sqrt(mpf_shift(mpf_pi(wp), 1), wp) c[0] = to_fixed(sq2pi, prec) for k in xrange(1, a): # c[k] = ((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k) term = mpf_mul_int(b, ((-1)**(k - 1) * (a - k)**k), wp) term = mpf_div(term, mpf_sqrt(from_int(a - k), wp), wp) c[k] = to_fixed(term, prec) # b = b / (e * k) b = mpf_div(b, mpf_mul(e, from_int(k), wp), wp) return c
def calc_spouge_coefficients(a, prec): wp = prec + int(a*1.4) c = [0] * a # b = exp(a-1) b = mpf_exp(from_int(a-1), wp) # e = exp(1) e = mpf_exp(fone, wp) # sqrt(2*pi) sq2pi = mpf_sqrt(mpf_shift(mpf_pi(wp), 1), wp) c[0] = to_fixed(sq2pi, prec) for k in xrange(1, a): # c[k] = ((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k) term = mpf_mul_int(b, ((-1)**(k-1) * (a-k)**k), wp) term = mpf_div(term, mpf_sqrt(from_int(a-k), wp), wp) c[k] = to_fixed(term, prec) # b = b / (e * k) b = mpf_div(b, mpf_mul(e, from_int(k), wp), wp) return c
def glaisher_fixed(prec): wp = prec + 30 # Number of direct terms to sum before applying the Euler-Maclaurin # formula to the tail. TODO: choose more intelligently N = int(0.33 * prec + 5) ONE = MP_ONE << wp # Euler-Maclaurin, step 1: sum log(k)/k**2 for k from 2 to N-1 s = MP_ZERO for k in range(2, N): #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) # 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 s += logN // (N**2 * 2) # E-M step 4: the series of derivatives pN = N**3 a = 1 b = -2 j = 3 fac = from_int(2) k = 1 while 1: # D(2*k-1) * B(2*k) / fac(2*k) [D(n) = nth derivative] D = ((a << wp) + b * logN) // pN D = from_man_exp(D, -wp) B = mpf_bernoulli(2 * k, wp) term = mpf_mul(B, D, wp) term = mpf_div(term, fac, wp) term = to_fixed(term, wp) if abs(term) < 100: break #if not k % 10: # print k, math.log(int(abs(term)), 10) s -= term # Advance derivative twice a, b, pN, j = b - a * j, -j * b, pN * N, j + 1 a, b, pN, j = b - a * j, -j * b, pN * N, j + 1 k += 1 fac = mpf_mul_int(fac, (2 * k) * (2 * k - 1), wp) # A = exp((6*s/pi**2 + log(2*pi) + euler)/12) pi = pi_fixed(wp) 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 //= 12 A = mpf_exp(from_man_exp(s, -wp), wp) return to_fixed(A, prec)
def glaisher_fixed(prec): wp = prec + 30 # Number of direct terms to sum before applying the Euler-Maclaurin # formula to the tail. TODO: choose more intelligently N = int(0.33*prec + 5) ONE = MPZ_ONE << wp # Euler-Maclaurin, step 1: sum log(k)/k**2 for k from 2 to N-1 s = MPZ_ZERO for k in range(2, N): #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) # 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 s += logN // (N**2 * 2) # E-M step 4: the series of derivatives pN = N**3 a = 1 b = -2 j = 3 fac = from_int(2) k = 1 while 1: # D(2*k-1) * B(2*k) / fac(2*k) [D(n) = nth derivative] D = ((a << wp) + b*logN) // pN D = from_man_exp(D, -wp) B = mpf_bernoulli(2*k, wp) term = mpf_mul(B, D, wp) term = mpf_div(term, fac, wp) term = to_fixed(term, wp) if abs(term) < 100: break #if not k % 10: # print k, math.log(int(abs(term)), 10) s -= term # Advance derivative twice a, b, pN, j = b-a*j, -j*b, pN*N, j+1 a, b, pN, j = b-a*j, -j*b, pN*N, j+1 k += 1 fac = mpf_mul_int(fac, (2*k)*(2*k-1), wp) # A = exp((6*s/pi**2 + log(2*pi) + euler)/12) pi = pi_fixed(wp) 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 //= 12 A = mpf_exp(from_man_exp(s, -wp), wp) return to_fixed(A, prec)
def mpf_zeta(s, prec, rnd=round_fast): sign, man, exp, bc = s if not man: if s == fzero: return mpf_neg(fhalf) if s == finf: return fone return fnan wp = prec + 20 # First term vanishes? if (not sign) and (exp + bc > (math.log(wp,2) + 2)): if rnd in (round_up, round_ceiling): return mpf_add(fone, mpf_shift(fone,-wp-10), prec, rnd) return fone elif exp >= 0: return mpf_zeta_int(to_int(s), prec, rnd) # Less than 0.5? if sign or (exp+bc) < 0: # XXX: -1 should be done exactly y = mpf_sub(fone, s, 10*wp) a = mpf_gamma(y, wp) b = mpf_zeta(y, wp) c = mpf_sin_pi(mpf_shift(s, -1), wp) wp2 = wp + (exp+bc) pi = mpf_pi(wp+wp2) d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) return mpf_mul(a,mpf_mul(b,mpf_mul(c,d,wp),wp),prec,rnd) t = MP_ZERO #wp += 16 - (prec & 15) # Use Borwein's algorithm n = int(wp/2.54 + 5) d = borwein_coefficients(n) t = MP_ZERO sf = to_fixed(s, wp) for k in xrange(n): u = from_man_exp(-sf*log_int_fixed(k+1, wp), -2*wp, wp) esign, eman, eexp, ebc = mpf_exp(u, wp) offset = eexp + wp if offset >= 0: w = ((d[k] - d[n]) * eman) << offset else: w = ((d[k] - d[n]) * eman) >> (-offset) if k & 1: t -= w else: t += w t = t // (-d[n]) t = from_man_exp(t, -wp, wp) q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_div(t, q, prec, rnd)
def mpf_zeta(s, prec, rnd=round_fast): sign, man, exp, bc = s if not man: if s == fzero: return mpf_neg(fhalf) if s == finf: return fone return fnan wp = prec + 20 # First term vanishes? if (not sign) and (exp + bc > (math.log(wp, 2) + 2)): if rnd in (round_up, round_ceiling): return mpf_add(fone, mpf_shift(fone, -wp - 10), prec, rnd) return fone elif exp >= 0: return mpf_zeta_int(to_int(s), prec, rnd) # Less than 0.5? if sign or (exp + bc) < 0: # XXX: -1 should be done exactly y = mpf_sub(fone, s, 10 * wp) a = mpf_gamma(y, wp) b = mpf_zeta(y, wp) c = mpf_sin_pi(mpf_shift(s, -1), wp) wp2 = wp + (exp + bc) pi = mpf_pi(wp + wp2) d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) return mpf_mul(a, mpf_mul(b, mpf_mul(c, d, wp), wp), prec, rnd) t = MP_ZERO #wp += 16 - (prec & 15) # Use Borwein's algorithm n = int(wp / 2.54 + 5) d = borwein_coefficients(n) t = MP_ZERO sf = to_fixed(s, wp) for k in xrange(n): u = from_man_exp(-sf * log_int_fixed(k + 1, wp), -2 * wp, wp) esign, eman, eexp, ebc = mpf_exp(u, wp) offset = eexp + wp if offset >= 0: w = ((d[k] - d[n]) * eman) << offset else: w = ((d[k] - d[n]) * eman) >> (-offset) if k & 1: t -= w else: t += w t = t // (-d[n]) t = from_man_exp(t, -wp, wp) q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_div(t, q, prec, rnd)
def mpf_gamma(x, prec, rounding=round_fast, p1=1): """ Computes the gamma function of a real floating-point argument. With p1=0, computes a factorial instead. """ sign, man, exp, bc = x if not man: if x == finf: return finf if x == fninf or x == fnan: return fnan # More precision is needed for enormous x. TODO: # use Stirling's formula + Euler-Maclaurin summation size = exp + bc if size > 5: size = int(size * math.log(size,2)) wp = prec + max(0, size) + 15 if exp >= 0: if sign or (p1 and not man): raise ValueError("gamma function pole") # A direct factorial is fastest if exp + bc <= 10: return from_int(ifac((man<<exp)-p1), prec, rounding) reflect = sign or exp+bc < -1 if p1: # Should be done exactly! x = mpf_sub(x, fone) # x < 0.25 if reflect: # gamma = pi / (sin(pi*x) * gamma(1-x)) wp += 15 pix = mpf_mul(x, mpf_pi(wp), wp) t = mpf_sin_pi(x, wp) g = mpf_gamma(mpf_sub(fone, x), wp) return mpf_div(pix, mpf_mul(t, g, wp), prec, rounding) sprec, a, c = get_spouge_coefficients(wp) s = spouge_sum_real(x, sprec, a, c) # gamma = exp(log(x+a)*(x+0.5) - xpa) * s xpa = mpf_add(x, from_int(a), wp) logxpa = mpf_log(xpa, wp) xph = mpf_add(x, fhalf, wp) t = mpf_sub(mpf_mul(logxpa, xph, wp), xpa, wp) t = mpf_mul(mpf_exp(t, wp), s, prec, rounding) return t
def mpf_gamma(x, prec, rounding=round_fast, p1=1): """ Computes the gamma function of a real floating-point argument. With p1=0, computes a factorial instead. """ sign, man, exp, bc = x if not man: if x == finf: return finf if x == fninf or x == fnan: return fnan # More precision is needed for enormous x. TODO: # use Stirling's formula + Euler-Maclaurin summation size = exp + bc if size > 5: size = int(size * math.log(size, 2)) wp = prec + max(0, size) + 15 if exp >= 0: if sign or (p1 and not man): raise ValueError("gamma function pole") # A direct factorial is fastest if exp + bc <= 10: return from_int(int_fac((man << exp) - p1), prec, rounding) reflect = sign or exp + bc < -1 if p1: # Should be done exactly! x = mpf_sub(x, fone, bc - exp + 2) # x < 0.25 if reflect: # gamma = pi / (sin(pi*x) * gamma(1-x)) wp += 15 pix = mpf_mul(x, mpf_pi(wp), wp) t = mpf_sin_pi(x, wp) g = mpf_gamma(mpf_sub(fone, x, wp), wp) return mpf_div(pix, mpf_mul(t, g, wp), prec, rounding) sprec, a, c = get_spouge_coefficients(wp) s = spouge_sum_real(x, sprec, a, c) # gamma = exp(log(x+a)*(x+0.5) - xpa) * s xpa = mpf_add(x, from_int(a), wp) logxpa = mpf_log(xpa, wp) xph = mpf_add(x, fhalf, wp) t = mpf_sub(mpf_mul(logxpa, xph, wp), xpa, wp) t = mpf_mul(mpf_exp(t, wp), s, prec, rounding) return t
def mpf_ei(x, prec, rnd=round_fast, e1=False): if e1: x = mpf_neg(x) sign, man, exp, bc = x if e1 and not sign: if x == fzero: return finf raise ComplexResult("E1(x) for x < 0") if man: xabs = 0, man, exp, bc xmag = exp + bc wp = prec + 20 can_use_asymp = xmag > wp if not can_use_asymp: if exp >= 0: xabsint = man << exp else: xabsint = man >> (-exp) can_use_asymp = xabsint > int(wp * 0.693) + 10 if can_use_asymp: if xmag > wp: v = fone else: v = from_man_exp(ei_asymptotic(to_fixed(x, wp), wp), -wp) v = mpf_mul(v, mpf_exp(x, wp), wp) v = mpf_div(v, x, prec, rnd) else: wp += 2 * int(to_int(xabs)) u = to_fixed(x, wp) v = ei_taylor(u, wp) + euler_fixed(wp) t1 = from_man_exp(v, -wp) t2 = mpf_log(xabs, wp) v = mpf_add(t1, t2, prec, rnd) else: if x == fzero: v = fninf elif x == finf: v = finf elif x == fninf: v = fzero else: v = fnan if e1: v = mpf_neg(v) return v
def mpf_ei(x, prec, rnd=round_fast, e1=False): if e1: x = mpf_neg(x) sign, man, exp, bc = x if e1 and not sign: if x == fzero: return finf raise ComplexResult("E1(x) for x < 0") if man: xabs = 0, man, exp, bc xmag = exp+bc wp = prec + 20 can_use_asymp = xmag > wp if not can_use_asymp: if exp >= 0: xabsint = man << exp else: xabsint = man >> (-exp) can_use_asymp = xabsint > int(wp*0.693) + 10 if can_use_asymp: if xmag > wp: v = fone else: v = from_man_exp(ei_asymptotic(to_fixed(x, wp), wp), -wp) v = mpf_mul(v, mpf_exp(x, wp), wp) v = mpf_div(v, x, prec, rnd) else: wp += 2*int(to_int(xabs)) u = to_fixed(x, wp) v = ei_taylor(u, wp) + euler_fixed(wp) t1 = from_man_exp(v,-wp) t2 = mpf_log(xabs,wp) v = mpf_add(t1, t2, prec, rnd) else: if x == fzero: v = fninf elif x == finf: v = finf elif x == fninf: v = fzero else: v = fnan if e1: v = mpf_neg(v) return v
def mpf_erfc(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fone if x == finf: return fzero if x == fninf: return ftwo return fnan wp = prec + 20 mag = bc + exp # Preserve full accuracy when exponent grows huge wp += max(0, 2 * mag) regular_erf = sign or mag < 2 if regular_erf or not erfc_check_series(x, wp): if regular_erf: return mpf_sub(fone, mpf_erf(x, prec + 10, negative_rnd[rnd]), prec, rnd) # 1-erf(x) ~ exp(-x^2), increase prec to deal with cancellation n = to_int(x) return mpf_sub(fone, mpf_erf(x, prec + int(n**2 * 1.44) + 10), prec, rnd) s = term = MP_ONE << wp term_prev = 0 t = (2 * to_fixed(x, wp)**2) >> wp k = 1 while 1: term = ((term * (2 * k - 1)) << wp) // t if k > 4 and term > term_prev or not term: break if k & 1: s -= term else: s += term term_prev = term #print k, to_str(from_man_exp(term, -wp, 50), 10) k += 1 s = (s << wp) // sqrt_fixed(pi_fixed(wp), wp) s = from_man_exp(s, -wp, wp) z = mpf_exp(mpf_neg(mpf_mul(x, x, wp), wp), wp) y = mpf_div(mpf_mul(z, s, wp), x, prec, rnd) return y
def mpf_erfc(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fone if x == finf: return fzero if x == fninf: return ftwo return fnan wp = prec + 20 mag = bc+exp # Preserve full accuracy when exponent grows huge wp += max(0, 2*mag) regular_erf = sign or mag < 2 if regular_erf or not erfc_check_series(x, wp): if regular_erf: return mpf_sub(fone, mpf_erf(x, prec+10, negative_rnd[rnd]), prec, rnd) # 1-erf(x) ~ exp(-x^2), increase prec to deal with cancellation n = to_int(x)+1 return mpf_sub(fone, mpf_erf(x, prec + int(n**2*1.44) + 10), prec, rnd) s = term = MPZ_ONE << wp term_prev = 0 t = (2 * to_fixed(x, wp) ** 2) >> wp k = 1 while 1: term = ((term * (2*k - 1)) << wp) // t if k > 4 and term > term_prev or not term: break if k & 1: s -= term else: s += term term_prev = term #print k, to_str(from_man_exp(term, -wp, 50), 10) k += 1 s = (s << wp) // sqrt_fixed(pi_fixed(wp), wp) s = from_man_exp(s, -wp, wp) z = mpf_exp(mpf_neg(mpf_mul(x,x,wp),wp),wp) y = mpf_div(mpf_mul(z, s, wp), x, prec, rnd) return y
def mpi_exp(s, prec): sa, sb = s # exp is monotonous a = mpf_exp(sa, prec, round_floor) b = mpf_exp(sb, prec, round_ceiling) return a, b
def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False): re, im = s if im == fzero: return mpf_zeta(re, prec, rnd, alt), fzero # slow for large s if (not force) and mpf_gt(mpc_abs(s, 10), from_int(prec)): raise NotImplementedError wp = prec + 20 # Near pole r = mpc_sub(mpc_one, s, wp) asign, aman, aexp, abc = mpc_abs(r, 10) pole_dist = -2 * (aexp + abc) if pole_dist > wp: if alt: q = mpf_ln2(wp) y = mpf_mul(q, mpf_euler(wp), wp) g = mpf_shift(mpf_mul(q, q, wp), -1) g = mpf_sub(y, g) z = mpc_mul_mpf(r, mpf_neg(g), wp) z = mpc_add_mpf(z, q, wp) return mpc_pos(z, prec, rnd) else: q = mpc_neg(mpc_div(mpc_one, r, wp)) q = mpc_add_mpf(q, mpf_euler(wp), wp) return mpc_pos(q, prec, rnd) else: wp += max(0, pole_dist) # Reflection formula. To be rigorous, we should reflect to the left of # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary # slowdown for interesting values of s if mpf_lt(re, fzero): # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpc_sub(mpc_one, s, 10 * wp) a = mpc_gamma(y, wp) b = mpc_zeta(y, wp) c = mpc_sin_pi(mpc_shift(s, -1), wp) rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im mag = max(rexp + rbc, iexp + ibc) wp2 = wp + mag pi = mpf_pi(wp + wp2) pi2 = (mpf_shift(pi, 1), fzero) d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) return mpc_mul(a, mpc_mul(b, mpc_mul(c, d, wp), wp), prec, rnd) n = int(wp / 2.54 + 5) n += int(0.9 * abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MPZ_ZERO tim = MPZ_ZERO one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2 * wp) critical_line = re == fhalf for k in xrange(n): log = log_int_fixed(k + 1, wp) # A square root is much cheaper than an exp if critical_line: w = one_2wp // sqrt_fixed((k + 1) << wp, wp) else: w = to_fixed(mpf_exp(from_man_exp(-ref * log, -2 * wp), wp), wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = mpf_cos_sin(from_man_exp(-imf * log, -2 * wp), wp) tre += (w * to_fixed(wre, wp)) >> wp tim += (w * to_fixed(wim, wp)) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) if alt: return mpc_pos((tre, tim), prec, rnd) else: q = mpc_sub(mpc_one, mpc_pow(mpc_two, r, wp), wp) return mpc_div((tre, tim), q, prec, rnd)
def mpf_expint(n, x, prec, rnd=round_fast, gamma=False): """ E_n(x), n an integer, x real With gamma=True, computes Gamma(n,x) (upper incomplete gamma function) Returns (real, None) if real, otherwise (real, imag) The imaginary part is an optional branch cut term """ sign, man, exp, bc = x if not man: if gamma: if x == fzero: # Actually gamma function pole if n <= 0: return finf return mpf_gamma_int(n, prec, rnd) if x == finf: return fzero, None # TODO: could return finite imaginary value at -inf return fnan, fnan else: if x == fzero: if n > 1: return from_rational(1, n - 1, prec, rnd), None else: return finf, None if x == finf: return fzero, None return fnan, fnan n_orig = n if gamma: n = 1 - n wp = prec + 20 xmag = exp + bc # Beware of near-poles if xmag < -10: raise NotImplementedError nmag = bitcount(abs(n)) have_imag = n > 0 and sign negx = mpf_neg(x) # Skip series if direct convergence if n == 0 or 2 * nmag - xmag < -wp: if gamma: v = mpf_exp(negx, wp) re = mpf_mul(v, mpf_pow_int(x, n_orig - 1, wp), prec, rnd) else: v = mpf_exp(negx, wp) re = mpf_div(v, x, prec, rnd) else: # Finite number of terms, or... can_use_asymptotic_series = -3 * wp < n <= 0 # ...large enough? if not can_use_asymptotic_series: xi = abs(to_int(x)) m = min(max(1, xi - n), 2 * wp) siz = -n * nmag + (m + n) * bitcount(abs(m + n)) - m * xmag - ( 144 * m // 100) tol = -wp - 10 can_use_asymptotic_series = siz < tol if can_use_asymptotic_series: r = ((-MP_ONE) << (wp + wp)) // to_fixed(x, wp) m = n t = r * m s = MP_ONE << wp while m and t: s += t m += 1 t = (m * r * t) >> wp v = mpf_exp(negx, wp) if gamma: # ~ exp(-x) * x^(n-1) * (1 + ...) v = mpf_mul(v, mpf_pow_int(x, n_orig - 1, wp), wp) else: # ~ exp(-x)/x * (1 + ...) v = mpf_div(v, x, wp) re = mpf_mul(v, from_man_exp(s, -wp), prec, rnd) elif n == 1: re = mpf_neg(mpf_ei(negx, prec, rnd)) elif n > 0 and n < 3 * wp: T1 = mpf_neg(mpf_ei(negx, wp)) if gamma: if n_orig & 1: T1 = mpf_neg(T1) else: T1 = mpf_mul(T1, mpf_pow_int(negx, n - 1, wp), wp) r = t = to_fixed(x, wp) facs = [1] * (n - 1) for k in range(1, n - 1): facs[k] = facs[k - 1] * k facs = facs[::-1] s = facs[0] << wp for k in range(1, n - 1): if k & 1: s -= facs[k] * t else: s += facs[k] * t t = (t * r) >> wp T2 = from_man_exp(s, -wp, wp) T2 = mpf_mul(T2, mpf_exp(negx, wp)) if gamma: T2 = mpf_mul(T2, mpf_pow_int(x, n_orig, wp), wp) R = mpf_add(T1, T2) re = mpf_div(R, from_int(int_fac(n - 1)), prec, rnd) else: raise NotImplementedError if have_imag: M = from_int(-int_fac(n - 1)) if gamma: im = mpf_div(mpf_pi(wp), M, prec, rnd) else: im = mpf_div( mpf_mul(mpf_pi(wp), mpf_pow_int(negx, n_orig - 1, wp), wp), M, prec, rnd) return re, im else: return re, None
def mpf_zeta(s, prec, rnd=round_fast, alt=0): sign, man, exp, bc = s if not man: if s == fzero: if alt: return fhalf else: return mpf_neg(fhalf) if s == finf: return fone return fnan wp = prec + 20 # First term vanishes? if (not sign) and (exp + bc > (math.log(wp, 2) + 2)): return mpf_perturb(fone, alt, prec, rnd) # Optimize for integer arguments elif exp >= 0: if alt: if s == fone: return mpf_ln2(prec, rnd) z = mpf_zeta_int(to_int(s), wp, negative_rnd[rnd]) q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(z, q, prec, rnd) else: return mpf_zeta_int(to_int(s), prec, rnd) # Negative: use the reflection formula # Borwein only proves the accuracy bound for x >= 1/2. However, based on # tests, the accuracy without reflection is quite good even some distance # to the left of 1/2. XXX: verify this. if sign: # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(mpf_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpf_sub(fone, s, 10 * wp) a = mpf_gamma(y, wp) b = mpf_zeta(y, wp) c = mpf_sin_pi(mpf_shift(s, -1), wp) wp2 = wp + (exp + bc) pi = mpf_pi(wp + wp2) d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) return mpf_mul(a, mpf_mul(b, mpf_mul(c, d, wp), wp), prec, rnd) t = MP_ZERO #wp += 16 - (prec & 15) # Use Borwein's algorithm n = int(wp / 2.54 + 5) d = borwein_coefficients(n) t = MP_ZERO sf = to_fixed(s, wp) for k in xrange(n): u = from_man_exp(-sf * log_int_fixed(k + 1, wp), -2 * wp, wp) esign, eman, eexp, ebc = mpf_exp(u, wp) offset = eexp + wp if offset >= 0: w = ((d[k] - d[n]) * eman) << offset else: w = ((d[k] - d[n]) * eman) >> (-offset) if k & 1: t -= w else: t += w t = t // (-d[n]) t = from_man_exp(t, -wp, wp) if alt: return mpf_pos(t, prec, rnd) else: 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): re, im = s if im == fzero: return mpf_zeta(re, prec, rnd, alt), fzero wp = prec + 20 # Reflection formula. To be rigorous, we should reflect to the left of # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary # slowdown for interesting values of s if mpf_lt(re, fzero): # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpc_sub(mpc_one, s, 10 * wp) a = mpc_gamma(y, wp) b = mpc_zeta(y, wp) c = mpc_sin_pi(mpc_shift(s, -1), wp) rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im mag = max(rexp + rbc, iexp + ibc) wp2 = wp + mag pi = mpf_pi(wp + wp2) pi2 = (mpf_shift(pi, 1), fzero) d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) return mpc_mul(a, mpc_mul(b, mpc_mul(c, d, wp), wp), prec, rnd) n = int(wp / 2.54 + 5) n += int(0.9 * abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MP_ZERO tim = MP_ZERO one = MP_ONE << wp one_2wp = MP_ONE << (2 * wp) critical_line = re == fhalf for k in xrange(n): log = log_int_fixed(k + 1, wp) # A square root is much cheaper than an exp if critical_line: w = one_2wp // sqrt_fixed((k + 1) << wp, wp) else: w = to_fixed(mpf_exp(from_man_exp(-ref * log, -2 * wp), wp), wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = cos_sin( from_man_exp(-imf * log_int_fixed(k + 1, wp), -2 * wp), wp) tre += (w * to_fixed(wre, wp)) >> wp tim += (w * to_fixed(wim, wp)) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) if alt: return mpc_pos((tre, tim), prec, rnd) else: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_div((tre, tim), q, prec, rnd)
def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False): re, im = s if im == fzero: return mpf_zeta(re, prec, rnd, alt), fzero # slow for large s if (not force) and mpf_gt(mpc_abs(s, 10), from_int(prec)): raise NotImplementedError wp = prec + 20 # Near pole r = mpc_sub(mpc_one, s, wp) asign, aman, aexp, abc = mpc_abs(r, 10) pole_dist = -2*(aexp+abc) if pole_dist > wp: if alt: q = mpf_ln2(wp) y = mpf_mul(q, mpf_euler(wp), wp) g = mpf_shift(mpf_mul(q, q, wp), -1) g = mpf_sub(y, g) z = mpc_mul_mpf(r, mpf_neg(g), wp) z = mpc_add_mpf(z, q, wp) return mpc_pos(z, prec, rnd) else: q = mpc_neg(mpc_div(mpc_one, r, wp)) q = mpc_add_mpf(q, mpf_euler(wp), wp) return mpc_pos(q, prec, rnd) else: wp += max(0, pole_dist) # Reflection formula. To be rigorous, we should reflect to the left of # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary # slowdown for interesting values of s if mpf_lt(re, fzero): # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpc_sub(mpc_one, s, 10*wp) a = mpc_gamma(y, wp) b = mpc_zeta(y, wp) c = mpc_sin_pi(mpc_shift(s, -1), wp) rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im mag = max(rexp+rbc, iexp+ibc) wp2 = wp + mag pi = mpf_pi(wp+wp2) pi2 = (mpf_shift(pi, 1), fzero) d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) return mpc_mul(a,mpc_mul(b,mpc_mul(c,d,wp),wp),prec,rnd) n = int(wp/2.54 + 5) n += int(0.9*abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MPZ_ZERO tim = MPZ_ZERO one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2*wp) critical_line = re == fhalf for k in xrange(n): log = log_int_fixed(k+1, wp) # A square root is much cheaper than an exp if critical_line: w = one_2wp // sqrt_fixed((k+1) << wp, wp) else: w = to_fixed(mpf_exp(from_man_exp(-ref*log, -2*wp), wp), wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = mpf_cos_sin(from_man_exp(-imf * log, -2*wp), wp) tre += (w * to_fixed(wre, wp)) >> wp tim += (w * to_fixed(wim, wp)) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) if alt: return mpc_pos((tre, tim), prec, rnd) else: q = mpc_sub(mpc_one, mpc_pow(mpc_two, r, wp), wp) return mpc_div((tre, tim), q, prec, rnd)
def mpf_zeta(s, prec, rnd=round_fast, alt=0): sign, man, exp, bc = s if not man: if s == fzero: if alt: return fhalf else: return mpf_neg(fhalf) if s == finf: return fone return fnan wp = prec + 20 # First term vanishes? if (not sign) and (exp + bc > (math.log(wp,2) + 2)): return mpf_perturb(fone, alt, prec, rnd) # Optimize for integer arguments elif exp >= 0: if alt: if s == fone: return mpf_ln2(prec, rnd) z = mpf_zeta_int(to_int(s), wp, negative_rnd[rnd]) q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(z, q, prec, rnd) else: return mpf_zeta_int(to_int(s), prec, rnd) # Negative: use the reflection formula # Borwein only proves the accuracy bound for x >= 1/2. However, based on # tests, the accuracy without reflection is quite good even some distance # to the left of 1/2. XXX: verify this. if sign: # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(mpf_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpf_sub(fone, s, 10*wp) a = mpf_gamma(y, wp) b = mpf_zeta(y, wp) c = mpf_sin_pi(mpf_shift(s, -1), wp) wp2 = wp + (exp+bc) pi = mpf_pi(wp+wp2) d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) return mpf_mul(a,mpf_mul(b,mpf_mul(c,d,wp),wp),prec,rnd) # Near pole r = mpf_sub(fone, s, wp) asign, aman, aexp, abc = mpf_abs(r) pole_dist = -2*(aexp+abc) if pole_dist > wp: if alt: return mpf_ln2(prec, rnd) else: q = mpf_neg(mpf_div(fone, r, wp)) return mpf_add(q, mpf_euler(wp), prec, rnd) else: wp += max(0, pole_dist) t = MPZ_ZERO #wp += 16 - (prec & 15) # Use Borwein's algorithm n = int(wp/2.54 + 5) d = borwein_coefficients(n) t = MPZ_ZERO sf = to_fixed(s, wp) for k in xrange(n): u = from_man_exp(-sf*log_int_fixed(k+1, wp), -2*wp, wp) esign, eman, eexp, ebc = mpf_exp(u, wp) offset = eexp + wp if offset >= 0: w = ((d[k] - d[n]) * eman) << offset else: w = ((d[k] - d[n]) * eman) >> (-offset) if k & 1: t -= w else: t += w t = t // (-d[n]) t = from_man_exp(t, -wp, wp) if alt: return mpf_pos(t, prec, rnd) else: q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_div(t, q, prec, rnd)
def mpf_expint(n, x, prec, rnd=round_fast, gamma=False): """ E_n(x), n an integer, x real With gamma=True, computes Gamma(n,x) (upper incomplete gamma function) Returns (real, None) if real, otherwise (real, imag) The imaginary part is an optional branch cut term """ sign, man, exp, bc = x if not man: if gamma: if x == fzero: # Actually gamma function pole if n <= 0: return finf, None return mpf_gamma_int(n, prec, rnd), None if x == finf: return fzero, None # TODO: could return finite imaginary value at -inf return fnan, fnan else: if x == fzero: if n > 1: return from_rational(1, n-1, prec, rnd), None else: return finf, None if x == finf: return fzero, None return fnan, fnan n_orig = n if gamma: n = 1-n wp = prec + 20 xmag = exp + bc # Beware of near-poles if xmag < -10: raise NotImplementedError nmag = bitcount(abs(n)) have_imag = n > 0 and sign negx = mpf_neg(x) # Skip series if direct convergence if n == 0 or 2*nmag - xmag < -wp: if gamma: v = mpf_exp(negx, wp) re = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), prec, rnd) else: v = mpf_exp(negx, wp) re = mpf_div(v, x, prec, rnd) else: # Finite number of terms, or... can_use_asymptotic_series = -3*wp < n <= 0 # ...large enough? if not can_use_asymptotic_series: xi = abs(to_int(x)) m = min(max(1, xi-n), 2*wp) siz = -n*nmag + (m+n)*bitcount(abs(m+n)) - m*xmag - (144*m//100) tol = -wp-10 can_use_asymptotic_series = siz < tol if can_use_asymptotic_series: r = ((-MPZ_ONE) << (wp+wp)) // to_fixed(x, wp) m = n t = r*m s = MPZ_ONE << wp while m and t: s += t m += 1 t = (m*r*t) >> wp v = mpf_exp(negx, wp) if gamma: # ~ exp(-x) * x^(n-1) * (1 + ...) v = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), wp) else: # ~ exp(-x)/x * (1 + ...) v = mpf_div(v, x, wp) re = mpf_mul(v, from_man_exp(s, -wp), prec, rnd) elif n == 1: re = mpf_neg(mpf_ei(negx, prec, rnd)) elif n > 0 and n < 3*wp: T1 = mpf_neg(mpf_ei(negx, wp)) if gamma: if n_orig & 1: T1 = mpf_neg(T1) else: T1 = mpf_mul(T1, mpf_pow_int(negx, n-1, wp), wp) r = t = to_fixed(x, wp) facs = [1] * (n-1) for k in range(1,n-1): facs[k] = facs[k-1] * k facs = facs[::-1] s = facs[0] << wp for k in range(1, n-1): if k & 1: s -= facs[k] * t else: s += facs[k] * t t = (t*r) >> wp T2 = from_man_exp(s, -wp, wp) T2 = mpf_mul(T2, mpf_exp(negx, wp)) if gamma: T2 = mpf_mul(T2, mpf_pow_int(x, n_orig, wp), wp) R = mpf_add(T1, T2) re = mpf_div(R, from_int(ifac(n-1)), prec, rnd) else: raise NotImplementedError if have_imag: M = from_int(-ifac(n-1)) if gamma: im = mpf_div(mpf_pi(wp), M, prec, rnd) else: im = mpf_div(mpf_mul(mpf_pi(wp), mpf_pow_int(negx, n_orig-1, wp), wp), M, prec, rnd) return re, im else: return re, None
We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i) for the computation. This formula is very nice because it is pewrectly stable; since we just do real multiplications, the only numerical errors that can crewp in are single-ulp rnd errors. The formula is efficient since mpmath's real exp is quite fast and since we can compute cos and sin simultaneously. It is no problem if a and b are large; if the implementations of exp/cos/sin are accurate and efficient for all real numbers, then so is this function for all complex numbers. """ if a == fzero: return cos_sin(b, prec, rnd) mag = mpf_exp(a, prec + 4, rnd) c, s = cos_sin(b, prec + 4, rnd) re = mpf_mul(mag, c, prec, rnd) im = mpf_mul(mag, s, prec, rnd) return re, im def mpc_log(z, prec, rnd=round_fast): return mpf_log(mpc_abs(z, prec, rnd), prec, rnd), mpc_arg(z, prec, rnd) def mpc_cos((a, b), prec, rnd=round_fast): """Complex cosine. The formula used is cos(a+bi) = cos(a)*cosh(b) - sin(a)*sinh(b)*i. The same comments apply as for the complex exp: only real
We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i) for the computation. This formula is very nice because it is pewrectly stable; since we just do real multiplications, the only numerical errors that can crewp in are single-ulp rnd errors. The formula is efficient since mpmath's real exp is quite fast and since we can compute cos and sin simultaneously. It is no problem if a and b are large; if the implementations of exp/cos/sin are accurate and efficient for all real numbers, then so is this function for all complex numbers. """ if a == fzero: return cos_sin(b, prec, rnd) mag = mpf_exp(a, prec+4, rnd) c, s = cos_sin(b, prec+4, rnd) re = mpf_mul(mag, c, prec, rnd) im = mpf_mul(mag, s, prec, rnd) return re, im def mpc_log(z, prec, rnd=round_fast): return mpf_log(mpc_abs(z, prec, rnd), prec, rnd), mpc_arg(z, prec, rnd) def mpc_cos((a, b), prec, rnd=round_fast): """Complex cosine. The formula used is cos(a+bi) = cos(a)*cosh(b) - sin(a)*sinh(b)*i. The same comments apply as for the complex exp: only real multiplications are pewrormed, so no cancellation errors are possible. The formula is also efficient since we can compute both