from .ctx_base import StandardBaseContext import math import cmath from . import math2 from . import function_docs from .libmp import mpf_bernoulli, to_float, int_types from . import libmp class FPContext(StandardBaseContext): """ Context for fast low-precision arithmetic (53-bit precision, giving at most about 15-digit accuracy), using Python's builtin float and complex. """ def __init__(ctx): StandardBaseContext.__init__(ctx) # Override SpecialFunctions implementation ctx.loggamma = math2.loggamma ctx._bernoulli_cache = {} ctx.pretty = False ctx._init_aliases() _mpq = lambda cls, x: float(x[0])/x[1] NoConvergence = libmp.NoConvergence def _get_prec(ctx): return 53 def _set_prec(ctx, p): return def _get_dps(ctx): return 15 def _set_dps(ctx, p): return _fixed_precision = True prec = property(_get_prec, _set_prec) dps = property(_get_dps, _set_dps) zero = 0.0 one = 1.0 eps = math2.EPS inf = math2.INF ninf = math2.NINF nan = math2.NAN j = 1j # Called by SpecialFunctions.__init__() @classmethod def _wrap_specfun(cls, name, f, wrap): if wrap: def f_wrapped(ctx, *args, **kwargs): convert = ctx.convert args = [convert(a) for a in args] return f(ctx, *args, **kwargs) else: f_wrapped = f f_wrapped.__doc__ = function_docs.__dict__.get(name, f.__doc__) setattr(cls, name, f_wrapped) def bernoulli(ctx, n): cache = ctx._bernoulli_cache if n in cache: return cache[n] cache[n] = to_float(mpf_bernoulli(n, 53, 'n'), strict=True) return cache[n] pi = math2.pi e = math2.e euler = math2.euler sqrt2 = 1.4142135623730950488 sqrt5 = 2.2360679774997896964 phi = 1.6180339887498948482 ln2 = 0.69314718055994530942 ln10 = 2.302585092994045684 euler = 0.57721566490153286061 catalan = 0.91596559417721901505 khinchin = 2.6854520010653064453 apery = 1.2020569031595942854 glaisher = 1.2824271291006226369 absmin = absmax = abs def is_special(ctx, x): return x - x != 0.0 def isnan(ctx, x): return x != x def isinf(ctx, x): return abs(x) == math2.INF def isnormal(ctx, x): if x: return x - x == 0.0 return False def isnpint(ctx, x): if type(x) is complex: if x.imag: return False x = x.real return x <= 0.0 and round(x) == x mpf = float mpc = complex def convert(ctx, x): try: return float(x) except: return complex(x) power = staticmethod(math2.pow) sqrt = staticmethod(math2.sqrt) exp = staticmethod(math2.exp) ln = log = staticmethod(math2.log) cos = staticmethod(math2.cos) sin = staticmethod(math2.sin) tan = staticmethod(math2.tan) cos_sin = staticmethod(math2.cos_sin) acos = staticmethod(math2.acos) asin = staticmethod(math2.asin) atan = staticmethod(math2.atan) cosh = staticmethod(math2.cosh) sinh = staticmethod(math2.sinh) tanh = staticmethod(math2.tanh) gamma = staticmethod(math2.gamma) rgamma = staticmethod(math2.rgamma) fac = factorial = staticmethod(math2.factorial) floor = staticmethod(math2.floor) ceil = staticmethod(math2.ceil) cospi = staticmethod(math2.cospi) sinpi = staticmethod(math2.sinpi) cbrt = staticmethod(math2.cbrt) _nthroot = staticmethod(math2.nthroot) _ei = staticmethod(math2.ei) _e1 = staticmethod(math2.e1) _zeta = _zeta_int = staticmethod(math2.zeta) # XXX: math2 def arg(ctx, z): z = complex(z) return math.atan2(z.imag, z.real) def expj(ctx, x): return ctx.exp(ctx.j*x) def expjpi(ctx, x): return ctx.exp(ctx.j*ctx.pi*x) ldexp = math.ldexp frexp = math.frexp def mag(ctx, z): if z: return ctx.frexp(abs(z))[1] return ctx.ninf def isint(ctx, z): if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 if z.imag: return False z = z.real try: return z == int(z) except: return False def nint_distance(ctx, z): if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 n = round(z.real) else: n = round(z) if n == z: return n, ctx.ninf return n, ctx.mag(abs(z-n)) def _convert_param(ctx, z): if type(z) is tuple: p, q = z return ctx.mpf(p) / q, 'R' if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 intz = int(z.real) else: intz = int(z) if z == intz: return intz, 'Z' return z, 'R' def _is_real_type(ctx, z): return isinstance(z, float) or isinstance(z, int_types) def _is_complex_type(ctx, z): return isinstance(z, complex) def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): coeffs = list(coeffs) num = range(p) den = range(p,p+q) tol = ctx.eps s = t = 1.0 k = 0 while 1: for i in num: t *= (coeffs[i]+k) for i in den: t /= (coeffs[i]+k) k += 1; t /= k; t *= z; s += t if abs(t) < tol: return s if k > maxterms: raise ctx.NoConvergence def atan2(ctx, x, y): return math.atan2(x, y) def psi(ctx, m, z): m = int(m) if m == 0: return ctx.digamma(z) return (-1)**(m+1) * ctx.fac(m) * ctx.zeta(m+1, z) digamma = staticmethod(math2.digamma) def harmonic(ctx, x): x = ctx.convert(x) if x == 0 or x == 1: return x return ctx.digamma(x+1) + ctx.euler nstr = str def to_fixed(ctx, x, prec): return int(math.ldexp(x, prec)) def rand(ctx): import random return random.random() _erf = staticmethod(math2.erf) _erfc = staticmethod(math2.erfc) def sum_accurately(ctx, terms, check_step=1): s = ctx.zero k = 0 for term in terms(): s += term if (not k % check_step) and term: if abs(term) <= 1e-18*abs(s): break k += 1 return s