scipy 强制系数之间距离的最佳方法

sf6xfgos  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(105)

bounty将在6天内到期。此问题的答案有资格获得+50声望奖励。Simd希望引起更多关注这个问题。

我使用basinhopping来执行全局优化。我的简单代码是:

from scipy.optimize import basinhopping, rosen

def build_show_bh(MIN=None):
    if MIN is None:
        MIN = [0]
    def fn(xx, f, accept):
        if f < MIN[-1]:
            print([round(x, 2) for x in xx], f)
            MIN.append(f)
    return fn

x0 = (0.4, 0.6, 0.8)
bounds = [(0,1)]*3
minimizer_kwargs = dict(method="L-BFGS-B", bounds=bounds)
progress_f = [0]
c = build_show_bh(progress_f)
print("Optimizing using basinhopping")
res = basinhopping(
    rosen, 
    x0,
    minimizer_kwargs=minimizer_kwargs,
    niter=10,
    callback=c, 
    disp=True
)
print(f"external way of keeping track of MINF: {progress_f}")

我想添加一个约束,即每个系数必须至少与其他两个系数相差0.1。我很高兴他们能按顺序排列,如果这有帮助的话。最好的办法是什么?

oogrdqng

oogrdqng1#

我将通过对连续值之间的差异创建约束来解决这个问题。

def constraint(x, use_epsilon=True):
    difference_between_coeffs = np.diff(x)
    epsilon = 1e-6  # Use this to make constraint slightly more conservative
    min_difference = 0.1 + (epsilon if use_epsilon else 0)
    difference_metric = difference_between_coeffs - min_difference
    difference_metric_capped = np.minimum(0, difference_metric)
    return np.sum(difference_metric_capped)

(Note:此约束还要求系数按排序顺序。)
在编写这个程序时,我遇到了这样一个问题:有时SLSQP会给予我的点稍微违反约束,我通过要求点稍微分开一点来解决这个问题。我发现一个epsilon = 1e-6就足以解决这个问题。
然后,你必须告诉最小化器使用这个约束:

constraints = [
    {"type": "eq", "fun": constraint}
]
minimizer_kwargs = dict(method="SLSQP", bounds=bounds, constraints=constraints)

注意:我将方法从L-BFGS-B切换到SLSQP,因为BFGS不支持约束。您也可以使用trust-constr-这是唯一一个同时支持约束和边界的方法。
接下来,我遇到的问题是,有时basinhopping选择了违反约束的点,导致最小化失败,然后认为basinhopping选择的点是正确的。我通过使用basinhopping的验收测试解决了这个问题。这样可以避免在点未通过约束时接受点。

def accept(x_new, **kwargs):
    return constraint(x_new) == 0

res = basinhopping(
    ...
    accept_test=accept,
)

完整代码:

from scipy.optimize import basinhopping, rosen
import numpy as np

def build_show_bh(MIN=None):
    if MIN is None:
        MIN = [0]
    def fn(xx, f, accept):
        if f < MIN[-1]:
            print([round(x, 2) for x in xx], f)
            MIN.append(f)
    return fn

def constraint(x, use_epsilon=True):
    difference_between_coeffs = np.diff(x)
    epsilon = 1e-6  # Use this to make constraint slightly more conservative
    min_difference = 0.1 + (epsilon if use_epsilon else 0)
    difference_metric = difference_between_coeffs - min_difference
    difference_metric_capped = np.minimum(0, difference_metric)
    return np.sum(difference_metric_capped)

def accept(x_new, **kwargs):
    return constraint(x_new) == 0

constraints = [
    {"type": "eq", "fun": constraint}
]
x0 = (0.4, 0.6, 0.8)
bounds = [(0,1)]*3
minimizer_kwargs = dict(method="SLSQP", bounds=bounds, constraints=constraints)
progress_f = [100]
c = build_show_bh(progress_f)
print("Optimizing using basinhopping")
res = basinhopping(
    rosen, 
    x0,
    minimizer_kwargs=minimizer_kwargs,
    accept_test=accept,
    niter=10,
    callback=c, 
    disp=True
)
print(f"external way of keeping track of MINF: {progress_f}")

相关问题