如何在两个向量之间的差异上使用scipy``最小''?

发布于 2025-02-10 01:56:24 字数 2408 浏览 2 评论 0原文

我有两个向量w1w2(长度100中的每个),我想最大程度地减少其绝对差异的总和,即

import numpy as np


def diff(w: np.ndarray) -> float:
    """Get the sum of absolute differences in the vector w.

    Args:
        w: A flattened vector of length 200, with the first 100 elements
            pertaining to w1, and the last 100 elements pertaining to w2.

    Returns:
        sum of absolute differences.
    """
    return np.sum(np.absolute(w[:100] - w[-100:]))

我需要编写diff()仅作为一个参数,因为scipy.opyimize.minimize要求传递给x0参数的数组为1维。

至于约束,我有

  1. w1是固定的, 允许更改
  2. w2更改
  3. 绝对值的总和w2 w2 < /code>在0.1和1.1之间:0.1&lt; = sum(abs(w2))&lt; = 1.1
  4. | w2_i | &lt; 0.01对于任何元素i in w2> i

我对我们如何使用 bunds lineArconstraints 对象。到目前为止,我尝试的是

from scipy.optimize import minimize, Bounds, LinearConstraint

bounds = Bounds(lb=[-0.01] * 200, ub=[0.01] * 200)  # constraint #4
lc = LinearConstraint([[1] * 200], [0.1], [1.1])  # constraint #3
res = minimize(
    fun=diff,
    method='trust-constr',
    x0=w,  # my flattened vector containing w1 first 100 elements, and w2 in last 100 elements
    bounds=bounds,
    constraints=(lc)
)

bends变量的以下逻辑来自约束#4,而lc变量变量来自约束#3。但是我知道我已经对此进行了编码,因为因为上下界限为200,这似乎表明它们都应用于w1w2 't将约束应用于w2(我得到错误value> value eRror:操作数不能与形状(200,)(100)(100,)一起播放,如果我尝试更改界限的数组长度从200到100)。

lineArconstraint的形状和参数类型对我特别令人困惑,但是我确实尝试遵循 scipy示例

当前的实施似乎从未完成,它永远悬而未决。

我如何正确实现bundslinearconstraint,以便满足我上面的约束列表,即使这甚至可能?

I have two vectors w1 and w2 (each of length 100), and I want to minimize the sum of their absolute difference i.e.

import numpy as np


def diff(w: np.ndarray) -> float:
    """Get the sum of absolute differences in the vector w.

    Args:
        w: A flattened vector of length 200, with the first 100 elements
            pertaining to w1, and the last 100 elements pertaining to w2.

    Returns:
        sum of absolute differences.
    """
    return np.sum(np.absolute(w[:100] - w[-100:]))

I need to write diff() as only taking one argument since scipy.opyimize.minimize requires the array passed to the x0 argument to be 1 dimensional.

As for constraints, I have

  1. w1 is fixed and not allowed to change
  2. w2 is allowed to change
  3. The sum of absolute values w2 is between 0.1 and 1.1: 0.1 <= sum(abs(w2)) <= 1.1
  4. |w2_i| < 0.01 for any element i in w2

I am confused as to how we code these constraints using the Bounds and LinearConstraints objects. What I've tried so far is the following

from scipy.optimize import minimize, Bounds, LinearConstraint

bounds = Bounds(lb=[-0.01] * 200, ub=[0.01] * 200)  # constraint #4
lc = LinearConstraint([[1] * 200], [0.1], [1.1])  # constraint #3
res = minimize(
    fun=diff,
    method='trust-constr',
    x0=w,  # my flattened vector containing w1 first 100 elements, and w2 in last 100 elements
    bounds=bounds,
    constraints=(lc)
)

My logic for the bounds variable is from constrain #4, and for the lc variable comes from constrain #3. However I know I've coded this wrong because because the lower and upper bounds are of length 200 which seems to indicate they are applied to both w1 and w2 whereas I only wan't to apply the constrains to w2 (I get an error ValueError: operands could not be broadcast together with shapes (200,) (100,) if I try to change the length of the array in Bounds from 200 to 100).

The shapes and argument types for LinearConstraint are especially confusing to me, but I did try to follow the scipy example.

This current implementation never seems to finish, it just hangs forever.

How do I properly implement bounds and LinearConstraint so that it satisfies my constraints list above, if that is even possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

方觉久 2025-02-17 01:56:24

您的问题很容易被提出为线性​​优化问题(LP)。您只需要重新调整优化变量的所有绝对值即可。

稍微更改符号(X现在是优化变量W2和W只是您给定的向量W1),您的问题读起来为

min |w_1 - x_1| + .... + |w_N - x_N|

s.t. lb <= |x1| + ... + |xN| <= ub         (3)
                       |x_i| <= 0.01 - eps (4) (models the strict inequality)                  

EPS只是一个足够小的数量,以建模严格的不平等。

让我们考虑约束(3)。在这里,我们添加其他正变量z并定义z_i = | x_i |。然后,我们替换所有绝对值| x_i |由z_i强加了约束-x_i&lt; = z_i&lt; = x_i,将关系建模为z_i = | x_i |。同样,您可以进行目标和约束(4)。后者是琐碎的,相当于 - (0.01 -eps)&lt; = x_i&lt; = 0.01 -eps。

最后,您的优化问题应读取(假设所有W_I都是肯定的):

min u1 + .... + uN

s.t.      lb <= z1 + ... + zN <= ub
          -x <=      z        <= x
 -0.01 + eps <=      x        <= 0.01 - eps
    -(w-x)   <=      u        <= w - x
          0  <= z
          0  <= u

使用3*n优化变量x1,...,xn,u1,...,un,Z1,...,Zn。将这些约束写入矩阵矢量乘积A_ineq * x&lt; = b_ineq并不难。然后,您可以通过 scipy。 Optimize.linprog 如下:

import numpy as np
from scipy.optimize import minimize, linprog

n = 100
w = np.abs(np.random.randn(n))
eps = 1e-10
lb = 0.1
ub = 1.1

# linear constraints: A_ub * (x, z, u)^T <= b_ub
A_ineq = np.block([
    [np.zeros(n), np.ones(n), np.zeros(n)],
    [np.zeros(n), -np.ones(n), np.zeros(n)],
    [-np.eye(n),  np.eye(n), np.zeros((n, n))],
    [-np.eye(n), -np.eye(n), np.zeros((n, n))],
    [ np.eye(n), np.zeros((n, n)), -np.eye(n)],
    [ np.eye(n), np.zeros((n, n)),  np.eye(n)],
])

b_ineq = np.hstack((ub, -lb, np.zeros(n), np.zeros(n), w, w))

# bounds: lower <= (x, z, u)^T <= upper 
lower = np.hstack(((-0.01 + eps) * np.ones(n), np.zeros(n), np.zeros(n)))
upper = np.hstack((( 0.01 - eps) * np.ones(n), np.inf*np.ones(n), np.inf*np.ones(n)))
bounds = [(l, u) for (l, u) in zip(lower, upper)]

# objective: c^T * (x, z, u)
c = np.hstack((np.zeros(n), np.zeros(n), np.ones(n)))

# solve the problem
res = linprog(c, A_ub=A_ineq, b_ub=b_ineq, method="highs")

# your solution
x = res.x[:n]
print(res.message)
print(x)

某些笔记以任意顺序:

  • 强烈建议用linprog而不是最小化 。前者提供了一个高点的接口,高性能Lp solver highs 在<代码的hood of最小化。但是,还值得一提的是,最小化用于非线性优化问题。

  • 如果您的值W并非全部为正,我们需要更改公式。

Your problem can easily be formulated as a linear optimization problem (LP). You only need to reformulate all absolute values of the optimization variables.

Changing the notation slightly (x is now the optimization variable w2 and w is just your given vector w1), your problem reads as

min |w_1 - x_1| + .... + |w_N - x_N|

s.t. lb <= |x1| + ... + |xN| <= ub         (3)
                       |x_i| <= 0.01 - eps (4) (models the strict inequality)                  

where eps is just a sufficiently small number in order to model the strict inequality.

Let's consider the constraint (3). Here, we add additional positive variables z and define z_i = |x_i|. Then, we replace all absolute values |x_i| by z_i and impose the constraints -x_i <= z_i <= x_i which model the relationship z_i = |x_i|. Similarly, you can proceed with the objective and the constraint (4). The latter is by the way trivial and equivalent to -(0.01 - eps) <= x_i <= 0.01 - eps.

In the end, your optimization problem should read (assuming that all your w_i are positive):

min u1 + .... + uN

s.t.      lb <= z1 + ... + zN <= ub
          -x <=      z        <= x
 -0.01 + eps <=      x        <= 0.01 - eps
    -(w-x)   <=      u        <= w - x
          0  <= z
          0  <= u

with 3*N optimization variables x1, ..., xN, u1, ..., uN, z1, ..., zN. It isn't hard to write these constraints as an matrix-vector product A_ineq * x <= b_ineq. Then, you can solve it by scipy.optimize.linprog as follows:

import numpy as np
from scipy.optimize import minimize, linprog

n = 100
w = np.abs(np.random.randn(n))
eps = 1e-10
lb = 0.1
ub = 1.1

# linear constraints: A_ub * (x, z, u)^T <= b_ub
A_ineq = np.block([
    [np.zeros(n), np.ones(n), np.zeros(n)],
    [np.zeros(n), -np.ones(n), np.zeros(n)],
    [-np.eye(n),  np.eye(n), np.zeros((n, n))],
    [-np.eye(n), -np.eye(n), np.zeros((n, n))],
    [ np.eye(n), np.zeros((n, n)), -np.eye(n)],
    [ np.eye(n), np.zeros((n, n)),  np.eye(n)],
])

b_ineq = np.hstack((ub, -lb, np.zeros(n), np.zeros(n), w, w))

# bounds: lower <= (x, z, u)^T <= upper 
lower = np.hstack(((-0.01 + eps) * np.ones(n), np.zeros(n), np.zeros(n)))
upper = np.hstack((( 0.01 - eps) * np.ones(n), np.inf*np.ones(n), np.inf*np.ones(n)))
bounds = [(l, u) for (l, u) in zip(lower, upper)]

# objective: c^T * (x, z, u)
c = np.hstack((np.zeros(n), np.zeros(n), np.ones(n)))

# solve the problem
res = linprog(c, A_ub=A_ineq, b_ub=b_ineq, method="highs")

# your solution
x = res.x[:n]
print(res.message)
print(x)

Some notes in arbitrary order:

  • It's highly recommended to solve linear optimization problems with linprog instead of minimize. The former provides an interface to HiGHS, a high-performance LP solver HiGHs that outperforms all algorithms under the hood of minimize. However, it's also worth mentioning that minimize is meant to be used for nonlinear optimization problems.

  • In case your values w are not all positive, we need to change the formulation.

别低头,皇冠会掉 2025-02-17 01:56:24

您可以(也许为了清楚起见),在中使用args参数最小化,并提供固定的向量作为您函数的额外参数。

如果您按以下方式设置方程式:

def diff(w2, w1):
    return np.sum(np.absolute(w1 - w2))

以及您的约束

bounds = Bounds(lb=[-0.01] * 100, ub=[0.01] * 100)  # constraint #4
lc = LinearConstraint([[1] * 100], [0.1], [1.1])  # constraint #3

,然后做

res = minimize(
    fun=diff,
    method='trust-constr',
    x0=w1,
    args=(w2,),
    bounds=bounds,
    constraints=[lc]
)

print(res.success, res.status, res.nit, np.abs(res.x).sum(), all(np.abs(res.x) < 0.01))

至少对我来说(至少对我来说),

(True, 1, 17, 0.9841520351691752, True)

这似乎是您想要的。

请注意,我的测试输入是:

w1 = (np.arange(100) - 50) / 1000
w2 = np.ones(100, dtype=float)

哪个可能有利于拟合程序。

You can (and perhaps should, for clarity), use the args argument in minimize, and provide the fixed vector as an extra argument to your function.

If you set up your equation as follows:

def diff(w2, w1):
    return np.sum(np.absolute(w1 - w2))

and your constraints with

bounds = Bounds(lb=[-0.01] * 100, ub=[0.01] * 100)  # constraint #4
lc = LinearConstraint([[1] * 100], [0.1], [1.1])  # constraint #3

and then do

res = minimize(
    fun=diff,
    method='trust-constr',
    x0=w1,
    args=(w2,),
    bounds=bounds,
    constraints=[lc]
)

Then:

print(res.success, res.status, res.nit, np.abs(res.x).sum(), all(np.abs(res.x) < 0.01))

yields (for me at least)

(True, 1, 17, 0.9841520351691752, True)

which seems to be what you want.

Note that my test inputs are:

w1 = (np.arange(100) - 50) / 1000
w2 = np.ones(100, dtype=float)

which may or may not be favourable to the fitting procedure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文