GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: tests/eiquadprog-fast.cpp Lines: 261 261 100.0 %
Date: 2023-11-29 12:38:05 Branches: 676 1352 50.0 %

Line Branch Exec Source
1
//
2
// Copyright (c) 2019 CNRS
3
//
4
// This file is part of eiquadprog.
5
//
6
// eiquadprog is free software: you can redistribute it and/or modify
7
// it under the terms of the GNU Lesser General Public License as published by
8
// the Free Software Foundation, either version 3 of the License, or
9
//(at your option) any later version.
10
11
// eiquadprog is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
// GNU Lesser General Public License for more details.
15
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with eiquadprog.  If not, see <https://www.gnu.org/licenses/>.
18
19
#include "eiquadprog/eiquadprog-fast.hpp"
20
21
#include <Eigen/Core>
22
#include <boost/test/unit_test.hpp>
23
#include <iostream>
24
25
using namespace eiquadprog::solvers;
26
27
/**
28
 * solves the problem
29
 * min. 0.5 * x' Hess x + g0' x
30
 * s.t. CE x + ce0 = 0
31
 *      CI x + ci0 >= 0
32
 */
33
34
BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)
35
36
// min ||x||^2
37
38
















4
BOOST_AUTO_TEST_CASE(test_unbiased) {
39
4
  EiquadprogFast qp;
40
2
  qp.reset(2, 0, 0);
41
42
4
  Eigen::MatrixXd Q(2, 2);
43
2
  Q.setZero();
44
2
  Q(0, 0) = 1.0;
45
2
  Q(1, 1) = 1.0;
46
47
4
  Eigen::VectorXd C(2);
48
2
  C.setZero();
49
50
4
  Eigen::MatrixXd Aeq(0, 2);
51
52
4
  Eigen::VectorXd Beq(0);
53
54
4
  Eigen::MatrixXd Aineq(0, 2);
55
56
4
  Eigen::VectorXd Bineq(0);
57
58
4
  Eigen::VectorXd x(2);
59
60
4
  Eigen::VectorXd solution(2);
61
2
  solution.setZero();
62
63
2
  double val = 0.0;
64
65
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_OPTIMAL;
66
67
  EiquadprogFast_status status =
68
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
69
70


2
  BOOST_CHECK_EQUAL(status, expected);
71
72



2
  BOOST_CHECK_CLOSE(qp.getObjValue(), val, 1e-6);
73
74



2
  BOOST_CHECK(x.isApprox(solution));
75
2
}
76
77
// min ||x-x_0||^2, x_0 = (1 1)^T
78
79
















4
BOOST_AUTO_TEST_CASE(test_biased) {
80
4
  EiquadprogFast qp;
81
2
  qp.reset(2, 0, 0);
82
83
4
  Eigen::MatrixXd Q(2, 2);
84
2
  Q.setZero();
85
2
  Q(0, 0) = 1.0;
86
2
  Q(1, 1) = 1.0;
87
88
4
  Eigen::VectorXd C(2);
89
2
  C(0) = -1.;
90
2
  C(1) = -1.;
91
92
4
  Eigen::MatrixXd Aeq(0, 2);
93
94
4
  Eigen::VectorXd Beq(0);
95
96
4
  Eigen::MatrixXd Aineq(0, 2);
97
98
4
  Eigen::VectorXd Bineq(0);
99
100
4
  Eigen::VectorXd x(2);
101
102
4
  Eigen::VectorXd solution(2);
103
2
  solution(0) = 1.;
104
2
  solution(1) = 1.;
105
106
2
  double val = -1.;
107
108
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_OPTIMAL;
109
110
  EiquadprogFast_status status =
111
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
112
113


2
  BOOST_CHECK_EQUAL(status, expected);
114
115



2
  BOOST_CHECK_CLOSE(qp.getObjValue(), val, 1e-6);
116
117



2
  BOOST_CHECK(x.isApprox(solution));
118
2
}
119
120
// min ||x||^2
121
//    s.t.
122
// x[1] = 1 - x[0]
123
124
















4
BOOST_AUTO_TEST_CASE(test_equality_constraints) {
125
4
  EiquadprogFast qp;
126
2
  qp.reset(2, 1, 0);
127
128
4
  Eigen::MatrixXd Q(2, 2);
129
2
  Q.setZero();
130
2
  Q(0, 0) = 1.0;
131
2
  Q(1, 1) = 1.0;
132
133
4
  Eigen::VectorXd C(2);
134
2
  C.setZero();
135
136
4
  Eigen::MatrixXd Aeq(1, 2);
137
2
  Aeq(0, 0) = 1.;
138
2
  Aeq(0, 1) = 1.;
139
140
4
  Eigen::VectorXd Beq(1);
141
2
  Beq(0) = -1.;
142
143
4
  Eigen::MatrixXd Aineq(0, 2);
144
145
4
  Eigen::VectorXd Bineq(0);
146
147
4
  Eigen::VectorXd x(2);
148
149
4
  Eigen::VectorXd solution(2);
150
2
  solution(0) = 0.5;
151
2
  solution(1) = 0.5;
152
153
2
  double val = 0.25;
154
155
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_OPTIMAL;
156
157
  EiquadprogFast_status status =
158
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
159
160


2
  BOOST_CHECK_EQUAL(status, expected);
161
162



2
  BOOST_CHECK_CLOSE(qp.getObjValue(), val, 1e-6);
163
164



2
  BOOST_CHECK(x.isApprox(solution));
165
2
}
166
167
// min ||x||^2
168
//    s.t.
169
// x[i] >= 1
170
171
















4
BOOST_AUTO_TEST_CASE(test_inequality_constraints) {
172
4
  EiquadprogFast qp;
173
2
  qp.reset(2, 0, 2);
174
175
4
  Eigen::MatrixXd Q(2, 2);
176
2
  Q.setZero();
177
2
  Q(0, 0) = 1.0;
178
2
  Q(1, 1) = 1.0;
179
180
4
  Eigen::VectorXd C(2);
181
2
  C.setZero();
182
183
4
  Eigen::MatrixXd Aeq(0, 2);
184
185
4
  Eigen::VectorXd Beq(0);
186
187
4
  Eigen::MatrixXd Aineq(2, 2);
188
2
  Aineq.setZero();
189
2
  Aineq(0, 0) = 1.;
190
2
  Aineq(1, 1) = 1.;
191
192
4
  Eigen::VectorXd Bineq(2);
193
2
  Bineq(0) = -1.;
194
2
  Bineq(1) = -1.;
195
196
4
  Eigen::VectorXd x(2);
197
198
4
  Eigen::VectorXd solution(2);
199
2
  solution(0) = 1.;
200
2
  solution(1) = 1.;
201
202
2
  double val = 1.;
203
204
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_OPTIMAL;
205
206
  EiquadprogFast_status status =
207
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
208
209


2
  BOOST_CHECK_EQUAL(status, expected);
210
211



2
  BOOST_CHECK_CLOSE(qp.getObjValue(), val, 1e-6);
212
213



2
  BOOST_CHECK(x.isApprox(solution));
214
2
}
215
216
// min ||x-x_0||^2, x_0 = (1 1)^T
217
//    s.t.
218
// x[1] = 5 - x[0]
219
// x[1] >= 3
220
221
















4
BOOST_AUTO_TEST_CASE(test_full) {
222
4
  EiquadprogFast qp;
223
2
  qp.reset(2, 1, 1);
224
225
4
  Eigen::MatrixXd Q(2, 2);
226
2
  Q.setZero();
227
2
  Q(0, 0) = 1.0;
228
2
  Q(1, 1) = 1.0;
229
230
4
  Eigen::VectorXd C(2);
231
2
  C(0) = -1.;
232
2
  C(1) = -1.;
233
234
4
  Eigen::MatrixXd Aeq(1, 2);
235
2
  Aeq(0, 0) = 1.;
236
2
  Aeq(0, 1) = 1.;
237
238
4
  Eigen::VectorXd Beq(1);
239
2
  Beq(0) = -5.;
240
241
4
  Eigen::MatrixXd Aineq(1, 2);
242
2
  Aineq.setZero();
243
2
  Aineq(0, 1) = 1.;
244
245
4
  Eigen::VectorXd Bineq(1);
246
2
  Bineq(0) = -3.;
247
248
4
  Eigen::VectorXd x(2);
249
250
4
  Eigen::VectorXd solution(2);
251
2
  solution(0) = 2.;
252
2
  solution(1) = 3.;
253
254
2
  double val = 1.5;
255
256
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_OPTIMAL;
257
258
  EiquadprogFast_status status =
259
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
260
261


2
  BOOST_CHECK_EQUAL(status, expected);
262
263



2
  BOOST_CHECK_CLOSE(qp.getObjValue(), val, 1e-6);
264
265



2
  BOOST_CHECK(x.isApprox(solution));
266
2
}
267
268
// min ||x||^2
269
//    s.t.
270
// x[0] =  1
271
// x[0] = -1
272
273
















4
BOOST_AUTO_TEST_CASE(test_unfeasible_equalities) {
274
4
  EiquadprogFast qp;
275
2
  qp.reset(2, 2, 0);
276
277
4
  Eigen::MatrixXd Q(2, 2);
278
2
  Q.setZero();
279
2
  Q(0, 0) = 1.0;
280
2
  Q(1, 1) = 1.0;
281
282
4
  Eigen::VectorXd C(2);
283
2
  C.setZero();
284
285
4
  Eigen::MatrixXd Aeq(2, 2);
286
2
  Aeq.setZero();
287
2
  Aeq(0, 0) = 1.;
288
2
  Aeq(1, 0) = 1.;
289
290
4
  Eigen::VectorXd Beq(2);
291
2
  Beq(0) = -1.;
292
2
  Beq(1) = 1.;
293
294
4
  Eigen::MatrixXd Aineq(0, 2);
295
296
4
  Eigen::VectorXd Bineq(0);
297
298
4
  Eigen::VectorXd x(2);
299
300
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_REDUNDANT_EQUALITIES;
301
302
  EiquadprogFast_status status =
303
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
304
305


2
  BOOST_CHECK_EQUAL(status, expected);
306
2
}
307
308
// min ||x||^2
309
//    s.t.
310
// x[0] >=  1
311
// x[0] <= -1
312
//
313
// correctly fails, but returns wrong error code
314
315
















4
BOOST_AUTO_TEST_CASE(test_unfeasible_inequalities) {
316
4
  EiquadprogFast qp;
317
2
  qp.reset(2, 0, 2);
318
319
4
  Eigen::MatrixXd Q(2, 2);
320
2
  Q.setZero();
321
2
  Q(0, 0) = 1.0;
322
2
  Q(1, 1) = 1.0;
323
324
4
  Eigen::VectorXd C(2);
325
2
  C.setZero();
326
327
4
  Eigen::MatrixXd Aeq(0, 2);
328
329
4
  Eigen::VectorXd Beq(0);
330
331
4
  Eigen::MatrixXd Aineq(2, 2);
332
2
  Aineq.setZero();
333
2
  Aineq(0, 0) = 1.;
334
2
  Aineq(1, 0) = -1.;
335
336
4
  Eigen::VectorXd Bineq(2);
337
2
  Bineq(0) = -1;
338
2
  Bineq(1) = -1;
339
340
4
  Eigen::VectorXd x(2);
341
342
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_INFEASIBLE;
343
344
  EiquadprogFast_status status =
345
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
346
347


2
  BOOST_WARN_EQUAL(status, expected);
348



2
  BOOST_CHECK(status != EIQUADPROG_FAST_OPTIMAL);
349
2
}
350
351
// min ||x-x_0||^2, x_0 = (1 1)^T
352
//    s.t.
353
// x[1] = 1 - x[0]
354
// x[0] <= 0
355
// x[1] <= 0
356
//
357
// correctly fails, but returns wrong error code
358
359
















4
BOOST_AUTO_TEST_CASE(test_unfeasible_constraints) {
360
4
  EiquadprogFast qp;
361
2
  qp.reset(2, 1, 2);
362
363
4
  Eigen::MatrixXd Q(2, 2);
364
2
  Q.setZero();
365
2
  Q(0, 0) = 1.0;
366
2
  Q(1, 1) = 1.0;
367
368
4
  Eigen::VectorXd C(2);
369
2
  C(0) = -1.;
370
2
  C(1) = -1.;
371
372
4
  Eigen::MatrixXd Aeq(1, 2);
373
2
  Aeq(0, 0) = 1.;
374
2
  Aeq(0, 1) = 1.;
375
376
4
  Eigen::VectorXd Beq(1);
377
2
  Beq(0) = -1.;
378
379
4
  Eigen::MatrixXd Aineq(2, 2);
380
2
  Aineq.setZero();
381
2
  Aineq(0, 0) = -1.;
382
2
  Aineq(1, 1) = -1.;
383
384
4
  Eigen::VectorXd Bineq(2);
385
2
  Bineq.setZero();
386
387
4
  Eigen::VectorXd x(2);
388
389
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_INFEASIBLE;
390
391
  EiquadprogFast_status status =
392
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
393
394


2
  BOOST_WARN_EQUAL(status, expected);
395



2
  BOOST_CHECK(status != EIQUADPROG_FAST_OPTIMAL);
396
2
}
397
398
// min -||x||^2
399
// DOES NOT WORK!
400
401
















4
BOOST_AUTO_TEST_CASE(test_unbounded) {
402
4
  EiquadprogFast qp;
403
2
  qp.reset(2, 0, 0);
404
405
4
  Eigen::MatrixXd Q(2, 2);
406
2
  Q.setZero();
407
2
  Q(0, 0) = -1.0;
408
2
  Q(1, 1) = -1.0;
409
410
4
  Eigen::VectorXd C(2);
411
2
  C.setZero();
412
413
4
  Eigen::MatrixXd Aeq(0, 2);
414
415
4
  Eigen::VectorXd Beq(0);
416
417
4
  Eigen::MatrixXd Aineq(0, 2);
418
419
4
  Eigen::VectorXd Bineq(0);
420
421
4
  Eigen::VectorXd x(2);
422
423
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_UNBOUNDED;
424
425
  EiquadprogFast_status status =
426
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
427
428


2
  BOOST_WARN_EQUAL(status, expected);
429



2
  BOOST_WARN(status != EIQUADPROG_FAST_OPTIMAL);  // SHOULD pass!
430
2
}
431
432
// min -||x||^2
433
//    s.t.
434
// 0<= x[0] <= 1
435
// 0<= x[1] <= 1
436
// DOES NOT WORK!
437
438
















4
BOOST_AUTO_TEST_CASE(test_nonconvex) {
439
4
  EiquadprogFast qp;
440
2
  qp.reset(2, 0, 4);
441
442
4
  Eigen::MatrixXd Q(2, 2);
443
2
  Q.setZero();
444
2
  Q(0, 0) = -1.0;
445
2
  Q(1, 1) = -1.0;
446
447
4
  Eigen::VectorXd C(2);
448
2
  C.setZero();
449
450
4
  Eigen::MatrixXd Aeq(0, 2);
451
452
4
  Eigen::VectorXd Beq(0);
453
454
4
  Eigen::MatrixXd Aineq(4, 2);
455
2
  Aineq.setZero();
456
2
  Aineq(0, 0) = 1.;
457
2
  Aineq(1, 0) = -1.;
458
2
  Aineq(2, 1) = 1.;
459
2
  Aineq(3, 1) = -1.;
460
461
4
  Eigen::VectorXd Bineq(4);
462
2
  Bineq(0) = 0.;
463
2
  Bineq(1) = 1.;
464
2
  Bineq(2) = 0.;
465
2
  Bineq(3) = 1.;
466
467
4
  Eigen::VectorXd x(2);
468
469
4
  Eigen::VectorXd solution(2);
470
2
  solution(0) = 1.;
471
2
  solution(1) = 1.;
472
473
2
  double val = -1.;
474
475
2
  EiquadprogFast_status expected = EIQUADPROG_FAST_OPTIMAL;
476
477
  EiquadprogFast_status status =
478
2
      qp.solve_quadprog(Q, C, Aeq, Beq, Aineq, Bineq, x);
479
480


2
  BOOST_CHECK_EQUAL(status, expected);
481
482



2
  BOOST_WARN_CLOSE(qp.getObjValue(), val, 1e-6);
483
484



2
  BOOST_WARN(x.isApprox(solution));
485
2
}
486
487
BOOST_AUTO_TEST_SUITE_END()