使用fmin(单纯形下山法)求解不动点迭代遇溢出错误求助
排查fmin优化不动点迭代时的"OVERFLOW ENCOUNTERS IN DOUBLE SCALAR"错误
你遇到的溢出错误,本质是微分方程迭代过程中某个变量的数值指数级增长,超出了双精度浮点数的范围。前两次循环正常、第三次出错,说明第三次循环的初始探索值组合恰好触发了微分方程的发散行为。结合你的代码,我整理了几个关键排查和修复步骤:
1. 修复代码中的明显bug
(1)函数调用语法错误
你的if判断里误用了calc[y],calc是函数,应该用calc(simplex)来计算当前解的损失值:
# 错误写法 if simplex[0]>0 and simplex[1]>0 and simplex[2]>0 and calc[y]<0.01: # 正确写法 if simplex[0]>0 and simplex[1]>0 and simplex[2]>0 and calc(simplex)<0.01:
这个错误会导致你错误判断损失值,可能把不满足条件的解加入列表,甚至引发额外数值问题。
(2)循环重置逻辑验证
确保三重循环的重置逻辑正确:每次t2循环开始时,必须重置tr=-5*accuracy;每次t1循环开始时,必须重置t2=-5*accuracy。如果重置语句缩进错误,会导致后续循环的初始值偏离预期,进而触发溢出。
2. 添加数值溢出监控与提前终止
在微分方程求解和优化过程中加入监控,定位具体是哪个变量或步骤导致的溢出:
(1)修改DGLS函数,添加数值检查
def DGLS(t, Y0, Y1, Y2, Y3, Y4): # 检查输入变量是否接近溢出 y_vals = [Y0, Y1, Y2, Y3, Y4] for idx, val in enumerate(y_vals): if abs(val) > 1e15 or not np.isfinite(val): print(f"⚠️ Y{idx}异常: {val},时间步t={t}") # 计算各项导数 term1 = (-Y0 + alpha - Y0*Y4*(Y1/(1+mu2*Y2)+c) - phi*Y0*Y4*(Y2+c) - chi*Y0*Y4*(Y3+c)) term2 = (-Y1 + nu*Y0*Y4/(1+mur*Y3)*(Y1/(1+mu2*Y2)+c)) term3 = (-Y2 + phi*nu*Y0*Y4/(1+mur*Y3)*(Y2+c)/(1+mu1*Y1/(1+mu2*Y2))) term4 = (-Y3 + chi*nu*Y0*Y4*(Y3+c)) term5 = (-Y4*(Y1+Y2+Y3)) # 检查导数计算是否溢出 terms = [term1, term2, term3, term4, term5] for idx, val in enumerate(terms): if not np.isfinite(val): print(f"❌ term{idx+1}溢出,Y值: {y_vals},时间步t={t}") return terms
(2)修改RK4_end函数,提前终止发散迭代
当变量超出安全范围时,直接返回极大值,让优化器避开这个区域:
def RK4_end(DGLS, Startwert, Steps, t0, dt): Y = np.array(Startwert, dtype=np.float64) for step in range(t0, Steps): # 检查当前Y是否溢出 if any(abs(Y) > 1e15) or any(not np.isfinite(Y)): print(f"🚨 迭代提前终止,步数{step},Y值: {Y}") return np.full(5, np.inf) # 返回极大值,让calc函数返回高损失 # 计算RK4的四个k值 k1 = np.array(DGLS(step, *Y), dtype=np.float64) if any(not np.isfinite(k1)): print(f"🚨 k1溢出,步数{step},Y值: {Y}") return np.full(5, np.inf) k2 = np.array(DGLS(step + 0.5*dt, *(Y + 0.5*dt*k1)), dtype=np.float64) if any(not np.isfinite(k2)): print(f"🚨 k2溢出,步数{step},Y值: {Y}") return np.full(5, np.inf) k3 = np.array(DGLS(step + 0.5*dt, *(Y + 0.5*dt*k2)), dtype=np.float64) if any(not np.isfinite(k3)): print(f"🚨 k3溢出,步数{step},Y值: {Y}") return np.full(5, np.inf) k4 = np.array(DGLS(step + dt, *(Y + dt*k3)), dtype=np.float64) if any(not np.isfinite(k4)): print(f"🚨 k4溢出,步数{step},Y值: {Y}") return np.full(5, np.inf) # 更新Y Y += (dt/6) * (k1 + 2*k2 + 2*k3 + k4) return Y
3. 限制初始值范围,避免极端探索
你的初始值用10**(t/accuracy)生成,当t接近0时值会快速增长,若循环逻辑出错导致t变为正数,10^t会爆炸式增长。给初始值加上范围限制:
def calc(y): # 将初始值限制在合理区间,避免极端值 y_clamped = np.clip(y, 1e-6, 1e3) Startwert = np.array([10., y_clamped[0], y_clamped[1], y_clamped[2], 1.]) solve = RK4_end(DGLS, Startwert, Steps, t0, dt) # 如果求解溢出,返回极大损失 if any(not np.isfinite(solve)): return 1e30 return (solve[1]-y_clamped[0])**2 + (solve[2]-y_clamped[1])**2 + (solve[3]-y_clamped[2])**2
同时,生成初始y时也加上限制:
y[0] = np.clip(10**(t1/accuracy), 1e-6, 1e3) y[1] = np.clip(10**(t2/accuracy), 1e-6, 1e3) y[2] = np.clip(10**(tr/accuracy), 1e-6, 1e3)
4. 调整fmin的优化参数
fmin默认行为可能探索极端值,开启调试输出或限制迭代次数:
# 开启调试输出,查看fmin的迭代过程 simplex = fmin(calc, y, ftol=1E-15, disp=1, maxfun=1000)
disp=1会打印每次迭代的损失值和参数,帮助你观察第三次循环时fmin是否在探索异常值。
5. 检查微分方程的稳定性
观察你的微分方程,Y4的导数是-Y4*(Y1+Y2+Y3):
- 如果
Y1+Y2+Y3 < 0,Y4会指数增长,而Y4又出现在其他所有项的乘积里,导致Y0-Y3也指数增长,最终触发溢出。 - 你需要确保初始值和迭代过程中
Y1+Y2+Y3始终为正,或者在DGLS函数中加入保护逻辑,比如当Y1+Y2+Y3 <=0时,强制设置Y4的导数为负数,避免其增长。
按照以上步骤排查,你应该能定位到具体是哪个环节导致的溢出,进而修复问题。
内容的提问来源于stack exchange,提问作者RyZ
相关产品推荐
相关产品推荐

