def eval_hyp2f1(a,b,c,z): prec, rnd = prec_rounding ar, af, ac = parse_param(a) br, bf, bc = parse_param(b) cr, cf, cc = parse_param(c) absz = abs(z) if absz == 1: # TODO: determine whether it actually does, and otherwise # return infinity instead print "Warning: 2F1 might not converge for |z| = 1" if absz <= 1: # All rational if ar and br and cr: return sum_hyp2f1_rat(ar[0], br[0], cr[0], z) return hypsum(ar+br, af+bf, ac+bc, cr, cf, cc, z) # Use 1/z transformation a = (ar and _as_num(ar[0])) or convert_lossless(a) b = (br and _as_num(br[0])) or convert_lossless(b) c = (cr and _as_num(cr[0])) or convert_lossless(c) orig = mp.prec try: mp.prec = orig + 15 h1 = eval_hyp2f1(a, mpq_1-c+a, mpq_1-b+a, 1/z) h2 = eval_hyp2f1(b, mpq_1-c+b, mpq_1-a+b, 1/z) #s1 = G(c)*G(b-a)/G(b)/G(c-a) * (-z)**(-a) * h1 #s2 = G(c)*G(a-b)/G(a)/G(c-b) * (-z)**(-b) * h2 f1 = gammaprod([c,b-a],[b,c-a]) f2 = gammaprod([c,a-b],[a,c-b]) s1 = f1 * (-z)**(mpq_0-a) * h1 s2 = f2 * (-z)**(mpq_0-b) * h2 v = s1 + s2 finally: mp.prec = orig return +v
def jacobi_elliptic_cn(u, m, verbose=False): """ Implements the jacobi elliptic cn function, using the expansion in terms of q, from Abramowitz 16.23.2. """ u = convert_lossless(u) m = convert_lossless(m) if verbose: print >> sys.stderr, '\nelliptic.jacobi_elliptic_cn' print >> sys.stderr, '\tu: %1.12f' % u print >> sys.stderr, '\tm: %1.12f' % m zero = mpf('0') onehalf = mpf('0.5') one = mpf('1') two = mpf('2') if m == zero: # cn collapses to cos(u) if verbose: print >> sys.stderr, 'cn: special case, m == 0' return cos(u) elif m == one: # cn collapses to sech(u) if verbose: print >> sys.stderr, 'cn: special case, m == 1' return sech(u) else: k = sqrt(m) # convert m to k q = calculate_nome(k) kprimesquared = one - k**2 kprime = sqrt(kprimesquared) v = (pi * u) / (two*ellipk(k**2)) sum = zero term = zero # series starts at zero if verbose: print >> sys.stderr, 'elliptic.jacobi_elliptic_cn: calculating' while True: factor1 = (q**(term + onehalf)) / (one + q**(two*term + one)) factor2 = cos((two*term + one)*v) term_n = factor1*factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if not factor2 == zero: #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + one answer = (two*pi) / (sqrt(m) * ellipk(k**2)) * sum return answer
def jacobi_theta_3(z, m): """ Implements the jacobi theta function 2, using the series expansion found in Abramowitz & Stegun [4]. z is any complex number, but only reals here? m is the parameter, which must be converted to the nome """ m = convert_lossless(m) z = convert_lossless(z) k = sqrt(m) q = calculate_nome(k) if abs(q) >= mpf('1'): raise ValueError delta_sum = 1 term = 1 # series starts at 1 zero = mpf('0') sum = zero if z == zero: factor2 = mpf('1') while True: factor1 = q**(term*term) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 else: while True: factor1 = q**(term*term) if z == zero: factor2 = 1 else: factor2 = cos(2*term*z) term_n = factor1 * factor2 sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break if factor2 != mpf('0'): # check precision iff cos != 0 #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 return 1 + 2*sum
def jacobi_theta_3(z, m): """ Implements the jacobi theta function 2, using the series expansion found in Abramowitz & Stegun [4]. z is any complex number, but only reals here? m is the parameter, which must be converted to the nome """ m = convert_lossless(m) z = convert_lossless(z) k = sqrt(m) q = calculate_nome(k) if abs(q) >= mpf('1'): raise ValueError delta_sum = 1 term = 1 # series starts at 1 zero = mpf('0') sum = zero if z == zero: factor2 = mpf('1') while True: factor1 = q**(term * term) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 else: while True: factor1 = q**(term * term) if z == zero: factor2 = 1 else: factor2 = cos(2 * term * z) term_n = factor1 * factor2 sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break if factor2 != mpf('0'): # check precision iff cos != 0 #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 return 1 + 2 * sum
def jacobi_elliptic_cn(u, m, verbose=False): """ Implements the jacobi elliptic cn function, using the expansion in terms of q, from Abramowitz 16.23.2. """ u = convert_lossless(u) m = convert_lossless(m) if verbose: print >> sys.stderr, '\nelliptic.jacobi_elliptic_cn' print >> sys.stderr, '\tu: %1.12f' % u print >> sys.stderr, '\tm: %1.12f' % m zero = mpf('0') onehalf = mpf('0.5') one = mpf('1') two = mpf('2') if m == zero: # cn collapses to cos(u) if verbose: print >> sys.stderr, 'cn: special case, m == 0' return cos(u) elif m == one: # cn collapses to sech(u) if verbose: print >> sys.stderr, 'cn: special case, m == 1' return sech(u) else: k = sqrt(m) # convert m to k q = calculate_nome(k) kprimesquared = one - k**2 kprime = sqrt(kprimesquared) v = (pi * u) / (two * ellipk(k**2)) sum = zero term = zero # series starts at zero if verbose: print >> sys.stderr, 'elliptic.jacobi_elliptic_cn: calculating' while True: factor1 = (q**(term + onehalf)) / (one + q**(two * term + one)) factor2 = cos((two * term + one) * v) term_n = factor1 * factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if not factor2 == zero: #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + one answer = (two * pi) / (sqrt(m) * ellipk(k**2)) * sum return answer
def eval_hyp2f1(a, b, c, z): prec, rnd = prec_rounding ar, af, ac = parse_param(a) br, bf, bc = parse_param(b) cr, cf, cc = parse_param(c) absz = abs(z) if absz == 1: # TODO: determine whether it actually does, and otherwise # return infinity instead print "Warning: 2F1 might not converge for |z| = 1" if absz <= 1: # All rational if ar and br and cr: return sum_hyp2f1_rat(ar[0], br[0], cr[0], z) return hypsum(ar + br, af + bf, ac + bc, cr, cf, cc, z) # Use 1/z transformation a = (ar and _as_num(ar[0])) or convert_lossless(a) b = (br and _as_num(br[0])) or convert_lossless(b) c = (cr and _as_num(cr[0])) or convert_lossless(c) orig = mp.prec try: mp.prec = orig + 15 h1 = eval_hyp2f1(a, mpq_1 - c + a, mpq_1 - b + a, 1 / z) h2 = eval_hyp2f1(b, mpq_1 - c + b, mpq_1 - a + b, 1 / z) #s1 = G(c)*G(b-a)/G(b)/G(c-a) * (-z)**(-a) * h1 #s2 = G(c)*G(a-b)/G(a)/G(c-b) * (-z)**(-b) * h2 f1 = gammaprod([c, b - a], [b, c - a]) f2 = gammaprod([c, a - b], [a, c - b]) s1 = f1 * (-z)**(mpq_0 - a) * h1 s2 = f2 * (-z)**(mpq_0 - b) * h2 v = s1 + s2 finally: mp.prec = orig return +v
def jacobi_elliptic_dn(u, m, verbose=False): """ Implements the jacobi elliptic cn function, using the expansion in terms of q, from Abramowitz 16.23.3. """ u = convert_lossless(u) m = convert_lossless(m) if verbose: print >> sys.stderr, '\nelliptic.jacobi_elliptic_dn' print >> sys.stderr, '\tu: %1.12f' % u print >> sys.stderr, '\tm: %1.12f' % m zero = mpf('0') onehalf = mpf('0.5') one = mpf('1') two = mpf('2') if m == zero: # dn collapes to 1 return one elif m == one: # dn collapses to sech(u) return sech(u) else: k = sqrt(m) # convert m to k q = calculate_nome(k) v = (pi * u) / (two*ellipk(k**2)) sum = zero term = one # series starts at one if verbose: print >> sys.stderr, 'elliptic.jacobi_elliptic_dn: calculating' while True: factor1 = (q**term) / (one + q**(two*term)) factor2 = cos(two*term*v) term_n = factor1*factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if not factor2 == zero: #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + one K = ellipk(k**2) answer = (pi / (two*K)) + (two*pi*sum)/(ellipk(k**2)) return answer
def jacobi_elliptic_dn(u, m, verbose=False): """ Implements the jacobi elliptic cn function, using the expansion in terms of q, from Abramowitz 16.23.3. """ u = convert_lossless(u) m = convert_lossless(m) if verbose: print >> sys.stderr, '\nelliptic.jacobi_elliptic_dn' print >> sys.stderr, '\tu: %1.12f' % u print >> sys.stderr, '\tm: %1.12f' % m zero = mpf('0') onehalf = mpf('0.5') one = mpf('1') two = mpf('2') if m == zero: # dn collapes to 1 return one elif m == one: # dn collapses to sech(u) return sech(u) else: k = sqrt(m) # convert m to k q = calculate_nome(k) v = (pi * u) / (two * ellipk(k**2)) sum = zero term = one # series starts at one if verbose: print >> sys.stderr, 'elliptic.jacobi_elliptic_dn: calculating' while True: factor1 = (q**term) / (one + q**(two * term)) factor2 = cos(two * term * v) term_n = factor1 * factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if not factor2 == zero: #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + one K = ellipk(k**2) answer = (pi / (two * K)) + (two * pi * sum) / (ellipk(k**2)) return answer
def transform(f, a, b): """ Given an integrand f defined over the interval [a, b], return an equivalent integrand g defined on the standard interval [-1, 1]. If a and b are finite, this is achived by means of a linear change of variables. If at least one point is infinite, the substitution t = 1/x is used. """ a = convert_lossless(a) b = convert_lossless(b) if (a, b) == (-1, 1): return f one = mpf(1) half = mpf(0.5) # The transformation 1/x sends [1, inf] to [0, 1], which in turn # can be transformed to [-1, 1] the usual way. For a double # infinite interval, we simply evaluate the function symmetrically if (a, b) == (-inf, inf): # return transform(lambda x: (f(-1/x+1)+f(1/x-1))/x**2, 0, 1) def y(x): u = 2 / (x + one) w = one - u return half * (f(w) + f(-w)) * u**2 return y if a == -inf: # return transform(lambda x: f(-1/x+b+1)/x**2, 0, 1) b1 = b + 1 def y(x): u = 2 / (x + one) return half * f(b1 - u) * u**2 return y if b == inf: # return transform(lambda x: f(1/x+a-1)/x**2, 0, 1) a1 = a - 1 def y(x): u = 2 / (x + one) return half * f(a1 + u) * u**2 return y # Simple linear change of variables C = (b - a) / 2 D = (b + a) / 2 def g(x): return C * f(D + C * x) return g
def calculate_nome(k): """ Calculate the nome, q, from the value for k. Useful factoids: k**2 = m; m is used in Abramowitz """ k = convert_lossless(k) if k > mpf('1'): # range error raise ValueError zero = mpf('0') one = mpf('1') if k == zero: return zero elif k == one: return one else: kprimesquared = one - k**2 kprime = sqrt(kprimesquared) top = ellipk(kprimesquared) bottom = ellipk(k**2) argument = mpf('-1')*pi*top/bottom nome = exp(argument) return nome
def npdf(x, mu=0, sigma=1): """ npdf(x, mu=0, sigma=1) -- probability density function of a normal distribution with mean value mu and variance sigma^2. """ sigma = convert_lossless(sigma) return exp(-(x-mu)**2/(2*sigma**2)) / (sigma*sqrt(2*pi))
def calculate_k(q, verbose=False): """ Calculates the value of k for a particular nome, q. Uses special cases of the jacobi theta functions, with q as an argument, rather than m. k = (v2(0, q)/v3(0, q))**2 """ zero = mpf('0') one = mpf('1') q = convert_lossless(q) if q > one or q < zero: raise ValueError # calculate v2(0, q) sum = zero term = zero # series starts at zero while True: factor1 = q**(term*(term + 1)) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if factor1 == zero: # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 v2 = 2*q**(mpf('0.25'))*sum # calculate v3(0, q) sum = zero term = one # series starts at one while True: factor1 = q**(term*term) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 v3 = one + 2*sum k = v2**2/v3**2 return k
def npdf(x, mu=0, sigma=1): """ npdf(x, mu=0, sigma=1) -- probability density function of a normal distribution with mean value mu and variance sigma^2. """ sigma = convert_lossless(sigma) return exp(-(x - mu)**2 / (2 * sigma**2)) / (sigma * sqrt(2 * pi))
def calculate_nome(k): """ Calculate the nome, q, from the value for k. Useful factoids: k**2 = m; m is used in Abramowitz """ k = convert_lossless(k) if k > mpf('1'): # range error raise ValueError zero = mpf('0') one = mpf('1') if k == zero: return zero elif k == one: return one else: kprimesquared = one - k**2 kprime = sqrt(kprimesquared) top = ellipk(kprimesquared) bottom = ellipk(k**2) argument = mpf('-1') * pi * top / bottom nome = exp(argument) return nome
def calculate_k(q, verbose=False): """ Calculates the value of k for a particular nome, q. Uses special cases of the jacobi theta functions, with q as an argument, rather than m. k = (v2(0, q)/v3(0, q))**2 """ zero = mpf('0') one = mpf('1') q = convert_lossless(q) if q > one or q < zero: raise ValueError # calculate v2(0, q) sum = zero term = zero # series starts at zero while True: factor1 = q**(term * (term + 1)) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if factor1 == zero: # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 v2 = 2 * q**(mpf('0.25')) * sum # calculate v3(0, q) sum = zero term = one # series starts at one while True: factor1 = q**(term * term) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 v3 = one + 2 * sum k = v2**2 / v3**2 return k
def sign(x): """Return sign(x), defined as x/abs(x), or 0 for x = 0.""" x = convert_lossless(x) if not x or isnan(x): return x if isinstance(x, mpf): return cmp(x, 0) return x / abs(x)
def transform(f, a, b): """ Given an integrand f defined over the interval [a, b], return an equivalent integrand g defined on the standard interval [-1, 1]. If a and b are finite, this is achived by means of a linear change of variables. If at least one point is infinite, the substitution t = 1/x is used. """ a = convert_lossless(a) b = convert_lossless(b) if (a, b) == (-1, 1): return f one = mpf(1) half = mpf(0.5) # The transformation 1/x sends [1, inf] to [0, 1], which in turn # can be transformed to [-1, 1] the usual way. For a double # infinite interval, we simply evaluate the function symmetrically if (a, b) == (-inf, inf): # return transform(lambda x: (f(-1/x+1)+f(1/x-1))/x**2, 0, 1) def y(x): u = 2/(x+one) w = one - u return half * (f(w)+f(-w)) * u**2 return y if a == -inf: # return transform(lambda x: f(-1/x+b+1)/x**2, 0, 1) b1 = b+1 def y(x): u = 2/(x+one) return half * f(b1-u) * u**2 return y if b == inf: # return transform(lambda x: f(1/x+a-1)/x**2, 0, 1) a1 = a-1 def y(x): u = 2/(x+one) return half * f(a1+u) * u**2 return y # Simple linear change of variables C = (b-a)/2 D = (b+a)/2 def g(x): return C * f(D + C*x) return g
def g(*args, **kwargs): orig = mp.prec try: args = [convert_lossless(z) for z in args] mp.prec = orig + 10 v = f(*args, **kwargs) finally: mp.prec = orig return +v
def parse_param(x): if isinstance(x, tuple): p, q = x return [[p, q]], [], [] if isinstance(x, (int, long)): return [[x, 1]], [], [] x = convert_lossless(x) if isinstance(x, mpf): return [], [x._mpf_], [] if isinstance(x, mpc): return [], [], [x._mpc_]
def quadosc(f, interval, period=None, zeros=None, alt=1): """ Integrates f(x) over interval = [a, b] where at least one of a and b is infinite and f is a slowly decaying oscillatory function. The zeros of f must be provided, either by specifying a period (suitable when f contains a pure sine or cosine factor) or providing a function that returns the nth zero (suitable when the oscillation is not strictly periodic). """ a, b = AS_POINTS(interval) a = convert_lossless(a) b = convert_lossless(b) if period is None and zeros is None: raise ValueError( \ "either the period or zeros keyword parameter must be specified") if a == -inf and b == inf: s1 = quadosc(f, [a, 0], zeros=zeros, period=period, alt=alt) s2 = quadosc(f, [0, b], zeros=zeros, period=period, alt=alt) return s1 + s2 if a == -inf: if zeros: return quadosc(lambda x:f(-x), [-b,-a], lambda n: zeros(-n), alt=alt) else: return quadosc(lambda x:f(-x), [-b,-a], period=period, alt=alt) if b != inf: raise ValueError("quadosc requires an infinite integration interval") if not zeros: zeros = lambda n: n*period/2 for n in range(1,10): p = zeros(n) if p > a: break if n >= 9: raise ValueError("zeros do not appear to be correctly indexed") if alt == 0: s = quadgl(f, [a, zeros(n+1)]) s += sumrich(lambda k: quadgl(f, [zeros(2*k), zeros(2*k+2)]), [n, inf]) else: s = quadgl(f, [a, zeros(n)]) s += sumsh(lambda k: quadgl(f, [zeros(k), zeros(k+1)]), [n, inf]) return s
def gammaprod(a, b): """ Computes the product / quotient of gamma functions G(a_0) G(a_1) ... G(a_p) ------------------------ G(b_0) G(b_1) ... G(a_q) with proper cancellation of poles (interpreting the expression as a limit). Returns +inf if the limit diverges. """ a = [convert_lossless(x) for x in a] b = [convert_lossless(x) for x in b] poles_num = [] poles_den = [] regular_num = [] regular_den = [] for x in a: [regular_num, poles_num][isnpint(x)].append(x) for x in b: [regular_den, poles_den][isnpint(x)].append(x) # One more pole in numerator or denominator gives 0 or inf if len(poles_num) < len(poles_den): return mpf(0) if len(poles_num) > len(poles_den): return mpf('+inf') # All poles cancel # lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i) p = mpf(1) orig = mp.prec try: mp.prec = orig + 15 while poles_num: i = poles_num.pop() j = poles_den.pop() p *= (-1)**(i + j) * gamma(1 - j) / gamma(1 - i) for x in regular_num: p *= gamma(x) for x in regular_den: p /= gamma(x) finally: mp.prec = orig return +p
def psi(m, z): """ Gives the polygamma function of order m of z, psi^(m)(z). Special cases are the digamma function (psi0), trigamma function (psi1), tetragamma (psi2) and pentagamma (psi4) functions. The parameter m should be a nonnegative integer. """ z = convert_lossless(z) m = int(m) if isinstance(z, mpf): return make_mpf(gammazeta.mpf_psi(m, z._mpf_, *prec_rounding)) else: return make_mpc(gammazeta.mpc_psi(m, z._mpc_, *prec_rounding))
def gammaprod(a, b): """ Computes the product / quotient of gamma functions G(a_0) G(a_1) ... G(a_p) ------------------------ G(b_0) G(b_1) ... G(a_q) with proper cancellation of poles (interpreting the expression as a limit). Returns +inf if the limit diverges. """ a = [convert_lossless(x) for x in a] b = [convert_lossless(x) for x in b] poles_num = [] poles_den = [] regular_num = [] regular_den = [] for x in a: [regular_num, poles_num][isnpint(x)].append(x) for x in b: [regular_den, poles_den][isnpint(x)].append(x) # One more pole in numerator or denominator gives 0 or inf if len(poles_num) < len(poles_den): return mpf(0) if len(poles_num) > len(poles_den): return mpf('+inf') # All poles cancel # lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i) p = mpf(1) orig = mp.prec try: mp.prec = orig + 15 while poles_num: i = poles_num.pop() j = poles_den.pop() p *= (-1)**(i+j) * gamma(1-j) / gamma(1-i) for x in regular_num: p *= gamma(x) for x in regular_den: p /= gamma(x) finally: mp.prec = orig return +p
def hyper(a_s, b_s, z): """ Hypergeometric function pFq, [ a_1, a_2, ..., a_p | ] pFq [ | z ] [ b_1, b_2, ..., b_q | ] The parameter lists a_s and b_s may contain real or complex numbers. Exact rational parameters can be given as tuples (p, q). """ p = len(a_s) q = len(b_s) z = convert_lossless(z) degree = p, q if degree == (0, 1): br, bf, bc = parse_param(b_s[0]) if br: return sum_hyp0f1_rat(br[0], z) return hypsum([], [], [], br, bf, bc, z) if degree == (1, 1): ar, af, ac = parse_param(a_s[0]) br, bf, bc = parse_param(b_s[0]) if ar and br: a, b = ar[0], br[0] return sum_hyp1f1_rat(a, b, z) return hypsum(ar, af, ac, br, bf, bc, z) if degree == (2, 1): return eval_hyp2f1(a_s[0], a_s[1], b_s[0], z) ars, afs, acs, brs, bfs, bcs = [], [], [], [], [], [] for a in a_s: r, f, c = parse_param(a) ars += r afs += f acs += c for b in b_s: r, f, c = parse_param(b) brs += r bfs += f bcs += c return hypsum(ars, afs, acs, brs, bfs, bcs, z)
def f(x, **kwargs): if not isinstance(x, mpnumeric): x = convert_lossless(x) prec, rounding = prec_rounding if kwargs: prec = kwargs.get('prec', prec) if 'dps' in kwargs: prec = dps_to_prec(kwargs['dps']) rounding = kwargs.get('rounding', rounding) if isinstance(x, mpf): try: return make_mpf(real_f(x._mpf_, prec, rounding)) except ComplexResult: # Handle propagation to complex if mp.trap_complex: raise return make_mpc(complex_f((x._mpf_, libmpf.fzero), prec, rounding)) elif isinstance(x, mpc): return make_mpc(complex_f(x._mpc_, prec, rounding)) elif isinstance(x, mpi): if interval_f: return make_mpi(interval_f(x._val, prec)) raise NotImplementedError("%s of a %s" % (name, type(x)))
def f(x, **kwargs): if not isinstance(x, mpnumeric): x = convert_lossless(x) prec, rounding = prec_rounding if kwargs: prec = kwargs.get('prec', prec) if 'dps' in kwargs: prec = dps_to_prec(kwargs['dps']) rounding = kwargs.get('rounding', rounding) if isinstance(x, mpf): try: return make_mpf(real_f(x._mpf_, prec, rounding)) except ComplexResult: # Handle propagation to complex if mp.trap_complex: raise return make_mpc( complex_f((x._mpf_, libmpf.fzero), prec, rounding)) elif isinstance(x, mpc): return make_mpc(complex_f(x._mpc_, prec, rounding)) elif isinstance(x, mpi): if interval_f: return make_mpi(interval_f(x._val, prec)) raise NotImplementedError("%s of a %s" % (name, type(x)))
def power(x, y): """Converts x and y to mpf or mpc and returns x**y = exp(y*log(x)).""" return convert_lossless(x)**convert_lossless(y)
def jacobi_elliptic_sn(u, m, verbose=False): """ Implements the jacobi elliptic sn function, using the expansion in terms of q, from Abramowitz 16.23.1. u is any complex number, m is the parameter Alternative implementation: Expansion in terms of jacobi theta functions appears to fail with round off error, despite I also think that the expansion in terms of q is much faster than four expansions in terms of q. ********************************** Previous implementation kept here: if not isinstance(u, mpf): raise TypeError if not isinstance(m, mpf): raise TypeError zero = mpf('0') if u == zero and m == 0: return zero else: q = calculate_nome(sqrt(m)) v3 = jacobi_theta_3(zero, q) v2 = jacobi_theta_2(zero, q) # mathworld says v4 arg1 = u / (v3*v3) v1 = jacobi_theta_1(arg1, q) v4 = jacobi_theta_4(arg1, q) sn = (v3/v2)*(v1/v4) return sn ********************************** """ u = convert_lossless(u) m = convert_lossless(m) if verbose: print >> sys.stderr, '\nelliptic.jacobi_elliptic_sn' print >> sys.stderr, '\tu: %1.12f' % u print >> sys.stderr, '\tm: %1.12f' % m zero = mpf('0') onehalf = mpf('0.5') one = mpf('1') two = mpf('2') if m == zero: # sn collapes to sin(u) if verbose: print >> sys.stderr, '\nsn: special case, m == 0' return sin(u) elif m == one: # sn collapses to tanh(u) if verbose: print >> sys.stderr, '\nsn: special case, m == 1' return tanh(u) else: k = sqrt(m) # convert m to k q = calculate_nome(k) v = (pi * u) / (two*ellipk(k**2)) if v == pi or v == zero: # sin factor always zero return zero sum = zero term = zero # series starts at zero while True: if verbose: print >> sys.stderr, 'elliptic.jacobi_elliptic_sn: calculating' factor1 = (q**(term + onehalf)) / (one - q**(two*term + one)) factor2 = sin((two*term + one)*v) term_n = factor1*factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if not factor2 == zero: #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + one answer = (two*pi) / (sqrt(m) * ellipk(k**2)) * sum return answer
def jacobi_theta_4(z, m): """ Implements the series expansion of the jacobi theta function 1, where z == 0. z is any complex number, but only reals here? m is the parameter, which must be converted to the nome """ m = convert_lossless(m) z = convert_lossless(z) k = sqrt(m) q = calculate_nome(k) if abs(q) >= mpf('1'): raise ValueError sum = 0 delta_sum = 1 term = 1 # series starts at 1 zero = mpf('0') if z == zero: factor2 = mpf('1') while True: if (term % 2) == 0: factor0 = 1 else: factor0 = -1 factor1 = q**(term*term) term_n = factor0 * factor1 * factor2 sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 else: while True: if (term % 2) == 0: factor0 = 1 else: factor0 = -1 factor1 = q**(term*term) if z == zero: factor2 = 1 else: factor2 = cos(2*term*z) term_n = factor0 * factor1 * factor2 sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break if factor2 != mpf('0'): # check precision iff cos != 0 #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 return 1 + 2*sum
def hypot(x, y): """Returns the Euclidean distance sqrt(x*x + y*y). Both x and y must be real.""" x = convert_lossless(x) y = convert_lossless(y) return make_mpf(libmpf.mpf_hypot(x._mpf_, y._mpf_, *prec_rounding))
def jacobi_elliptic_sn(u, m, verbose=False): """ Implements the jacobi elliptic sn function, using the expansion in terms of q, from Abramowitz 16.23.1. u is any complex number, m is the parameter Alternative implementation: Expansion in terms of jacobi theta functions appears to fail with round off error, despite I also think that the expansion in terms of q is much faster than four expansions in terms of q. ********************************** Previous implementation kept here: if not isinstance(u, mpf): raise TypeError if not isinstance(m, mpf): raise TypeError zero = mpf('0') if u == zero and m == 0: return zero else: q = calculate_nome(sqrt(m)) v3 = jacobi_theta_3(zero, q) v2 = jacobi_theta_2(zero, q) # mathworld says v4 arg1 = u / (v3*v3) v1 = jacobi_theta_1(arg1, q) v4 = jacobi_theta_4(arg1, q) sn = (v3/v2)*(v1/v4) return sn ********************************** """ u = convert_lossless(u) m = convert_lossless(m) if verbose: print >> sys.stderr, '\nelliptic.jacobi_elliptic_sn' print >> sys.stderr, '\tu: %1.12f' % u print >> sys.stderr, '\tm: %1.12f' % m zero = mpf('0') onehalf = mpf('0.5') one = mpf('1') two = mpf('2') if m == zero: # sn collapes to sin(u) if verbose: print >> sys.stderr, '\nsn: special case, m == 0' return sin(u) elif m == one: # sn collapses to tanh(u) if verbose: print >> sys.stderr, '\nsn: special case, m == 1' return tanh(u) else: k = sqrt(m) # convert m to k q = calculate_nome(k) v = (pi * u) / (two * ellipk(k**2)) if v == pi or v == zero: # sin factor always zero return zero sum = zero term = zero # series starts at zero while True: if verbose: print >> sys.stderr, 'elliptic.jacobi_elliptic_sn: calculating' factor1 = (q**(term + onehalf)) / (one - q**(two * term + one)) factor2 = sin((two * term + one) * v) term_n = factor1 * factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if not factor2 == zero: #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + one answer = (two * pi) / (sqrt(m) * ellipk(k**2)) * sum return answer
def frexp(x): """Convert x to a scaled number y in the range [0.5, 1). Returns (y, n) such that x = y * 2**n. No rounding is performed.""" x = convert_lossless(x) y, n = libmpf.mpf_frexp(x._mpf_) return make_mpf(y), n
def jacobi_theta_2(z, m, verbose=False): """ Implements the jacobi theta function 2, using the series expansion found in Abramowitz & Stegun [4]. z is any complex number, but only reals here? m is the parameter, which must be converted to the nome """ if verbose: print >> sys.stderr, 'elliptic.jacobi_theta_2' m = convert_lossless(m) z = convert_lossless(z) k = sqrt(m) q = calculate_nome(k) if verbose: print >> sys.stderr, '\tk: %f ' % k print >> sys.stderr, '\tq: %f ' % q if abs(q) >= mpf('1'): raise ValueError sum = 0 term = 0 # series starts at zero zero = mpf('0') if q == zero: if verbose: print >> sys.stderr, 'elliptic.jacobi_theta_2: q == 0, return 0' return zero else: if verbose: print >> sys.stderr, 'elliptic.jacobi_theta_2: calculating' if z == zero: factor2 = mpf('1') while True: factor1 = q**(term * (term + 1)) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if factor1 == zero: # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 else: while True: factor1 = q**(term * (term + 1)) if z == zero: factor2 = 1 else: factor2 = cos((2 * term + 1) * z) term_n = factor1 * factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if factor1 == zero: # all further terms will be zero break if factor2 != zero: # check precision iff cos != 0 #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 return (2 * q**(0.25)) * sum # can't get here print >> sys.stderr, 'elliptic.jacobi_theta_2 in impossible state' sys.exit(2)
def ldexp(x, n): """Calculate mpf(x) * 2**n efficiently. No rounding is performed.""" x = convert_lossless(x) return make_mpf(libmpf.mpf_shift(x._mpf_, n))
def power(x, y): """Converts x and y to mpf or mpc and returns x**y = exp(y*log(x)).""" return convert_lossless(x) ** convert_lossless(y)
def jacobi_theta_2(z, m, verbose=False): """ Implements the jacobi theta function 2, using the series expansion found in Abramowitz & Stegun [4]. z is any complex number, but only reals here? m is the parameter, which must be converted to the nome """ if verbose: print >> sys.stderr, 'elliptic.jacobi_theta_2' m = convert_lossless(m) z = convert_lossless(z) k = sqrt(m) q = calculate_nome(k) if verbose: print >> sys.stderr, '\tk: %f ' % k print >> sys.stderr, '\tq: %f ' % q if abs(q) >= mpf('1'): raise ValueError sum = 0 term = 0 # series starts at zero zero = mpf('0') if q == zero: if verbose: print >> sys.stderr, 'elliptic.jacobi_theta_2: q == 0, return 0' return zero else: if verbose: print >> sys.stderr, 'elliptic.jacobi_theta_2: calculating' if z == zero: factor2 = mpf('1') while True: factor1 = q**(term*(term + 1)) term_n = factor1 # suboptimal, kept for readability sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if factor1 == zero: # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 else: while True: factor1 = q**(term*(term + 1)) if z == zero: factor2 = 1 else: factor2 = cos((2*term + 1)*z) term_n = factor1 * factor2 sum = sum + term_n if verbose: print >> sys.stderr, '\tTerm: %d' % term, print >> sys.stderr, '\tterm_n: %e' % term_n, print >> sys.stderr, '\tsum: %e' % sum if factor1 == zero: # all further terms will be zero break if factor2 != zero: # check precision iff cos != 0 #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 return (2*q**(0.25))*sum # can't get here print >> sys.stderr, 'elliptic.jacobi_theta_2 in impossible state' sys.exit(2)
def polyroots(coeffs, maxsteps=50, cleanup=True, extraprec=10, error=False): """ Numerically locate all (complex) roots of a polynomial using the Durand-Kerner method. With error=True, this function returns a tuple (roots, err) where roots is a list of complex numbers sorted by absolute value, and err is an estimate of the maximum error. The polynomial should be given as a list of coefficients, in the same format as accepted by polyval(). The leading coefficient must be nonzero. These are the roots of x^3 - x^2 - 14*x + 24 and 4x^2 + 3x + 2: >>> nprint(polyroots([1,-1,-14,24]), 4) [-4.0, 2.0, 3.0] >>> nprint(polyroots([4,3,2], error=True)) ([(-0.375 - 0.599479j), (-0.375 + 0.599479j)], 2.22045e-16) """ if len(coeffs) <= 1: if not coeffs or not coeffs[0]: raise ValueError("Input to polyroots must not be the zero polynomial") # Constant polynomial with no roots return [] orig = mp.prec weps = +eps try: mp.prec += 10 deg = len(coeffs) - 1 # Must be monic lead = convert_lossless(coeffs[0]) if lead == 1: coeffs = map(convert_lossless, coeffs) else: coeffs = [c/lead for c in coeffs] f = lambda x: polyval(coeffs, x) roots = [mpc((0.4+0.9j)**n) for n in xrange(deg)] err = [mpf(1) for n in xrange(deg)] for step in xrange(maxsteps): if max(err).ae(0): break for i in xrange(deg): if not err[i].ae(0): p = roots[i] x = f(p) for j in range(deg): if i != j: try: x /= (p-roots[j]) except ZeroDivisionError: continue roots[i] = p - x err[i] = abs(x) if cleanup: for i in xrange(deg): if abs(roots[i].imag) < weps: roots[i] = roots[i].real elif abs(roots[i].real) < weps: roots[i] = roots[i].imag * 1j roots.sort(key=lambda x: (abs(x.imag), x.real)) finally: mp.prec = orig if error: err = max(err) err = max(err, ldexp(1, -orig+1)) return [+r for r in roots], +err else: return [+r for r in roots]
def jacobi_theta_4(z, m): """ Implements the series expansion of the jacobi theta function 1, where z == 0. z is any complex number, but only reals here? m is the parameter, which must be converted to the nome """ m = convert_lossless(m) z = convert_lossless(z) k = sqrt(m) q = calculate_nome(k) if abs(q) >= mpf('1'): raise ValueError sum = 0 delta_sum = 1 term = 1 # series starts at 1 zero = mpf('0') if z == zero: factor2 = mpf('1') while True: if (term % 2) == 0: factor0 = 1 else: factor0 = -1 factor1 = q**(term * term) term_n = factor0 * factor1 * factor2 sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 else: while True: if (term % 2) == 0: factor0 = 1 else: factor0 = -1 factor1 = q**(term * term) if z == zero: factor2 = 1 else: factor2 = cos(2 * term * z) term_n = factor0 * factor1 * factor2 sum = sum + term_n if factor1 == mpf('0'): # all further terms will be zero break if factor2 != mpf('0'): # check precision iff cos != 0 #if log(term_n, '10') < -1*mpf.dps: if abs(term_n) < eps: break term = term + 1 return 1 + 2 * sum
def modf(x, y): """Converts x and y to mpf or mpc and returns x % y""" x = convert_lossless(x) y = convert_lossless(y) return x % y
def modf(x,y): """Converts x and y to mpf or mpc and returns x % y""" x = convert_lossless(x) y = convert_lossless(y) return x % y
def atan2(y,x): """atan2(y, x) has the same magnitude as atan(y/x) but accounts for the signs of y and x. (Defined for real x and y only.)""" x = convert_lossless(x) y = convert_lossless(y) return make_mpf(libelefun.mpf_atan2(y._mpf_, x._mpf_, *prec_rounding))
def atan2(y, x): """atan2(y, x) has the same magnitude as atan(y/x) but accounts for the signs of y and x. (Defined for real x and y only.)""" x = convert_lossless(x) y = convert_lossless(y) return make_mpf(libelefun.mpf_atan2(y._mpf_, x._mpf_, *prec_rounding))