目标-使用给定的最大值和系数,在约束内求解线性方程式
问题-定义约束
代码:
import numpy as np
coefficients = np.array([
[0, 9, 6, 9, 4, 0 ],
[0, 9, 7, 7, 3, 2 ],
[0, 9, 5, 9, 3, 2 ],
[0, 11, 2, 6, 4, 5],
[0, 11, 1, 7, 2, 7],
[1, 10, 1, 5, 3, 8]
])
maxPoints = np.array([
[4239100],
[4204767],
[4170434],
[4136101],
[4101768],
[4067435]
])
x = np.linalg.solve(coefficients, maxPoints)
print(x)
输出
[[256694.51339286]
[213778.26339286]
[140820.63839286]
[123654.13839286]
[89321.13839286]
[80737.88839286]]
问题是我想应用约束使其成为
x[0] <= x[1] <= x[2] <= x[3] <= x[4] <= x[5]
另一个问题是,这目前只能解决这个较小的矩阵,我需要使用它来处理更大的矩阵,因为我的maxPoints是1列乘32行,系数是6列乘32行。使用上面的linalg方法无法解决此问题。
出现错误消息:
Traceback (most recent call last):
File "Untitled-1.py", line 74, in <module>
X = np.linalg.solve(coefficients, maxPoints)
File "<__array_function__ internals>", line 6, in solve
File "/home/comfortablynumb/.local/lib/python3.7/site-packages/numpy/linalg/linalg.py", line 390, in solve
_assertNdSquareness(a)
File "/home/comfortablynumb/.local/lib/python3.7/site- packages/numpy/linalg/linalg.py", line 213, in _assertNdSquareness
raise LinAlgError('Last 2 dimensions of the array must be square')
numpy.linalg.LinAlgError: Last 2 dimensions of the array must be square
感谢您的帮助。
编辑:
这是我正在使用的完整数据集
`maxPoints = np.array([
[4239100],
[4204767],
[4170434],
[4136101],
[4101768],
[4067435],
[4033102],
[3998769],
[3964436],
[3930103],
[3895770],
[3861437],
[3827104],
[3792771],
[3758438],
[3724105],
[3689772],
[3655439],
[3621106],
[3586773],
[3552440],
[3518107],
[3483774],
[3449441],
[3415108],
[3380775],
[3346442],
[3312109],
[3277776],
[3243443],
[3209110],
[3174777]])`
`coefficients = np.array([
[0, 9, 6, 9, 4, 0 ],
[0, 9, 7, 7, 3, 2 ],
[0, 9, 5, 9, 3, 2 ],
[0, 11, 2, 6, 4, 5],
[0, 11, 1, 7, 2, 7],
[1, 10, 1, 5, 3, 8],
[2, 9, 1, 5, 2, 9 ],
[2, 8, 2, 4, 3, 9 ],
[2, 8, 2, 3, 4, 9 ],
[2, 8, 1, 4, 1, 12],
[3, 6, 1, 5, 1, 12],
[4, 5, 1, 5, 0, 13],
[5, 4, 1, 5, 0, 13],
[5, 4, 0, 5, 1, 13],
[5, 4, 1, 4, 1, 13],
[5, 4, 2, 3, 1, 13],
[5, 4, 2, 3, 1, 13],
[6, 3, 2, 3, 1, 13],
[6, 3, 2, 2, 1, 14],
[6, 3, 2, 1, 2, 14],
[6, 4, 1, 1, 2, 14],
[6, 4, 1, 1, 0, 16],
[6, 3, 2, 1, 0, 16],
[6, 4, 1, 1, 0, 16],
[6, 4, 1, 1, 0, 16],
[6, 4, 1, 1, 0, 16],
[6, 4, 1, 1, 0, 16],
[7, 3, 1, 1, 0, 16],
[7, 3, 1, 1, 0, 16],
[7, 3, 1, 1, 0, 16],
[7, 3, 1, 1, 0, 16],
[7, 3, 1, 1, 0, 16]
])`
答案 0 :(得分:1)
描述内容冗长,而且不够精确。因此,我不确定这是否是正确的数学模型,但这是我的解释:
r
可以解释为残差。我认为问题中提到的最大值意味着b>=Ax
或我所说的r>=0
。当然,很容易取消r>=0
限制。
这是一个最小二乘问题,带有一些侧面约束。它被表述为二次规划(QP)问题。
请注意,也可以用线性目标来表述:只需最小化r的总和即可。那会给你一个LP问题。
有了数学模型,编写一些代码非常容易:
import numpy as np
import cvxpy as cp
import pandas as pd
b = np.array([[4239100],[4204767],[4170434],[4136101],[4101768],[4067435],[4033102],[3998769],[3964436],[3930103],
[3895770],[3861437],[3827104],[3792771],[3758438],[3724105],[3689772],[3655439],[3621106],[3586773],[3552440],
[3518107],[3483774],[3449441],[3415108],[3380775],[3346442],[3312109],[3277776],[3243443],[3209110],[3174777]])
A = np.array([[0, 9, 6, 9, 4, 0 ],[0, 9, 7, 7, 3, 2 ],[0, 9, 5, 9, 3, 2 ],[0, 11, 2, 6, 4, 5],[0, 11, 1, 7, 2, 7],
[1, 10, 1, 5, 3, 8],[2, 9, 1, 5, 2, 9 ],[2, 8, 2, 4, 3, 9 ],[2, 8, 2, 3, 4, 9 ],[2, 8, 1, 4, 1, 12],
[3, 6, 1, 5, 1, 12],[4, 5, 1, 5, 0, 13],[5, 4, 1, 5, 0, 13],[5, 4, 0, 5, 1, 13],[5, 4, 1, 4, 1, 13],
[5, 4, 2, 3, 1, 13],[5, 4, 2, 3, 1, 13],[6, 3, 2, 3, 1, 13],[6, 3, 2, 2, 1, 14],[6, 3, 2, 1, 2, 14],
[6, 4, 1, 1, 2, 14],[6, 4, 1, 1, 0, 16],[6, 3, 2, 1, 0, 16],[6, 4, 1, 1, 0, 16],[6, 4, 1, 1, 0, 16],
[6, 4, 1, 1, 0, 16],[6, 4, 1, 1, 0, 16],[7, 3, 1, 1, 0, 16],[7, 3, 1, 1, 0, 16],[7, 3, 1, 1, 0, 16],
[7, 3, 1, 1, 0, 16],[7, 3, 1, 1, 0, 16]])
m,n = np.shape(A)
print("m,n=",m,n)
x = cp.Variable((n,1))
r = cp.Variable((m,1),nonneg=True)
ordered = [x[i] >= x[i-1] for i in range(1,n)]
prob = cp.Problem(cp.Minimize(cp.sum_squares(r)),
[r == b-A@x] + ordered)
prob.solve(verbose=True)
print("x:\n",pd.DataFrame(x.value))
print("r:\n",pd.DataFrame(r.value))
CVXPY模型由默认的QP求解器OSQP求解。这是一个相当新的开源一阶算法。求解器日志如下:
-----------------------------------------------------------------
OSQP v0.6.0 - Operator Splitting QP Solver
(c) Bartolomeo Stellato, Goran Banjac
University of Oxford - Stanford University 2019
-----------------------------------------------------------------
problem: variables n = 38, constraints m = 69
nnz(P) + nnz(A) = 278
settings: linear system solver = qdldl,
eps_abs = 1.0e-05, eps_rel = 1.0e-05,
eps_prim_inf = 1.0e-04, eps_dual_inf = 1.0e-04,
rho = 1.00e-01 (adaptive),
sigma = 1.00e-06, alpha = 1.60, max_iter = 10000
check_termination: on (interval 25),
scaling: on, scaled_termination: off
warm start: on, polish: on, time_limit: off
iter objective pri res dua res rho time
1 0.0000e+00 4.24e+06 1.18e+10 1.00e-01 5.06e-04s
200 5.6400e+11 3.43e+01 9.03e+00 1.03e+00 1.68e-03s
225 5.6410e+11 1.06e+01 2.79e+00 1.03e+00 2.50e-03s
plsh 5.6415e+11 2.79e-09 1.77e-08 -------- 3.22e-03s
status: solved
solution polish: successful
number of iterations: 225
optimal objective: 564145476298.7255
run time: 3.22e-03s
optimal rho estimate: 1.44e+00
解向量x
如下:
x:
0
0 -101723.089140
1 60977.386991
2 174769.759793
3 189344.863121
4 208736.990006
5 208736.990006
当然,在实践中,我们会稍微扩展(单位更改)。 b
,x
和物镜的数字有点大。
答案 1 :(得分:0)
约束
假设您所指的是以下形式的线性方程组:
ax = b
其中a是代码段中的值矩阵(coefficients
,而b是代码中的常数矢量(maxPoints
),则您要求解x。
在这里添加约束实际上没有意义,因为在以下情况下系统是可解决的:
coefficients
是可逆的(或至少具有伪逆)。maxPoints
的所有元素都是有限的。LinAlgError
实质上,您可以想到numpy.linalg.solve(a,b)
会执行以下操作:
解决:ax = b
for x
从某种意义上讲,numpy
可以计算矩阵a的逆。这里,numpy
提出了LinAlgError
,因为矩阵不是正方形的,因此逆定义不明确。 numpy
实际上在幕后做了一些其他工作(请参阅LAPACK),但这已经足够在此处进行讨论了。
我认为可能是问题所在
您遇到以下问题:
coefficients
中的列数为6,行数为32。maxPoints
中的行数是32。第一步是使用numpy.linalg.lstsq
(请参阅here)。您可以使用完全相同的语法。
x_ls = np.linalg.lstsq(coefficients, maxPoints)
此求解器给出的答案使平方的欧几里德2范数最小。 相反您要使用x[j] <= x[j+1]
对所有j < len(x)
的约束来最小化。
一些解决方案的步骤
我可能稍后再使用Github代码段进行编辑,因为这是一个非常有趣的问题。
编辑: trust-constr
似乎比SLSQP更好。
我认为您应该进一步了解scipy
中的optimization documentation,尤其是trust-constr
算法。我认为您非常想对矩阵的一部分做一个最小二乘解,然后向前迭代。作为一种合理的算法:
trust-constr
。界限(自由选择BOUND
):
[-np.inf,BOUND]
[BOUND, np.inf]
[-np.inf,x_p[4]]
[x_p[4],x_p[5]]
[x_p[5],np.inf]
这不是世界上最好的算法,但是,如果您确实选择了错误的BOUND
值,则可以满足您的约束,并且迭代性质将有所纠正。如果希望获得全局解决方案,则可以对bound的许多不同值进行重复,并查看哪一个在最终trust-constr
拟合中返回的误差最小。
编辑:实现:
import numpy as np
from scipy.optimize import minimize
from scipy.optimize import Bounds
# Set up bounds and make a,b objects
a,b = coefficients, maxPoints[:,0]
# Set acceptable bounds to be whatever you like here.
# I've chosen -100000 to 100000 in steps of 10000.
iter_of_acceptable_bounds = range(-100000,100000,10000)
def e2n(x,*args):
'Compute the sum of the residuals (Euclidean 2-norm).'
a,b = args
return np.inner(b-a@x,b-a@x)
solutions, errors = [], []
for BOUND in iter_of_acceptable_bounds:
# Repeat the algorithm for each initial choice of bounds.
# Generate an array for the bounds, based no the initial choice.
bounds_arr = np.concatenate(([-np.inf],[BOUND,BOUND],[np.inf]))
for i in range(2,a.shape[1]+1):
# Initial guess of 0.
x0 = [0 for j in range(i)]
# Start with 2 columns, end at a.shape[1]
# (a.shape[1]==6 for the example).
bounds = Bounds(
*bounds_arr.reshape(i,2).T.tolist()
)
# Slice up a accordingly
A = a[:,-i:]
# Minimize using trust-constr, and unpack the solution
xr = minimize(e2n,x0,
args=(A,b),
bounds=bounds,
method='trust-constr').x
# Generate a new array for the bounds, dynamically based on the previous choices.
bounds_arr = np.concatenate(
[[-np.inf]] + [(xr[j],xr[j]) for j in range(i)] + [[np.inf]]
)
# Save the solution and error from the full matrix
solutions.append(xr)
errors.append(np.sqrt(e2n(xr,a,b)))
这似乎表现不错。如果我们进行快速绘图:
import matplotlib.pyplot as plt
# lstsq, for comparison (no constraints)
lstsq_fit, lstsq_res, *_ = np.linalg.lstsq(a,b)
lstsq_err = np.sqrt(lstsq_res)
# Plot errors
plt.plot(iter_of_acceptable_bounds,100*(errors-lstsq_err)/lstsq_err)
plt.xlabel('Starting choice of BOUND for algorithm')
plt.ylabel('Increase in error compared to numpy.linalg.lstsq')
plt.show()
通常,numpy.linalg.lstsq
可以很好地解决此问题(这只是线性回归问题),但是它没有所需的约束。但是,总的来说,这种算法是可以的-对于我们选择的numpy.linalg.lstsq
,数据仅比BOUND
差40%。
通过这种粗略搜索,最佳值由以下给出:
>>> my_sol = solutions[errors.index(min(errors))]
>>> print(my_sol)
array([-186017.00778511, 6680.13364168, 184099.89046232,
220587.55247874, 257275.09670101, 257275.09670101])