1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801 |
- """
- Adaptive numerical evaluation of SymPy expressions, using mpmath
- for mathematical functions.
- """
- from __future__ import annotations
- from typing import Tuple as tTuple, Optional, Union as tUnion, Callable, List, Dict as tDict, Type, TYPE_CHECKING, \
- Any, overload
- import math
- import mpmath.libmp as libmp
- from mpmath import (
- make_mpc, make_mpf, mp, mpc, mpf, nsum, quadts, quadosc, workprec)
- from mpmath import inf as mpmath_inf
- from mpmath.libmp import (from_int, from_man_exp, from_rational, fhalf,
- fnan, finf, fninf, fnone, fone, fzero, mpf_abs, mpf_add,
- mpf_atan, mpf_atan2, mpf_cmp, mpf_cos, mpf_e, mpf_exp, mpf_log, mpf_lt,
- mpf_mul, mpf_neg, mpf_pi, mpf_pow, mpf_pow_int, mpf_shift, mpf_sin,
- mpf_sqrt, normalize, round_nearest, to_int, to_str)
- from mpmath.libmp import bitcount as mpmath_bitcount
- from mpmath.libmp.backend import MPZ
- from mpmath.libmp.libmpc import _infs_nan
- from mpmath.libmp.libmpf import dps_to_prec, prec_to_dps
- from .sympify import sympify
- from .singleton import S
- from sympy.external.gmpy import SYMPY_INTS
- from sympy.utilities.iterables import is_sequence
- from sympy.utilities.lambdify import lambdify
- from sympy.utilities.misc import as_int
- if TYPE_CHECKING:
- from sympy.core.expr import Expr
- from sympy.core.add import Add
- from sympy.core.mul import Mul
- from sympy.core.power import Pow
- from sympy.core.symbol import Symbol
- from sympy.integrals.integrals import Integral
- from sympy.concrete.summations import Sum
- from sympy.concrete.products import Product
- from sympy.functions.elementary.exponential import exp, log
- from sympy.functions.elementary.complexes import Abs, re, im
- from sympy.functions.elementary.integers import ceiling, floor
- from sympy.functions.elementary.trigonometric import atan
- from .numbers import Float, Rational, Integer, AlgebraicNumber, Number
- LG10 = math.log(10, 2)
- rnd = round_nearest
- def bitcount(n):
- """Return smallest integer, b, such that |n|/2**b < 1.
- """
- return mpmath_bitcount(abs(int(n)))
- # Used in a few places as placeholder values to denote exponents and
- # precision levels, e.g. of exact numbers. Must be careful to avoid
- # passing these to mpmath functions or returning them in final results.
- INF = float(mpmath_inf)
- MINUS_INF = float(-mpmath_inf)
- # ~= 100 digits. Real men set this to INF.
- DEFAULT_MAXPREC = 333
- class PrecisionExhausted(ArithmeticError):
- pass
- #----------------------------------------------------------------------------#
- # #
- # Helper functions for arithmetic and complex parts #
- # #
- #----------------------------------------------------------------------------#
- """
- An mpf value tuple is a tuple of integers (sign, man, exp, bc)
- representing a floating-point number: [1, -1][sign]*man*2**exp where
- sign is 0 or 1 and bc should correspond to the number of bits used to
- represent the mantissa (man) in binary notation, e.g.
- """
- MPF_TUP = tTuple[int, int, int, int] # mpf value tuple
- """
- Explanation
- ===========
- >>> from sympy.core.evalf import bitcount
- >>> sign, man, exp, bc = 0, 5, 1, 3
- >>> n = [1, -1][sign]*man*2**exp
- >>> n, bitcount(man)
- (10, 3)
- A temporary result is a tuple (re, im, re_acc, im_acc) where
- re and im are nonzero mpf value tuples representing approximate
- numbers, or None to denote exact zeros.
- re_acc, im_acc are integers denoting log2(e) where e is the estimated
- relative accuracy of the respective complex part, but may be anything
- if the corresponding complex part is None.
- """
- TMP_RES = Any # temporary result, should be some variant of
- # tUnion[tTuple[Optional[MPF_TUP], Optional[MPF_TUP],
- # Optional[int], Optional[int]],
- # 'ComplexInfinity']
- # but mypy reports error because it doesn't know as we know
- # 1. re and re_acc are either both None or both MPF_TUP
- # 2. sometimes the result can't be zoo
- # type of the "options" parameter in internal evalf functions
- OPT_DICT = tDict[str, Any]
- def fastlog(x: Optional[MPF_TUP]) -> tUnion[int, Any]:
- """Fast approximation of log2(x) for an mpf value tuple x.
- Explanation
- ===========
- Calculated as exponent + width of mantissa. This is an
- approximation for two reasons: 1) it gives the ceil(log2(abs(x)))
- value and 2) it is too high by 1 in the case that x is an exact
- power of 2. Although this is easy to remedy by testing to see if
- the odd mpf mantissa is 1 (indicating that one was dealing with
- an exact power of 2) that would decrease the speed and is not
- necessary as this is only being used as an approximation for the
- number of bits in x. The correct return value could be written as
- "x[2] + (x[3] if x[1] != 1 else 0)".
- Since mpf tuples always have an odd mantissa, no check is done
- to see if the mantissa is a multiple of 2 (in which case the
- result would be too large by 1).
- Examples
- ========
- >>> from sympy import log
- >>> from sympy.core.evalf import fastlog, bitcount
- >>> s, m, e = 0, 5, 1
- >>> bc = bitcount(m)
- >>> n = [1, -1][s]*m*2**e
- >>> n, (log(n)/log(2)).evalf(2), fastlog((s, m, e, bc))
- (10, 3.3, 4)
- """
- if not x or x == fzero:
- return MINUS_INF
- return x[2] + x[3]
- def pure_complex(v: 'Expr', or_real=False) -> tuple['Number', 'Number'] | None:
- """Return a and b if v matches a + I*b where b is not zero and
- a and b are Numbers, else None. If `or_real` is True then 0 will
- be returned for `b` if `v` is a real number.
- Examples
- ========
- >>> from sympy.core.evalf import pure_complex
- >>> from sympy import sqrt, I, S
- >>> a, b, surd = S(2), S(3), sqrt(2)
- >>> pure_complex(a)
- >>> pure_complex(a, or_real=True)
- (2, 0)
- >>> pure_complex(surd)
- >>> pure_complex(a + b*I)
- (2, 3)
- >>> pure_complex(I)
- (0, 1)
- """
- h, t = v.as_coeff_Add()
- if t:
- c, i = t.as_coeff_Mul()
- if i is S.ImaginaryUnit:
- return h, c
- elif or_real:
- return h, S.Zero
- return None
- # I don't know what this is, see function scaled_zero below
- SCALED_ZERO_TUP = tTuple[List[int], int, int, int]
- @overload
- def scaled_zero(mag: SCALED_ZERO_TUP, sign=1) -> MPF_TUP:
- ...
- @overload
- def scaled_zero(mag: int, sign=1) -> tTuple[SCALED_ZERO_TUP, int]:
- ...
- def scaled_zero(mag: tUnion[SCALED_ZERO_TUP, int], sign=1) -> \
- tUnion[MPF_TUP, tTuple[SCALED_ZERO_TUP, int]]:
- """Return an mpf representing a power of two with magnitude ``mag``
- and -1 for precision. Or, if ``mag`` is a scaled_zero tuple, then just
- remove the sign from within the list that it was initially wrapped
- in.
- Examples
- ========
- >>> from sympy.core.evalf import scaled_zero
- >>> from sympy import Float
- >>> z, p = scaled_zero(100)
- >>> z, p
- (([0], 1, 100, 1), -1)
- >>> ok = scaled_zero(z)
- >>> ok
- (0, 1, 100, 1)
- >>> Float(ok)
- 1.26765060022823e+30
- >>> Float(ok, p)
- 0.e+30
- >>> ok, p = scaled_zero(100, -1)
- >>> Float(scaled_zero(ok), p)
- -0.e+30
- """
- if isinstance(mag, tuple) and len(mag) == 4 and iszero(mag, scaled=True):
- return (mag[0][0],) + mag[1:]
- elif isinstance(mag, SYMPY_INTS):
- if sign not in [-1, 1]:
- raise ValueError('sign must be +/-1')
- rv, p = mpf_shift(fone, mag), -1
- s = 0 if sign == 1 else 1
- rv = ([s],) + rv[1:]
- return rv, p
- else:
- raise ValueError('scaled zero expects int or scaled_zero tuple.')
- def iszero(mpf: tUnion[MPF_TUP, SCALED_ZERO_TUP, None], scaled=False) -> Optional[bool]:
- if not scaled:
- return not mpf or not mpf[1] and not mpf[-1]
- return mpf and isinstance(mpf[0], list) and mpf[1] == mpf[-1] == 1
- def complex_accuracy(result: TMP_RES) -> tUnion[int, Any]:
- """
- Returns relative accuracy of a complex number with given accuracies
- for the real and imaginary parts. The relative accuracy is defined
- in the complex norm sense as ||z|+|error|| / |z| where error
- is equal to (real absolute error) + (imag absolute error)*i.
- The full expression for the (logarithmic) error can be approximated
- easily by using the max norm to approximate the complex norm.
- In the worst case (re and im equal), this is wrong by a factor
- sqrt(2), or by log2(sqrt(2)) = 0.5 bit.
- """
- if result is S.ComplexInfinity:
- return INF
- re, im, re_acc, im_acc = result
- if not im:
- if not re:
- return INF
- return re_acc
- if not re:
- return im_acc
- re_size = fastlog(re)
- im_size = fastlog(im)
- absolute_error = max(re_size - re_acc, im_size - im_acc)
- relative_error = absolute_error - max(re_size, im_size)
- return -relative_error
- def get_abs(expr: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES:
- result = evalf(expr, prec + 2, options)
- if result is S.ComplexInfinity:
- return finf, None, prec, None
- re, im, re_acc, im_acc = result
- if not re:
- re, re_acc, im, im_acc = im, im_acc, re, re_acc
- if im:
- if expr.is_number:
- abs_expr, _, acc, _ = evalf(abs(N(expr, prec + 2)),
- prec + 2, options)
- return abs_expr, None, acc, None
- else:
- if 'subs' in options:
- return libmp.mpc_abs((re, im), prec), None, re_acc, None
- return abs(expr), None, prec, None
- elif re:
- return mpf_abs(re), None, re_acc, None
- else:
- return None, None, None, None
- def get_complex_part(expr: 'Expr', no: int, prec: int, options: OPT_DICT) -> TMP_RES:
- """no = 0 for real part, no = 1 for imaginary part"""
- workprec = prec
- i = 0
- while 1:
- res = evalf(expr, workprec, options)
- if res is S.ComplexInfinity:
- return fnan, None, prec, None
- value, accuracy = res[no::2]
- # XXX is the last one correct? Consider re((1+I)**2).n()
- if (not value) or accuracy >= prec or -value[2] > prec:
- return value, None, accuracy, None
- workprec += max(30, 2**i)
- i += 1
- def evalf_abs(expr: 'Abs', prec: int, options: OPT_DICT) -> TMP_RES:
- return get_abs(expr.args[0], prec, options)
- def evalf_re(expr: 're', prec: int, options: OPT_DICT) -> TMP_RES:
- return get_complex_part(expr.args[0], 0, prec, options)
- def evalf_im(expr: 'im', prec: int, options: OPT_DICT) -> TMP_RES:
- return get_complex_part(expr.args[0], 1, prec, options)
- def finalize_complex(re: MPF_TUP, im: MPF_TUP, prec: int) -> TMP_RES:
- if re == fzero and im == fzero:
- raise ValueError("got complex zero with unknown accuracy")
- elif re == fzero:
- return None, im, None, prec
- elif im == fzero:
- return re, None, prec, None
- size_re = fastlog(re)
- 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 chop_parts(value: TMP_RES, prec: int) -> TMP_RES:
- """
- Chop off tiny real or complex parts.
- """
- if value is S.ComplexInfinity:
- return value
- re, im, re_acc, im_acc = value
- # Method 1: chop based on absolute value
- if re and re not in _infs_nan and (fastlog(re) < -prec + 4):
- re, re_acc = None, None
- if im and im not in _infs_nan and (fastlog(im) < -prec + 4):
- im, im_acc = None, None
- # Method 2: chop if inaccurate and relatively small
- if re and im:
- delta = fastlog(re) - fastlog(im)
- if re_acc < 2 and (delta - re_acc <= -prec + 4):
- re, re_acc = None, None
- if im_acc < 2 and (delta - im_acc >= prec - 4):
- im, im_acc = None, None
- return re, im, re_acc, im_acc
- def check_target(expr: 'Expr', result: TMP_RES, prec: int):
- a = complex_accuracy(result)
- if a < prec:
- raise PrecisionExhausted("Failed to distinguish the expression: \n\n%s\n\n"
- "from zero. Try simplifying the input, using chop=True, or providing "
- "a higher maxn for evalf" % (expr))
- def get_integer_part(expr: 'Expr', no: int, options: OPT_DICT, return_ints=False) -> \
- tUnion[TMP_RES, tTuple[int, int]]:
- """
- With no = 1, computes ceiling(expr)
- With no = -1, computes floor(expr)
- Note: this function either gives the exact result or signals failure.
- """
- from sympy.functions.elementary.complexes import re, im
- # The expression is likely less than 2^30 or so
- assumed_size = 30
- result = evalf(expr, assumed_size, options)
- if result is S.ComplexInfinity:
- raise ValueError("Cannot get integer part of Complex Infinity")
- ire, iim, ire_acc, iim_acc = result
- # We now know the size, so we can calculate how much extra precision
- # (if any) is needed to get within the nearest integer
- if ire and iim:
- gap = max(fastlog(ire) - ire_acc, fastlog(iim) - iim_acc)
- elif ire:
- gap = fastlog(ire) - ire_acc
- elif iim:
- gap = fastlog(iim) - iim_acc
- else:
- # ... or maybe the expression was exactly zero
- if return_ints:
- return 0, 0
- else:
- return None, None, None, None
- margin = 10
- if gap >= -margin:
- prec = margin + assumed_size + gap
- ire, iim, ire_acc, iim_acc = evalf(
- expr, prec, options)
- else:
- prec = assumed_size
- # We can now easily find the nearest integer, but to find floor/ceil, we
- # must also calculate whether the difference to the nearest integer is
- # positive or negative (which may fail if very close).
- def calc_part(re_im: 'Expr', nexpr: MPF_TUP):
- from .add import Add
- _, _, exponent, _ = nexpr
- is_int = exponent == 0
- nint = int(to_int(nexpr, rnd))
- if is_int:
- # make sure that we had enough precision to distinguish
- # between nint and the re or im part (re_im) of expr that
- # was passed to calc_part
- ire, iim, ire_acc, iim_acc = evalf(
- re_im - nint, 10, options) # don't need much precision
- assert not iim
- size = -fastlog(ire) + 2 # -ve b/c ire is less than 1
- if size > prec:
- ire, iim, ire_acc, iim_acc = evalf(
- re_im, size, options)
- assert not iim
- nexpr = ire
- nint = int(to_int(nexpr, rnd))
- _, _, new_exp, _ = ire
- is_int = new_exp == 0
- if not is_int:
- # if there are subs and they all contain integer re/im parts
- # then we can (hopefully) safely substitute them into the
- # expression
- s = options.get('subs', False)
- if s:
- doit = True
- # use strict=False with as_int because we take
- # 2.0 == 2
- for v in s.values():
- try:
- as_int(v, strict=False)
- except ValueError:
- try:
- [as_int(i, strict=False) for i in v.as_real_imag()]
- continue
- except (ValueError, AttributeError):
- doit = False
- break
- if doit:
- re_im = re_im.subs(s)
- re_im = Add(re_im, -nint, evaluate=False)
- x, _, x_acc, _ = evalf(re_im, 10, options)
- try:
- check_target(re_im, (x, None, x_acc, None), 3)
- except PrecisionExhausted:
- if not re_im.equals(0):
- raise PrecisionExhausted
- x = fzero
- nint += int(no*(mpf_cmp(x or fzero, fzero) == no))
- nint = from_int(nint)
- return nint, INF
- re_, im_, re_acc, im_acc = None, None, None, None
- if ire:
- re_, re_acc = calc_part(re(expr, evaluate=False), ire)
- if iim:
- im_, im_acc = calc_part(im(expr, evaluate=False), iim)
- if return_ints:
- return int(to_int(re_ or fzero)), int(to_int(im_ or fzero))
- return re_, im_, re_acc, im_acc
- def evalf_ceiling(expr: 'ceiling', prec: int, options: OPT_DICT) -> TMP_RES:
- return get_integer_part(expr.args[0], 1, options)
- def evalf_floor(expr: 'floor', prec: int, options: OPT_DICT) -> TMP_RES:
- return get_integer_part(expr.args[0], -1, options)
- def evalf_float(expr: 'Float', prec: int, options: OPT_DICT) -> TMP_RES:
- return expr._mpf_, None, prec, None
- def evalf_rational(expr: 'Rational', prec: int, options: OPT_DICT) -> TMP_RES:
- return from_rational(expr.p, expr.q, prec), None, prec, None
- def evalf_integer(expr: 'Integer', prec: int, options: OPT_DICT) -> TMP_RES:
- return from_int(expr.p, prec), None, prec, None
- #----------------------------------------------------------------------------#
- # #
- # Arithmetic operations #
- # #
- #----------------------------------------------------------------------------#
- def add_terms(terms: list, prec: int, target_prec: int) -> \
- tTuple[tUnion[MPF_TUP, SCALED_ZERO_TUP, None], Optional[int]]:
- """
- Helper for evalf_add. Adds a list of (mpfval, accuracy) terms.
- Returns
- =======
- - None, None if there are no non-zero terms;
- - terms[0] if there is only 1 term;
- - scaled_zero if the sum of the terms produces a zero by cancellation
- e.g. mpfs representing 1 and -1 would produce a scaled zero which need
- special handling since they are not actually zero and they are purposely
- malformed to ensure that they cannot be used in anything but accuracy
- calculations;
- - a tuple that is scaled to target_prec that corresponds to the
- sum of the terms.
- The returned mpf tuple will be normalized to target_prec; the input
- prec is used to define the working precision.
- XXX explain why this is needed and why one cannot just loop using mpf_add
- """
- terms = [t for t in terms if not iszero(t[0])]
- if not terms:
- return None, None
- elif len(terms) == 1:
- return terms[0]
- # see if any argument is NaN or oo and thus warrants a special return
- special = []
- from .numbers import Float
- for t in terms:
- arg = Float._new(t[0], 1)
- if arg is S.NaN or arg.is_infinite:
- special.append(arg)
- if special:
- from .add import Add
- rv = evalf(Add(*special), prec + 4, {})
- return rv[0], rv[2]
- working_prec = 2*prec
- sum_man, sum_exp = 0, 0
- absolute_err: List[int] = []
- for x, accuracy in terms:
- sign, man, exp, bc = x
- if sign:
- man = -man
- absolute_err.append(bc + exp - accuracy)
- delta = exp - sum_exp
- if exp >= sum_exp:
- # x much larger than existing sum?
- # first: quick test
- if ((delta > working_prec) and
- ((not sum_man) or
- delta - bitcount(abs(sum_man)) > working_prec)):
- sum_man = man
- sum_exp = exp
- else:
- sum_man += (man << delta)
- else:
- delta = -delta
- # x much smaller than existing sum?
- if delta - bc > working_prec:
- if not sum_man:
- sum_man, sum_exp = man, exp
- else:
- sum_man = (sum_man << delta) + man
- sum_exp = exp
- absolute_error = max(absolute_err)
- if not sum_man:
- return scaled_zero(absolute_error)
- 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,
- rnd), sum_accuracy
- return r
- def evalf_add(v: 'Add', prec: int, options: OPT_DICT) -> TMP_RES:
- res = pure_complex(v)
- if res:
- h, c = res
- re, _, re_acc, _ = evalf(h, prec, options)
- im, _, im_acc, _ = evalf(c, prec, options)
- return re, im, re_acc, im_acc
- oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC)
- i = 0
- target_prec = prec
- while 1:
- options['maxprec'] = min(oldmaxprec, 2*prec)
- terms = [evalf(arg, prec + 10, options) for arg in v.args]
- n = terms.count(S.ComplexInfinity)
- if n >= 2:
- return fnan, None, prec, None
- re, re_acc = add_terms(
- [a[0::2] for a in terms if isinstance(a, tuple) and a[0]], prec, target_prec)
- im, im_acc = add_terms(
- [a[1::2] for a in terms if isinstance(a, tuple) and a[1]], prec, target_prec)
- if n == 1:
- if re in (finf, fninf, fnan) or im in (finf, fninf, fnan):
- return fnan, None, prec, None
- return S.ComplexInfinity
- acc = complex_accuracy((re, im, re_acc, im_acc))
- if acc >= target_prec:
- if options.get('verbose'):
- print("ADD: wanted", target_prec, "accurate bits, got", re_acc, im_acc)
- break
- else:
- if (prec - target_prec) > options['maxprec']:
- break
- prec = prec + max(10 + 2**i, target_prec - acc)
- i += 1
- if options.get('verbose'):
- print("ADD: restarting with prec", prec)
- options['maxprec'] = oldmaxprec
- if iszero(re, scaled=True):
- re = scaled_zero(re)
- if iszero(im, scaled=True):
- im = scaled_zero(im)
- return re, im, re_acc, im_acc
- def evalf_mul(v: 'Mul', prec: int, options: OPT_DICT) -> TMP_RES:
- 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
- has_zero = False
- special = []
- from .numbers import Float
- for arg in args:
- result = evalf(arg, prec, options)
- if result is S.ComplexInfinity:
- special.append(result)
- continue
- if result[0] is None:
- if result[1] is None:
- has_zero = True
- continue
- num = Float._new(result[0], 1)
- if num is S.NaN:
- return fnan, None, prec, None
- if num.is_infinite:
- special.append(num)
- if special:
- if has_zero:
- return fnan, None, prec, None
- from .mul import Mul
- return evalf(Mul(*special), prec + 4, {})
- if has_zero:
- return None, None, None, None
- # 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
- while bc > 3*working_prec:
- man >>= working_prec
- exp += working_prec
- bc -= 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_pow(v: 'Pow', prec: int, options) -> TMP_RES:
- target_prec = prec
- base, exp = v.args
- # We handle x**n separately. This has two purposes: 1) it is much
- # faster, because we avoid calling evalf on the exponent, and 2) it
- # allows better handling of real/imaginary parts that are exactly zero
- if exp.is_Integer:
- p: int = exp.p # type: ignore
- # Exact
- if not p:
- return fone, None, prec, None
- # Exponentiation by p magnifies relative error by |p|, so the
- # base must be evaluated with increased precision if p is large
- prec += int(math.log(abs(p), 2))
- result = evalf(base, prec + 5, options)
- if result is S.ComplexInfinity:
- if p < 0:
- return None, None, None, None
- return result
- re, im, re_acc, im_acc = result
- # Real to integer power
- if re and not im:
- return mpf_pow_int(re, p, target_prec), None, target_prec, None
- # (x*I)**n = I**n * x**n
- if im and not re:
- z = mpf_pow_int(im, p, target_prec)
- case = p % 4
- if case == 0:
- return z, None, target_prec, None
- if case == 1:
- return None, z, None, target_prec
- if case == 2:
- return mpf_neg(z), None, target_prec, None
- if case == 3:
- return None, mpf_neg(z), None, target_prec
- # Zero raised to an integer power
- if not re:
- if p < 0:
- return S.ComplexInfinity
- return None, None, None, None
- # General complex number to arbitrary integer power
- re, im = libmp.mpc_pow_int((re, im), p, prec)
- # Assumes full accuracy in input
- return finalize_complex(re, im, target_prec)
- result = evalf(base, prec + 5, options)
- if result is S.ComplexInfinity:
- if exp.is_Rational:
- if exp < 0:
- return None, None, None, None
- return result
- raise NotImplementedError
- # Pure square root
- if exp is S.Half:
- xre, xim, _, _ = result
- # General complex square root
- if xim:
- re, im = libmp.mpc_sqrt((xre or fzero, xim), prec)
- return finalize_complex(re, im, prec)
- if not xre:
- return None, None, None, None
- # Square root of a negative real number
- if mpf_lt(xre, fzero):
- return None, mpf_sqrt(mpf_neg(xre), prec), None, prec
- # Positive square root
- return mpf_sqrt(xre, prec), None, prec, None
- # We first evaluate the exponent to find its magnitude
- # This determines the working precision that must be used
- prec += 10
- result = evalf(exp, prec, options)
- if result is S.ComplexInfinity:
- return fnan, None, prec, None
- yre, yim, _, _ = result
- # Special cases: x**0
- if not (yre or yim):
- return fone, None, prec, None
- ysize = fastlog(yre)
- # Restart if too big
- # XXX: prec + ysize might exceed maxprec
- if ysize > 5:
- prec += ysize
- yre, yim, _, _ = evalf(exp, prec, options)
- # Pure exponential function; no need to evalf the base
- if base is S.Exp1:
- if yim:
- re, im = libmp.mpc_exp((yre or fzero, yim), prec)
- return finalize_complex(re, im, target_prec)
- return mpf_exp(yre, target_prec), None, target_prec, None
- xre, xim, _, _ = evalf(base, prec + 5, options)
- # 0**y
- if not (xre or xim):
- if yim:
- return fnan, None, prec, None
- if yre[0] == 1: # y < 0
- return S.ComplexInfinity
- return None, None, None, None
- # (real ** complex) or (complex ** complex)
- if yim:
- re, im = libmp.mpc_pow(
- (xre or fzero, xim or fzero), (yre or fzero, yim),
- target_prec)
- return finalize_complex(re, im, target_prec)
- # complex ** real
- if xim:
- re, im = libmp.mpc_pow_mpf((xre or fzero, xim), yre, target_prec)
- return finalize_complex(re, im, target_prec)
- # negative ** real
- elif mpf_lt(xre, fzero):
- re, im = libmp.mpc_pow_mpf((xre, fzero), yre, target_prec)
- return finalize_complex(re, im, target_prec)
- # positive ** real
- else:
- return mpf_pow(xre, yre, target_prec), None, target_prec, None
- #----------------------------------------------------------------------------#
- # #
- # Special functions #
- # #
- #----------------------------------------------------------------------------#
- def evalf_exp(expr: 'exp', prec: int, options: OPT_DICT) -> TMP_RES:
- from .power import Pow
- return evalf_pow(Pow(S.Exp1, expr.exp, evaluate=False), prec, options)
- def evalf_trig(v: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES:
- """
- This function handles sin and cos of complex arguments.
- TODO: should also handle tan of complex arguments.
- """
- from sympy.functions.elementary.trigonometric import cos, sin
- if isinstance(v, cos):
- func = mpf_cos
- elif isinstance(v, sin):
- func = mpf_sin
- else:
- raise NotImplementedError
- arg = v.args[0]
- # 20 extra bits is possibly overkill. It does make the need
- # to restart very unlikely
- xprec = prec + 20
- re, im, re_acc, im_acc = evalf(arg, xprec, options)
- if im:
- if 'subs' in options:
- v = v.subs(options['subs'])
- return evalf(v._eval_evalf(prec), prec, options)
- if not re:
- if isinstance(v, cos):
- return fone, None, prec, None
- elif isinstance(v, sin):
- return None, None, None, None
- else:
- raise NotImplementedError
- # For trigonometric functions, we are interested in the
- # fixed-point (absolute) accuracy of the argument.
- xsize = fastlog(re)
- # Magnitude <= 1.0. OK to compute directly, because there is no
- # danger of hitting the first root of cos (with sin, magnitude
- # <= 2.0 would actually be ok)
- if xsize < 1:
- return func(re, prec, rnd), None, prec, None
- # Very large
- if xsize >= 10:
- xprec = prec + xsize
- re, im, re_acc, im_acc = evalf(arg, xprec, options)
- # Need to repeat in case the argument is very close to a
- # multiple of pi (or pi/2), hitting close to a root
- while 1:
- y = func(re, prec, rnd)
- ysize = fastlog(y)
- gap = -ysize
- accuracy = (xprec - xsize) - gap
- if accuracy < prec:
- if options.get('verbose'):
- print("SIN/COS", accuracy, "wanted", prec, "gap", gap)
- print(to_str(y, 10))
- if xprec > options.get('maxprec', DEFAULT_MAXPREC):
- return y, None, accuracy, None
- xprec += gap
- re, im, re_acc, im_acc = evalf(arg, xprec, options)
- continue
- else:
- return y, None, prec, None
- def evalf_log(expr: 'log', prec: int, options: OPT_DICT) -> TMP_RES:
- if len(expr.args)>1:
- expr = expr.doit()
- return evalf(expr, prec, options)
- arg = expr.args[0]
- workprec = prec + 10
- result = evalf(arg, workprec, options)
- if result is S.ComplexInfinity:
- return result
- xre, xim, xacc, _ = result
- # evalf can return NoneTypes if chop=True
- # issue 18516, 19623
- if xre is xim is None:
- # Dear reviewer, I do not know what -inf is;
- # it looks to be (1, 0, -789, -3)
- # but I'm not sure in general,
- # so we just let mpmath figure
- # it out by taking log of 0 directly.
- # It would be better to return -inf instead.
- xre = fzero
- if xim:
- from sympy.functions.elementary.complexes import Abs
- from sympy.functions.elementary.exponential import log
- # XXX: use get_abs etc instead
- re = evalf_log(
- log(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 and re != fzero:
- from .add import Add
- # We actually need to compute 1+x accurately, not x
- add = Add(S.NegativeOne, arg, evaluate=False)
- xre, xim, _, _ = evalf_add(add, 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 evalf_atan(v: 'atan', prec: int, options: OPT_DICT) -> TMP_RES:
- arg = v.args[0]
- xre, xim, reacc, imacc = evalf(arg, prec + 5, options)
- if xre is xim is None:
- return (None,)*4
- if xim:
- raise NotImplementedError
- return mpf_atan(xre, prec, rnd), None, prec, None
- def evalf_subs(prec: int, subs: dict) -> dict:
- """ Change all Float entries in `subs` to have precision prec. """
- newsubs = {}
- for a, b in subs.items():
- b = S(b)
- if b.is_Float:
- b = b._eval_evalf(prec)
- newsubs[a] = b
- return newsubs
- def evalf_piecewise(expr: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES:
- from .numbers import Float, Integer
- if 'subs' in options:
- expr = expr.subs(evalf_subs(prec, options['subs']))
- newopts = options.copy()
- del newopts['subs']
- if hasattr(expr, 'func'):
- return evalf(expr, prec, newopts)
- if isinstance(expr, float):
- return evalf(Float(expr), prec, newopts)
- if isinstance(expr, int):
- return evalf(Integer(expr), prec, newopts)
- # We still have undefined symbols
- raise NotImplementedError
- def evalf_alg_num(a: 'AlgebraicNumber', prec: int, options: OPT_DICT) -> TMP_RES:
- return evalf(a.to_root(), prec, options)
- #----------------------------------------------------------------------------#
- # #
- # High-level operations #
- # #
- #----------------------------------------------------------------------------#
- def as_mpmath(x: Any, prec: int, options: OPT_DICT) -> tUnion[mpc, mpf]:
- from .numbers import Infinity, NegativeInfinity, Zero
- x = sympify(x)
- if isinstance(x, Zero) or x == 0.0:
- return mpf(0)
- if isinstance(x, Infinity):
- return mpf('inf')
- if isinstance(x, NegativeInfinity):
- return mpf('-inf')
- # XXX
- result = evalf(x, prec, options)
- return quad_to_mpmath(result)
- def do_integral(expr: 'Integral', prec: int, options: OPT_DICT) -> TMP_RES:
- func = expr.args[0]
- x, xlow, xhigh = expr.args[1]
- if xlow == xhigh:
- xlow = xhigh = 0
- elif x not in func.free_symbols:
- # only the difference in limits matters in this case
- # so if there is a symbol in common that will cancel
- # out when taking the difference, then use that
- # difference
- if xhigh.free_symbols & xlow.free_symbols:
- diff = xhigh - xlow
- if diff.is_number:
- xlow, xhigh = 0, diff
- oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC)
- options['maxprec'] = min(oldmaxprec, 2*prec)
- with workprec(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
- from sympy.functions.elementary.trigonometric import cos, sin
- from .symbol import Wild
- have_part = [False, False]
- max_real_term: tUnion[float, int] = MINUS_INF
- max_imag_term: tUnion[float, int] = MINUS_INF
- def f(t: 'Expr') -> tUnion[mpc, mpf]:
- nonlocal max_real_term, max_imag_term
- 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 = max(max_real_term, fastlog(re))
- max_imag_term = max(max_imag_term, fastlog(im))
- if im:
- return mpc(re or fzero, im)
- return mpf(re or fzero)
- if options.get('quad') == 'osc':
- A = Wild('A', exclude=[x])
- B = Wild('B', exclude=[x])
- D = Wild('D')
- m = func.match(cos(A*x + B)*D)
- if not m:
- m = func.match(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_err = quadts(f, [xlow, xhigh], error=1)
- quadrature_error = fastlog(quadrature_err._mpf_)
- options['maxprec'] = oldmaxprec
- if have_part[0]:
- re: Optional[MPF_TUP] = result.real._mpf_
- re_acc: Optional[int]
- if re == fzero:
- re_s, re_acc = scaled_zero(int(-max(prec, max_real_term, quadrature_error)))
- re = scaled_zero(re_s) # handled ok in evalf_integral
- else:
- re_acc = int(-max(max_real_term - fastlog(re) - prec, quadrature_error))
- else:
- re, re_acc = None, None
- if have_part[1]:
- im: Optional[MPF_TUP] = result.imag._mpf_
- im_acc: Optional[int]
- if im == fzero:
- im_s, im_acc = scaled_zero(int(-max(prec, max_imag_term, quadrature_error)))
- im = scaled_zero(im_s) # handled ok in evalf_integral
- else:
- im_acc = int(-max(max_imag_term - fastlog(im) - prec, quadrature_error))
- else:
- im, im_acc = None, None
- result = re, im, re_acc, im_acc
- return result
- def evalf_integral(expr: 'Integral', prec: int, options: OPT_DICT) -> TMP_RES:
- limits = expr.limits
- if len(limits) != 1 or len(limits[0]) != 3:
- raise NotImplementedError
- workprec = prec
- i = 0
- maxprec = options.get('maxprec', INF)
- while 1:
- result = do_integral(expr, workprec, options)
- accuracy = complex_accuracy(result)
- if accuracy >= prec: # achieved desired precision
- break
- if workprec >= maxprec: # can't increase accuracy any more
- break
- if accuracy == -1:
- # maybe the answer really is zero and maybe we just haven't increased
- # the precision enough. So increase by doubling to not take too long
- # to get to maxprec.
- workprec *= 2
- else:
- workprec += max(prec, 2**i)
- workprec = min(workprec, maxprec)
- i += 1
- return result
- def check_convergence(numer: 'Expr', denom: 'Expr', n: 'Symbol') -> tTuple[int, Any, Any]:
- """
- Returns
- =======
- (h, g, p) where
- -- h is:
- > 0 for convergence of rate 1/factorial(n)**h
- < 0 for divergence of rate factorial(n)**(-h)
- = 0 for geometric or polynomial convergence or divergence
- -- abs(g) is:
- > 1 for geometric convergence of rate 1/h**n
- < 1 for geometric divergence of rate h**n
- = 1 for polynomial convergence or divergence
- (g < 0 indicates an alternating series)
- -- p is:
- > 1 for polynomial convergence of rate 1/n**h
- <= 1 for polynomial divergence of rate n**(-h)
- """
- from sympy.polys.polytools import Poly
- npol = Poly(numer, n)
- dpol = Poly(denom, n)
- p = npol.degree()
- q = dpol.degree()
- rate = q - p
- if rate:
- return rate, None, None
- constant = dpol.LC() / npol.LC()
- from .numbers import equal_valued
- if not equal_valued(abs(constant), 1):
- return rate, constant, None
- if npol.degree() == dpol.degree() == 0:
- return rate, constant, 0
- pc = npol.all_coeffs()[1]
- qc = dpol.all_coeffs()[1]
- return rate, constant, (qc - pc)/dpol.LC()
- def hypsum(expr: 'Expr', n: 'Symbol', start: int, prec: int) -> mpf:
- """
- Sum a rapidly convergent infinite hypergeometric series with
- given general term, e.g. e = hypsum(1/factorial(n), n). The
- quotient between successive terms must be a quotient of integer
- polynomials.
- """
- from .numbers import Float, equal_valued
- from sympy.simplify.simplify import hypersimp
- if prec == float('inf'):
- raise NotImplementedError('does not support inf prec')
- if start:
- expr = expr.subs(n, n + start)
- hs = hypersimp(expr, n)
- if hs is None:
- raise NotImplementedError("a hypergeometric series is required")
- num, den = hs.as_numer_denom()
- func1 = lambdify(n, num)
- func2 = lambdify(n, den)
- h, g, p = check_convergence(num, den, n)
- if h < 0:
- raise ValueError("Sum diverges like (n!)^%i" % (-h))
- term = expr.subs(n, 0)
- if not term.is_Rational:
- raise NotImplementedError("Non rational term functionality is not implemented.")
- # Direct summation if geometric or faster
- if h > 0 or (h == 0 and abs(g) > 1):
- term = (MPZ(term.p) << prec) // term.q
- s = term
- k = 1
- while abs(term) > 5:
- term *= MPZ(func1(k - 1))
- term //= MPZ(func2(k - 1))
- s += term
- k += 1
- return from_man_exp(s, -prec)
- else:
- alt = g < 0
- if abs(g) < 1:
- raise ValueError("Sum diverges like (%i)^n" % abs(1/g))
- if p < 1 or (equal_valued(p, 1) and not alt):
- raise ValueError("Sum diverges like n^%i" % (-p))
- # We have polynomial convergence: use Richardson extrapolation
- vold = None
- ndig = prec_to_dps(prec)
- while True:
- # Need to use at least quad precision because a lot of cancellation
- # might occur in the extrapolation process; we check the answer to
- # make sure that the desired precision has been reached, too.
- prec2 = 4*prec
- term0 = (MPZ(term.p) << prec2) // term.q
- def summand(k, _term=[term0]):
- if k:
- k = int(k)
- _term[0] *= MPZ(func1(k - 1))
- _term[0] //= MPZ(func2(k - 1))
- return make_mpf(from_man_exp(_term[0], -prec2))
- with workprec(prec):
- v = nsum(summand, [0, mpmath_inf], method='richardson')
- vf = Float(v, ndig)
- if vold is not None and vold == vf:
- break
- prec += prec # double precision each time
- vold = vf
- return v._mpf_
- def evalf_prod(expr: 'Product', prec: int, options: OPT_DICT) -> TMP_RES:
- if all((l[1] - l[2]).is_Integer for l in expr.limits):
- result = evalf(expr.doit(), prec=prec, options=options)
- else:
- from sympy.concrete.summations import Sum
- result = evalf(expr.rewrite(Sum), prec=prec, options=options)
- return result
- def evalf_sum(expr: 'Sum', prec: int, options: OPT_DICT) -> TMP_RES:
- from .numbers import Float
- if 'subs' in options:
- expr = expr.subs(options['subs'])
- func = expr.function
- limits = expr.limits
- if len(limits) != 1 or len(limits[0]) != 3:
- raise NotImplementedError
- if func.is_zero:
- return None, None, prec, None
- prec2 = prec + 10
- try:
- n, a, b = limits[0]
- if b is not S.Infinity or a is S.NegativeInfinity or a != int(a):
- raise NotImplementedError
- # Use fast hypergeometric summation if possible
- v = hypsum(func, n, int(a), prec2)
- delta = prec - fastlog(v)
- if fastlog(v) < -10:
- v = hypsum(func, n, int(a), delta)
- return v, None, min(prec, delta), None
- except NotImplementedError:
- # Euler-Maclaurin summation for general series
- eps = Float(2.0)**(-prec)
- for i in range(1, 5):
- m = n = 2**i * prec
- s, err = expr.euler_maclaurin(m=m, n=n, eps=eps,
- eval_integral=False)
- err = err.evalf()
- if err is S.NaN:
- raise NotImplementedError
- if err <= eps:
- break
- err = fastlog(evalf(abs(err), 20, options)[0])
- re, im, re_acc, im_acc = evalf(s, prec2, options)
- if re_acc is None:
- re_acc = -err
- if im_acc is None:
- im_acc = -err
- return re, im, re_acc, im_acc
- #----------------------------------------------------------------------------#
- # #
- # Symbolic interface #
- # #
- #----------------------------------------------------------------------------#
- def evalf_symbol(x: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES:
- val = options['subs'][x]
- if isinstance(val, mpf):
- if not val:
- return None, None, None, None
- return val._mpf_, None, prec, None
- else:
- if '_cache' not in options:
- options['_cache'] = {}
- cache = options['_cache']
- cached, cached_prec = cache.get(x, (None, MINUS_INF))
- if cached_prec >= prec:
- return cached
- v = evalf(sympify(val), prec, options)
- cache[x] = (v, prec)
- return v
- evalf_table: tDict[Type['Expr'], Callable[['Expr', int, OPT_DICT], TMP_RES]] = {}
- def _create_evalf_table():
- global evalf_table
- from sympy.concrete.products import Product
- from sympy.concrete.summations import Sum
- from .add import Add
- from .mul import Mul
- from .numbers import Exp1, Float, Half, ImaginaryUnit, Integer, NaN, NegativeOne, One, Pi, Rational, \
- Zero, ComplexInfinity, AlgebraicNumber
- from .power import Pow
- from .symbol import Dummy, Symbol
- from sympy.functions.elementary.complexes import Abs, im, re
- from sympy.functions.elementary.exponential import exp, log
- from sympy.functions.elementary.integers import ceiling, floor
- from sympy.functions.elementary.piecewise import Piecewise
- from sympy.functions.elementary.trigonometric import atan, cos, sin
- from sympy.integrals.integrals import Integral
- evalf_table = {
- Symbol: evalf_symbol,
- Dummy: evalf_symbol,
- Float: evalf_float,
- Rational: evalf_rational,
- Integer: evalf_integer,
- Zero: lambda x, prec, options: (None, None, prec, None),
- One: lambda x, prec, options: (fone, None, prec, None),
- Half: lambda x, prec, options: (fhalf, None, prec, None),
- Pi: lambda x, prec, options: (mpf_pi(prec), None, prec, None),
- Exp1: lambda x, prec, options: (mpf_e(prec), None, prec, None),
- ImaginaryUnit: lambda x, prec, options: (None, fone, None, prec),
- NegativeOne: lambda x, prec, options: (fnone, None, prec, None),
- ComplexInfinity: lambda x, prec, options: S.ComplexInfinity,
- NaN: lambda x, prec, options: (fnan, None, prec, None),
- exp: evalf_exp,
- cos: evalf_trig,
- sin: evalf_trig,
- Add: evalf_add,
- Mul: evalf_mul,
- Pow: evalf_pow,
- log: evalf_log,
- atan: evalf_atan,
- Abs: evalf_abs,
- re: evalf_re,
- im: evalf_im,
- floor: evalf_floor,
- ceiling: evalf_ceiling,
- Integral: evalf_integral,
- Sum: evalf_sum,
- Product: evalf_prod,
- Piecewise: evalf_piecewise,
- AlgebraicNumber: evalf_alg_num,
- }
- def evalf(x: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES:
- """
- Evaluate the ``Expr`` instance, ``x``
- to a binary precision of ``prec``. This
- function is supposed to be used internally.
- Parameters
- ==========
- x : Expr
- The formula to evaluate to a float.
- prec : int
- The binary precision that the output should have.
- options : dict
- A dictionary with the same entries as
- ``EvalfMixin.evalf`` and in addition,
- ``maxprec`` which is the maximum working precision.
- Returns
- =======
- An optional tuple, ``(re, im, re_acc, im_acc)``
- which are the real, imaginary, real accuracy
- and imaginary accuracy respectively. ``re`` is
- an mpf value tuple and so is ``im``. ``re_acc``
- and ``im_acc`` are ints.
- NB: all these return values can be ``None``.
- If all values are ``None``, then that represents 0.
- Note that 0 is also represented as ``fzero = (0, 0, 0, 0)``.
- """
- from sympy.functions.elementary.complexes import re as re_, im as im_
- try:
- rf = evalf_table[type(x)]
- r = rf(x, prec, options)
- except KeyError:
- # Fall back to ordinary evalf if possible
- if 'subs' in options:
- x = x.subs(evalf_subs(prec, options['subs']))
- xe = x._eval_evalf(prec)
- if xe is None:
- raise NotImplementedError
- as_real_imag = getattr(xe, "as_real_imag", None)
- if as_real_imag is None:
- raise NotImplementedError # e.g. FiniteSet(-1.0, 1.0).evalf()
- re, im = as_real_imag()
- if re.has(re_) or im.has(im_):
- raise NotImplementedError
- if re == 0.0:
- re = None
- reprec = None
- elif re.is_number:
- re = re._to_mpmath(prec, allow_ints=False)._mpf_
- reprec = prec
- else:
- raise NotImplementedError
- if im == 0.0:
- im = None
- imprec = None
- elif im.is_number:
- im = im._to_mpmath(prec, allow_ints=False)._mpf_
- imprec = prec
- else:
- raise NotImplementedError
- r = re, im, reprec, imprec
- if options.get("verbose"):
- print("### input", x)
- print("### output", to_str(r[0] or fzero, 50) if isinstance(r, tuple) else r)
- print("### raw", r) # r[0], r[2]
- print()
- chop = options.get('chop', False)
- if chop:
- if chop is True:
- chop_prec = prec
- else:
- # convert (approximately) from given tolerance;
- # the formula here will will make 1e-i rounds to 0 for
- # i in the range +/-27 while 2e-i will not be chopped
- chop_prec = int(round(-3.321*math.log10(chop) + 2.5))
- if chop_prec == 3:
- chop_prec -= 1
- r = chop_parts(r, chop_prec)
- if options.get("strict"):
- check_target(x, r, prec)
- return r
- def quad_to_mpmath(q, ctx=None):
- """Turn the quad returned by ``evalf`` into an ``mpf`` or ``mpc``. """
- mpc = make_mpc if ctx is None else ctx.make_mpc
- mpf = make_mpf if ctx is None else ctx.make_mpf
- if q is S.ComplexInfinity:
- raise NotImplementedError
- re, im, _, _ = q
- if im:
- if not re:
- re = fzero
- return mpc((re, im))
- elif re:
- return mpf(re)
- else:
- return mpf(fzero)
- class EvalfMixin:
- """Mixin class adding evalf capability."""
- __slots__ = () # type: tTuple[str, ...]
- def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False):
- """
- Evaluate the given formula to an accuracy of *n* digits.
- Parameters
- ==========
- subs : dict, optional
- Substitute numerical values for symbols, e.g.
- ``subs={x:3, y:1+pi}``. The substitutions must be given as a
- dictionary.
- maxn : int, optional
- Allow a maximum temporary working precision of maxn digits.
- chop : bool or number, optional
- Specifies how to replace tiny real or imaginary parts in
- subresults by exact zeros.
- When ``True`` the chop value defaults to standard precision.
- Otherwise the chop value is used to determine the
- magnitude of "small" for purposes of chopping.
- >>> from sympy import N
- >>> x = 1e-4
- >>> N(x, chop=True)
- 0.000100000000000000
- >>> N(x, chop=1e-5)
- 0.000100000000000000
- >>> N(x, chop=1e-4)
- 0
- strict : bool, optional
- Raise ``PrecisionExhausted`` if any subresult fails to
- evaluate to full accuracy, given the available maxprec.
- quad : str, optional
- Choose algorithm for numerical quadrature. By default,
- tanh-sinh quadrature is used. For oscillatory
- integrals on an infinite interval, try ``quad='osc'``.
- verbose : bool, optional
- Print debug information.
- Notes
- =====
- When Floats are naively substituted into an expression,
- precision errors may adversely affect the result. For example,
- adding 1e16 (a Float) to 1 will truncate to 1e16; if 1e16 is
- then subtracted, the result will be 0.
- That is exactly what happens in the following:
- >>> from sympy.abc import x, y, z
- >>> values = {x: 1e16, y: 1, z: 1e16}
- >>> (x + y - z).subs(values)
- 0
- Using the subs argument for evalf is the accurate way to
- evaluate such an expression:
- >>> (x + y - z).evalf(subs=values)
- 1.00000000000000
- """
- from .numbers import Float, Number
- n = n if n is not None else 15
- if subs and is_sequence(subs):
- raise TypeError('subs must be given as a dictionary')
- # for sake of sage that doesn't like evalf(1)
- if n == 1 and isinstance(self, Number):
- from .expr import _mag
- rv = self.evalf(2, subs, maxn, chop, strict, quad, verbose)
- m = _mag(rv)
- rv = rv.round(1 - m)
- return rv
- if not evalf_table:
- _create_evalf_table()
- prec = dps_to_prec(n)
- options = {'maxprec': max(prec, int(maxn*LG10)), 'chop': chop,
- 'strict': strict, 'verbose': verbose}
- if subs is not None:
- options['subs'] = subs
- if quad is not None:
- options['quad'] = quad
- try:
- result = evalf(self, prec + 4, options)
- except NotImplementedError:
- # Fall back to the ordinary evalf
- if hasattr(self, 'subs') and subs is not None: # issue 20291
- v = self.subs(subs)._eval_evalf(prec)
- else:
- v = self._eval_evalf(prec)
- if v is None:
- return self
- elif not v.is_number:
- return v
- try:
- # If the result is numerical, normalize it
- result = evalf(v, prec, options)
- except NotImplementedError:
- # Probably contains symbols or unknown functions
- return v
- if result is S.ComplexInfinity:
- return result
- re, im, re_acc, im_acc = result
- if re is S.NaN or im is S.NaN:
- return S.NaN
- if re:
- p = max(min(prec, re_acc), 1)
- re = Float._new(re, p)
- else:
- re = S.Zero
- if im:
- p = max(min(prec, im_acc), 1)
- im = Float._new(im, p)
- return re + im*S.ImaginaryUnit
- else:
- return re
- n = evalf
- def _evalf(self, prec):
- """Helper for evalf. Does the same thing but takes binary precision"""
- r = self._eval_evalf(prec)
- if r is None:
- r = self
- return r
- def _eval_evalf(self, prec):
- return
- def _to_mpmath(self, prec, allow_ints=True):
- # mpmath functions accept ints as input
- errmsg = "cannot convert to mpmath number"
- if allow_ints and self.is_Integer:
- return self.p
- if hasattr(self, '_as_mpf_val'):
- return make_mpf(self._as_mpf_val(prec))
- try:
- result = evalf(self, prec, {})
- return quad_to_mpmath(result)
- except NotImplementedError:
- v = self._eval_evalf(prec)
- if v is None:
- raise ValueError(errmsg)
- if v.is_Float:
- return make_mpf(v._mpf_)
- # Number + Number*I is also fine
- re, im = v.as_real_imag()
- if allow_ints and re.is_Integer:
- re = from_int(re.p)
- elif re.is_Float:
- re = re._mpf_
- else:
- raise ValueError(errmsg)
- if allow_ints and im.is_Integer:
- im = from_int(im.p)
- elif im.is_Float:
- im = im._mpf_
- else:
- raise ValueError(errmsg)
- return make_mpc((re, im))
- def N(x, n=15, **options):
- r"""
- Calls x.evalf(n, \*\*options).
- Explanations
- ============
- Both .n() and N() are equivalent to .evalf(); use the one that you like better.
- See also the docstring of .evalf() for information on the options.
- Examples
- ========
- >>> from sympy import Sum, oo, N
- >>> from sympy.abc import k
- >>> Sum(1/k**k, (k, 1, oo))
- Sum(k**(-k), (k, 1, oo))
- >>> N(_, 4)
- 1.291
- """
- # by using rational=True, any evaluation of a string
- # will be done using exact values for the Floats
- return sympify(x, rational=True).evalf(n, **options)
- def _evalf_with_bounded_error(x: 'Expr', eps: 'Optional[Expr]' = None,
- m: int = 0,
- options: Optional[OPT_DICT] = None) -> TMP_RES:
- """
- Evaluate *x* to within a bounded absolute error.
- Parameters
- ==========
- x : Expr
- The quantity to be evaluated.
- eps : Expr, None, optional (default=None)
- Positive real upper bound on the acceptable error.
- m : int, optional (default=0)
- If *eps* is None, then use 2**(-m) as the upper bound on the error.
- options: OPT_DICT
- As in the ``evalf`` function.
- Returns
- =======
- A tuple ``(re, im, re_acc, im_acc)``, as returned by ``evalf``.
- See Also
- ========
- evalf
- """
- if eps is not None:
- if not (eps.is_Rational or eps.is_Float) or not eps > 0:
- raise ValueError("eps must be positive")
- r, _, _, _ = evalf(1/eps, 1, {})
- m = fastlog(r)
- c, d, _, _ = evalf(x, 1, {})
- # Note: If x = a + b*I, then |a| <= 2|c| and |b| <= 2|d|, with equality
- # only in the zero case.
- # If a is non-zero, then |c| = 2**nc for some integer nc, and c has
- # bitcount 1. Therefore 2**fastlog(c) = 2**(nc+1) = 2|c| is an upper bound
- # on |a|. Likewise for b and d.
- nr, ni = fastlog(c), fastlog(d)
- n = max(nr, ni) + 1
- # If x is 0, then n is MINUS_INF, and p will be 1. Otherwise,
- # n - 1 bits get us past the integer parts of a and b, and +1 accounts for
- # the factor of <= sqrt(2) that is |x|/max(|a|, |b|).
- p = max(1, m + n + 1)
- options = options or {}
- return evalf(x, p, options)
|