Python3下sympy的nonlinsolve求解绝对值等式返回False问题
问题描述
求解含绝对值的等式时,我使用sympy库的nonlinsolve函数实现计算。
例如求解|a + b| = a + b时,我编写代码如下:
from sympy import nonlinsolve, symbols, Abs a,b = symbols('a, b', real = True) eqn = [Abs(a + b) - (a + b)] nonlinsolve(eqn, [a, b])
运行输出结果正常,为:{(Interval(-b, oo), b)}
但当我尝试按如下代码求解|a - b| = -(a + b)时:
from sympy import nonlinsolve, symbols, Abs a,b = symbols('a, b', real = True) eqn = [Abs(a - b) + (a + b)] res = nonlinsolve(eqn, [a, b])
运行输出结果为:False
可显然该等式至少存在a = b = 0这类有效解。
问题补充
该行为存在版本差异:Python 2.7环境配套的旧版SymPy中运行可得到正常求解结果(结果见对应截图:
),但Python 3环境下的新版官方SymPy Shell中运行会返回False。
我希望在Python 3环境下使用该功能,请问出现该版本差异的原因是什么,有哪些可行的应对方案?
问题原因
这个返回False的问题是SymPy 1.9版本后nonlinsolve模块的已知回归bug:
- Python 2环境配套的SymPy普遍为1.8及更早版本,求解含
Abs的方程时会自动按绝对值分段性质拆分约束,能正常识别可行解。 - 1.9版本重构了非线性方程组的约束判定逻辑,处理同时带
Abs项和多变量线性组合的方程时,约束合并环节出错,直接把有解的方程误判为恒不成立,返回代表无解的False,这个bug截至目前的最新稳定版仍未修复。
可行应对方案
- 方案1:换用
solveset函数替代nonlinsolve
单方程求解场景下,solveset对Abs的处理逻辑没有受这次重构影响,求解结果准确,代码示例:
运行后可以正确得到和手动推导一致的结果:from sympy import solveset, symbols, Abs, S a,b = symbols('a b', real=True) # 指定实数域求解关于a的解 res = solveset(Abs(a - b) + (a + b), a, domain=S.Reals)a=0且b<=0、b=0且a<=0两个解分支,包含a=b=0的特解。 - 方案2:手动拆分绝对值分段约束再求解
利用Abs(x)的分段性质,手动把绝对值方程拆成两个带不等式约束的方程组求解,绕开求解器自动处理Abs的bug:
运行后可以正确得到两个解分支的并集,不会出现误判返回from sympy import nonlinsolve, symbols a,b = symbols('a b', real=True) # 拆分|a-b|的两种取值情况 case1 = (a - b) + (a + b) # 当a-b >=0时,Abs(a-b)=a-b case2 = -(a - b) + (a + b) # 当a-b <0时,Abs(a-b)=b-a res1 = nonlinsolve([case1, a - b >=0], [a,b]) res2 = nonlinsolve([case2, a - b <0], [a,b]) full_res = res1.union(res2)False的问题。 - 方案3:降级SymPy版本
如果必须使用nonlinsolve且不想修改现有代码,可以把Python3环境下的SymPy降级到1.8版本,复现旧版本的正常求解行为,安装命令:pip install sympy==1.8
内容的提问来源于stack exchange,提问作者Artem
相关产品推荐
相关产品推荐

