我偶然发现了这一点,因为它没有按照我的预期行事。 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)
找不到关于此的好文档。也许有人知道答案?否则我可能需要查看源代码以了解更多...
答案 0 :(得分:2)
有关选项的信息,另请参阅.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。