SciPy 开发版 line_search 对合法 c1 报负数平方根错误
SciPy 2.0 开发构建在三次插值失败时可能从 math.sqrt 抛出 ValueError,尚未返回到二次插值回退。c1 满足 Wolfe 参数范围也可能触发,不能当成参数非法。
在 SciPy 2.0 开发构建中,line_search() 的 c1 明明满足参数范围,仍报 ValueError: expected a nonnegative input。如果栈落在 _cubicmin 的 math.sqrt(radical),先检查三次插值的回退路径,不要把它解释成 c1 必须非负之外的额外限制。
这一回归出现在 2.0.0.dev0+git20261011.d1d4a8a。正式 SciPy 1.18.1 的相应实现使用 NumPy 平方根并在失败后返回回退信号;不能把开发构建的错误扩大为正式 1.18.1 同样失败。
参数合法仍可能没有可用的三次插值点
下面的函数、梯度与下降方向构成这一触发场景,示例只说明调用条件:
import numpy as np
from scipy.optimize import line_search
def objective(x):
return x[0] ** 4
def gradient(x):
return np.array([4 * x[0] ** 3])
x = np.array([1.5])
direction = -gradient(x)
result = line_search(objective, gradient, x, direction, c1=0.4, c2=0.9)
Wolfe 参数要求 0 < c1 < c2 < 1。本例满足该关系,但搜索过程中拟合的三次多项式仍可能出现负判别式,无法据此得到实数极小点。搜索算法本来可以放弃这次三次插值,转用二次插值或区间二分。
受影响的开发实现将 np.sqrt 换成 math.sqrt 后,负数输入变成 ValueError。原有捕获只覆盖 ArithmeticError,没有接住这个异常,程序便在回退之前退出。np.errstate 也不会改变 Python math.sqrt 的异常类型。
先确认是否必须使用这份开发构建
应用不依赖开发版特性时,可按项目兼容范围与依赖锁文件,回到已经包含正常回退路径的正式版本,例如这里可对照的 1.18.1。回到正式版本仍须重验完整优化任务,不能只因最小例子不再抛错,就认定目标函数、约束或最终结果均正确。
确实要跟踪开发版时,应在采用更新构建前确认它处理了无实数插值点的情况,再用原输入检查。不要在应用层捕获所有 ValueError 后随意返回一个步长;目标函数、梯度或参数本身也可能抛出同类错误,吞掉它们会隐藏另一个故障。
降低 c1 可能绕过某一次插值,但也改变了充分下降条件,不是等价修复。更不能把负判别式取绝对值后继续开平方,那会制造一个不属于原插值问题的候选点。
返回步长后仍需验收搜索结果
line_search 的第一项可能为 None,表示没有得到满足要求的步长;没有异常不等于搜索成功。保留原来的函数、梯度、初始点、方向和参数,确认返回步长有限、为正,并按同一组 c1、c2 核对下降与曲率条件。若它作为外层优化器的一步,还要检查整个迭代是否满足原收敛要求。
不同目标函数下触发插值失败的位置会不同,本例的参数数值不构成通用临界点。保留开发构建的完整版本与回退前的依赖记录,避免在不同构建之间混用一次成功结果。


