对于大小为4的向量x,我需要解决形式为Ax = b的经典问题.A大约为500个数据点,因此是一个密集的500x4矩阵。
目前我可以使用here描述的常规方程来解决这个问题,但是它可以正常工作但是我想将x中的一个参数约束为永远不会高于某个值。
有没有一种很好的方法可以用Eigen以编程方式进行此操作?
答案 0 :(得分:1)
您可以尝试基于Eigen there的四元编程求解器。你仍然需要形成正规方程式。
答案 1 :(得分:-1)
这里有一些 python-demo (使用与Eigen不太远的numpy)显示加速投影梯度算法来解决您的问题。通常,这种方法用于大规模问题(包含二阶信息的其他算法可能会遇到困难),但实施起来也很不错。
这个版本是对我的一些旧代码的一个小修改,并不是我们使用的最简单的方法:
您可以删除行搜索并调整步长。也不需要动力。
由于我现在没有做太多的C ++,我不认为我会把它移植到Eigen。但我敢肯定,如果你想要移植它,那并不难。 Eigen不应该与numpy太不同。
我没有测量表现,但结果是立即(感知)计算的。
编辑:一些非科学时间(更多动量;比下面的代码更小的容忍度):
A=(500,4)
:
Solve non-bounded with scipys lsq_linear
used: 0.004898870004675975
cost : 244.58267993
Solve bounded (0.001, 0.05) with scipys lsq_linear
used: 0.005605718416479959
cost : 246.990611225
Solve bounded (0.001, 0.05) with accelerated projected gradient
early-stopping @ it: 3
used: 0.002282825315435914
cost: 246.990611225
A=(50000, 500)
:
Solve non-bounded with scipys lsq_linear
used: 4.118898701951786 secs
cost : 24843.7115776
Solve bounded (0.001, 0.05) with scipys lsq_linear
used: 14.727660030288007 secs
cost : 25025.0328661
Solve bounded (0.001, 0.05) with accelerated projected gradient
early-stopping @ it: 14
used: 5.319953458329618 secs
cost: 25025.0330754
基本思想是使用一些类似梯度下降的算法,并在每个梯度步骤之后投影到我们的约束。如果可以有效地完成投影,这种方法非常强大。 Box-constraint-projection很简单!
Page 4 in this pdf显示了框约束投影。
我们只是将解决方案向量剪辑到lower_bound, upper_bound
。 numpy中的剪辑描述为:给定间隔,区间外的值被剪切到间隔边缘。例如,如果指定了[0,1]的间隔,则小于0的值将变为0,而大于1的值将变为1.
它是一个近似解决方案的迭代算法,我认为每个使用的算法都是迭代算法。
import numpy as np
from scipy.optimize import lsq_linear
np.random.seed(1)
A = np.random.normal(size=(500, 4))
b = np.random.normal(size=500)
""" Solve Ax=b
----------
"""
print('Solve non-bounded with scipys lsq_linear')
sol = lsq_linear(A, b)
print('Ax=b sol: ', sol['x'])
print('cost : ', sol['cost'])
print()
""" Solve Ax=b with box-constraints
-------------------------------
"""
print('Solve bounded (0.001, 0.05) with scipys lsq_linear')
sol = lsq_linear(A, b, bounds=(0.001, 0.05))
print('Ax=b constrained sol: ', sol['x'])
print('cost : ', sol['cost'])
print()
""" Solve Ax=b with box-constraints using a projected gradient algorithm
--------------------------------------------------------------------
"""
def solve_pg(A, b, bounds=(-np.inf, np.inf), momentum=0.9, maxiter=1000):
""" remarks:
algorithm: accelerated projected gradient
projection: proj onto box constraints
line-search: armijo-rule along projection-arc (Bertsekas book)
stopping-criterion: naive
gradient-calculation: precomputes AtA
"""
lb = np.empty(A.shape[1])
ub = np.empty(A.shape[1])
if len(bounds) == 2:
# apply lb & ub to all variables
lb = bounds[0]
ub = bounds[1]
else:
# assume dimensions are ok
lb = np.array(bounds[0])
ub = np.array(bounds[1])
M, N = A.shape
x = np.zeros(N)
AtA = A.T.dot(A)
Atb = A.T.dot(b)
stop_count = 0
def gradient(x):
return AtA.dot(x) - Atb
def obj(x):
return 0.5 * np.linalg.norm(A.dot(x) - b)**2
it = 0
while True:
grad = gradient(x)
# line search
alpha = 1
beta = 0.5
sigma=1e-2
old_obj = obj(x)
while True:
new_x = x - alpha * grad
new_obj = obj(new_x)
if old_obj - new_obj >= sigma * grad.dot(x - new_x):
break
else:
alpha *= beta
x_old = x[:]
x = x - alpha*grad
# projection
np.clip(x, lb, ub, out=x) # Projection onto box constraints
# see SO-text
# in-place clipping
y = x + momentum * (x - x_old)
if np.abs(old_obj - obj(x)) < 1e-2:
stop_count += 1
else:
stop_count = 0
if stop_count == 3:
print('early-stopping @ it: ', it)
return x
it += 1
if it == maxiter:
return x
print('Solve bounded (0.001, 0.05) with accelerated projected gradient')
sol = solve_pg(A, b, bounds=(0.001, 0.05))
print(sol)
print('cost: ', 0.5 * (np.square(np.linalg.norm(A.dot(sol) - b))))
Solve non-bounded with scipys lsq_linear
Ax=b sol: [ 0.06627173 -0.06104991 -0.07010355 0.04024075]
cost : 244.58267993
Solve bounded (0.001, 0.05) with scipys lsq_linear
Ax=b constrained sol: [ 0.05 0.001 0.001 0.03902291]
cost : 246.990611225
Solve bounded (0.001, 0.05) with accelerated projected gradient
early-stopping @ it: 3
[ 0.05 0.001 0.001 0.03902229]
cost: 246.990611225