为什么 Sympy subs() 不能始终如一地工作?
Why isn't Sympy subs() working consistently?
我想知道为什么 Sympy 的 subs() 在以下特定情况下似乎不起作用。这是 MWE:
import sympy as sy
L_ee, L_es, L_ev, L_ss, L_sv, L_vv, X, Y, Z, J_e, J_s, J_v = sy.symbols('L_ee L_es L_ev L_ss L_sv L_vv X Y Z J_e J_s J_v')
eq1 = sy.Eq(J_e, -L_ee * X - L_es * Y - L_ev * Z)
eq2 = sy.Eq(J_s, -L_es * X - L_ss * Y - L_sv * Z)
eq3 = sy.Eq(J_v, -L_ev * X - L_sv * Y - L_vv * Z)
sol1 = sy.solve(eq3, Z)
# Replace Z into the 1st eq.
eq1 = eq1.subs(Z, sol1[0])
sol2 = sy.solve(eq1, X)
eq2 = eq2.subs(X, sol2[0])
eq2 = eq2.subs([(Z, sol1[0]), (X, sol2[0])])
eq2 = sy.factor(eq2, (Y, J_v, J_e))
T, bismuth = sy.symbols('T bismuth')
flux_eq = sy.Eq(T*eq2.lhs, T*eq2.rhs)
eq2_flux = flux_eq.subs([(J_v, 0), (J_e, 0)])
bismuth_exp = sy.symbols('bismuth_exp')
bismuth_exp = -eq2_flux.rhs / Y
print(eq2_flux.rhs)
# Works as expected here
print(eq2_flux.rhs.subs(bismuth_exp, bismuth), type(eq2_flux.rhs.subs(bismuth_exp, bismuth)))
print(bismuth_exp)
# But then, why doesn't it work here?
print(eq2.rhs.subs(bismuth_exp, bismuth), type(eq2.rhs.subs(bismuth_exp, bismuth)))
我的目标是用 "bismuth" 来写 eq2.rhs。在上面的代码中,我首先让"bismuth"出现在一个与eq2.rhs非常相似的表达式中,即eq2_flux.rhs。所以我先打印那个表达式,然后我用铋打印相同的表达式。我得到正确的“-Y*bismuth”,即 Sympy 可以毫无问题地使 "bismuth" 出现在 eq2_flux.rhs
中
当我用 eq2.rhs 尝试完全相同的事情时,"bismuth" 永远不会替换它应该替换的大块。通过眼睛,我应该得到类似 -J_e(something) - J_v(something else) - bismuth * Y / T.
请注意,我打印了每个 eq.rhs 的类型并且它们匹配。所以我真的对发生的事情一无所知,想知道 Sympy 是否无法在一个非常类似于它能够做的表达式中用相同的块替换 "bismuth"。
为了回应可能要替换的表达式应该按原样显示的评论,这里有一个与我的情况等效但有效的示例:
x, y, z, v = sy.symbols('x y z v')
test = x + y + (v/2.0)*z
print(test.subs(z, 3.0/5 *v))
在这种情况下,z 被正确替换。
我已将“+Y”添加到 eq2_flux 并且替换仍然有效,但在这种情况下 eq2_flux 不是铋的常数倍。我正确地得到了“-Y*bismuth + Y”。至少可以说非常令人费解。
你不应该假设 subs 能够匹配不是你的表达式的字面部分的东西,例如:
In [54]: (x**3).subs(x**2, 1)
Out[54]:
3
x
在某些情况下它会起作用,例如
In [55]: (x**4).subs(x**2, 1)
Out[55]: 1
但这种替代品通常总是不稳定的,所以我不会依赖它。
考虑到这一点,您需要确保 subs 的第一个参数是表达式树中的文字节点。然后保证 subs 会找到它的所有实例并替换它们。在你的情况下,我们可以用
In [114]: F = T/(L_ee*L_vv-L_ev**2)
In [115]: eq2.rhs.subs(bismuth_exp/F, bismuth/F)
Out[115]:
⎛ ⎛ 2⎞⎞
⎜ Y⋅bismuth⋅⎝Lₑₑ⋅Lᵥᵥ - Lₑᵥ ⎠⎟
-⎜Jₑ⋅(-Lₑₛ⋅Lᵥᵥ + Lₑᵥ⋅Lₛᵥ) + Jᵥ⋅(-Lₑₑ⋅Lₛᵥ + Lₑₛ⋅Lₑᵥ) + ──────────────────────────⎟
⎝ T ⎠
──────────────────────────────────────────────────────────────────────────────────
2
Lₑₑ⋅Lᵥᵥ - Lₑᵥ
In [116]: signsimp(_.apart(bismuth)).collect([J_e, J_v])
Out[116]:
Jₑ⋅(Lₑₛ⋅Lᵥᵥ - Lₑᵥ⋅Lₛᵥ) + Jᵥ⋅(Lₑₑ⋅Lₛᵥ - Lₑₛ⋅Lₑᵥ) Y⋅bismuth
─────────────────────────────────────────────── - ─────────
2 T
Lₑₑ⋅Lᵥᵥ - Lₑᵥ
这看起来像您期望的形式。
我想知道为什么 Sympy 的 subs() 在以下特定情况下似乎不起作用。这是 MWE:
import sympy as sy
L_ee, L_es, L_ev, L_ss, L_sv, L_vv, X, Y, Z, J_e, J_s, J_v = sy.symbols('L_ee L_es L_ev L_ss L_sv L_vv X Y Z J_e J_s J_v')
eq1 = sy.Eq(J_e, -L_ee * X - L_es * Y - L_ev * Z)
eq2 = sy.Eq(J_s, -L_es * X - L_ss * Y - L_sv * Z)
eq3 = sy.Eq(J_v, -L_ev * X - L_sv * Y - L_vv * Z)
sol1 = sy.solve(eq3, Z)
# Replace Z into the 1st eq.
eq1 = eq1.subs(Z, sol1[0])
sol2 = sy.solve(eq1, X)
eq2 = eq2.subs(X, sol2[0])
eq2 = eq2.subs([(Z, sol1[0]), (X, sol2[0])])
eq2 = sy.factor(eq2, (Y, J_v, J_e))
T, bismuth = sy.symbols('T bismuth')
flux_eq = sy.Eq(T*eq2.lhs, T*eq2.rhs)
eq2_flux = flux_eq.subs([(J_v, 0), (J_e, 0)])
bismuth_exp = sy.symbols('bismuth_exp')
bismuth_exp = -eq2_flux.rhs / Y
print(eq2_flux.rhs)
# Works as expected here
print(eq2_flux.rhs.subs(bismuth_exp, bismuth), type(eq2_flux.rhs.subs(bismuth_exp, bismuth)))
print(bismuth_exp)
# But then, why doesn't it work here?
print(eq2.rhs.subs(bismuth_exp, bismuth), type(eq2.rhs.subs(bismuth_exp, bismuth)))
我的目标是用 "bismuth" 来写 eq2.rhs。在上面的代码中,我首先让"bismuth"出现在一个与eq2.rhs非常相似的表达式中,即eq2_flux.rhs。所以我先打印那个表达式,然后我用铋打印相同的表达式。我得到正确的“-Y*bismuth”,即 Sympy 可以毫无问题地使 "bismuth" 出现在 eq2_flux.rhs
中当我用 eq2.rhs 尝试完全相同的事情时,"bismuth" 永远不会替换它应该替换的大块。通过眼睛,我应该得到类似 -J_e(something) - J_v(something else) - bismuth * Y / T.
请注意,我打印了每个 eq.rhs 的类型并且它们匹配。所以我真的对发生的事情一无所知,想知道 Sympy 是否无法在一个非常类似于它能够做的表达式中用相同的块替换 "bismuth"。
为了回应可能要替换的表达式应该按原样显示的评论,这里有一个与我的情况等效但有效的示例:
x, y, z, v = sy.symbols('x y z v')
test = x + y + (v/2.0)*z
print(test.subs(z, 3.0/5 *v))
在这种情况下,z 被正确替换。
我已将“+Y”添加到 eq2_flux 并且替换仍然有效,但在这种情况下 eq2_flux 不是铋的常数倍。我正确地得到了“-Y*bismuth + Y”。至少可以说非常令人费解。
你不应该假设 subs 能够匹配不是你的表达式的字面部分的东西,例如:
In [54]: (x**3).subs(x**2, 1)
Out[54]:
3
x
在某些情况下它会起作用,例如
In [55]: (x**4).subs(x**2, 1)
Out[55]: 1
但这种替代品通常总是不稳定的,所以我不会依赖它。
考虑到这一点,您需要确保 subs 的第一个参数是表达式树中的文字节点。然后保证 subs 会找到它的所有实例并替换它们。在你的情况下,我们可以用
In [114]: F = T/(L_ee*L_vv-L_ev**2)
In [115]: eq2.rhs.subs(bismuth_exp/F, bismuth/F)
Out[115]:
⎛ ⎛ 2⎞⎞
⎜ Y⋅bismuth⋅⎝Lₑₑ⋅Lᵥᵥ - Lₑᵥ ⎠⎟
-⎜Jₑ⋅(-Lₑₛ⋅Lᵥᵥ + Lₑᵥ⋅Lₛᵥ) + Jᵥ⋅(-Lₑₑ⋅Lₛᵥ + Lₑₛ⋅Lₑᵥ) + ──────────────────────────⎟
⎝ T ⎠
──────────────────────────────────────────────────────────────────────────────────
2
Lₑₑ⋅Lᵥᵥ - Lₑᵥ
In [116]: signsimp(_.apart(bismuth)).collect([J_e, J_v])
Out[116]:
Jₑ⋅(Lₑₛ⋅Lᵥᵥ - Lₑᵥ⋅Lₛᵥ) + Jᵥ⋅(Lₑₑ⋅Lₛᵥ - Lₑₛ⋅Lₑᵥ) Y⋅bismuth
─────────────────────────────────────────────── - ─────────
2 T
Lₑₑ⋅Lᵥᵥ - Lₑᵥ
这看起来像您期望的形式。