def npartitions(n, verbose=False): """ Calculate the partition function P(n), i.e. the number of ways that n can be written as a sum of positive integers. P(n) is computed using the Hardy-Ramanujan-Rademacher formula, described e.g. at http://mathworld.wolfram.com/PartitionFunctionP.html The correctness of this implementation has been tested for 10**n up to n = 8. """ n = int(n) if n < 0: return 0 if n <= 5: return [1, 1, 2, 3, 5, 7][n] # Estimate number of bits in p(n). This formula could be tidied pbits = int((math.pi*(2*n/3.)**0.5-math.log(4*n))/math.log(10)+1)*\ math.log(10,2) prec = p = int(pbits*1.1 + 100) s = fzero M = max(6, int(0.24*n**0.5+4)) sq23pi = mpf_mul(mpf_sqrt(from_rational(2,3,p), p), mpf_pi(p), p) sqrt8 = mpf_sqrt(from_int(8), p) for q in xrange(1, M): a = A(n,q,p) d = D(n,q,p, sq23pi, sqrt8) s = mpf_add(s, mpf_mul(a, d), prec) if verbose: print "step", q, "of", M, to_str(a, 10), to_str(d, 10) # On average, the terms decrease rapidly in magnitude. Dynamically # reducing the precision greatly improves performance. p = bitcount(abs(to_int(d))) + 50 np = to_int(mpf_add(s, fhalf, prec)) return int(np)
def npartitions(n, verbose=False): """ Calculate the partition function P(n), i.e. the number of ways that n can be written as a sum of positive integers. P(n) is computed using the Hardy-Ramanujan-Rademacher formula, described e.g. at http://mathworld.wolfram.com/PartitionFunctionP.html The correctness of this implementation has been tested for 10**n up to n = 8. """ n = int(n) if n < 0: return 0 if n <= 5: return [1, 1, 2, 3, 5, 7][n] # Estimate number of bits in p(n). This formula could be tidied pbits = int((math.pi*(2*n/3.)**0.5-math.log(4*n))/math.log(10)+1)*\ math.log(10,2) prec = p = int(pbits * 1.1 + 100) s = fzero M = max(6, int(0.24 * n**0.5 + 4)) sq23pi = mpf_mul(mpf_sqrt(from_rational(2, 3, p), p), mpf_pi(p), p) sqrt8 = mpf_sqrt(from_int(8), p) for q in xrange(1, M): a = A(n, q, p) d = D(n, q, p, sq23pi, sqrt8) s = mpf_add(s, mpf_mul(a, d), prec) if verbose: print "step", q, "of", M, to_str(a, 10), to_str(d, 10) # On average, the terms decrease rapidly in magnitude. Dynamically # reducing the precision greatly improves performance. p = bitcount(abs(to_int(d))) + 50 np = to_int(mpf_add(s, fhalf, prec)) return int(np)
def evalf_log(expr, prec, options): arg = expr.args[0] workprec = prec + 10 xre, xim, xacc, _ = evalf(arg, workprec, options) if xim: # XXX: use get_abs etc instead re = evalf_log(C.log(C.Abs(arg, evaluate=False), evaluate=False), prec, options) im = mpf_atan2(xim, xre or fzero, prec) return re[0], im, re[2], prec imaginary_term = mpf_cmp(xre, fzero) < 0 re = mpf_log(mpf_abs(xre), prec, rnd) size = fastlog(re) if prec - size > workprec: # We actually need to compute 1+x accurately, not x arg = C.Add(S.NegativeOne, arg, evaluate=False) xre, xim, _, _ = evalf_add(arg, prec, options) prec2 = workprec - fastlog(xre) re = mpf_log(mpf_add(xre, fone, prec2), prec, rnd) re_acc = prec if imaginary_term: return re, mpf_pi(prec), re_acc, prec else: return re, None, re_acc, None
def _a(n, j, prec): """Compute the inner sum in the HRR formula.""" if j == 1: return fone s = fzero pi = pi_fixed(prec) for h in xrange(1, j): if igcd(h, j) != 1: continue # & with mask to compute fractional part of fixed-point number one = 1 << prec onemask = one - 1 half = one >> 1 g = 0 if j >= 3: for k in xrange(1, j): t = h*k*one//j if t > 0: frac = t & onemask else: frac = -((-t) & onemask) g += k*(frac - half) g = ((g - 2*h*n*one)*pi//j) >> prec s = mpf_add(s, mpf_cos(from_man_exp(g, -prec), prec), prec) return s
def evalf_log(expr, prec, options): arg = expr.args[0] workprec = prec + 10 xre, xim, xacc, _ = evalf(arg, workprec, options) if xim: # XXX: use get_abs etc instead re = evalf_log( C.log(C.Abs(arg, evaluate=False), evaluate=False), prec, options) im = mpf_atan2(xim, xre or fzero, prec) return re[0], im, re[2], prec imaginary_term = (mpf_cmp(xre, fzero) < 0) re = mpf_log(mpf_abs(xre), prec, rnd) size = fastlog(re) if prec - size > workprec: # We actually need to compute 1+x accurately, not x arg = C.Add(S.NegativeOne, arg, evaluate=False) xre, xim, _, _ = evalf_add(arg, prec, options) prec2 = workprec - fastlog(xre) # xre is now x - 1 so we add 1 back here to calculate x re = mpf_log(mpf_abs(mpf_add(xre, fone, prec2)), prec, rnd) re_acc = prec if imaginary_term: return re, mpf_pi(prec), re_acc, prec else: return re, None, re_acc, None
def __add__(self, other): if (other is S.NaN) or (self is NaN): return S.NaN if isinstance(other, Number): rhs, prec = other._as_mpf_op(self._prec) return Real._new(mlib.mpf_add(self._mpf_, rhs, prec, rnd), prec) return Number.__add__(self, other)
def evalf_mul(v, prec, options): from sympy.core.core import C res = pure_complex(v) if res: # the only pure complex that is a mul is h*I _, h = res im, _, im_acc, _ = evalf(h, prec, options) return None, im, None, im_acc args = list(v.args) # see if any argument is NaN or oo and thus warrants a special return special = [] for arg in args: arg = evalf(arg, prec, options) if arg[0] is None: continue arg = C.Float._new(arg[0], 1) if arg is S.NaN or arg.is_unbounded: special.append(arg) if special: from sympy.core.mul import Mul special = Mul(*special) return evalf(special, prec + 4, {}) # With guard digits, multiplication in the real case does not destroy # accuracy. This is also true in the complex case when considering the # total accuracy; however accuracy for the real or imaginary parts # separately may be lower. acc = prec # XXX: big overestimate working_prec = prec + len(args) + 5 # Empty product is 1 start = man, exp, bc = MPZ(1), 0, 1 # First, we multiply all pure real or pure imaginary numbers. # direction tells us that the result should be multiplied by # I**direction; all other numbers get put into complex_factors # to be multiplied out after the first phase. last = len(args) direction = 0 args.append(S.One) complex_factors = [] for i, arg in enumerate(args): if i != last and pure_complex(arg): args[-1] = (args[-1] * arg).expand() continue elif i == last and arg is S.One: continue re, im, re_acc, im_acc = evalf(arg, working_prec, options) if re and im: complex_factors.append((re, im, re_acc, im_acc)) continue elif re: (s, m, e, b), w_acc = re, re_acc elif im: (s, m, e, b), w_acc = im, im_acc direction += 1 else: return None, None, None, None direction += 2 * s man *= m exp += e bc += b if bc > 3 * working_prec: man >>= working_prec exp += working_prec acc = min(acc, w_acc) sign = (direction & 2) >> 1 if not complex_factors: v = normalize(sign, man, exp, bitcount(man), prec, rnd) # multiply by i if direction & 1: return None, v, None, acc else: return v, None, acc, None else: # initialize with the first term if (man, exp, bc) != start: # there was a real part; give it an imaginary part re, im = (sign, man, exp, bitcount(man)), (0, MPZ(0), 0, 0) i0 = 0 else: # there is no real part to start (other than the starting 1) wre, wim, wre_acc, wim_acc = complex_factors[0] acc = min(acc, complex_accuracy((wre, wim, wre_acc, wim_acc))) re = wre im = wim i0 = 1 for wre, wim, wre_acc, wim_acc in complex_factors[i0:]: # acc is the overall accuracy of the product; we aren't # computing exact accuracies of the product. acc = min(acc, complex_accuracy((wre, wim, wre_acc, wim_acc))) use_prec = working_prec A = mpf_mul(re, wre, use_prec) B = mpf_mul(mpf_neg(im), wim, use_prec) C = mpf_mul(re, wim, use_prec) D = mpf_mul(im, wre, use_prec) re = mpf_add(A, B, use_prec) im = mpf_add(C, D, use_prec) if options.get("verbose"): print "MUL: wanted", prec, "accurate bits, got", acc # multiply by I if direction & 1: re, im = mpf_neg(im), re return re, im, acc, acc
def evalf_mul(v, prec, options): from sympy.core.core import C res = pure_complex(v) if res: # the only pure complex that is a mul is h*I _, h = res im, _, im_acc, _ = evalf(h, prec, options) return None, im, None, im_acc args = list(v.args) # see if any argument is NaN or oo and thus warrants a special return special = [] for arg in args: arg = evalf(arg, prec, options) if arg[0] is None: continue arg = C.Float._new(arg[0], 1) if arg is S.NaN or arg.is_infinite: special.append(arg) if special: from sympy.core.mul import Mul special = Mul(*special) return evalf(special, prec + 4, {}) # With guard digits, multiplication in the real case does not destroy # accuracy. This is also true in the complex case when considering the # total accuracy; however accuracy for the real or imaginary parts # separately may be lower. acc = prec # XXX: big overestimate working_prec = prec + len(args) + 5 # Empty product is 1 start = man, exp, bc = MPZ(1), 0, 1 # First, we multiply all pure real or pure imaginary numbers. # direction tells us that the result should be multiplied by # I**direction; all other numbers get put into complex_factors # to be multiplied out after the first phase. last = len(args) direction = 0 args.append(S.One) complex_factors = [] for i, arg in enumerate(args): if i != last and pure_complex(arg): args[-1] = (args[-1]*arg).expand() continue elif i == last and arg is S.One: continue re, im, re_acc, im_acc = evalf(arg, working_prec, options) if re and im: complex_factors.append((re, im, re_acc, im_acc)) continue elif re: (s, m, e, b), w_acc = re, re_acc elif im: (s, m, e, b), w_acc = im, im_acc direction += 1 else: return None, None, None, None direction += 2*s man *= m exp += e bc += b if bc > 3*working_prec: man >>= working_prec exp += working_prec acc = min(acc, w_acc) sign = (direction & 2) >> 1 if not complex_factors: v = normalize(sign, man, exp, bitcount(man), prec, rnd) # multiply by i if direction & 1: return None, v, None, acc else: return v, None, acc, None else: # initialize with the first term if (man, exp, bc) != start: # there was a real part; give it an imaginary part re, im = (sign, man, exp, bitcount(man)), (0, MPZ(0), 0, 0) i0 = 0 else: # there is no real part to start (other than the starting 1) wre, wim, wre_acc, wim_acc = complex_factors[0] acc = min(acc, complex_accuracy((wre, wim, wre_acc, wim_acc))) re = wre im = wim i0 = 1 for wre, wim, wre_acc, wim_acc in complex_factors[i0:]: # acc is the overall accuracy of the product; we aren't # computing exact accuracies of the product. acc = min(acc, complex_accuracy((wre, wim, wre_acc, wim_acc))) use_prec = working_prec A = mpf_mul(re, wre, use_prec) B = mpf_mul(mpf_neg(im), wim, use_prec) C = mpf_mul(re, wim, use_prec) D = mpf_mul(im, wre, use_prec) re = mpf_add(A, B, use_prec) im = mpf_add(C, D, use_prec) if options.get('verbose'): print("MUL: wanted", prec, "accurate bits, got", acc) # multiply by I if direction & 1: re, im = mpf_neg(im), re return re, im, acc, acc