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

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

Best way to enforce a distance between coefficients

问题

我正在使用 basinhopping 进行全局优化。我的简单代码如下:

  1. from scipy.optimize import basinhopping, rosen
  2. def build_show_bh(MIN=None):
  3. if MIN is None:
  4. MIN = [0]
  5. def fn(xx, f, accept):
  6. if f < MIN[-1]:
  7. print([round(x, 2) for x in xx], f)
  8. MIN.append(f)
  9. return fn
  10. x0 = (0.4, 0.6, 0.8)
  11. bounds = [(0,1)]*3
  12. minimizer_kwargs = dict(method="L-BFGS-B", bounds=bounds)
  13. progress_f = [0]
  14. c = build_show_bh(progress_f)
  15. print("Optimizing using basinhopping")
  16. res = basinhopping(
  17. rosen,
  18. x0,
  19. minimizer_kwargs=minimizer_kwargs,
  20. niter=10,
  21. callback=c,
  22. disp=True
  23. )
  24. 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:

  1. from scipy.optimize import basinhopping, rosen
  2. def build_show_bh(MIN=None):
  3. if MIN is None:
  4. MIN = [0]
  5. def fn(xx, f, accept):
  6. if f &lt; MIN[-1]:
  7. print([round(x, 2) for x in xx], f)
  8. MIN.append(f)
  9. return fn
  10. x0 = (0.4, 0.6, 0.8)
  11. bounds = [(0,1)]*3
  12. minimizer_kwargs = dict(method=&quot;L-BFGS-B&quot;, bounds=bounds)
  13. progress_f = [0]
  14. c = build_show_bh(progress_f)
  15. print(&quot;Optimizing using basinhopping&quot;)
  16. res = basinhopping(
  17. rosen,
  18. x0,
  19. minimizer_kwargs=minimizer_kwargs,
  20. niter=10,
  21. callback=c,
  22. disp=True
  23. )
  24. 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

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

  1. # 我会通过创建一个关于连续值之间差异的约束来解决这个问题。
  2. def constraint(x, use_epsilon=True):
  3. difference_between_coeffs = np.diff(x)
  4. epsilon = 1e-6 # 使用此值使约束稍微保守一些
  5. min_difference = 0.1 + (epsilon if use_epsilon else 0)
  6. difference_metric = difference_between_coeffs - min_difference
  7. difference_metric_capped = np.minimum(0, difference_metric)
  8. return np.sum(difference_metric_capped)
  9. # 注意:此约束还要求系数按排序顺序排列。
  10. # 写这个程序时,我遇到的问题是,有时SLSQP会给我一些稍微违反约束的点,我通过要求点之间稍微远一些来解决了这个问题。我发现`epsilon = 1e-6`足以解决这个问题。
  11. # 然后,您必须告诉最小化器使用此约束:
  12. constraints = [
  13. {"type": "eq", "fun": constraint}
  14. ]
  15. minimizer_kwargs = dict(method="SLSQP", bounds=bounds, constraints=constraints)
  16. # 注意:我将方法从L-BFGS-B切换到SLSQP,因为BFGS不支持约束。您还可以使用trust-constr - 这是唯一支持约束和边界的其他方法。
  17. # 接下来,我遇到的问题是,有时候basinhopping选择了一个违反约束的点,这导致了最小化的失败,然后认为basinhopping选择的点是正确的。我通过为basinhopping使用接受测试来解决了这个问题。这样,如果点违反了约束,就不会接受这些点。
  18. def accept(x_new, **kwargs):
  19. return constraint(x_new) == 0
  20. res = basinhopping(
  21. # ...
  22. accept_test=accept,
  23. )
  24. # 完整代码:
  25. from scipy.optimize import basinhopping, rosen
  26. import numpy as np
  27. def build_show_bh(MIN=None):
  28. if MIN is None:
  29. MIN = [0]
  30. def fn(xx, f, accept):
  31. if f < MIN[-1]:
  32. print([round(x, 2) for x in xx], f)
  33. MIN.append(f)
  34. return fn
  35. def constraint(x, use_epsilon=True):
  36. difference_between_coeffs = np.diff(x)
  37. epsilon = 1e-6 # 使用此值使约束稍微保守一些
  38. min_difference = 0.1 + (epsilon if use_epsilon else 0)
  39. difference_metric = difference_between_coeffs - min_difference
  40. difference_metric_capped = np.minimum(0, difference_metric)
  41. return np.sum(difference_metric_capped)
  42. def accept(x_new, **kwargs):
  43. return constraint(x_new) == 0
  44. constraints = [
  45. {"type": "eq", "fun": constraint}
  46. ]
  47. x0 = (0.4, 0.6, 0.8)
  48. bounds = [(0, 1)] * 3
  49. minimizer_kwargs = dict(method="SLSQP", bounds=bounds, constraints=constraints)
  50. progress_f = [100]
  51. c = build_show_bh(progress_f)
  52. print("Optimizing using basinhopping")
  53. res = basinhopping(
  54. rosen,
  55. x0,
  56. minimizer_kwargs=minimizer_kwargs,
  57. accept_test=accept,
  58. niter=10,
  59. callback=c,
  60. disp=True
  61. )
  62. 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.

  1. def constraint(x, use_epsilon=True):
  2. difference_between_coeffs = np.diff(x)
  3. epsilon = 1e-6 # Use this to make constraint slightly more conservative
  4. min_difference = 0.1 + (epsilon if use_epsilon else 0)
  5. difference_metric = difference_between_coeffs - min_difference
  6. difference_metric_capped = np.minimum(0, difference_metric)
  7. 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:

  1. constraints = [
  2. {&quot;type&quot;: &quot;eq&quot;, &quot;fun&quot;: constraint}
  3. ]
  4. 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.

  1. def accept(x_new, **kwargs):
  2. return constraint(x_new) == 0
  3. res = basinhopping(
  4. ...
  5. accept_test=accept,
  6. )

Full code:

  1. from scipy.optimize import basinhopping, rosen
  2. import numpy as np
  3. def build_show_bh(MIN=None):
  4. if MIN is None:
  5. MIN = [0]
  6. def fn(xx, f, accept):
  7. if f &lt; MIN[-1]:
  8. print([round(x, 2) for x in xx], f)
  9. MIN.append(f)
  10. return fn
  11. def constraint(x, use_epsilon=True):
  12. difference_between_coeffs = np.diff(x)
  13. epsilon = 1e-6 # Use this to make constraint slightly more conservative
  14. min_difference = 0.1 + (epsilon if use_epsilon else 0)
  15. difference_metric = difference_between_coeffs - min_difference
  16. difference_metric_capped = np.minimum(0, difference_metric)
  17. return np.sum(difference_metric_capped)
  18. def accept(x_new, **kwargs):
  19. return constraint(x_new) == 0
  20. constraints = [
  21. {&quot;type&quot;: &quot;eq&quot;, &quot;fun&quot;: constraint}
  22. ]
  23. x0 = (0.4, 0.6, 0.8)
  24. bounds = [(0,1)]*3
  25. minimizer_kwargs = dict(method=&quot;SLSQP&quot;, bounds=bounds, constraints=constraints)
  26. progress_f = [100]
  27. c = build_show_bh(progress_f)
  28. print(&quot;Optimizing using basinhopping&quot;)
  29. res = basinhopping(
  30. rosen,
  31. x0,
  32. minimizer_kwargs=minimizer_kwargs,
  33. accept_test=accept,
  34. niter=10,
  35. callback=c,
  36. disp=True
  37. )
  38. 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:

确定