| | 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) |
| |
|
| | |
| | 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 |
| |
|
| | |
| | @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) |
| |
|
| | |
| | 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"): |
| | 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"): |
| | 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"): |
| | 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 |
| |
|