SymPy Expression的`evalf`方法的`subs`参数究竟是什么?就像`s.evalf(subs = {...})`?

时间:2018-05-20 20:22:46

标签: python numpy sympy

我偶然发现了这一点,因为它没有按照我的预期行事。 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 :(得分:2)

The documentation只是说:

  

有关选项的信息,另请参阅.evalf()的文档字符串。

您可以在互动会话中执行help(sympy.evalf),也可以点击N上的source并向上滚动到文档字符串。无论哪种方式:

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

如果你看一下the source for evalf itself,最终会做的是:

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

evalf_subs的作用是:

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选项。那&#34;其他evalf选项&#34;从源头看,目前只有prec,但可能会在未来发生变化。

无论如何,如果您尝试使用给定的精度进行数值近似,则将prec应用于所有替换是个好主意。

但是,如果你试图继续使用符号计算,那可能是一个坏主意。我怀疑这可能是你在这里看到的,虽然这部分只是猜测。当然,无论cos(omega*0)是什么,t都将为1。但是cos(omega*t)其中t在某个特定精度的0 ulp之内,可能是1加或减几个ulps,具体取决于omega的值。所以,它无法减少。

答案 1 :(得分:1)

我的猜测是,这类似于Mathematica&#39; s Replace method。同样this answer对类似问题也许有用。

从根本上说,它用值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

在第一个示例中,{<1}}在区分之前用5 替换(并且关于x的25的导数是0),在第二个示例中,首先计算导数({ {1}})然后在此衍生表达式中,符号x被替换为5,因此答案为2*x

在@abarnet评论之后编辑:

x

因此,仅提供 2*5=10对于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是什么。< / p>

编辑2:

第二个示例有效,因为在替换后执行评估,而第三个示例尝试执行带有给定替换的数值评估 并且失败以数字方式评估表达式(没有omega*0),因此它以符号形式留下表达式。第一个例子也有效,因为它只是将omega替换为0并简化了表达式。然而,

omega

编辑3:

调查代码,显然t代码(实际上是In [86]: s.evalf() Out[86]: 3.14159265358979*cos(omega*t) 个函数之一)尝试独立评估each argument in args 在继续评估表达式之前使用给定选项(即evalf)。因此,在第一种情况下,evalf_*,第一个subs没有做任何事情(好吧,几乎...... - 它评估s.evalf().subs({t: 0})eval())和{{1将pi替换为0,然后将表达式重新评估/简化为3.1415...。在第二种情况下,首先执行替换,然后将subs({t: 0})评估为t。在第三种情况下,评估3.14...中的每个参数。代码在这里失败(因为它无法评估pi给定的3.1415...选项)并返回输入表达式。

也就是说,第三个选项s.args 需要定义omega的所有参数。执行此检查here for the mul function(显然subs master已超出我的已安装版本。)

要看到这一点,请尝试一个简单的表达式:

s.eval(subs={t: 0})

答案 2 :(得分:1)

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算法在遇到符号时应该用数字替换哪些符号。如果您对细节很感兴趣,可以看the source of evalf.py

答案 3 :(得分:1)

您可以认为s.evalf(subs={t: 0})是说“以t = 0的值评估s”的一种方式。逃逸的机器会尝试为您提供所需精度的答案,同时在计算过程中要注意中等精度。 (Aaron已经给出了一个很好的例子,如果简单地将值替换为Python所指示的值并对其求值,则求和将给出错误的答案。)

答案 4 :(得分:0)

我也在寻找解决此问题的方法。对于asmeurer's code sample,以下方法似乎可以解决问题:

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。