1 // Copyright (c) 2018 ERGO-Code. See license.txt for license.
2
3 #include "model.h"
4 #include <algorithm>
5 #include <cassert>
6 #include <cmath>
7 #include "utils.h"
8
9 namespace ipx {
10
Load(const Control & control,Int num_constr,Int num_var,const Int * Ap,const Int * Ai,const double * Ax,const double * rhs,const char * constr_type,const double * obj,const double * lbuser,const double * ubuser)11 Int Model::Load(const Control& control, Int num_constr, Int num_var,
12 const Int* Ap, const Int* Ai, const double* Ax,
13 const double* rhs, const char* constr_type, const double* obj,
14 const double* lbuser, const double* ubuser) {
15 clear();
16 Int errflag = CopyInput(num_constr, num_var, Ap, Ai, Ax, rhs, constr_type,
17 obj, lbuser, ubuser);
18 if (errflag)
19 return errflag;
20 control.Log()
21 << "Input\n"
22 << Textline("Number of variables:") << num_var_ << '\n'
23 << Textline("Number of free variables:") << num_free_var_ << '\n'
24 << Textline("Number of constraints:") << num_constr_ << '\n'
25 << Textline("Number of equality constraints:") << num_eqconstr_ << '\n'
26 << Textline("Number of matrix entries:") << num_entries_ << '\n';
27 PrintCoefficientRange(control);
28 ScaleModel(control);
29
30 // Make an automatic decision for dualization if not specified by user.
31 Int dualize = control.dualize();
32 if (dualize < 0)
33 dualize = num_constr > 2*num_var;
34 if (dualize)
35 LoadDual();
36 else
37 LoadPrimal();
38
39 A_.clear();
40 AIt_ = Transpose(AI_);
41 assert(AI_.begin(num_cols_ + num_rows_) == AIt_.begin(num_rows_));
42 FindDenseColumns();
43 norm_c_ = Infnorm(c_);
44 norm_bounds_ = Infnorm(b_);
45 for (double x : lb_)
46 if (std::isfinite(x))
47 norm_bounds_ = std::max(norm_bounds_, std::abs(x));
48 for (double x : ub_)
49 if (std::isfinite(x))
50 norm_bounds_ = std::max(norm_bounds_, std::abs(x));
51 PrintPreprocessingLog(control);
52 return 0;
53 }
54
GetInfo(Info * info) const55 void Model::GetInfo(Info *info) const {
56 info->num_var = num_var_;
57 info->num_constr = num_constr_;
58 info->num_entries = num_entries_;
59 info->num_rows_solver = num_rows_;
60 info->num_cols_solver = num_cols_ + num_rows_; // including slack columns
61 info->num_entries_solver = AI_.entries();
62 info->dualized = dualized_;
63 info->dense_cols = num_dense_cols();
64 }
65
clear()66 void Model::clear() {
67 // clear computational form model
68 dualized_ = false;
69 num_rows_ = 0;
70 num_cols_ = 0;
71 num_dense_cols_ = 0;
72 nz_dense_ = 0;
73 AI_.clear();
74 AIt_.clear();
75 b_.resize(0);
76 c_.resize(0);
77 lb_.resize(0);
78 ub_.resize(0);
79 norm_bounds_ = 0.0;
80 norm_c_ = 0.0;
81
82 // clear user model
83 num_constr_ = 0;
84 num_eqconstr_ = 0;
85 num_var_ = 0;
86 num_free_var_ = 0;
87 num_entries_ = 0;
88 boxed_vars_.clear();
89 constr_type_.clear();
90 norm_obj_ = 0.0;
91 norm_rhs_ = 0.0;
92 scaled_obj_.resize(0);
93 scaled_rhs_.resize(0);
94 scaled_lbuser_.resize(0);
95 scaled_ubuser_.resize(0);
96 A_.clear();
97
98 flipped_vars_.clear();
99 colscale_.resize(0);
100 rowscale_.resize(0);
101 }
102
PresolveStartingPoint(const double * x_user,const double * slack_user,const double * y_user,const double * z_user,Vector & x_solver,Vector & y_solver,Vector & z_solver) const103 void Model::PresolveStartingPoint(const double* x_user,
104 const double* slack_user,
105 const double* y_user,
106 const double* z_user,
107 Vector& x_solver,
108 Vector& y_solver,
109 Vector& z_solver) const {
110 const Int m = rows();
111 const Int n = cols();
112 assert(x_solver.size() == n+m);
113 assert(y_solver.size() == m);
114 assert(z_solver.size() == n+m);
115
116 Vector x_temp(num_var_);
117 Vector slack_temp(num_constr_);
118 Vector y_temp(num_constr_);
119 Vector z_temp(num_var_);
120 if (x_user)
121 std::copy_n(x_user, num_var_, std::begin(x_temp));
122 if (slack_user)
123 std::copy_n(slack_user, num_constr_, std::begin(slack_temp));
124 if (y_user)
125 std::copy_n(y_user, num_constr_, std::begin(y_temp));
126 if (z_user)
127 std::copy_n(z_user, num_var_, std::begin(z_temp));
128 ScalePoint(x_temp, slack_temp, y_temp, z_temp);
129 DualizeBasicSolution(x_temp, slack_temp, y_temp, z_temp,
130 x_solver, y_solver, z_solver);
131 }
132
PresolveIPMStartingPoint(const double * x_user,const double * xl_user,const double * xu_user,const double * slack_user,const double * y_user,const double * zl_user,const double * zu_user,Vector & x_solver,Vector & xl_solver,Vector & xu_solver,Vector & y_solver,Vector & zl_solver,Vector & zu_solver) const133 Int Model::PresolveIPMStartingPoint(const double* x_user,
134 const double* xl_user,
135 const double* xu_user,
136 const double* slack_user,
137 const double* y_user,
138 const double* zl_user,
139 const double* zu_user,
140 Vector& x_solver,
141 Vector& xl_solver,
142 Vector& xu_solver,
143 Vector& y_solver,
144 Vector& zl_solver,
145 Vector& zu_solver) const {
146 if (!x_user || !xl_user || !xu_user || !slack_user ||
147 !y_user || !zl_user || !zu_user)
148 return IPX_ERROR_argument_null;
149 if (dualized_)
150 return IPX_ERROR_not_implemented;
151
152 // Copy user point into workspace and apply model scaling.
153 Vector x_temp(x_user, num_var_);
154 Vector xl_temp(xl_user, num_var_);
155 Vector xu_temp(xu_user, num_var_);
156 Vector slack_temp(slack_user, num_constr_);
157 Vector y_temp(y_user, num_constr_);
158 Vector zl_temp(zl_user, num_var_);
159 Vector zu_temp(zu_user, num_var_);
160 ScalePoint(x_temp, xl_temp, xu_temp, slack_temp, y_temp, zl_temp, zu_temp);
161
162 // Check that point is compatible with bounds.
163 for (Int j = 0; j < num_var_; ++j) {
164 if (!std::isfinite(x_temp[j]))
165 return IPX_ERROR_invalid_vector;
166 }
167 for (Int j = 0; j < num_var_; ++j) {
168 if (!(xl_temp[j] >= 0.0) ||
169 scaled_lbuser_[j] == -INFINITY && xl_temp[j] != INFINITY ||
170 scaled_lbuser_[j] != -INFINITY && xl_temp[j] == INFINITY)
171 return IPX_ERROR_invalid_vector;
172 }
173 for (Int j = 0; j < num_var_; ++j) {
174 if (!(xu_temp[j] >= 0.0) ||
175 scaled_ubuser_[j] == INFINITY && xu_temp[j] != INFINITY ||
176 scaled_ubuser_[j] != INFINITY && xu_temp[j] == INFINITY)
177 return IPX_ERROR_invalid_vector;
178 }
179 for (Int i = 0; i < num_constr_; ++i) {
180 if (!std::isfinite(slack_temp[i]) ||
181 constr_type_[i] == '=' && !(slack_temp[i] == 0.0) ||
182 constr_type_[i] == '<' && !(slack_temp[i] >= 0.0) ||
183 constr_type_[i] == '>' && !(slack_temp[i] <= 0.0) )
184 return IPX_ERROR_invalid_vector;
185 }
186 for (Int i = 0; i < num_constr_; ++i) {
187 if (!std::isfinite(y_temp[i]) ||
188 constr_type_[i] == '<' && !(y_temp[i] <= 0.0) ||
189 constr_type_[i] == '>' && !(y_temp[i] >= 0.0) )
190 return IPX_ERROR_invalid_vector;
191 }
192 for (Int j = 0; j < num_var_; ++j) {
193 if (!(zl_temp[j] >= 0.0 && zl_temp[j] < INFINITY) ||
194 scaled_lbuser_[j] == -INFINITY && zl_temp[j] != 0.0)
195 return IPX_ERROR_invalid_vector;
196 }
197 for (Int j = 0; j < num_var_; ++j) {
198 if (!(zu_temp[j] >= 0.0 && zu_temp[j] < INFINITY) ||
199 scaled_ubuser_[j] == INFINITY && zu_temp[j] != 0.0)
200 return IPX_ERROR_invalid_vector;
201 }
202
203 DualizeIPMStartingPoint(x_temp, xl_temp, xu_temp, slack_temp,
204 y_temp, zl_temp, zu_temp,
205 x_solver, xl_solver, xu_solver,
206 y_solver, zl_solver, zu_solver);
207 return 0;
208 }
209
210
PostsolveInteriorSolution(const Vector & x_solver,const Vector & xl_solver,const Vector & xu_solver,const Vector & y_solver,const Vector & zl_solver,const Vector & zu_solver,double * x_user,double * xl_user,double * xu_user,double * slack_user,double * y_user,double * zl_user,double * zu_user) const211 void Model::PostsolveInteriorSolution(const Vector& x_solver,
212 const Vector& xl_solver,
213 const Vector& xu_solver,
214 const Vector& y_solver,
215 const Vector& zl_solver,
216 const Vector& zu_solver,
217 double* x_user,
218 double* xl_user, double* xu_user,
219 double* slack_user,
220 double* y_user,
221 double* zl_user, double* zu_user) const {
222 const Int m = rows();
223 const Int n = cols();
224 assert(x_solver.size() == n+m);
225 assert(xl_solver.size() == n+m);
226 assert(xu_solver.size() == n+m);
227 assert(y_solver.size() == m);
228 assert(zl_solver.size() == n+m);
229 assert(zu_solver.size() == n+m);
230
231 Vector x_temp(num_var_);
232 Vector xl_temp(num_var_);
233 Vector xu_temp(num_var_);
234 Vector slack_temp(num_constr_);
235 Vector y_temp(num_constr_);
236 Vector zl_temp(num_var_);
237 Vector zu_temp(num_var_);
238 DualizeBackInteriorSolution(x_solver, xl_solver, xu_solver, y_solver,
239 zl_solver, zu_solver, x_temp, xl_temp, xu_temp,
240 slack_temp, y_temp, zl_temp, zu_temp);
241 ScaleBackInteriorSolution(x_temp, xl_temp, xu_temp, slack_temp, y_temp,
242 zl_temp, zu_temp);
243 if (x_user)
244 std::copy(std::begin(x_temp), std::end(x_temp), x_user);
245 if (xl_user)
246 std::copy(std::begin(xl_temp), std::end(xl_temp), xl_user);
247 if (xu_user)
248 std::copy(std::begin(xu_temp), std::end(xu_temp), xu_user);
249 if (slack_user)
250 std::copy(std::begin(slack_temp), std::end(slack_temp), slack_user);
251 if (y_user)
252 std::copy(std::begin(y_temp), std::end(y_temp), y_user);
253 if (zl_user)
254 std::copy(std::begin(zl_temp), std::end(zl_temp), zl_user);
255 if (zu_user)
256 std::copy(std::begin(zu_temp), std::end(zu_temp), zu_user);
257 }
258
EvaluateInteriorSolution(const Vector & x_solver,const Vector & xl_solver,const Vector & xu_solver,const Vector & y_solver,const Vector & zl_solver,const Vector & zu_solver,Info * info) const259 void Model::EvaluateInteriorSolution(const Vector& x_solver,
260 const Vector& xl_solver,
261 const Vector& xu_solver,
262 const Vector& y_solver,
263 const Vector& zl_solver,
264 const Vector& zu_solver,
265 Info* info) const {
266 const Int m = rows();
267 const Int n = cols();
268 assert(x_solver.size() == n+m);
269 assert(xl_solver.size() == n+m);
270 assert(xu_solver.size() == n+m);
271 assert(y_solver.size() == m);
272 assert(zl_solver.size() == n+m);
273 assert(zu_solver.size() == n+m);
274
275 // Build solution to scaled user model.
276 Vector x(num_var_);
277 Vector xl(num_var_);
278 Vector xu(num_var_);
279 Vector slack(num_constr_);
280 Vector y(num_constr_);
281 Vector zl(num_var_);
282 Vector zu(num_var_);
283 DualizeBackInteriorSolution(x_solver, xl_solver, xu_solver, y_solver,
284 zl_solver, zu_solver, x, xl, xu, slack, y, zl,
285 zu);
286
287 // Build residuals for scaled model.
288 // rl = lb-x+xl
289 Vector rl(num_var_);
290 for (Int j = 0; j < num_var_; j++) {
291 if (std::isfinite(scaled_lbuser_[j]))
292 rl[j] = scaled_lbuser_[j] - x[j] + xl[j];
293 else
294 assert(xl[j] == INFINITY);
295 }
296 // ru = ub-x-xu
297 Vector ru(num_var_);
298 for (Int j = 0; j < num_var_; j++) {
299 if (std::isfinite(scaled_ubuser_[j]))
300 ru[j] = scaled_ubuser_[j] - x[j] - xu[j];
301 else
302 assert(xu[j] == INFINITY);
303 }
304 // rb = rhs-slack-A*x
305 Vector rb = scaled_rhs_ - slack;
306 assert(scaled_rhs_.size() == num_constr_);
307 assert(rb.size() == num_constr_);
308 MultiplyWithScaledMatrix(x, -1.0, rb, 'N');
309 // rc = obj-zl+zu-A'y
310 Vector rc = scaled_obj_ - zl + zu;
311 MultiplyWithScaledMatrix(y, -1.0, rc, 'T');
312
313 ScaleBackResiduals(rb, rc, rl, ru);
314 double presidual = Infnorm(rb);
315 presidual = std::max(presidual, Infnorm(rl));
316 presidual = std::max(presidual, Infnorm(ru));
317 double dresidual = Infnorm(rc);
318
319 double pobjective = Dot(scaled_obj_, x);
320 double dobjective = Dot(scaled_rhs_, y);
321 for (Int j = 0; j < num_var_; j++) {
322 if (std::isfinite(scaled_lbuser_[j]))
323 dobjective += scaled_lbuser_[j] * zl[j];
324 if (std::isfinite(scaled_ubuser_[j]))
325 dobjective -= scaled_ubuser_[j] * zu[j];
326 }
327 assert(std::isfinite(dobjective));
328 double objective_gap = (pobjective-dobjective) /
329 (1.0 + 0.5*std::abs(pobjective+dobjective));
330
331 double complementarity = 0.0;
332 for (Int j = 0; j < num_var_; j++) {
333 if (std::isfinite(scaled_lbuser_[j]))
334 complementarity += xl[j]*zl[j];
335 if (std::isfinite(scaled_ubuser_[j]))
336 complementarity += xu[j]*zu[j];
337 }
338 for (Int i = 0; i < num_constr_; i++)
339 complementarity -= y[i]*slack[i];
340
341 // For computing the norms of the user variables, we have to scale back.
342 ScaleBackInteriorSolution(x, xl, xu, slack, y, zl, zu);
343
344 info->abs_presidual = presidual;
345 info->abs_dresidual = dresidual;
346 info->rel_presidual = presidual/(1.0+norm_rhs_);
347 info->rel_dresidual = dresidual/(1.0+norm_obj_);
348 info->pobjval = pobjective;
349 info->dobjval = dobjective;
350 info->rel_objgap = objective_gap;
351 info->complementarity = complementarity;
352 info->normx = Infnorm(x);
353 info->normy = Infnorm(y);
354 info->normz = std::max(Infnorm(zl), Infnorm(zu));
355 }
356
PostsolveBasicSolution(const Vector & x_solver,const Vector & y_solver,const Vector & z_solver,const std::vector<Int> & basic_status_solver,double * x_user,double * slack_user,double * y_user,double * z_user) const357 void Model::PostsolveBasicSolution(const Vector& x_solver,
358 const Vector& y_solver,
359 const Vector& z_solver,
360 const std::vector<Int>& basic_status_solver,
361 double* x_user, double* slack_user,
362 double* y_user, double* z_user) const {
363 const Int m = rows();
364 const Int n = cols();
365 assert(x_solver.size() == n+m);
366 assert(y_solver.size() == m);
367 assert(z_solver.size() == n+m);
368 assert(basic_status_solver.size() == n+m);
369
370 Vector x_temp(num_var_);
371 Vector slack_temp(num_constr_);
372 Vector y_temp(num_constr_);
373 Vector z_temp(num_var_);
374 std::vector<Int> cbasis_temp(num_constr_);
375 std::vector<Int> vbasis_temp(num_var_);
376 DualizeBackBasicSolution(x_solver, y_solver, z_solver, x_temp, slack_temp,
377 y_temp, z_temp);
378 DualizeBackBasis(basic_status_solver, cbasis_temp, vbasis_temp);
379 CorrectScaledBasicSolution(x_temp, slack_temp, y_temp, z_temp, cbasis_temp,
380 vbasis_temp);
381 ScaleBackBasicSolution(x_temp, slack_temp, y_temp, z_temp);
382 if (x_user)
383 std::copy(std::begin(x_temp), std::end(x_temp), x_user);
384 if (slack_user)
385 std::copy(std::begin(slack_temp), std::end(slack_temp), slack_user);
386 if (y_user)
387 std::copy(std::begin(y_temp), std::end(y_temp), y_user);
388 if (z_user)
389 std::copy(std::begin(z_temp), std::end(z_temp), z_user);
390 }
391
EvaluateBasicSolution(const Vector & x_solver,const Vector & y_solver,const Vector & z_solver,const std::vector<Int> & basic_status_solver,Info * info) const392 void Model::EvaluateBasicSolution(const Vector& x_solver,
393 const Vector& y_solver,
394 const Vector& z_solver,
395 const std::vector<Int>& basic_status_solver,
396 Info* info) const {
397 const Int m = rows();
398 const Int n = cols();
399 assert(x_solver.size() == n+m);
400 assert(y_solver.size() == m);
401 assert(z_solver.size() == n+m);
402 assert(basic_status_solver.size() == n+m);
403
404 // Build basic solution to scaled user model.
405 Vector x(num_var_);
406 Vector slack(num_constr_);
407 Vector y(num_constr_);
408 Vector z(num_var_);
409 std::vector<Int> cbasis(num_constr_);
410 std::vector<Int> vbasis(num_var_);
411 DualizeBackBasicSolution(x_solver, y_solver, z_solver, x, slack, y, z);
412 DualizeBackBasis(basic_status_solver, cbasis, vbasis);
413 CorrectScaledBasicSolution(x, slack, y, z, cbasis, vbasis);
414 double pobj = Dot(scaled_obj_, x);
415
416 // Build infeasibilities in scaled user model.
417 Vector xinfeas(num_var_);
418 Vector sinfeas(num_constr_);
419 Vector yinfeas(num_constr_);
420 Vector zinfeas(num_var_);
421 for (Int j = 0; j < num_var_; j++) {
422 if (x[j] < scaled_lbuser_[j])
423 xinfeas[j] = x[j]-scaled_lbuser_[j];
424 if (x[j] > scaled_ubuser_[j])
425 xinfeas[j] = x[j]-scaled_ubuser_[j];
426 if (vbasis[j] != IPX_nonbasic_lb && z[j] > 0.0)
427 zinfeas[j] = z[j];
428 if (vbasis[j] != IPX_nonbasic_ub && z[j] < 0.0)
429 zinfeas[j] = z[j];
430 }
431 for (Int i = 0; i < num_constr_; i++) {
432 if (constr_type_[i] == '<') {
433 if (slack[i] < 0.0)
434 sinfeas[i] = slack[i];
435 if (y[i] > 0.0)
436 yinfeas[i] = y[i];
437 }
438 if (constr_type_[i] == '>') {
439 if (slack[i] > 0.0)
440 sinfeas[i] = slack[i];
441 if (y[i] < 0.0)
442 yinfeas[i] = y[i];
443 }
444 }
445
446 // Scale back basic solution and infeasibilities.
447 ScaleBackBasicSolution(x, slack, y, z);
448 ScaleBackBasicSolution(xinfeas, sinfeas, yinfeas, zinfeas);
449
450 info->primal_infeas = std::max(Infnorm(xinfeas), Infnorm(sinfeas));
451 info->dual_infeas = std::max(Infnorm(zinfeas), Infnorm(yinfeas));
452 info->objval = pobj;
453 }
454
PostsolveBasis(const std::vector<Int> & basic_status_solver,Int * cbasis_user,Int * vbasis_user) const455 void Model::PostsolveBasis(const std::vector<Int>& basic_status_solver,
456 Int* cbasis_user, Int* vbasis_user) const {
457 const Int m = rows();
458 const Int n = cols();
459 assert(basic_status_solver.size() == n+m);
460
461 std::vector<Int> cbasis_temp(num_constr_);
462 std::vector<Int> vbasis_temp(num_var_);
463 DualizeBackBasis(basic_status_solver, cbasis_temp, vbasis_temp);
464 ScaleBackBasis(cbasis_temp, vbasis_temp);
465 if (cbasis_user)
466 std::copy(std::begin(cbasis_temp), std::end(cbasis_temp), cbasis_user);
467 if (vbasis_user)
468 std::copy(std::begin(vbasis_temp), std::end(vbasis_temp), vbasis_user);
469 }
470
471 // Checks if the vectors are valid LP data vectors. Returns 0 if OK and a
472 // negative value if a vector is invalid.
CheckVectors(Int m,Int n,const double * rhs,const char * constr_type,const double * obj,const double * lb,const double * ub)473 static int CheckVectors(Int m, Int n, const double* rhs,const char* constr_type,
474 const double* obj, const double* lb, const double* ub) {
475 for (Int i = 0; i < m; i++)
476 if (!std::isfinite(rhs[i]))
477 return -1;
478 for (Int j = 0; j < n; j++)
479 if (!std::isfinite(obj[j]))
480 return -2;
481 for (Int j = 0; j < n; j++) {
482 if (!std::isfinite(lb[j]) && lb[j] != -INFINITY)
483 return -3;
484 if (!std::isfinite(ub[j]) && ub[j] != INFINITY)
485 return -3;
486 if (lb[j] > ub[j])
487 return -3;
488 }
489 for (Int i = 0; i < m; i++)
490 if (constr_type[i] != '=' && constr_type[i] != '<' &&
491 constr_type[i] != '>')
492 return -4;
493 return 0;
494 }
495
496 // Checks if A is a valid m-by-n matrix in CSC format. Returns 0 if OK and a
497 // negative value otherwise.
CheckMatrix(Int m,Int n,const Int * Ap,const Int * Ai,const double * Ax)498 Int CheckMatrix(Int m, Int n, const Int *Ap, const Int *Ai, const double *Ax) {
499 if (Ap[0] != 0)
500 return -5;
501 for (Int j = 0; j < n; j++)
502 if (Ap[j] > Ap[j+1])
503 return -5;
504 for (Int p = 0; p < Ap[n]; p++)
505 if (!std::isfinite(Ax[p]))
506 return -6;
507 // Test for out of bound indices and duplicates.
508 std::vector<Int> marked(m, -1);
509 for (Int j = 0; j < n; j++) {
510 for (Int p = Ap[j]; p < Ap[j+1]; p++) {
511 Int i = Ai[p];
512 if (i < 0 || i >= m)
513 return -7;
514 if (marked[i] == j)
515 return -8;
516 marked[i] = j;
517 }
518 }
519 return 0;
520 }
521
CopyInput(Int num_constr,Int num_var,const Int * Ap,const Int * Ai,const double * Ax,const double * rhs,const char * constr_type,const double * obj,const double * lbuser,const double * ubuser)522 Int Model::CopyInput(Int num_constr, Int num_var, const Int* Ap, const Int* Ai,
523 const double* Ax, const double* rhs,
524 const char* constr_type, const double* obj,
525 const double* lbuser, const double* ubuser) {
526 if (!(Ap && Ai && Ax && rhs && constr_type && obj && lbuser && ubuser)) {
527 return IPX_ERROR_argument_null;
528 }
529 if (num_constr < 0 || num_var <= 0) {
530 return IPX_ERROR_invalid_dimension;
531 }
532 if (CheckVectors(num_constr, num_var, rhs, constr_type, obj, lbuser, ubuser)
533 != 0) {
534 return IPX_ERROR_invalid_vector;
535 }
536 if (CheckMatrix(num_constr, num_var, Ap, Ai, Ax) != 0) {
537 return IPX_ERROR_invalid_matrix;
538 }
539 num_constr_ = num_constr;
540 num_eqconstr_ = std::count(constr_type, constr_type+num_constr, '=');
541 num_var_ = num_var;
542 num_entries_ = Ap[num_var];
543 num_free_var_ = 0;
544 boxed_vars_.clear();
545 for (Int j = 0; j < num_var; j++) {
546 if (std::isinf(lbuser[j]) && std::isinf(ubuser[j]))
547 num_free_var_++;
548 if (std::isfinite(lbuser[j]) && std::isfinite(ubuser[j]))
549 boxed_vars_.push_back(j);
550 }
551 constr_type_ = std::vector<char>(constr_type, constr_type+num_constr);
552 scaled_obj_ = Vector(obj, num_var);
553 scaled_rhs_ = Vector(rhs, num_constr);
554 scaled_lbuser_ = Vector(lbuser, num_var);
555 scaled_ubuser_ = Vector(ubuser, num_var);
556 A_.LoadFromArrays(num_constr, num_var, Ap, Ap+1, Ai, Ax);
557 norm_obj_ = Infnorm(scaled_obj_);
558 norm_rhs_ = Infnorm(scaled_rhs_);
559 for (double x : scaled_lbuser_)
560 if (std::isfinite(x))
561 norm_rhs_ = std::max(norm_rhs_, std::abs(x));
562 for (double x : scaled_ubuser_)
563 if (std::isfinite(x))
564 norm_rhs_ = std::max(norm_rhs_, std::abs(x));
565 return 0;
566 }
567
ScaleModel(const Control & control)568 void Model::ScaleModel(const Control& control) {
569 flipped_vars_.clear();
570 for (Int j = 0; j < num_var_; j++) {
571 if (std::isfinite(scaled_ubuser_[j]) && std::isinf(scaled_lbuser_[j])) {
572 scaled_lbuser_[j] = -scaled_ubuser_[j];
573 scaled_ubuser_[j] = INFINITY;
574 ScaleColumn(A_, j, -1.0);
575 scaled_obj_[j] *= -1.0;
576 flipped_vars_.push_back(j);
577 }
578 }
579 colscale_.resize(0);
580 rowscale_.resize(0);
581
582 // Choose scaling method.
583 if (control.scale() >= 1)
584 EquilibrateMatrix();
585
586 // Apply scaling to vectors.
587 if (colscale_.size() > 0) {
588 assert(colscale_.size() == num_var_);
589 scaled_obj_ *= colscale_;
590 scaled_lbuser_ /= colscale_;
591 scaled_ubuser_ /= colscale_;
592 }
593 if (rowscale_.size() > 0) {
594 assert(rowscale_.size() == num_constr_);
595 scaled_rhs_ *= rowscale_;
596 }
597 }
598
LoadPrimal()599 void Model::LoadPrimal() {
600 num_rows_ = num_constr_;
601 num_cols_ = num_var_;
602 dualized_ = false;
603
604 // Copy A and append identity matrix.
605 AI_ = A_;
606 for (Int i = 0; i < num_constr_; i++) {
607 AI_.push_back(i, 1.0);
608 AI_.add_column();
609 }
610 assert(AI_.cols() == num_var_+num_constr_);
611
612 // Copy vectors and set bounds on slack variables.
613 b_ = scaled_rhs_;
614 c_.resize(num_var_+num_constr_);
615 c_ = 0.0;
616 std::copy_n(std::begin(scaled_obj_), num_var_, std::begin(c_));
617 lb_.resize(num_rows_+num_cols_);
618 std::copy_n(std::begin(scaled_lbuser_), num_var_, std::begin(lb_));
619 ub_.resize(num_rows_+num_cols_);
620 std::copy_n(std::begin(scaled_ubuser_), num_var_, std::begin(ub_));
621 for (Int i = 0; i < num_constr_; i++) {
622 switch(constr_type_[i]) {
623 case '=':
624 lb_[num_var_+i] = 0.0;
625 ub_[num_var_+i] = 0.0;
626 break;
627 case '<':
628 lb_[num_var_+i] = 0.0;
629 ub_[num_var_+i] = INFINITY;
630 break;
631 case '>':
632 lb_[num_var_+i] = -INFINITY;
633 ub_[num_var_+i] = 0.0;
634 break;
635 }
636 }
637 }
638
LoadDual()639 void Model::LoadDual() {
640 num_rows_ = num_var_;
641 num_cols_ = num_constr_ + boxed_vars_.size();
642 dualized_ = true;
643
644 // Check that every variable with finite scaled_ubuser_ has finite
645 // scaled_lbuser_ (must be the case after scaling).
646 for (Int j = 0; j < num_var_; j++) {
647 if (std::isfinite(scaled_ubuser_[j]))
648 assert(std::isfinite(scaled_lbuser_[j]));
649 }
650
651 // Build AI.
652 AI_ = Transpose(A_);
653 for (Int j = 0; j < num_var_; j++) {
654 if (std::isfinite(scaled_ubuser_[j])) {
655 AI_.push_back(j, -1.0);
656 AI_.add_column();
657 }
658 }
659 assert(AI_.cols() == num_cols_);
660 for (Int i = 0; i < num_rows_; i++) {
661 AI_.push_back(i, 1.0);
662 AI_.add_column();
663 }
664
665 // Build vectors.
666 b_ = scaled_obj_;
667 c_.resize(num_cols_+num_rows_);
668 Int put = 0;
669 for (double x : scaled_rhs_)
670 c_[put++] = -x;
671 for (double x : scaled_ubuser_)
672 if (std::isfinite(x))
673 c_[put++] = x;
674 assert(put == num_cols_);
675 for (double x : scaled_lbuser_)
676 // If x is negative infinity, then the variable will be fixed and we can
677 // give it any (finite) cost.
678 c_[put++] = std::isfinite(x) ? -x : 0.0;
679 lb_.resize(num_cols_+num_rows_);
680 ub_.resize(num_cols_+num_rows_);
681 for (Int i = 0; i < num_constr_; i++)
682 switch(constr_type_[i]) {
683 case '=':
684 lb_[i] = -INFINITY;
685 ub_[i] = INFINITY;
686 break;
687 case '<':
688 lb_[i] = -INFINITY;
689 ub_[i] = 0.0;
690 break;
691 case '>':
692 lb_[i] = 0.0;
693 ub_[i] = INFINITY;
694 break;
695 }
696 for (Int j = num_constr_; j < num_cols_; j++) {
697 lb_[j] = 0.0;
698 ub_[j] = INFINITY;
699 }
700 for (Int j = 0; j < num_var_; j++) {
701 lb_[num_cols_+j] = 0.0;
702 ub_[num_cols_+j] = std::isfinite(scaled_lbuser_[j]) ? INFINITY : 0.0;
703 }
704 }
705
706 // Returns a power-of-2 factor s such that s*2^exp becomes closer to the
707 // interval [2^expmin, 2^expmax].
EquilibrationFactor(int expmin,int expmax,int exp)708 static double EquilibrationFactor(int expmin, int expmax, int exp) {
709 if (exp < expmin)
710 return std::ldexp(1.0, (expmin-exp+1)/2);
711 if (exp > expmax)
712 return std::ldexp(1.0, -((exp-expmax+1)/2));
713 return 1.0;
714 }
715
EquilibrateMatrix()716 void Model::EquilibrateMatrix() {
717 const Int m = A_.rows();
718 const Int n = A_.cols();
719 const Int* Ap = A_.colptr();
720 const Int* Ai = A_.rowidx();
721 double* Ax = A_.values();
722
723 colscale_.resize(0);
724 rowscale_.resize(0);
725
726 // The absolute value of each nonzero entry of AI can be written as x*2^exp
727 // with x in the range [0.5,1). We consider AI well scaled if each entry is
728 // such that expmin <= exp <= expmax for parameters expmin and expmax.
729 // For example,
730 //
731 // expmin expmax allowed range
732 // -----------------------------
733 // 0 2 [0.5, 4)
734 // -1 3 [0.25, 8)
735 // 1 7 [1.0, 128)
736 //
737 // If AI is not well scaled, a recursive row and column equilibration is
738 // applied. In each iteration, the scaling factors are roughly 1/sqrt(max),
739 // where max is the maximum row or column entry. However, the factors are
740 // truncated to powers of 2, so that no round-off errors occur.
741
742 constexpr int expmin = 0;
743 constexpr int expmax = 3;
744 constexpr Int maxround = 10;
745
746 // Quick return if entries are within the target range.
747 bool out_of_range = false;
748 for (Int p = 0; p < Ap[n]; p++) {
749 int exp;
750 std::frexp(std::abs(Ax[p]), &exp);
751 if (exp < expmin || exp > expmax) {
752 out_of_range = true;
753 break;
754 }
755 }
756 if (!out_of_range)
757 return;
758
759 colscale_.resize(n);
760 rowscale_.resize(m);
761 colscale_ = 1.0;
762 rowscale_ = 1.0;
763 Vector colmax(n), rowmax(m);
764
765 for (Int round = 0; round < maxround; round++) {
766 // Compute infinity norm of each row and column.
767 rowmax = 0.0;
768 for (Int j = 0; j < n; j++) {
769 colmax[j] = 0.0;
770 for (Int p = Ap[j]; p < Ap[j+1]; p++) {
771 Int i = Ai[p];
772 double xa = std::abs(Ax[p]);
773 colmax[j] = std::max(colmax[j], xa);
774 rowmax[i] = std::max(rowmax[i], xa);
775 }
776 }
777 // Replace rowmax and colmax entries by scaling factors from this round.
778 bool out_of_range = false;
779 for (Int i = 0; i < m; i++) {
780 int exp;
781 std::frexp(rowmax[i], &exp);
782 rowmax[i] = EquilibrationFactor(expmin, expmax, exp);
783 if (rowmax[i] != 1.0) {
784 out_of_range = true;
785 rowscale_[i] *= rowmax[i];
786 }
787 }
788 for (Int j = 0; j < n; j++) {
789 int exp;
790 std::frexp(colmax[j], &exp);
791 colmax[j] = EquilibrationFactor(expmin, expmax, exp);
792 if (colmax[j] != 1.0) {
793 out_of_range = true;
794 colscale_[j] *= colmax[j];
795 }
796 }
797 if (!out_of_range)
798 break;
799 // Rescale A.
800 for (Int j = 0; j < n; j++) {
801 for (Int p = Ap[j]; p < Ap[j+1]; p++) {
802 Ax[p] *= colmax[j]; // column scaling
803 Ax[p] *= rowmax[Ai[p]]; // row scaling
804 }
805 }
806 }
807 }
808
FindDenseColumns()809 void Model::FindDenseColumns() {
810 num_dense_cols_ = 0;
811 nz_dense_ = rows() + 1;
812
813 std::vector<Int> colcount(num_cols_);
814 for (Int j = 0; j < num_cols_; j++)
815 colcount[j] = AI_.end(j)-AI_.begin(j);
816 std::sort(colcount.begin(), colcount.end());
817
818 for (Int j = 1; j < num_cols_; j++) {
819 if (colcount[j] >
820 std::max((ipx::Int)40l, (ipx::Int)10l*colcount[j-1])) {
821 // j is the first dense column
822 num_dense_cols_ = num_cols_ - j;
823 nz_dense_ = colcount[j];
824 break;
825 }
826 }
827
828 if (num_dense_cols_ > 1000) {
829 num_dense_cols_ = 0;
830 nz_dense_ = rows() + 1;
831 }
832 }
833
PrintCoefficientRange(const Control & control) const834 void Model::PrintCoefficientRange(const Control& control) const {
835 double amin = INFINITY;
836 double amax = 0.0;
837 for (Int j = 0; j < A_.cols(); j++) {
838 for (Int p = A_.begin(j); p < A_.end(j); p++) {
839 double x = A_.value(p);
840 if (x != 0.0) {
841 amin = std::min(amin, std::abs(x));
842 amax = std::max(amax, std::abs(x));
843 }
844 }
845 }
846 if (amin == INFINITY) // no nonzero entries in A_
847 amin = 0.0;
848 control.Log()
849 << Textline("Matrix range:")
850 << "[" << Scientific(amin, 5, 0) << ", "
851 << Scientific(amax, 5, 0) << "]\n";
852
853 double rhsmin = INFINITY;
854 double rhsmax = 0.0;
855 for (double x : scaled_rhs_) {
856 if (x != 0.0) {
857 rhsmin = std::min(rhsmin, std::abs(x));
858 rhsmax = std::max(rhsmax, std::abs(x));
859 }
860 }
861 if (rhsmin == INFINITY) // no nonzero entries in rhs
862 rhsmin = 0.0;
863 control.Log()
864 << Textline("RHS range:")
865 << "[" << Scientific(rhsmin, 5, 0) << ", "
866 << Scientific(rhsmax, 5, 0) << "]\n";
867
868 double objmin = INFINITY;
869 double objmax = 0.0;
870 for (double x : scaled_obj_) {
871 if (x != 0.0) {
872 objmin = std::min(objmin, std::abs(x));
873 objmax = std::max(objmax, std::abs(x));
874 }
875 }
876 if (objmin == INFINITY) // no nonzero entries in obj
877 objmin = 0.0;
878 control.Log()
879 << Textline("Objective range:")
880 << "[" << Scientific(objmin, 5, 0) << ", "
881 << Scientific(objmax, 5, 0) << "]\n";
882
883 double boundmin = INFINITY;
884 double boundmax = 0.0;
885 for (double x : scaled_lbuser_) {
886 if (x != 0.0 && std::isfinite(x)) {
887 boundmin = std::min(boundmin, std::abs(x));
888 boundmax = std::max(boundmax, std::abs(x));
889 }
890 }
891 for (double x : scaled_ubuser_) {
892 if (x != 0.0 && std::isfinite(x)) {
893 boundmin = std::min(boundmin, std::abs(x));
894 boundmax = std::max(boundmax, std::abs(x));
895 }
896 }
897 if (boundmin == INFINITY) // no finite nonzeros entries in bounds
898 boundmin = 0.0;
899 control.Log()
900 << Textline("Bounds range:")
901 << "[" << Scientific(boundmin, 5, 0) << ", "
902 << Scientific(boundmax, 5, 0) << "]\n";
903 }
904
PrintPreprocessingLog(const Control & control) const905 void Model::PrintPreprocessingLog(const Control& control) const {
906 // Find the minimum and maximum scaling factor.
907 double minscale = INFINITY;
908 double maxscale = 0.0;
909 if (colscale_.size() > 0) {
910 auto minmax = std::minmax_element(std::begin(colscale_),
911 std::end(colscale_));
912 minscale = std::min(minscale, *minmax.first);
913 maxscale = std::max(maxscale, *minmax.second);
914 }
915 if (rowscale_.size() > 0) {
916 auto minmax = std::minmax_element(std::begin(rowscale_),
917 std::end(rowscale_));
918 minscale = std::min(minscale, *minmax.first);
919 maxscale = std::max(maxscale, *minmax.second);
920 }
921 if (minscale == INFINITY)
922 minscale = 1.0;
923 if (maxscale == 0.0)
924 maxscale = 1.0;
925
926 control.Log()
927 << "Preprocessing\n"
928 << Textline("Dualized model:") << (dualized() ? "yes" : "no") << '\n'
929 << Textline("Number of dense columns:") << num_dense_cols() << '\n';
930 if (control.scale() > 0) {
931 control.Log()
932 << Textline("Range of scaling factors:") << "["
933 << Scientific(minscale, 8, 2) << ", "
934 << Scientific(maxscale, 8, 2) << "]\n";
935 }
936 }
937
ScalePoint(Vector & x,Vector & slack,Vector & y,Vector & z) const938 void Model::ScalePoint(Vector& x, Vector& slack, Vector& y, Vector& z) const {
939 if (colscale_.size() > 0) {
940 x /= colscale_;
941 z *= colscale_;
942 }
943 if (rowscale_.size() > 0) {
944 y /= rowscale_;
945 slack *= rowscale_;
946 }
947 for (Int j : flipped_vars_) {
948 x[j] *= -1.0;
949 z[j] *= -1.0;
950 }
951 }
952
ScalePoint(Vector & x,Vector & xl,Vector & xu,Vector & slack,Vector & y,Vector & zl,Vector & zu) const953 void Model::ScalePoint(Vector& x, Vector& xl, Vector& xu, Vector& slack,
954 Vector& y, Vector& zl, Vector& zu) const {
955 if (colscale_.size() > 0) {
956 x /= colscale_;
957 xl /= colscale_;
958 xu /= colscale_;
959 zl *= colscale_;
960 zu *= colscale_;
961 }
962 if (rowscale_.size() > 0) {
963 y /= rowscale_;
964 slack *= rowscale_;
965 }
966 for (Int j : flipped_vars_) {
967 x[j] *= -1.0;
968 xl[j] = xu[j];
969 xu[j] = INFINITY;
970 zl[j] = zu[j];
971 zu[j] = 0.0;
972 }
973 }
974
ScaleBackInteriorSolution(Vector & x,Vector & xl,Vector & xu,Vector & slack,Vector & y,Vector & zl,Vector & zu) const975 void Model::ScaleBackInteriorSolution(Vector& x, Vector& xl, Vector& xu,
976 Vector& slack, Vector& y, Vector& zl,
977 Vector& zu) const {
978 if (colscale_.size() > 0) {
979 x *= colscale_;
980 xl *= colscale_;
981 xu *= colscale_;
982 zl /= colscale_;
983 zu /= colscale_;
984 }
985 if (rowscale_.size() > 0) {
986 y *= rowscale_;
987 slack /= rowscale_;
988 }
989 for (Int j : flipped_vars_) {
990 assert(std::isfinite(xl[j]));
991 assert(std::isinf(xu[j]));
992 assert(zu[j] == 0.0);
993 x[j] *= -1.0;
994 xu[j] = xl[j];
995 xl[j] = INFINITY;
996 zu[j] = zl[j];
997 zl[j] = 0.0;
998 }
999 }
1000
ScaleBackResiduals(Vector & rb,Vector & rc,Vector & rl,Vector & ru) const1001 void Model::ScaleBackResiduals(Vector& rb, Vector& rc, Vector& rl,
1002 Vector& ru) const {
1003 if (colscale_.size() > 0) {
1004 rc /= colscale_;
1005 rl *= colscale_;
1006 ru *= colscale_;
1007 }
1008 if (rowscale_.size() > 0)
1009 rb /= rowscale_;
1010 for (Int j : flipped_vars_) {
1011 rc[j] *= -1.0;
1012 assert(ru[j] == 0.0);
1013 ru[j] = -rl[j];
1014 rl[j] = 0.0;
1015 }
1016 }
1017
ScaleBackBasicSolution(Vector & x,Vector & slack,Vector & y,Vector & z) const1018 void Model::ScaleBackBasicSolution(Vector& x, Vector& slack, Vector& y,
1019 Vector& z) const {
1020 if (colscale_.size() > 0) {
1021 x *= colscale_;
1022 z /= colscale_;
1023 }
1024 if (rowscale_.size() > 0) {
1025 y *= rowscale_;
1026 slack /= rowscale_;
1027 }
1028 for (Int j : flipped_vars_) {
1029 x[j] *= -1.0;
1030 z[j] *= -1.0;
1031 }
1032 }
1033
ScaleBackBasis(std::vector<Int> & cbasis,std::vector<Int> & vbasis) const1034 void Model::ScaleBackBasis(std::vector<Int>& cbasis,
1035 std::vector<Int>& vbasis) const {
1036 for (Int j : flipped_vars_) {
1037 assert(vbasis[j] != IPX_nonbasic_ub);
1038 if (vbasis[j] == IPX_nonbasic_lb)
1039 vbasis[j] = IPX_nonbasic_ub;
1040 }
1041 }
1042
DualizeBasicSolution(const Vector & x_user,const Vector & slack_user,const Vector & y_user,const Vector & z_user,Vector & x_solver,Vector & y_solver,Vector & z_solver) const1043 void Model::DualizeBasicSolution(const Vector& x_user,
1044 const Vector& slack_user,
1045 const Vector& y_user,
1046 const Vector& z_user,
1047 Vector& x_solver,
1048 Vector& y_solver,
1049 Vector& z_solver) const {
1050 const Int m = rows();
1051 const Int n = cols();
1052
1053 if (dualized_) {
1054 assert(num_var_ == m);
1055 assert(num_constr_ + boxed_vars_.size() == n);
1056
1057 // Build dual solver variables from primal user variables.
1058 y_solver = -x_user;
1059 for (Int i = 0; i < num_constr_; i++)
1060 z_solver[i] = -slack_user[i];
1061 for (Int k = 0; k < boxed_vars_.size(); k++) {
1062 Int j = boxed_vars_[k];
1063 z_solver[num_constr_+k] = c(num_constr_+k) + y_solver[j];
1064 }
1065 for (Int i = 0; i < m; i++)
1066 z_solver[n+i] = c(n+i)-y_solver[i];
1067
1068 // Build primal solver variables from dual user variables.
1069 std::copy_n(std::begin(y_user), num_constr_, std::begin(x_solver));
1070 std::copy_n(std::begin(z_user), num_var_, std::begin(x_solver) + n);
1071 for (Int k = 0; k < boxed_vars_.size(); k++) {
1072 Int j = boxed_vars_[k];
1073 if (x_solver[n+j] < 0.0) {
1074 // j is a boxed variable and z_user[j] < 0
1075 x_solver[num_constr_+k] = -x_solver[n+j];
1076 x_solver[n+j] = 0.0;
1077 } else {
1078 x_solver[num_constr_+k] = 0.0;
1079 }
1080 }
1081 }
1082 else {
1083 assert(num_constr_ == m);
1084 assert(num_var_ == n);
1085 std::copy_n(std::begin(x_user), n, std::begin(x_solver));
1086 std::copy_n(std::begin(slack_user), m, std::begin(x_solver) + n);
1087 std::copy_n(std::begin(y_user), m, std::begin(y_solver));
1088 std::copy_n(std::begin(z_user), n, std::begin(z_solver));
1089 for (Int i = 0; i < m; i++)
1090 z_solver[n+i] = c(n+i)-y_solver[i];
1091 }
1092 }
1093
DualizeIPMStartingPoint(const Vector & x_user,const Vector & xl_user,const Vector & xu_user,const Vector & slack_user,const Vector & y_user,const Vector & zl_user,const Vector & zu_user,Vector & x_solver,Vector & xl_solver,Vector & xu_solver,Vector & y_solver,Vector & zl_solver,Vector & zu_solver) const1094 void Model::DualizeIPMStartingPoint(const Vector& x_user,
1095 const Vector& xl_user,
1096 const Vector& xu_user,
1097 const Vector& slack_user,
1098 const Vector& y_user,
1099 const Vector& zl_user,
1100 const Vector& zu_user,
1101 Vector& x_solver,
1102 Vector& xl_solver,
1103 Vector& xu_solver,
1104 Vector& y_solver,
1105 Vector& zl_solver,
1106 Vector& zu_solver) const {
1107 const Int m = rows();
1108 const Int n = cols();
1109
1110 if (dualized_) {
1111 // Not implemented.
1112 assert(false);
1113 }
1114 else {
1115 assert(num_constr_ == m);
1116 assert(num_var_ == n);
1117
1118 std::copy_n(std::begin(x_user), num_var_, std::begin(x_solver));
1119 std::copy_n(std::begin(slack_user), num_constr_,
1120 std::begin(x_solver) + n);
1121 std::copy_n(std::begin(xl_user), num_var_, std::begin(xl_solver));
1122 std::copy_n(std::begin(xu_user), num_var_, std::begin(xu_solver));
1123 std::copy_n(std::begin(y_user), num_constr_, std::begin(y_solver));
1124 std::copy_n(std::begin(zl_user), num_var_, std::begin(zl_solver));
1125 std::copy_n(std::begin(zu_user), num_var_, std::begin(zu_solver));
1126
1127 for (Int i = 0; i < m; i++) {
1128 switch (constr_type_[i]) {
1129 case '=':
1130 assert(lb_[n+i] == 0.0 && ub_[n+i] == 0.0);
1131 // For a fixed slack variable xl, xu, zl and zu won't be used
1132 // by the IPM. Just put them to zero.
1133 xl_solver[n+i] = 0.0;
1134 xu_solver[n+i] = 0.0;
1135 zl_solver[n+i] = 0.0;
1136 zu_solver[n+i] = 0.0;
1137 break;
1138 case '<':
1139 assert(lb_[n+i] == 0.0 && ub_[n+i] == INFINITY);
1140 xl_solver[n+i] = slack_user[i];
1141 xu_solver[n+i] = INFINITY;
1142 zl_solver[n+i] = -y_user[i];
1143 zu_solver[n+i] = 0.0;
1144 break;
1145 case '>':
1146 assert(lb_[n+i] == -INFINITY && ub_[n+i] == 0.0);
1147 xl_solver[n+i] = INFINITY;
1148 xu_solver[n+i] = -slack_user[i];
1149 zl_solver[n+i] = 0.0;
1150 zu_solver[n+i] = y_user[i];
1151 break;
1152 }
1153 }
1154 }
1155 }
1156
DualizeBackInteriorSolution(const Vector & x_solver,const Vector & xl_solver,const Vector & xu_solver,const Vector & y_solver,const Vector & zl_solver,const Vector & zu_solver,Vector & x_user,Vector & xl_user,Vector & xu_user,Vector & slack_user,Vector & y_user,Vector & zl_user,Vector & zu_user) const1157 void Model::DualizeBackInteriorSolution(const Vector& x_solver,
1158 const Vector& xl_solver,
1159 const Vector& xu_solver,
1160 const Vector& y_solver,
1161 const Vector& zl_solver,
1162 const Vector& zu_solver,
1163 Vector& x_user,
1164 Vector& xl_user,
1165 Vector& xu_user,
1166 Vector& slack_user,
1167 Vector& y_user,
1168 Vector& zl_user,
1169 Vector& zu_user) const {
1170 const Int m = rows();
1171 const Int n = cols();
1172
1173 if (dualized_) {
1174 assert(num_var_ == m);
1175 assert(num_constr_ + boxed_vars_.size() == n);
1176 x_user = -y_solver;
1177
1178 // If the solution from the solver would be exact, we could copy the
1179 // first num_constr_ entries from x_solver into y_user. However, to
1180 // satisfy the sign condition on y_user even if the solution is not
1181 // exact, we have to use the xl_solver and xu_solver entries for
1182 // inequality constraints.
1183 for (Int i = 0; i < num_constr_; i++) {
1184 switch (constr_type_[i]) {
1185 case '=':
1186 y_user[i] = x_solver[i];
1187 break;
1188 case '<':
1189 y_user[i] = -xu_solver[i];
1190 break;
1191 case '>':
1192 y_user[i] = xl_solver[i];
1193 break;
1194 }
1195 assert(std::isfinite(y_user[i]));
1196 }
1197
1198 // Dual variables associated with lbuser <= x in the scaled user model
1199 // are the slack variables from the solver. For an exact solution we
1200 // would have x_solver[n+1:n+m] == xl_solver[n+1:n+m]. Using xl_solver
1201 // guarantees that zl_user >= 0 in any case. If variable j has no lower
1202 // bound in the scaled user model (i.e. is free), then the j-th slack
1203 // variable was fixed at zero in the solver model, but the IPM solution
1204 // may not satisfy this. Hence we must set zl_user[j] = 0 explicitly.
1205 std::copy_n(std::begin(xl_solver) + n, num_var_, std::begin(zl_user));
1206 for (Int j = 0; j < num_var_; j++)
1207 if (!std::isfinite(scaled_lbuser_[j]))
1208 zl_user[j] = 0.0;
1209
1210 // Dual variables associated with x <= ubuser in the scaled user model
1211 // are the primal variables that were added for boxed variables in the
1212 // solver model.
1213 zu_user = 0.0;
1214 Int k = num_constr_;
1215 for (Int j : boxed_vars_)
1216 zu_user[j] = xl_solver[k++];
1217 assert(k == n);
1218
1219 // xl in the scaled user model is zl[n+1:n+m] in the solver model or
1220 // infinity.
1221 for (Int i = 0; i < m; i++) {
1222 if (std::isfinite(scaled_lbuser_[i]))
1223 xl_user[i] = zl_solver[n+i];
1224 else
1225 xl_user[i] = INFINITY;
1226 }
1227
1228 // xu in the scaled user model are the entries in zl for columns of the
1229 // negative identity matrix (that were added for boxed variables).
1230 xu_user = INFINITY;
1231 k = num_constr_;
1232 for (Int j : boxed_vars_)
1233 xu_user[j] = zl_solver[k++];
1234 assert(k == n);
1235
1236 for (Int i = 0; i < num_constr_; i++) {
1237 switch (constr_type_[i]) {
1238 case '=':
1239 slack_user[i] = 0.0;
1240 break;
1241 case '<':
1242 slack_user[i] = zu_solver[i];
1243 break;
1244 case '>':
1245 slack_user[i] = -zl_solver[i];
1246 break;
1247 }
1248 }
1249 }
1250 else {
1251 assert(num_constr_ == m);
1252 assert(num_var_ == n);
1253 std::copy_n(std::begin(x_solver), num_var_, std::begin(x_user));
1254
1255 // Instead of copying y_solver into y_user, we use the entries from
1256 // zl_solver and zu_solver for inequality constraints, so that the sign
1257 // condition on y_user is satisfied.
1258 for (Int i = 0; i < m; i++) {
1259 assert(lb_[n+i] == 0.0 || lb_[n+i] == -INFINITY);
1260 assert(ub_[n+i] == 0.0 || ub_[n+i] == INFINITY);
1261 assert(lb_[n+i] == 0.0 || ub_[n+i] == 0.0);
1262 switch (constr_type_[i]) {
1263 case '=':
1264 y_user[i] = y_solver[i];
1265 break;
1266 case '<':
1267 y_user[i] = -zl_solver[n+i];
1268 break;
1269 case '>':
1270 y_user[i] = zu_solver[n+i];
1271 break;
1272 }
1273 assert(std::isfinite(y_user[i]));
1274 }
1275 std::copy_n(std::begin(zl_solver), num_var_, std::begin(zl_user));
1276 std::copy_n(std::begin(zu_solver), num_var_, std::begin(zu_user));
1277 std::copy_n(std::begin(xl_solver), num_var_, std::begin(xl_user));
1278 std::copy_n(std::begin(xu_solver), num_var_, std::begin(xu_user));
1279
1280 // If the solution would be exact, slack_user were given by the entries
1281 // of x_solver corresponding to slack columns. To satisfy the sign
1282 // condition in any case, we build the slack for inequality constraints
1283 // from xl_solver and xu_solver and set the slack for equality
1284 // constraints to zero.
1285 for (Int i = 0; i < m; i++) {
1286 switch (constr_type_[i]) {
1287 case '=':
1288 slack_user[i] = 0.0;
1289 break;
1290 case '<':
1291 slack_user[i] = xl_solver[n+i];
1292 break;
1293 case '>':
1294 slack_user[i] = -xu_solver[n+i];
1295 break;
1296 }
1297 assert(std::isfinite(slack_user[i]));
1298 }
1299 }
1300 }
1301
DualizeBackBasicSolution(const Vector & x_solver,const Vector & y_solver,const Vector & z_solver,Vector & x_user,Vector & slack_user,Vector & y_user,Vector & z_user) const1302 void Model::DualizeBackBasicSolution(const Vector& x_solver,
1303 const Vector& y_solver,
1304 const Vector& z_solver,
1305 Vector& x_user,
1306 Vector& slack_user,
1307 Vector& y_user,
1308 Vector& z_user) const {
1309 const Int m = rows();
1310 const Int n = cols();
1311
1312 if (dualized_) {
1313 assert(num_var_ == m);
1314 assert(num_constr_ + boxed_vars_.size() == n);
1315 x_user = -y_solver;
1316 for (Int i = 0; i < num_constr_; i++)
1317 slack_user[i] = -z_solver[i];
1318 std::copy_n(std::begin(x_solver), num_constr_, std::begin(y_user));
1319 std::copy_n(std::begin(x_solver) + n, num_var_, std::begin(z_user));
1320 Int k = num_constr_;
1321 for (Int j : boxed_vars_)
1322 z_user[j] -= x_solver[k++];
1323 assert(k == n);
1324 }
1325 else {
1326 assert(num_constr_ == m);
1327 assert(num_var_ == n);
1328 std::copy_n(std::begin(x_solver), num_var_, std::begin(x_user));
1329 std::copy_n(std::begin(x_solver) + n, num_constr_,
1330 std::begin(slack_user));
1331 std::copy_n(std::begin(y_solver), num_constr_, std::begin(y_user));
1332 std::copy_n(std::begin(z_solver), num_var_, std::begin(z_user));
1333 }
1334 }
1335
DualizeBackBasis(const std::vector<Int> & basic_status_solver,std::vector<Int> & cbasis_user,std::vector<Int> & vbasis_user) const1336 void Model::DualizeBackBasis(const std::vector<Int>& basic_status_solver,
1337 std::vector<Int>& cbasis_user,
1338 std::vector<Int>& vbasis_user) const {
1339 const Int m = rows();
1340 const Int n = cols();
1341
1342 if (dualized_) {
1343 assert(num_var_ == m);
1344 assert(num_constr_ + boxed_vars_.size() == n);
1345 for (Int i = 0; i < num_constr_; i++) {
1346 if (basic_status_solver[i] == IPX_basic)
1347 cbasis_user[i] = IPX_nonbasic;
1348 else
1349 cbasis_user[i] = IPX_basic;
1350 }
1351 for (Int j = 0; j < num_var_; j++) {
1352 // slack cannot be superbasic
1353 assert(basic_status_solver[n+j] != IPX_superbasic);
1354 if (basic_status_solver[n+j] == 0)
1355 vbasis_user[j] = std::isfinite(scaled_lbuser_[j]) ?
1356 IPX_nonbasic_lb : IPX_superbasic;
1357 else
1358 vbasis_user[j] = IPX_basic;
1359 }
1360 Int k = num_constr_;
1361 for (Int j : boxed_vars_)
1362 if (basic_status_solver[k++] == IPX_basic) {
1363 assert(vbasis_user[j] == IPX_basic);
1364 vbasis_user[j] = IPX_nonbasic_ub;
1365 }
1366 }
1367 else {
1368 assert(num_constr_ == m);
1369 assert(num_var_ == n);
1370 for (Int i = 0; i < num_constr_; i++) {
1371 // slack cannot be superbasic
1372 assert(basic_status_solver[n+i] != IPX_superbasic);
1373 if (basic_status_solver[n+i] == IPX_basic)
1374 cbasis_user[i] = IPX_basic;
1375 else
1376 cbasis_user[i] = IPX_nonbasic;
1377 }
1378 for (Int j = 0; j < num_var_; j++)
1379 vbasis_user[j] = basic_status_solver[j];
1380 }
1381 }
1382
CorrectScaledBasicSolution(Vector & x,Vector & slack,Vector & y,Vector & z,const std::vector<Int> cbasis,const std::vector<Int> vbasis) const1383 void Model::CorrectScaledBasicSolution(Vector& x, Vector& slack, Vector& y,
1384 Vector& z,
1385 const std::vector<Int> cbasis,
1386 const std::vector<Int> vbasis) const {
1387 for (Int j = 0; j < num_var_; j++) {
1388 if (vbasis[j] == IPX_nonbasic_lb)
1389 x[j] = scaled_lbuser_[j];
1390 if (vbasis[j] == IPX_nonbasic_ub)
1391 x[j] = scaled_ubuser_[j];
1392 if (vbasis[j] == IPX_basic)
1393 z[j] = 0.0;
1394 }
1395 for (Int i = 0; i < num_constr_; i++) {
1396 if (cbasis[i] == IPX_nonbasic)
1397 slack[i] = 0.0;
1398 if (cbasis[i] == IPX_basic)
1399 y[i] = 0.0;
1400 }
1401 }
1402
MultiplyWithScaledMatrix(const Vector & rhs,double alpha,Vector & lhs,char trans) const1403 void Model::MultiplyWithScaledMatrix(const Vector& rhs, double alpha,
1404 Vector& lhs, char trans) const {
1405 if (trans == 't' || trans == 'T') {
1406 assert(rhs.size() == num_constr_);
1407 assert(lhs.size() == num_var_);
1408 if (dualized())
1409 for (Int i = 0; i < num_constr_; i++)
1410 ScatterColumn(AI_, i, alpha*rhs[i], lhs);
1411 else
1412 for (Int j = 0; j < num_var_; j++)
1413 lhs[j] += alpha * DotColumn(AI_, j, rhs);
1414 }
1415 else {
1416 assert(rhs.size() == num_var_);
1417 assert(lhs.size() == num_constr_);
1418 if (dualized())
1419 for (Int i = 0; i < num_constr_; i++)
1420 lhs[i] += alpha * DotColumn(AI_, i, rhs);
1421 else
1422 for (Int j = 0; j < num_var_; j++)
1423 ScatterColumn(AI_, j, alpha*rhs[j], lhs);
1424 }
1425 }
1426
PrimalInfeasibility(const Model & model,const Vector & x)1427 double PrimalInfeasibility(const Model& model, const Vector& x) {
1428 const Vector& lb = model.lb();
1429 const Vector& ub = model.ub();
1430 assert(x.size() == lb.size());
1431
1432 double infeas = 0.0;
1433 for (Int j = 0; j < x.size(); j++) {
1434 infeas = std::max(infeas, lb[j]-x[j]);
1435 infeas = std::max(infeas, x[j]-ub[j]);
1436 }
1437 return infeas;
1438 }
1439
DualInfeasibility(const Model & model,const Vector & x,const Vector & z)1440 double DualInfeasibility(const Model& model, const Vector& x,
1441 const Vector& z) {
1442 const Vector& lb = model.lb();
1443 const Vector& ub = model.ub();
1444 assert(x.size() == lb.size());
1445 assert(z.size() == lb.size());
1446
1447 double infeas = 0.0;
1448 for (Int j = 0; j < x.size(); j++) {
1449 if (x[j] > lb[j])
1450 infeas = std::max(infeas, z[j]);
1451 if (x[j] < ub[j])
1452 infeas = std::max(infeas, -z[j]);
1453 }
1454 return infeas;
1455 }
1456
PrimalResidual(const Model & model,const Vector & x)1457 double PrimalResidual(const Model& model, const Vector& x) {
1458 const SparseMatrix& AIt = model.AIt();
1459 const Vector& b = model.b();
1460 assert(x.size() == AIt.rows());
1461
1462 double res = 0.0;
1463 for (Int i = 0; i < b.size(); i++) {
1464 double r = b[i] - DotColumn(AIt, i, x);
1465 res = std::max(res, std::abs(r));
1466 }
1467 return res;
1468 }
1469
DualResidual(const Model & model,const Vector & y,const Vector & z)1470 double DualResidual(const Model& model, const Vector& y, const Vector& z) {
1471 const SparseMatrix& AI = model.AI();
1472 const Vector& c = model.c();
1473 assert(y.size() == AI.rows());
1474 assert(z.size() == AI.cols());
1475
1476 double res = 0.0;
1477 for (Int j = 0; j < c.size(); j++) {
1478 double r = c[j] - z[j] - DotColumn(AI, j, y);
1479 res = std::max(res, std::abs(r));
1480 }
1481 return res;
1482 }
1483
1484 } // namespace ipx
1485