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 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 finalize_complex(re, im, prec): assert re and im if re == fzero and im == fzero: raise ValueError("got complex zero with unknown accuracy") size_re = fastlog(re) size_im = fastlog(im) # Convert fzeros to scaled zeros if re == fzero: re = mpf_shift(fone, size_im-prec) size_re = fastlog(re) elif im == fzero: im = mpf_shift(fone, size_re-prec) size_im = fastlog(im) if size_re > size_im: re_acc = prec im_acc = prec + min(-(size_re - size_im), 0) else: im_acc = prec re_acc = prec + min(-(size_im - size_re), 0) return re, im, re_acc, im_acc
def do_integral(expr, prec, options): func = expr.args[0] x, (xlow, xhigh) = expr.args[1][0] orig = mp.prec oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC) options['maxprec'] = min(oldmaxprec, 2*prec) try: mp.prec = prec+5 xlow = as_mpmath(xlow, prec+15, options) xhigh = as_mpmath(xhigh, prec+15, options) # Integration is like summation, and we can phone home from # the integrand function to update accuracy summation style # Note that this accuracy is inaccurate, since it fails # to account for the variable quadrature weights, # but it is better than nothing have_part = [False, False] max_real_term = [MINUS_INF] max_imag_term = [MINUS_INF] def f(t): re, im, re_acc, im_acc = evalf(func, mp.prec, {'subs':{x:t}}) have_part[0] = re or have_part[0] have_part[1] = im or have_part[1] max_real_term[0] = max(max_real_term[0], fastlog(re)) max_imag_term[0] = max(max_imag_term[0], fastlog(im)) if im: return mpc(re or fzero, im) return mpf(re or fzero) if options.get('quad') == 'osc': A = C.Wild('A', exclude=[x]) B = C.Wild('B', exclude=[x]) D = C.Wild('D') m = func.match(C.cos(A*x+B)*D) if not m: m = func.match(C.sin(A*x+B)*D) if not m: raise ValueError("An integrand of the form sin(A*x+B)*f(x) " "or cos(A*x+B)*f(x) is required for oscillatory quadrature") period = as_mpmath(2*S.Pi/m[A], prec+15, options) result = quadosc(f, [xlow, xhigh], period=period) # XXX: quadosc does not do error detection yet quadrature_error = MINUS_INF else: result, quadrature_error = quadts(f, [xlow, xhigh], error=1) quadrature_error = fastlog(quadrature_error._mpf_) finally: options['maxprec'] = oldmaxprec mp.prec = orig if have_part[0]: re = result.real._mpf_ if re == fzero: re = mpf_shift(fone, min(-prec,-max_real_term[0],-quadrature_error)) re_acc = -1 else: re_acc = -max(max_real_term[0]-fastlog(re)-prec, quadrature_error) else: re, re_acc = None, None if have_part[1]: im = result.imag._mpf_ if im == fzero: im = mpf_shift(fone, min(-prec,-max_imag_term[0],-quadrature_error)) im_acc = -1 else: im_acc = -max(max_imag_term[0]-fastlog(im)-prec, quadrature_error) else: im, im_acc = None, None result = re, im, re_acc, im_acc return result