Запретить оценку экспонент в SymPy - PullRequest
1 голос
/ 17 апреля 2020

Я использую 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 для предотвращения оценки экспоненты при таких операциях?

1 Ответ

1 голос
/ 17 апреля 2020

Наиболее очевидным моментом является то, что вы используете float для целочисленных значений, например:

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⋅ℯ 
...