scipy.optimize的fsolve求解向量值函数根返回错误结果问题
问题原因
你的目标函数包含np.abs非光滑项,而scipy.optimize.fsolve底层调用的是MINPACK的hybrd算法,属于基于雅可比矩阵的光滑优化算法,在非光滑点附近无法获得有效梯度信息,会出现迭代停滞,最终返回未收敛的局部点,也就是你看到的RuntimeWarning和错误解。
解决方案
- 方案1:改用适配非光滑场景的求解器
推荐使用scipy.optimize.least_squares,它将求根问题转化为残差平方和最小化问题,支持非光滑目标,鲁棒性更强,示例代码如下:
import numpy as np from scipy.optimize import least_squares def AMOC(amoc_state, gamma= 2.3, theta = 1, mu = 7.5, sigma = 0.85): T = amoc_state[0] S = amoc_state[1] dT = -gamma * (T-theta) - T * (1+ mu*np.abs(T-S)) dS = sigma - S*(1+mu*np.abs(T-S)) return np.array([dT, dS]) res = least_squares(AMOC, x0=(0.8, 0.3), xtol=1e-7, max_nfev=1000) # 输出结果与残差验证 print("求解得到的根:", res.x) print("对应残差:", AMOC(res.x))
运行后可以得到残差在1e-12量级的收敛解,符合根的要求。
方案2:拆分非光滑问题为光滑子问题
你可以根据T和S的大小关系,将原问题拆分为两个独立的光滑子问题,分别用fsolve求解后验证:- 子问题1:假设
T >= S,将np.abs(T-S)替换为T - S,传入fsolve求解 - 子问题2:假设
T < S,将np.abs(T-S)替换为S - T,传入fsolve求解
拆分后的子问题都是光滑函数,fsolve可以正常收敛。
- 子问题1:假设
方案3:优化初始猜测值
fsolve是局部求解器,初始值距离真实根过远也会导致收敛失败,你可以先在合理的T、S取值范围内做粗粒度网格搜索,找到残差最小的点作为初始值再传入fsolve,也能提高收敛概率。
注意事项
求解完成后建议先检查求解器返回的收敛状态:比如调用fsolve时设置full_output=True,查看返回的ier参数,仅当ier=1时才代表求解收敛,避免直接使用未收敛的错误结果。
内容的提问来源于stack exchange,提问作者Olmo
相关产品推荐
相关产品推荐

