由于 scipy SLSQP 中不遵守约束而导致的数学域错误最小化
Math domain error due to disrespected constraint in scipy SLSQP minimize
考虑一个简单的问题:
max log(x)
subject to x >= 1e-4
用scipy.optimize.minimize
解决问题:
import numpy as np
from scipy.optimize import minimize
from math import log
def func(x):
return log(x[0])
def func_deriv(x):
return np.array([1 / x[0]])
cons = ({'type': 'ineq',
'fun' : lambda x: x[0] - 1e-4,
'jac' : lambda x: np.array([1])})
minimize(func, [1.0], jac=func_deriv, constraints=cons, method='SLSQP')
脚本遇到 ValueError
,因为 log(x)
的计算结果为负值 x
。似乎即使不满足约束也计算函数值。
我知道在minimize()
中使用bounds
可以避免这个问题,但这只是我原来问题的简化。在我最初的问题中,约束 x >= 1e-4
不能轻易表示为 x
的边界,而是 g(x) >= C
的形式,因此 bounds
无济于事。
如果我们只关心x > ε
的函数值,可以定义一个扩展域的安全函数。
以log
函数为例。可以用另一个三次函数扩展log
,同时使桥接点ε平滑:
safe_log(x) = log(x) if x > ε else a * (x - b)**3
要计算a
和b
,我们必须满足:
log(ε) = a * (ε - b)**3
1 / ε = 3 * a * (ε - b)**2
因此 safe_log 函数:
eps = 1e-3
def safe_log(x):
if x > eps:
return log(x)
logeps = log(eps)
a = 1 / (3 * eps * (3 * logeps * eps)**2)
b = eps * (1 - 3 * logeps)
return a * (x - b)**3
看起来像这样:
考虑一个简单的问题:
max log(x)
subject to x >= 1e-4
用scipy.optimize.minimize
解决问题:
import numpy as np
from scipy.optimize import minimize
from math import log
def func(x):
return log(x[0])
def func_deriv(x):
return np.array([1 / x[0]])
cons = ({'type': 'ineq',
'fun' : lambda x: x[0] - 1e-4,
'jac' : lambda x: np.array([1])})
minimize(func, [1.0], jac=func_deriv, constraints=cons, method='SLSQP')
脚本遇到 ValueError
,因为 log(x)
的计算结果为负值 x
。似乎即使不满足约束也计算函数值。
我知道在minimize()
中使用bounds
可以避免这个问题,但这只是我原来问题的简化。在我最初的问题中,约束 x >= 1e-4
不能轻易表示为 x
的边界,而是 g(x) >= C
的形式,因此 bounds
无济于事。
如果我们只关心x > ε
的函数值,可以定义一个扩展域的安全函数。
以log
函数为例。可以用另一个三次函数扩展log
,同时使桥接点ε平滑:
safe_log(x) = log(x) if x > ε else a * (x - b)**3
要计算a
和b
,我们必须满足:
log(ε) = a * (ε - b)**3
1 / ε = 3 * a * (ε - b)**2
因此 safe_log 函数:
eps = 1e-3
def safe_log(x):
if x > eps:
return log(x)
logeps = log(eps)
a = 1 / (3 * eps * (3 * logeps * eps)**2)
b = eps * (1 - 3 * logeps)
return a * (x - b)**3
看起来像这样: