python sympy中先使用evalf还是先使用subs的区别

4ioopgfo  于 2023-08-02  发布在  Python
关注(0)|答案(2)|浏览(169)

我使用evalfsubs来计算表达式。

result_1 = (1/x).evalf(subs={x: 3.0}, n=25)
result_2 = (1/x).subs(x, 3.0).evalf(25)

字符串
首先使用evalf计算表达式1/x后,result_1近似等于0.3333333333333333333333333(精度设置为25)。但result_2近似等于0.3333333333333333148296163
我想知道这是如何运作的幕后。
我会为任何信息和资源的链接greatful。

ve7v8dk2

ve7v8dk21#

让我们关注这行代码:

result_1 = (1/x).evalf(subs={x: 3.0}, n=25)

字符串
{x: 3.0}evalf处理,这将创建一个新的字典,其中所有浮点数都将具有指定的精度。从本质上讲,新字典看起来像:{x: Float(3.0, precision=29)}。注意我使用了precision=29。这不是一个错误:从sympy 1.12开始,evalf将精度增加4个单位。
然后,这个字典被替换到表达式中,如下所示:(1/x).subs({x: Float(3.0, precision=29)}):这会触发一个求值,生成一个精度为n=29的新浮点数。最后,evalf计算结果,直到达到用户指定的精度,在本例中n=25。
第二行代码:

result_2 = (1/x).subs(x, 3.0).evalf(25)


这里,subs将把数字3.0 sympifyFloat(3.0, precision=15)(默认精度)。这个数字在表达式中被替换,这会触发一个计算,产生一个精度为15的新浮点数。最后,evalf计算该数字,直到精度n=25。
主要的区别是第一行代码产生一个精确的结果,直到指定的精度n=25,而第二个数字只是一些近似值,因为初始计算是在精度=15的情况下执行的。
参考:源代码,特别是sympy/core/eval.py

dxxyhpgq

dxxyhpgq2#

TL;DR

(1/x).subs(x, 3.0)

字符串

0.3333333333333333


现在,将evalf()应用于它将是:

sympy.core.numbers.Float(0.3333333333333333).evalf(25)


它会给予

0.3333333333333333148296163


所以,

(1/x).subs(x, 3.0).evalf(25) == sympy.core.numbers.Float(0.3333333333333333).evalf(25)


在15位之后,它失去精度。
链接到eval()
(1/x).evalf(subs={x: 3.0}, n=25)试图避免替换可能发生的重要性损失。
替换的默认精度是15位,不足以保留该信息
链接到evalf()
https://github.com/sympy/sympy/blob/master/sympy/core/evalf.py
您也可以通过执行help(sympy.evalf)来阅读详细信息
名称sympy.core.evalf
描述SymPy表达式的自适应数值计算,使用数学函数的mpmath。
CLASSES builtins.ArithmeticError(builtins.Exception)PrecisionExhausted builtins.object EvalfMixin

class EvalfMixin(builtins.object)
 |  Mixin class adding evalf capability.
 |  
 |  Methods defined here:
 |  
 |  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
 |  
 |  n = evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False)

class PrecisionExhausted(builtins.ArithmeticError)
 |  Method resolution order:
 |      PrecisionExhausted
 |      builtins.ArithmeticError
 |      builtins.Exception
 |      builtins.BaseException
 |      builtins.object
 |  
 |  Data descriptors defined here:
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from builtins.ArithmeticError:
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods inherited from builtins.ArithmeticError:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from builtins.BaseException:
 |  
 |  __delattr__(self, name, /)
 |      Implement delattr(self, name).
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __reduce__(...)
 |      Helper for pickle.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __setattr__(self, name, value, /)
 |      Implement setattr(self, name, value).
 |  
 |  __setstate__(...)
 |  
 |  __str__(self, /)
 |      Return str(self).
 |  
 |  with_traceback(...)
 |      Exception.with_traceback(tb) --
 |      set self.__traceback__ to tb and return self.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from builtins.BaseException:
 |  
 |  __cause__
 |      exception cause
 |  
 |  __context__
 |      exception context
 |  
 |  __dict__
 |  
 |  __suppress_context__
 |  
 |  __traceback__
 |  
 |  args


FUNCTIONS N(x,n=15,**options)调用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

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

as_mpmath(x: 'Any', prec: 'int', options: 'OPT_DICT') -> 'tUnion[mpc, mpf]'

bitcount(n)
    Return smallest integer, b, such that |n|/2**b < 1.

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)

check_target(expr: "'Expr'", result: 'TMP_RES', prec: 'int')

chop_parts(value: 'TMP_RES', prec: 'int') -> 'TMP_RES'
    Chop off tiny real or complex parts.

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.

do_integral(expr: "'Integral'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

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)``.

evalf_abs(expr: "'Abs'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_add(v: "'Add'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_alg_num(a: "'AlgebraicNumber'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_atan(v: "'atan'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_bernoulli(expr: "'bernoulli'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_ceiling(expr: "'ceiling'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_exp(expr: "'exp'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_float(expr: "'Float'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_floor(expr: "'floor'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_im(expr: "'im'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_integer(expr: "'Integer'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_integral(expr: "'Integral'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_log(expr: "'log'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_mul(v: "'Mul'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_piecewise(expr: "'Expr'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_pow(v: "'Pow'", prec: 'int', options) -> 'TMP_RES'

evalf_prod(expr: "'Product'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_rational(expr: "'Rational'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_re(expr: "'re'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_subs(prec: 'int', subs: 'dict') -> 'dict'
    Change all Float entries in `subs` to have precision prec.

evalf_sum(expr: "'Sum'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

evalf_symbol(x: "'Expr'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

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.

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)

finalize_complex(re: 'MPF_TUP', im: 'MPF_TUP', prec: 'int') -> 'TMP_RES'

get_abs(expr: "'Expr'", prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'

get_complex_part(expr: "'Expr'", no: 'int', prec: 'int', options: 'OPT_DICT') -> 'TMP_RES'
    no = 0 for real part, no = 1 for imaginary part

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.

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.

iszero(mpf: 'tUnion[MPF_TUP, SCALED_ZERO_TUP, None]', scaled=False) -> 'Optional[bool]'

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)

quad_to_mpmath(q)
    Turn the quad returned by ``evalf`` into an ``mpf`` or ``mpc``.

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


DATA Any = typing.Any Special type表示不受约束的类型。

- Any is compatible with every type.
    - Any assumed to have all methods.
    - All values assumed to be instances of Any.
    
    Note that all the above statements are true from the point of view of
    static type checkers. At runtime, Any should not be used with instance
    or class checks.

Callable = typing.Callable
    Callable type; Callable[[int], str] is a function of (int) -> str.
    
    The subscription syntax must always be used with exactly two
    values: the argument list and the return type.  The argument list
    must be a list of types or ellipsis; the return type must be a single type.
    
    There is no syntax to indicate optional or keyword arguments,
    such function types are rarely used as callback types.

DEFAULT_MAXPREC = 333
INF = inf
LG10 = 3.3219280948873626
List = typing.List
    A generic version of list.

MINUS_INF = -inf
MPF_TUP = typing.Tuple[int, int, int, int]
OPT_DICT = typing.Dict[str, typing.Any]
Optional = typing.Optional
    Optional type.
    
    Optional[X] is equivalent to Union[X, None].

S = S
SCALED_ZERO_TUP = typing.Tuple[typing.List[int], int, int, int]
SYMPY_INTS = (<class 'int'>,)
TMP_RES = typing.Any
    Special type indicating an unconstrained type.
    
    - Any is compatible with every type.
    - Any assumed to have all methods.
    - All values assumed to be instances of Any.
    
    Note that all the above statements are true from the point of view of
    static type checkers. At runtime, Any should not be used with instance
    or class checks.

TYPE_CHECKING = False
Type = typing.Type
    A special construct usable to annotate class objects.
    
    For example, suppose we have the following classes::
    
      class User: ...  # Abstract base for User classes
      class BasicUser(User): ...
      class ProUser(User): ...
      class TeamUser(User): ...
    
    And a function that takes a class argument that's a subclass of
    User and returns an instance of the corresponding class::
    
      U = TypeVar('U', bound=User)
      def new_user(user_class: Type[U]) -> U:
          user = user_class()
          # (Here we could write the user object to a database)
          return user
    
      joe = new_user(BasicUser)
    
    At this point the type checker knows that joe has type BasicUser.

__annotations__ = {'evalf_table': "tDict[Type['Expr'], Callable[['Expr...
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 10, 0, 'alpha', 0), 1...
evalf_table = {<class 'sympy.core.numbers.Zero'>: <function _create_ev...
fhalf = (0, 1, -1, 1)
finf = (0, 0, -456, -2)
fnan = (0, 0, -123, -1)
fninf = (1, 0, -789, -3)
fnone = (1, 1, 0, 1)
fone = (0, 1, 0, 1)
fzero = (0, 0, 0, 0)
mp = <mpmath.ctx_mp.MPContext object>
mpmath_inf = mpf('+inf')
rnd = 'n'
round_nearest = 'n'
tDict = typing.Dict
    A generic version of dict.

tTuple = typing.Tuple
    Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
    
    Example: Tuple[T1, T2] is a tuple of two elements corresponding
    to type variables T1 and T2.  Tuple[int, float, str] is a tuple
    of an int, a float and a string.
    
    To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].

tUnion = typing.Union
    Union type; Union[X, Y] means either X or Y.
    
    To define a union, use e.g. Union[int, str].  Details:
    - The arguments must be types and there must be at least one.
    - None as an argument is a special case and is replaced by
      type(None).
    - Unions of unions are flattened, e.g.::
    
        Union[Union[int, str], float] == Union[int, str, float]
    
    - Unions of a single argument vanish, e.g.::
    
        Union[int] == int  # The constructor actually returns int
    
    - Redundant arguments are skipped, e.g.::
    
        Union[int, str, int] == Union[int, str]
    
    - When comparing unions, the argument order is ignored, e.g.::
    
        Union[int, str] == Union[str, int]
    
    - You cannot subclass or instantiate a union.
    - You can use Optional[X] as a shorthand for Union[X, None].

相关问题