-
Notifications
You must be signed in to change notification settings - Fork 11
/
forbes_qp.m
189 lines (167 loc) · 5.17 KB
/
forbes_qp.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
% FORBES_QP
%
% FORBES_QP(H, q, A, lb, ub, Aeq, beq, lx, ux, opt, out1) solves the
% quadratic programming problem
%
% minimize (1/2)*x'*H*x + q'*x
% subject to Aeq*x == beq
% lb <= A*x <= ub
% lx <= x <= ux
%
% All arguments are optional and may be empty, except for the first.
% If last argument out1 is specified, then the solution process is
% warm-started based on the output of a previous call.
function out = forbes_qp(H, q, A, lb, ub, Aeq, beq, lx, ux, opt, out1)
t0 = tic();
% Arguments parsing
if nargin < 1 || isempty(H)
error('first parameter H is mandatory');
end
n = size(H, 2);
if nargin < 2 || isempty(q)
q = zeros(n, 1);
end
if nargin < 3 || isempty(A)
flag_ineq = 0;
else
flag_ineq = 1;
if size(A, 2) ~= n
error('argument A is incompatible with H and q');
end
end
m = size(A, 1);
if nargin < 4 || isempty(lb)
lb = -inf(m, 1);
end
if any(size(lb) ~= [m, 1])
error('argument lb is incompatible with A');
end
if nargin < 5 || isempty(ub)
ub = +inf(m, 1);
end
if any(size(ub) ~= [m, 1])
error('argument ub is incompatible with A');
end
if nargin < 6 || isempty(Aeq)
flag_eq = 0;
else
flag_eq = 1;
if size(Aeq, 2) ~= n
error('size of Aeq is incompatible with H and q');
end
end
if flag_eq == 1 && (nargin < 7 || isempty(beq) || any(size(beq) ~= [size(Aeq,1), 1]))
error('argument beq is incompatible with Aeq');
end
if nargin < 8 || isempty(lx)
flag_lx = 0;
lx = -inf(n, 1);
else
flag_lx = 1;
if isscalar(lx)
lx = lx*ones(n, 1);
end
end
if nargin < 9 || isempty(ux)
flag_ux = 0;
ux = +inf(n, 1);
else
flag_ux = 1;
if isscalar(ux)
ux = ux*ones(n, 1);
end
end
if nargin < 10, opt = []; end
if nargin < 11, out1 = []; end
if ~isfield(opt, 'prescale') || isempty(opt.prescale)
opt.prescale = true;
end
% Problem setup and solution
if flag_ineq == 0 && flag_eq == 0
f = quadratic(H, q);
g = indBox(lx, ux);
if isempty(out1)
x0 = zeros(n, 1);
else
opt.Lf = out1.solver.prob.Lf;
x0 = out1.x;
end
tprep = toc(t0);
out = forbes(f, g, x0, [], [], opt);
else
A_ext = A;
lb_ext = lb;
ub_ext = ub;
m_ext = m;
% Extend inequality constraints so as to include bounds on x
% (if necessary)
if flag_lx || flag_ux
A_ext = [A_ext; speye(n)];
lb_ext = [lb_ext; lx];
ub_ext = [ub_ext; ux];
m_ext = m_ext + n;
end
if flag_eq == 0 % NO equality constraints
f = quadratic(H, q);
opt_eigs.issym = 1;
opt_eigs.tol = 1e-3;
if (n <= 500 && min(eig(H)) <= 1e-16) || ...
(n > 500 && eigs(H, 1, 'SM', opt_eigs) <= 1e-16)
out.status = 2;
out.msg = 'not strongly convex';
return;
end
if opt.prescale
% Scale inequality constraints
scale = 1./sqrt(diag(A_ext*(H\A_ext')));
A_ext = diag(sparse(scale))*A_ext;
lb_ext = scale.*lb_ext;
ub_ext = scale.*ub_ext;
end
else % YES equality constraints
f = quadraticOverAffine(Aeq, beq, H, q);
if opt.prescale
scale = zeros(size(A_ext, 1), 1);
callfconj = f.makefconj();
[~, p] = callfconj(zeros(size(A_ext, 2),1));
for i = 1:size(A_ext, 1)
[~, dgradi] = callfconj(A_ext(i, :)');
w = A_ext(i, :)*(dgradi-p);
if w >= 1e-14
scale(i) = 1/sqrt(w);
else
scale(i) = 1;
end
end
% Scale inequality constraints
A_ext = diag(sparse(scale))*A_ext;
lb_ext = scale.*lb_ext;
ub_ext = scale.*ub_ext;
end
end
g = indBox(lb_ext, ub_ext);
constr = {A_ext, -speye(m_ext), zeros(m_ext, 1)};
if isempty(out1)
% cold start
y0 = zeros(m_ext, 1);
else
% warm start
opt.Lf = out1.solver.dual.prob.Lf;
y0 = [out1.y_ineq; out1.y_bnd];
end
tprep = toc(t0);
out_forbes = forbes(f, g, y0, [], constr, opt);
end
ttot = toc(t0);
out.status = out_forbes.flag;
out.msg = out_forbes.message;
out.x = out_forbes.x1;
out.y_ineq = out_forbes.y(1:m);
out.y_bnd = out_forbes.y(m+1:end);
out.pobj = (out.x'*(H*out.x))/2 + q'*out.x;
out.dobj = -out_forbes.dual.objective(end); % dual is solved as minimization
out.iterations = out_forbes.iterations;
out.preprocess = tprep;
out.time = ttot;
out.solver = out_forbes;
end