我可以设置简单的边界以与双退火一起使用:例如
upper_bound = 20
num_points = 30
bounds = [(0, upper_bound) for i in range(num_points)]
res = dual_annealing(fun, bounds, maxiter=1000)
但我还想约束变量,以便每个 i 的 x_i >= x_{i-1}+0.5
。也就是说,每个变量应至少比前一个变量大 0.5。
你怎么能做到这一点?
如果 scipy 不能做到这一点,是否有其他具有全局优化器的库可以做到这一点?
最佳答案
不知道我的理解是否正确,如有错误,请指正。
问题
根据您的边界规范,我了解到 fun
是 30 个变量的函数 {x_1, x_2, ...,x_i-1, x_i, x_i+1, ..., x_30}
。您想要的是强制这些变量遵循关系 x_i >= x_i-1+0.5
。正如另一个答案所述,这确实可以通过拉格朗日乘子来实现,但有时方程太复杂。
这是一个非常基本的强力替代方案 - 约束数值优化:
from scipy.optimize import dual_annealing
import numpy as np
# an example objective function
def fun(x: np.ndarray) -> float:
return np.sum(x)
# let's create a simple wrapper using a callable class that will
# 'watch' that constrains are fulfilled.
# hit and miss attributes are only for convenience
# to see some stats at the end
class Wrapper:
def __init__(self, fun):
self.fun = fun
self.hit = 0
self.miss = 0
def __call__(self, x: np.ndarray) -> np.ndarray:
# check if all differences are higher than 0.5
# if yes than return the value of objective function
if np.all(np.diff(x) > 0.5):
self.hit += 1
return self.fun(x)
# if the constraints are not met than return inf. This effectively
# discourages the optimizer from visiting the points of function where
# the constrains are not fulfilled
else:
self.miss += 1
return np.inf
upper_bound = 20
num_points = 30
bounds = [(0, upper_bound) for i in range(num_points)]
wrapped = Wrapper(fun)
# We specify x0 starting point as the optimizer might fail to start otherwise.
# This is because your constraints are quite limiting and it might be hard to
# generate initial state. The optimizer makes 1000 attempts to do so and than
# fails. This avoids that
res = dual_annealing(
wrapped,
bounds,
maxiter=1000,
x0=np.linspace(0, upper_bound, 30)
)
print(res.x)
print(wrapped.hit, wrapped.miss)
注释
这里有一些缺点需要注意:
- 我不知道计算强度如何
fun
,但在如此严格的约束下,这可能会导致资源密集,甚至在 30D 空间中速度非常快。如果运行此命令,您将看到命中/未命中比率约为 5/8 - 由于计算强度,您可能会遇到收敛问题
- 您还应该考虑限制的严格程度。也许他们可以放松一点,np.inf 可以用多项式函数的指数代替。这会更好,因为它不会引入现在存在的任何不连续性,尽管模拟退火应该可以使用它们。
- 由于使用“np.inf”和它引入的不连续性,可能需要使用
dual_annealing(..., no_local_search=True)
关闭本地搜索,因为它使用 BFGS,您可以在那里遇到梯度爆炸。 - 如果您的函数足够简单,您应该尝试使用拉格朗日乘子在纸上完成此操作,这始终是更好的方法。
关于python - 如何使用 Dual_annealing 设置简单的线性约束?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75698797/