防止在 SymPy 中计算指数
Prevent evaluation of exponentials in SymPy
我使用 SymPy 作为包含许多指数的函数的代码生成器。因此,不评估指数的参数对于数值稳定性很重要。我想阻止这种情况:
>>> import sympy as sp
>>> x, y = sp.symbols('x y')
>>> expr = sp.exp(5.*x - 10.)
>>> print(expr)
4.53999297624849e-5*exp(5.0*x)
因为它会导致数值不准确。
我可以防止指数计算如下:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> print(expr)
exp(5.0*x - 10.0)
但是,当我对表达式执行替换或微分等操作时,指数会再次计算:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> expr.subs(x, y)
4.53999297624849e-5*exp(5.0*y)
>>> expr.diff(x, 1)
5.0*(4.53999297624849e-5*exp(5.0*x))
在 SymPy 中防止在此类操作下计算指数的正确方法是什么?
最明显的一点是您对整数值使用浮点数,例如:
In [8]: exp(5*x - 10)
Out[8]:
5⋅x - 10
ℯ
In [9]: exp(5.*x - 10.)
Out[9]:
5.0⋅x
4.53999297624849e-5⋅ℯ
也许在您的实际问题中您想要使用非整数。同样,应使用有理数进行精确计算:
In [10]: exp(Rational(1, 3)*x - S(3)/2)
Out[10]:
x 3
─ - ─
3 2
ℯ
也许您输入的数字不是真正有理数,您将它们作为 Python 浮点数,但您不想让它们计算。您可以使用符号,然后仅在评估时替换它们:
In [13]: exp(a*x + b).evalf(subs={a:5.0, b:10.})
Out[13]:
a⋅x + b
ℯ
In [14]: exp(a*x + b).evalf(subs={x:1, a:5.0, b:10.})
Out[14]: 3269017.37247211
In [15]: exp(a*x + b).subs({a:5.0, b:10.})
Out[15]:
5.0⋅x
22026.4657948067⋅ℯ
如果所有这些看起来很尴尬,而你真的只是想填充浮动并阻止计算,那么你可以使用 UnevaluatedExpr
:
In [21]: e = exp(UnevaluatedExpr(5.0)*x - UnevaluatedExpr(10.))
In [22]: e
Out[22]:
x⋅5.0 - 10.0
ℯ
In [23]: e.doit() # doit triggers evaluation
Out[23]:
5.0⋅x
4.53999297624849e-5⋅ℯ
我使用 SymPy 作为包含许多指数的函数的代码生成器。因此,不评估指数的参数对于数值稳定性很重要。我想阻止这种情况:
>>> import sympy as sp
>>> x, y = sp.symbols('x y')
>>> expr = sp.exp(5.*x - 10.)
>>> print(expr)
4.53999297624849e-5*exp(5.0*x)
因为它会导致数值不准确。
我可以防止指数计算如下:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> print(expr)
exp(5.0*x - 10.0)
但是,当我对表达式执行替换或微分等操作时,指数会再次计算:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> expr.subs(x, y)
4.53999297624849e-5*exp(5.0*y)
>>> expr.diff(x, 1)
5.0*(4.53999297624849e-5*exp(5.0*x))
在 SymPy 中防止在此类操作下计算指数的正确方法是什么?
最明显的一点是您对整数值使用浮点数,例如:
In [8]: exp(5*x - 10)
Out[8]:
5⋅x - 10
ℯ
In [9]: exp(5.*x - 10.)
Out[9]:
5.0⋅x
4.53999297624849e-5⋅ℯ
也许在您的实际问题中您想要使用非整数。同样,应使用有理数进行精确计算:
In [10]: exp(Rational(1, 3)*x - S(3)/2)
Out[10]:
x 3
─ - ─
3 2
ℯ
也许您输入的数字不是真正有理数,您将它们作为 Python 浮点数,但您不想让它们计算。您可以使用符号,然后仅在评估时替换它们:
In [13]: exp(a*x + b).evalf(subs={a:5.0, b:10.})
Out[13]:
a⋅x + b
ℯ
In [14]: exp(a*x + b).evalf(subs={x:1, a:5.0, b:10.})
Out[14]: 3269017.37247211
In [15]: exp(a*x + b).subs({a:5.0, b:10.})
Out[15]:
5.0⋅x
22026.4657948067⋅ℯ
如果所有这些看起来很尴尬,而你真的只是想填充浮动并阻止计算,那么你可以使用 UnevaluatedExpr
:
In [21]: e = exp(UnevaluatedExpr(5.0)*x - UnevaluatedExpr(10.))
In [22]: e
Out[22]:
x⋅5.0 - 10.0
ℯ
In [23]: e.doit() # doit triggers evaluation
Out[23]:
5.0⋅x
4.53999297624849e-5⋅ℯ