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

huangapple go评论122阅读模式
英文:

Best way to enforce a distance between coefficients

问题

我正在使用 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。如果这有助于的话,我愿意让它们按顺序排列。最佳的方法是什么?

英文:

I am using basinhopping to perform a global optimization. My simple code is:

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 &lt; 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=&quot;L-BFGS-B&quot;, bounds=bounds)
progress_f = [0]
c = build_show_bh(progress_f)
print(&quot;Optimizing using basinhopping&quot;)
res = basinhopping(
    rosen, 
    x0,
    minimizer_kwargs=minimizer_kwargs,
    niter=10,
    callback=c, 
    disp=True
)
print(f&quot;external way of keeping track of MINF: {progress_f}&quot;)

I would like to add a constraint that each of the coefficients must be at last 0.1 from both the other coefficients. I am happy for them to be in sorted order if that helps. What is the best way of doing that?

答案1

得分: 2

以下是翻译好的代码部分:

# 我会通过创建一个关于连续值之间差异的约束来解决这个问题。

def constraint(x, use_epsilon=True):
    difference_between_coeffs = np.diff(x)
    epsilon = 1e-6  # 使用此值使约束稍微保守一些
    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)

# 注意:此约束还要求系数按排序顺序排列。

# 写这个程序时,我遇到的问题是,有时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  # 使用此值使约束稍微保守一些
    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}")

希望这有助于您理解代码。

英文:

I would solve this problem by creating a constraint on the difference between successive values.

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: this constraint also requires the coefficients to be in sorted order.)

While writing this program, I had the problem that sometimes SLSQP would give me points which slightly violated the constraint, which I solved by requiring points to be slightly further apart. I found that an epsilon = 1e-6 was sufficient to fix this.

You then have to tell the minimizer to use this constraint:

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

Note: I switched the method from L-BFGS-B to SLSQP, as BFGS does not support constraints. You can use trust-constr as well - this is the only other method which supports both constraints and bounds.

Next, I had the problem that sometimes basinhopping picked a point which violated the constraint, which caused minimization to fail, which then considers the point picked by basinhopping to be correct. I fixed this by using an acceptance test for basinhopping. This avoid accepting points if they fail the constraint.

def accept(x_new, **kwargs):
return constraint(x_new) == 0
res = basinhopping(
...
accept_test=accept,
)

Full code:

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 &lt; 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 = [
{&quot;type&quot;: &quot;eq&quot;, &quot;fun&quot;: constraint}
]
x0 = (0.4, 0.6, 0.8)
bounds = [(0,1)]*3
minimizer_kwargs = dict(method=&quot;SLSQP&quot;, bounds=bounds, constraints=constraints)
progress_f = [100]
c = build_show_bh(progress_f)
print(&quot;Optimizing using basinhopping&quot;)
res = basinhopping(
rosen, 
x0,
minimizer_kwargs=minimizer_kwargs,
accept_test=accept,
niter=10,
callback=c, 
disp=True
)
print(f&quot;external way of keeping track of MINF: {progress_f}&quot;)

huangapple
  • 本文由 发表于 2023年6月19日 22:21:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76507527.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定