Для чего конкретно нужен аргумент `subs` метода` evalf` выражения SymPy?Как `s.evalf (subs = {...})`? - PullRequest
0 голосов
/ 20 мая 2018

Я наткнулся на это, поскольку он не вел себя так, как я ожидал.Для чего конкретно s.evalf(subs={t: 0})?

Кажется, это не «ярлык» для s.evalf().subs({t: 0}) и не для s.evalf().subs({t: 0}), так как

import sympy

omega, t = sympy.symbols("omega, t")
s = sympy.pi * sympy.cos(omega*t)

# both result in 3.14159265358979
print(s.evalf().subs({t: 0}))
print(s.subs({t: 0}).evalf())
# results in 3.14159265358979*cos(omega*t)
print(s.evalf(subs={t: 0}))

приводит к

3.14159265358979
3.14159265358979
3.14159265358979*cos(omega*t)

Не удалось найти хорошую документацию по этому вопросу.Может кто знает ответ?В противном случае мне, возможно, придется взглянуть на исходный код, чтобы узнать больше ...

Ответы [ 5 ]

0 голосов
/ 05 сентября 2018

Я тоже искал решение этой проблемы.Следующее, кажется, делает трюк в случае примера кода asmeurer :

from sympy import symbols, Float
x, y, z = symbols('x y z')

(x + y - z).subs({x: Float(1e100, 100), y: 1, z: Float(1e100, 100)})

, который правильно оценивается в 1,0.

0 голосов
/ 06 июля 2018

Вы можете думать о s.evalf(subs={t: 0}) как о способе сказать "оцените s по значению t = 0".Механизм evalf пытается дать вам ответ с желаемой точностью, заботясь о промежуточной точности во время расчета.(Аарон уже привел хороший пример, где оценка суммы даст неправильный ответ, если значения просто подставляются и оцениваются в порядке, определяемом Python.)

0 голосов
/ 20 мая 2018

Я предполагаю, что это похоже на Mathematica Replace метод .Также этот ответ на подобный вопрос может быть полезен.

По сути, он заменяет символ t значением 0.В вашем примере это не очень важно:

print(s.evalf().subs({t: 0}))
print(s.subs({t: 0}).evalf())

Оба оператора приводят к одному и тому же конечному значению.Однако при вычислении, например, производных, порядок имеет значение.Например,

In [76]: x=sympy.Symbol('x')

In [77]: sympy.diff(x**2, x)
Out[77]: 2*x

In [78]: sympy.diff((x**2).subs({'x':5}), x)
Out[78]: 0

In [79]: sympy.diff(x**2, x).subs({'x':5})
Out[79]: 10

В первом примере x заменяется на 5 до дифференцирования (а производная от 25 по x равна 0), а во втором примересначала вычисляется производная (2*x), а затем в этом производном выражении символ x заменяется на 5, так что ответом является 2*5=10.


РЕДАКТИРОВАТЬ после комментария @abarnet:

In [85]: import sympy
    ...: 
    ...: omega, t = sympy.symbols("omega, t")
    ...: s = sympy.pi * sympy.cos(omega*t)
    ...: 
    ...: # both result in 3.14159265358979
    ...: print(s.evalf().subs({t: 0}))
    ...: print(s.subs({t: 0}).evalf())
    ...: # results in 3.14159265358979*cos(omega*t)
    ...: print(s.evalf(subs={t: 0, omega: 1}))
    ...: 
    ...: 
3.14159265358979
3.14159265358979
3.14159265358979

Итак, предоставления просто t недостаточно для sympy - он не может оценить, что такое omega*0, не зная, что такое omega.


РЕДАКТИРОВАТЬ 2:

Второй пример работает, потому что оценка выполняется после замены, в то время как третий пример пытается выполнить числовую оценку с данными заменами и не может вычислить выражение численно (без omega), и поэтому оставляет выражение в символической форме.Первый пример также работает, потому что он просто заменяет t на 0 и упрощает выражение.Однако

In [86]: s.evalf()
Out[86]: 3.14159265358979*cos(omega*t)

РЕДАКТИРОВАТЬ 3:

Изучая код, становится ясно, что код evalf (фактически одна из функций evalf_*) пытается оценить каждый аргумент в args независимо с заданными параметрами (т. Е. subs), прежде чем приступить к оценке выражения.Так, в первом случае s.evalf().subs({t: 0}), первый eval() ничего не делает (ну, почти ... - он оценивает pi в 3.1415...), а subs({t: 0}) заменяет t на 0, а затемвыражение переоценивается / упрощается до 3.14....Во втором случае сначала выполняется замена, а затем pi оценивается как 3.1415....В третьем случае каждый аргумент в s.args оценивается.Код здесь не выполняется (поскольку он не может оценить omega с учетом предоставленной опции subs) и возвращает входное выражение без изменений.

То есть третий параметр s.eval(subs={t: 0}) требует всех аргументовs подлежит определению.Проверка этого выполняется здесь для mul функции (очевидно, sympy master опережает мою установленную версию).

Чтобы увидеть это, попробуйте простое выражение:

In [1]: import sympy
    ...: from sympy.core import evalf
    ...: t, x = sympy.symbols("t, x")
    ...: 
    ...: 

In [2]: (t*x).evalf(subs={t:0})
Out[2]: t*x

In [3]: evalf.evalf(t*x, 10, {t: 0})
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/.../lib/python3.5/site-packages/sympy/core/evalf.py in evalf(x, prec, options)
    1285         rf = evalf_table[x.func]
-> 1286         r = rf(x, prec, options)
    1287     except KeyError:

~/.../lib/python3.5/site-packages/sympy/core/evalf.py in evalf_symbol(x, prec, options)
    1203 def evalf_symbol(x, prec, options):
-> 1204     val = options['subs'][x]
    1205     if isinstance(val, mpf):

KeyError: 'subs'

During handling of the above exception, another exception occurred:

AttributeError                            Traceback (most recent call last)
~/.../lib/python3.5/site-packages/sympy/core/evalf.py in evalf(x, prec, options)
    1292             xe = x._eval_evalf(prec)
-> 1293             re, im = xe.as_real_imag()
    1294             if re.has(re_) or im.has(im_):

AttributeError: 'NoneType' object has no attribute 'as_real_imag'

During handling of the above exception, another exception occurred:

NotImplementedError                       Traceback (most recent call last)
<ipython-input-3-e075388b139d> in <module>()
----> 1 evalf.evalf(t*x, 10, {t: 0})

~/.../lib/python3.5/site-packages/sympy/core/evalf.py in evalf(x, prec, options)
    1284     try:
    1285         rf = evalf_table[x.func]
-> 1286         r = rf(x, prec, options)
    1287     except KeyError:
    1288         try:

~/.../lib/python3.5/site-packages/sympy/core/evalf.py in evalf_mul(v, prec, options)
    536     from sympy.core.numbers import Float
    537     for arg in args:
--> 538         arg = evalf(arg, prec, options)
    539         if arg[0] is None:
    540             continue

~/.../lib/python3.5/site-packages/sympy/core/evalf.py in evalf(x, prec, options)
    1308             r = re, im, reprec, imprec
    1309         except AttributeError:
-> 1310             raise NotImplementedError
    1311     if options.get("verbose"):
    1312         print("### input", x)

NotImplementedError: 

In [4]: 
0 голосов
/ 22 мая 2018

evalf(subs=...) пытается избежать потери значимости, которая может произойти из-за наивного замещения.

Например,

>>> (x + y - z).subs({x: 1e100, y: 1, z: 1e100})
0
>>> (x + y - z).evalf(subs={x: 1e100, y: 1, z: 1e100})
1.00000000000000

Наивное замещение оценивает 1e100 + 1 - 1e100, что приводит к потере 1, поскольку точности по умолчанию, 15 цифр, недостаточно для сохранения этой информации.С evalf(subs=...) выражение выполняется через алгоритм evalf, который осторожен в таких вопросах, как эта, где значение может быть потеряно.Словарь subs сообщает алгоритмам evalf, какие символы следует заменять числами при их обнаружении.Вы можете увидеть источник evalf.py , если вас интересуют мельчайшие подробности.

0 голосов
/ 20 мая 2018

В документации просто сказано:

См. Также строку документации .evalf () для получения информации о параметрах.

Вы можете сделатьhelp(sympy.evalf) в интерактивном сеансе, или вы можете нажать source на N и немного прокрутить до строки документации.В любом случае:

   subs=<dict>
       Substitute numerical values for symbols, e.g.
       subs={x:3, y:1+pi}. The substitutions must be given as a
       dictionary.

Если вы посмотрите на источник evalf самого , то в конечном итоге это будет:

x = x.subs(evalf_subs(prec, options['subs']))

1024 * делает:

def evalf_subs(prec, subs):
    """ 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

Итак, вы можете видеть, что это не совсем то же самое, что просто позвонить subs перед вызовом eval.(И, конечно, оба они сильно отличаются от вызова subs после eval, хотя в некоторых простых примерах они заканчивают тем же.)

Намерение кажетсяsubs= может применять другие параметры evalf во время замены.Эти «другие evalf опции», по-видимому, в настоящее время выглядят как prec, но, предположительно, они могут измениться в будущем.

В любом случае, применяя prec ко всемиз замен является хорошей идеей, если вы пытаетесь сделать числовое приближение с заданной точностью.

Но это может быть плохой идеей, если вы пытаетесь остаться с символическими вычислениями.И я подозреваю, что это может быть то, что вы видите здесь, хотя эта часть только предположение.Конечно, cos(omega*0) будет 1 независимо от того, что t.Но cos(omega*t), где t находится в пределах одного ulp от 0 с некоторой определенной точностью, вероятно, составляет 1 плюс или минус несколько ulps в зависимости от значения omega.Таким образом, это не может быть уменьшено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...