def add_terms(terms, prec, target_prec): """ Helper for evalf_add. Adds a list of (mpfval, accuracy) terms. """ if len(terms) == 1: if not terms[0]: # XXX: this is supposed to represent a scaled zero return mpf_shift(fone, target_prec), -1 return terms[0] max_extra_prec = 2*prec sum_man, sum_exp, absolute_error = 0, 0, MINUS_INF for x, accuracy in terms: if not x: continue sign, man, exp, bc = x if sign: man = -man absolute_error = max(absolute_error, bc+exp-accuracy) delta = exp - sum_exp if exp >= sum_exp: # x much larger than existing sum? # first: quick test if (delta > max_extra_prec) and \ ((not sum_man) or delta-bitcount(abs(sum_man)) > max_extra_prec): sum_man = man sum_exp = exp else: sum_man += (man << delta) else: delta = -delta # x much smaller than existing sum? if delta-bc > max_extra_prec: if not sum_man: sum_man, sum_exp = man, exp else: sum_man = (sum_man << delta) + man sum_exp = exp if absolute_error == MINUS_INF: return None, None if not sum_man: # XXX: this is supposed to represent a scaled zero return mpf_shift(fone, absolute_error), -1 if sum_man < 0: sum_sign = 1 sum_man = -sum_man else: sum_sign = 0 sum_bc = bitcount(sum_man) sum_accuracy = sum_exp + sum_bc - absolute_error r = normalize(sum_sign, sum_man, sum_exp, sum_bc, target_prec, round_nearest), sum_accuracy #print "returning", to_str(r[0],50), r[1] return r
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 add_terms(terms, prec, target_prec): """ Helper for evalf_add. Adds a list of (mpfval, accuracy) terms. """ if len(terms) == 1: if not terms[0]: # XXX: this is supposed to represent a scaled zero return mpf_shift(fone, target_prec), -1 return terms[0] sum_man, sum_exp, absolute_error = 0, 0, MINUS_INF for x, accuracy in terms: if not x: continue sign, man, exp, bc = x if sign: man = -man absolute_error = max(absolute_error, bc+exp-accuracy) delta = exp - sum_exp if exp >= sum_exp: if delta > 4*prec: sum_man = man sum_exp = exp else: sum_man += man << delta else: if (-delta) > 4*prec: pass else: sum_man = (sum_man << (-delta)) + man sum_exp = exp if absolute_error == MINUS_INF: return None, None if not sum_man: # XXX: this is supposed to represent a scaled zero return mpf_shift(fone, absolute_error), -1 if sum_man < 0: sum_sign = 1 sum_man = -sum_man else: sum_sign = 0 sum_bc = bitcount(sum_man) sum_accuracy = sum_exp + sum_bc - absolute_error r = normalize(sum_sign, sum_man, sum_exp, sum_bc, target_prec, round_nearest), sum_accuracy #print "returning", to_str(r[0],50), r[1] return r
def evalf_mul(v, prec, options): args = v.args # 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 target_prec = prec # XXX: big overestimate prec = prec + len(args) + 5 direction = 0 # Empty product is 1 man, exp, bc = MP_BASE(1), 0, 1 direction = 0 complex_factors = [] # First, we multiply all pure real or pure imaginary numbers. # direction tells us that the result should be multiplied by # i**direction for arg in args: re, im, a, aim = evalf(arg, prec, options) if re and im: complex_factors.append((re, im, a, aim)) continue elif re: s, m, e, b = re elif im: a = aim direction += 1 s, m, e, b = im else: return None, None, None, None direction += 2*s man *= m exp += e bc += b if bc > 3*prec: man >>= prec exp += prec acc = min(acc, a) sign = (direction & 2) >> 1 v = normalize(sign, man, exp, bitcount(man), prec, round_nearest) if complex_factors: # Multiply first complex number by the existing real scalar re, im, re_acc, im_acc = complex_factors[0] re = mpf_mul(re, v, prec) im = mpf_mul(im, v, prec) re_acc = min(re_acc, acc) im_acc = min(im_acc, acc) # Multiply consecutive complex factors complex_factors = complex_factors[1:] for wre, wim, wre_acc, wim_acc in complex_factors: re, im, re_acc, im_acc = cmul((re, re_acc), (im,im_acc), (wre,wre_acc), (wim,wim_acc), prec, target_prec) if options.get('verbose'): print "MUL: obtained accuracy", re_acc, im_acc, "expected", target_prec # multiply by i if direction & 1: return mpf_neg(im), re, re_acc, im_acc else: return re, im, re_acc, im_acc else: # multiply by i if direction & 1: return None, v, None, acc else: return v, None, acc, None
def evalf_mul(v, prec, options): args = v.args # 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 target_prec = prec # XXX: big overestimate prec = prec + len(args) + 5 direction = 0 # Empty product is 1 man, exp, bc = MP_BASE(1), 0, 1 direction = 0 complex_factors = [] # First, we multiply all pure real or pure imaginary numbers. # direction tells us that the result should be multiplied by # i**direction for arg in args: re, im, re_acc, im_acc = evalf(arg, 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*prec: man >>= prec exp += prec acc = min(acc, w_acc) sign = (direction & 2) >> 1 v = normalize(sign, man, exp, bitcount(man), prec, round_nearest) if complex_factors: # make existing real scalar look like an imaginary and # multiply by the remaining complex numbers re, im = v, (0, MP_BASE(0), 0, 0) for wre, wim, wre_acc, wim_acc in complex_factors: # 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))) A = mpf_mul(re, wre, prec) B = mpf_mul(mpf_neg(im), wim, prec) C = mpf_mul(re, wim, prec) D = mpf_mul(im, wre, prec) re, xre_acc = add_terms([(A, acc), (B, acc)], prec, target_prec) im, xim_acc = add_terms([(C, acc), (D, acc)], prec, target_prec) if options.get('verbose'): print "MUL: wanted", target_prec, "accurate bits, got", acc # multiply by i if direction & 1: return mpf_neg(im), re, acc, acc else: return re, im, acc, acc else: # multiply by i if direction & 1: return None, v, None, acc else: return v, None, acc, None