test_simple_optimization_problem.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
26 
27 #include <corbo-core/console.h>
28 #include <corbo-core/macros.h>
29 #include <corbo-core/types.h>
30 #include <corbo-core/utilities.h>
33 
34 #include <array>
35 #include <functional>
36 
37 #include "gtest/gtest.h"
38 
41 
42 class TestStandardOptimizationProblem : public testing::Test
43 {
44  protected:
45  // You can do set-up work for each test here.
47  // You can do clean-up work that doesn't throw exceptions here.
49  // If the constructor and destructor are not enough for setting up
50  // and cleaning up each test, you can define the following methods:
51 
52  // Code here will be called immediately after the constructor (right
53  // before each test).
54  void SetUp() override {}
55  // Code here will be called immediately after each test (right
56  // before the destructor).
57  // virtual void TearDown()
58 
60 };
61 
63 {
64  Eigen::VectorXd x(1);
65  x[0] = 2;
66  optim.setX(x);
67 
68  // define objective function
69  // f = x^2 +1
70  auto objective = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) { values[0] = x[0] * x[0] + 1; };
71  optim.setObjectiveFunction(objective, 1, false);
72 
73  EXPECT_EQ(optim.getObjectiveDimension(), 1);
74  EXPECT_EQ(optim.getNonLsqObjectiveDimension(), 1);
75  EXPECT_EQ(optim.getLsqObjectiveDimension(), 0);
76 
77  // compute values
78  double value = optim.computeValueObjective();
79  EXPECT_EQ(value, 5) << "x*x+1 with x=2";
80 
81  value = optim.computeValueNonLsqObjective();
82  EXPECT_EQ(value, 5) << "x*x+1 with x=2";
83 
84  // compute gradient
85  Eigen::VectorXd gradient(optim.getParameterDimension());
86  optim.computeGradientObjective(gradient);
87 
88  EXPECT_NEAR(gradient(0), 4, 1e-5) << "2*x = 2*2 = 4";
89 
90  // compute hessian
91  Eigen::MatrixXd hessian(optim.getParameterDimension(), optim.getParameterDimension());
92  optim.computeDenseHessianObjective(hessian);
93 
94  EXPECT_NEAR(hessian(0, 0), 2, 1e-5);
95 }
96 
97 TEST_F(TestStandardOptimizationProblem, compute_objective_lsq)
98 {
99  Eigen::VectorXd x(1);
100  x[0] = 2;
101  optim.setX(x);
102 
103  // define lsq objective function
104  // f = (x-1)^2 + (6-x)^2
105  auto objective = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
106  values[0] = x[0] - 1;
107  values[1] = 6 - x[0];
108  };
109  optim.setObjectiveFunction(objective, 2, true);
110 
111  EXPECT_EQ(optim.getObjectiveDimension(), 1);
112  EXPECT_EQ(optim.getNonLsqObjectiveDimension(), 0);
113  EXPECT_EQ(optim.getLsqObjectiveDimension(), 2);
114 
115  // compute values
116  Eigen::Vector2d lsq_values;
117  optim.computeValuesLsqObjective(lsq_values);
118  EXPECT_EQ(lsq_values(0), 1) << "(2-1)";
119  EXPECT_EQ(lsq_values(1), 4) << "(6-2)";
120 
121  double value = optim.computeValueObjective();
122 
123  EXPECT_EQ(value, lsq_values.squaredNorm()) << "(2-1)^2 + (6-4)^2 = 1 + 16 = 17";
124 
125  // compute lsq jacobian
126  Eigen::MatrixXd lsq_jacob(optim.getLsqObjectiveDimension(), optim.getParameterDimension());
127  optim.computeDenseJacobianLsqObjective(lsq_jacob);
128  EXPECT_NEAR(lsq_jacob(0, 0), 1, 1e-5);
129  EXPECT_NEAR(lsq_jacob(1, 0), -1, 1e-5);
130 
131  // compute overall objective gradient
132  // df/dx = 2*(x-1) - 2*(6-x) = 2*1 - 2*4 = -6;
133  Eigen::VectorXd gradient(optim.getParameterDimension());
134  optim.computeGradientObjective(gradient);
135  EXPECT_NEAR(gradient(0), -6, 1e-5);
136 
137  // compute overall objective hessian
138  // df/dx^2 = 2 + 2 = 4
139  Eigen::MatrixXd hessian(optim.getParameterDimension(), optim.getParameterDimension());
140  optim.computeDenseHessianObjective(hessian);
141  EXPECT_NEAR(hessian(0, 0), 4, 1e-4);
142 }
143 
144 TEST_F(TestStandardOptimizationProblem, compute_objective_sparse_lsq)
145 {
146  Eigen::VectorXd x(1);
147  x[0] = 2;
148  optim.setX(x);
149 
150  // define lsq objective function
151  // f = (x-1)^2 + (6-x)^2
152  auto objective = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
153  values[0] = x[0] - 1;
154  values[1] = 6 - x[0];
155  };
156  optim.setObjectiveFunction(objective, 2, true);
157 
158  // compute sparse jacobian
159  int nnz = optim.computeSparseJacobianLsqObjectiveNNZ();
160  EXPECT_GE(nnz, 2);
161 
162  Eigen::VectorXi irow(nnz);
163  Eigen::VectorXi icol(nnz);
164  Eigen::VectorXd values(nnz);
165  optim.computeSparseJacobianLsqObjectiveStructure(irow, icol);
166  optim.computeSparseJacobianLsqObjectiveValues(values);
167 
168  // convert to Eigen type for convenient comparison
169  Eigen::SparseMatrix<double> jacob_sparse(optim.getLsqObjectiveDimension(), optim.getParameterDimension());
170  std::vector<Eigen::Triplet<double>> triplets;
171  corbo::convert_triplet(irow, icol, values, triplets);
172  if (!triplets.empty()) jacob_sparse.setFromTriplets(triplets.begin(), triplets.end());
173 
174  Eigen::MatrixXd jacob_sol(2, 1);
175  jacob_sol << 1, -1;
176 
177  EXPECT_EQ_MATRIX(jacob_sparse, jacob_sol, 1e-5);
178 
179  // compute sparse eigen jacobian directly
180  Eigen::SparseMatrix<double> jacob_sparse_eigen(optim.getLsqObjectiveDimension(), optim.getParameterDimension());
181  optim.computeSparseJacobianLsqObjective(jacob_sparse_eigen);
182  EXPECT_EQ_MATRIX(jacob_sparse_eigen, jacob_sol, 1e-5);
183 
184  // compute sparse hessian
185  int hess_nnz = optim.computeSparseHessianObjectiveNNZ();
186 
187  Eigen::VectorXi hess_irow(hess_nnz);
188  Eigen::VectorXi hess_icol(hess_nnz);
189  Eigen::VectorXd hess_values(hess_nnz);
190  optim.computeSparseHessianObjectiveStructure(hess_irow, hess_icol);
191  optim.computeSparseHessianObjectiveValues(hess_values);
192 
193  // convert to Eigen type for convenient comparison
194  triplets.clear();
195  Eigen::SparseMatrix<double> sparse_hessian(optim.getParameterDimension(), optim.getParameterDimension());
196  corbo::convert_triplet(hess_irow, hess_icol, hess_values, triplets);
197  if (!triplets.empty()) sparse_hessian.setFromTriplets(triplets.begin(), triplets.end());
198 
199  Eigen::MatrixXd hessian_sol(1, 1);
200  hessian_sol << 4;
201 
202  EXPECT_EQ_MATRIX(sparse_hessian, hessian_sol, 1e-4);
203 
204  // compute sparse eigen hessian directly
205  Eigen::SparseMatrix<double> sparse_hessian_eigen(optim.getParameterDimension(), optim.getParameterDimension());
206  optim.computeSparseHessianObjective(sparse_hessian_eigen);
207  EXPECT_EQ_MATRIX(sparse_hessian_eigen, hessian_sol, 1e-4);
208 }
209 
210 TEST_F(TestStandardOptimizationProblem, compute_equality_constr)
211 {
212  Eigen::VectorXd x(3);
213  x[0] = 2;
214  x[1] = 3;
215  x[2] = 4;
216  optim.setX(x);
217 
218  // define equality constraint function
219  // ceq = [x1^2+1; x2+x3-10]
220  auto ceq = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
221  values[0] = x[0] * x[0] + 1;
222  values[1] = x[1] + x[2] - 10;
223  };
224  optim.setEqualityConstraint(ceq, 2);
225 
226  // compute values
227  Eigen::VectorXd values(optim.getEqualityDimension());
228  optim.computeValuesEquality(values);
229 
230  EXPECT_EQ(values[0], 5) << "x*x+1 with x=2";
231  EXPECT_EQ(values[1], -3) << "3+4-10";
232 
233  // compute jacobian
234  Eigen::MatrixXd jacobian(optim.getEqualityDimension(), optim.getParameterDimension());
235  optim.computeDenseJacobianEqualities(jacobian, nullptr);
236 
237  Eigen::MatrixXd jacob_sol(2, 3);
238  jacob_sol << 4, 0, 0, 0, 1, 1;
239 
240  EXPECT_EQ_MATRIX(jacobian, jacob_sol, 1e-5);
241 
242  // compute hessian
243  Eigen::MatrixXd hessian(optim.getParameterDimension(), optim.getParameterDimension());
244  optim.computeDenseHessianEqualities(hessian, nullptr);
245 
246  Eigen::MatrixXd hessian_sol(3, 3);
247  hessian_sol << 2, 0, 0, 0, 0, 0, 0, 0, 0;
248 
249  EXPECT_EQ_MATRIX(hessian, hessian_sol, 1e-5);
250 }
251 
252 TEST_F(TestStandardOptimizationProblem, compute_equality_constr_sparse)
253 {
254  Eigen::VectorXd x(3);
255  x[0] = 2;
256  x[1] = 3;
257  x[2] = 4;
258  optim.setX(x);
259 
260  // define equality constraint
261  // ceq = [x1^2+1; x2+x3-10]
262  auto ceq = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
263  values[0] = x[0] * x[0] + 1;
264  values[1] = x[1] + x[2] - 10;
265  };
266  optim.setEqualityConstraint(ceq, 2);
267 
268  // compute sparse jacobian
269  int nnz = optim.computeSparseJacobianEqualitiesNNZ();
270  // This formulation does not support sparse matrices actually, so we check if default conversions are working
271  EXPECT_EQ(nnz, optim.getParameterDimension() * optim.getEqualityDimension());
272 
273  Eigen::VectorXi irow(nnz);
274  Eigen::VectorXi icol(nnz);
275  Eigen::VectorXd values(nnz);
276  optim.computeSparseJacobianEqualitiesStructure(irow, icol);
277  optim.computeSparseJacobianEqualitiesValues(values);
278 
279  // convert to Eigen type for convenient comparison
280  std::vector<Eigen::Triplet<double>> triplet_list;
281  for (int i = 0; i < nnz; ++i) triplet_list.emplace_back(irow[i], icol[i], values[i]);
282  Eigen::SparseMatrix<double> sparse_jacobian(optim.getEqualityDimension(), optim.getParameterDimension());
283  sparse_jacobian.setFromTriplets(triplet_list.begin(), triplet_list.end());
284 
285  Eigen::MatrixXd jacob_sol(2, 3);
286  jacob_sol << 4, 0, 0, 0, 1, 1;
287 
288  EXPECT_EQ_MATRIX(sparse_jacobian, jacob_sol, 1e-5);
289 
290  // compute sparse eigen jacobian directly
291  Eigen::SparseMatrix<double> sparse_jacob_eigen(optim.getEqualityDimension(), optim.getParameterDimension());
292  optim.computeSparseJacobianEqualities(sparse_jacob_eigen);
293  EXPECT_EQ_MATRIX(sparse_jacob_eigen, jacob_sol, 1e-5);
294 
295  // compute sparse hessian
296  int hess_nnz = optim.computeSparseHessianEqualitiesNNZ();
297 
298  Eigen::VectorXi hess_irow(hess_nnz);
299  Eigen::VectorXi hess_icol(hess_nnz);
300  Eigen::VectorXd hess_values(hess_nnz);
301  optim.computeSparseHessianEqualitiesStructure(hess_irow, hess_icol);
302  optim.computeSparseHessianEqualitiesValues(hess_values);
303 
304  // convert to Eigen type for convenient comparison
305  triplet_list.clear();
306  for (int i = 0; i < hess_nnz; ++i) triplet_list.emplace_back(hess_irow[i], hess_icol[i], hess_values[i]);
307  Eigen::SparseMatrix<double> sparse_hessian(optim.getParameterDimension(), optim.getParameterDimension());
308  sparse_hessian.setFromTriplets(triplet_list.begin(), triplet_list.end());
309 
310  Eigen::MatrixXd hessian_sol(3, 3);
311  hessian_sol << 2, 0, 0, 0, 0, 0, 0, 0, 0;
312 
313  EXPECT_EQ_MATRIX(sparse_hessian, hessian_sol, 1e-5);
314 
315  // compute sparse eigen hessian directly
316  Eigen::SparseMatrix<double> sparse_hessian_eigen(optim.getParameterDimension(), optim.getParameterDimension());
317  optim.computeSparseHessianEqualities(sparse_hessian_eigen);
318  EXPECT_EQ_MATRIX(sparse_hessian_eigen, hessian_sol, 1e-5);
319 }
320 
321 TEST_F(TestStandardOptimizationProblem, compute_inequality_constr)
322 {
323  Eigen::VectorXd x(3);
324  x[0] = 2;
325  x[1] = 3;
326  x[2] = -4;
327  optim.setX(x);
328 
329  // define inequality constraint function
330  // c = [x1^2+1; x2+x3-9]
331  auto c = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
332  values[0] = x[0] * x[0] + 1;
333  values[1] = x[1] + x[2] - 9;
334  };
335  optim.setInequalityConstraint(c, 2);
336 
337  // compute values
338  Eigen::VectorXd values(optim.getInequalityDimension());
339  optim.computeValuesInequality(values);
340 
341  EXPECT_EQ(values[0], 5) << "x*x+1 with x=2";
342  EXPECT_EQ(values[1], -10) << "3-4-9";
343 
344  // compute only active constraints max(0,c(x))
345  optim.computeValuesActiveInequality(values);
346  EXPECT_EQ(values[0], 5);
347  EXPECT_EQ(values[1], 0);
348 
349  // compute jacobian
350  Eigen::MatrixXd jacobian(optim.getInequalityDimension(), optim.getParameterDimension());
351  optim.computeDenseJacobianInequalities(jacobian, nullptr);
352 
353  Eigen::MatrixXd jacob_sol(2, 3);
354  jacob_sol << 4, 0, 0, 0, 1, 1;
355  EXPECT_EQ_MATRIX(jacobian, jacob_sol, 1e-5);
356 
357  // compute jacobian with zero-rows for inactive constraints
358  optim.computeDenseJacobianActiveInequalities(jacobian);
359 
360  Eigen::MatrixXd active_ineq_jacob_sol(2, 3);
361  active_ineq_jacob_sol << 4, 0, 0, 0, 0, 0;
362  EXPECT_EQ_MATRIX(jacobian, active_ineq_jacob_sol, 1e-5);
363 
364  // compute hessian
365  Eigen::MatrixXd hessian(optim.getParameterDimension(), optim.getParameterDimension());
366  optim.computeDenseHessianInequalities(hessian, nullptr);
367 
368  Eigen::MatrixXd hessian_sol(3, 3);
369  hessian_sol << 2, 0, 0, 0, 0, 0, 0, 0, 0;
370 
371  EXPECT_EQ_MATRIX(hessian, hessian_sol, 1e-5);
372 }
373 
374 TEST_F(TestStandardOptimizationProblem, compute_inequality_constr_sparse)
375 {
376  Eigen::VectorXd x(3);
377  x[0] = 2;
378  x[1] = 3;
379  x[2] = -4;
380  optim.setX(x);
381 
382  // define inequality constraint function
383  // c = [x1^2+1; x2+x3-9]
384  auto c = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
385  values[0] = x[0] * x[0] + 1;
386  values[1] = x[1] + x[2] - 9;
387  };
388  optim.setInequalityConstraint(c, 2);
389 
390  // compute sparse jacobian
391  int nnz = optim.computeSparseJacobianInequalitiesNNZ();
392  // This formulation does not support sparse matrices actually, so we check if default conversions are working
393  EXPECT_EQ(nnz, optim.getParameterDimension() * optim.getInequalityDimension());
394 
395  Eigen::VectorXi irow(nnz);
396  Eigen::VectorXi icol(nnz);
397  Eigen::VectorXd values(nnz);
398  optim.computeSparseJacobianInequalitiesStructure(irow, icol);
399  optim.computeSparseJacobianInequalitiesValues(values);
400 
401  // convert to Eigen type for convenient comparison
402  std::vector<Eigen::Triplet<double>> triplet_list;
403  for (int i = 0; i < nnz; ++i) triplet_list.emplace_back(irow[i], icol[i], values[i]);
404  Eigen::SparseMatrix<double> sparse_jacobian(optim.getInequalityDimension(), optim.getParameterDimension());
405  sparse_jacobian.setFromTriplets(triplet_list.begin(), triplet_list.end());
406 
407  Eigen::MatrixXd jacob_sol(2, 3);
408  jacob_sol << 4, 0, 0, 0, 1, 1;
409 
410  EXPECT_EQ_MATRIX(sparse_jacobian, jacob_sol, 1e-5);
411 
412  // compute sparse eigen matrix directly
413  Eigen::SparseMatrix<double> sparse_jacob_eigen(optim.getInequalityDimension(), optim.getParameterDimension());
414  optim.computeSparseJacobianInequalities(sparse_jacob_eigen);
415  EXPECT_EQ_MATRIX(sparse_jacob_eigen, jacob_sol, 1e-5);
416 
417  // compute jacobian with zero-rows for inactive constraints
418  optim.computeSparseJacobianActiveInequalitiesValues(values);
419  triplet_list.clear();
420  for (int i = 0; i < nnz; ++i) triplet_list.emplace_back(irow[i], icol[i], values[i]);
421  sparse_jacobian.setFromTriplets(triplet_list.begin(), triplet_list.end());
422 
423  Eigen::MatrixXd active_ineq_jacob_sol(2, 3);
424  active_ineq_jacob_sol << 4, 0, 0, 0, 0, 0;
425  EXPECT_EQ_MATRIX(sparse_jacobian, active_ineq_jacob_sol, 1e-5);
426 
427  // compute sparse eigen jacobian directly
428  optim.computeSparseJacobianActiveInequalities(sparse_jacob_eigen);
429  EXPECT_EQ_MATRIX(sparse_jacob_eigen, active_ineq_jacob_sol, 1e-5);
430 
431  // compute sparse hessian
432  int hess_nnz = optim.computeSparseHessianInequalitiesNNZ();
433 
434  Eigen::VectorXi hess_irow(hess_nnz);
435  Eigen::VectorXi hess_icol(hess_nnz);
436  Eigen::VectorXd hess_values(hess_nnz);
437  optim.computeSparseHessianInequalitiesStructure(hess_irow, hess_icol);
438  optim.computeSparseHessianInequalitiesValues(hess_values);
439 
440  // convert to Eigen type for convenient comparison
441  triplet_list.clear();
442  for (int i = 0; i < hess_nnz; ++i) triplet_list.emplace_back(hess_irow[i], hess_icol[i], hess_values[i]);
443  Eigen::SparseMatrix<double> sparse_hessian(optim.getParameterDimension(), optim.getParameterDimension());
444  sparse_hessian.setFromTriplets(triplet_list.begin(), triplet_list.end());
445 
446  Eigen::MatrixXd hessian_sol(3, 3);
447  hessian_sol << 2, 0, 0, 0, 0, 0, 0, 0, 0;
448 
449  EXPECT_EQ_MATRIX(sparse_hessian, hessian_sol, 1e-5);
450 
451  // compute sparse eigen hessian directly
452  Eigen::SparseMatrix<double> sparse_hessian_eigen(optim.getParameterDimension(), optim.getParameterDimension());
453  optim.computeSparseHessianInequalities(sparse_hessian_eigen);
454  EXPECT_EQ_MATRIX(sparse_hessian_eigen, hessian_sol, 1e-5);
455 }
456 
458 {
459  optim.resizeParameterVector(3);
460 
461  Eigen::VectorXd x(3);
462  x[0] = 2;
463  x[1] = 3;
464  x[2] = 4;
465  optim.setParameterVector(x);
466 
467  // set bounds
468  Eigen::VectorXd lb(3);
469  lb[0] = -CORBO_INF_DBL;
470  lb[1] = -2;
471  lb[2] = -CORBO_INF_DBL;
472  optim.setLowerBounds(lb);
473 
474  // optim.setUpperBound(0, CORBO_INF_DBL); //<- Should be initialized with CORBO_INF_DBL
475  optim.setUpperBound(1, 5);
476  optim.setUpperBound(2, 6);
477 
478  // check bounds
479  Eigen::VectorXd lb_return(optim.getParameterDimension());
480  Eigen::VectorXd ub_return(optim.getParameterDimension());
481  optim.getBounds(lb_return, ub_return);
482 
483  EXPECT_EQ_MATRIX(lb_return, lb, 0.01);
484  EXPECT_GE(ub_return[0], CORBO_INF_DBL);
485  EXPECT_EQ(ub_return[1], 5);
486  EXPECT_EQ(ub_return[2], 6);
487 
488  // check dimension of active bounds
489  EXPECT_EQ(optim.finiteBoundsDimension(), 3);
490  EXPECT_EQ(optim.finiteCombinedBoundsDimension(), 2);
491 
492  // Jacobian for bounds
493 
494  // Reduced Jacobian for active bounds only
495  Eigen::MatrixXd active_bound_jacobian(optim.finiteCombinedBoundsDimension(), optim.getParameterDimension());
496  optim.computeDenseJacobianFiniteCombinedBounds(active_bound_jacobian);
497 
498  EXPECT_ZERO_MATRIX(active_bound_jacobian, 1e-5);
499 
500  // now change parameters such that bounds are violated
501  optim.setParameterValue(1, -3); // violating bounds now
502  optim.setParameterValue(2, 6.01); // here as well
503  Eigen::MatrixXd active_bound_jacobian_sol(2, 3);
504  active_bound_jacobian_sol << 0, -1, 0, 0, 0, 1;
505 
506  optim.computeDenseJacobianFiniteCombinedBounds(active_bound_jacobian);
507  EXPECT_EQ_MATRIX(active_bound_jacobian, active_bound_jacobian_sol, 1e-5);
508 }
509 
511 {
512  optim.resizeParameterVector(3);
513 
514  Eigen::VectorXd x(3);
515  x[0] = 2;
516  x[1] = 3;
517  x[2] = 4;
518  optim.setParameterVector(x);
519 
520  // set bounds
521  Eigen::VectorXd lb(3);
522  lb[0] = -CORBO_INF_DBL;
523  lb[1] = -2;
524  lb[2] = -CORBO_INF_DBL;
525  optim.setLowerBounds(lb);
526 
527  // optim.setUpperBound(0, CORBO_INF_DBL); //<- Should be initialized with CORBO_INF_DBL
528  optim.setUpperBound(1, 5);
529  optim.setUpperBound(2, 6);
530 
531  // Reduced Jacobian for active bounds only
532  // compute sparse jacobian
533  int nnz = optim.computeSparseJacobianFiniteCombinedBoundsNNZ();
534 
535  Eigen::VectorXi irow(nnz);
536  Eigen::VectorXi icol(nnz);
537  Eigen::VectorXd values(nnz);
538  optim.computeSparseJacobianFiniteCombinedBoundsStructure(irow, icol);
539  optim.computeSparseJacobianFiniteCombinedBoundsValues(values);
540 
541  // convert to Eigen type for convenient comparison
542  std::vector<Eigen::Triplet<double>> triplet_list;
543  for (int i = 0; i < nnz; ++i) triplet_list.emplace_back(irow[i], icol[i], values[i]);
544  Eigen::SparseMatrix<double> sparse_jacobian(optim.finiteCombinedBoundsDimension(), optim.getParameterDimension());
545  sparse_jacobian.setFromTriplets(triplet_list.begin(), triplet_list.end());
546  EXPECT_ZERO_MATRIX(sparse_jacobian.toDense(), 1e-5);
547 
548  // compute sparse eigen matrix directly
549  Eigen::SparseMatrix<double> sparse_jacob_eigen(optim.finiteCombinedBoundsDimension(), optim.getParameterDimension());
550  optim.computeSparseJacobianFiniteCombinedBounds(sparse_jacob_eigen);
551  EXPECT_ZERO_MATRIX(sparse_jacobian.toDense(), 1e-5);
552 
553  // now change parameters such that bounds are violated
554  optim.setParameterValue(1, -3); // violating bounds now
555  optim.setParameterValue(2, 6.01); // here as well
556 
557  optim.computeSparseJacobianFiniteCombinedBoundsValues(values);
558 
559  triplet_list.clear();
560  for (int i = 0; i < nnz; ++i) triplet_list.emplace_back(irow[i], icol[i], values[i]);
561  sparse_jacobian.setFromTriplets(triplet_list.begin(), triplet_list.end());
562 
563  Eigen::MatrixXd active_bound_jacobian_sol(2, 3);
564  active_bound_jacobian_sol << 0, -1, 0, 0, 0, 1;
565 
566  EXPECT_EQ_MATRIX(sparse_jacobian, active_bound_jacobian_sol, 1e-5);
567 
568  // compute sparse eigen matrix directly
569  optim.computeSparseJacobianFiniteCombinedBounds(sparse_jacob_eigen);
570  EXPECT_EQ_MATRIX(sparse_jacob_eigen, active_bound_jacobian_sol, 1e-5);
571 }
572 
573 TEST_F(TestStandardOptimizationProblem, test_combined_jacobian_sparse)
574 {
575  optim.resizeParameterVector(3);
576 
577  Eigen::VectorXd x(3);
578  x[0] = 2;
579  x[1] = -3;
580  x[2] = 7;
581  optim.setParameterVector(x);
582 
583  // set bounds
584  Eigen::VectorXd lb(3);
585  lb[0] = -CORBO_INF_DBL;
586  lb[1] = -2;
587  lb[2] = -CORBO_INF_DBL;
588  optim.setLowerBounds(lb);
589 
590  // optim.setUpperBound(0, CORBO_INF_DBL); //<- Should be initialized with CORBO_INF_DBL
591  optim.setUpperBound(1, 5);
592  optim.setUpperBound(2, 6);
593 
594  // define objective function
595  // f = (x^2 +1)^2
596  auto objective = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) { values[0] = x[0] * x[0] + 1; };
597  optim.setObjectiveFunction(objective, 1, true);
598 
599  // define equality constraint function
600  // ceq = [x1^2+1; x2+x3-10]
601  auto ceq = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
602  values[0] = x[0] * x[0] + 1;
603  values[1] = x[1] + x[2] - 10;
604  };
605  optim.setEqualityConstraint(ceq, 2);
606 
607  // define inequality constraint function
608  // c = [x1^2+1; x2+x3-9]
609  auto c = [](const Eigen::VectorXd& x, Eigen::Ref<Eigen::VectorXd> values) {
610  values[0] = x[0] * x[0] + 1;
611  values[1] = x[1] + x[2] - 9;
612  };
613  optim.setInequalityConstraint(c, 2);
614 
615  int dim_obj_lsq = optim.getLsqObjectiveDimension();
616  int dim_eq = optim.getEqualityDimension();
617  int dim_ineq = optim.getInequalityDimension();
618  int dim_bounds = optim.finiteCombinedBoundsDimension();
619 
620  int dim_total = dim_obj_lsq + dim_eq + dim_ineq + dim_bounds;
621 
622  Eigen::SparseMatrix<double> jacobian(dim_total, optim.getParameterDimension());
623  optim.computeCombinedSparseJacobian(jacobian, true, true, true, true, true, 2, 3, 4);
624 
625  Eigen::MatrixXd sol1(7, 3);
626  sol1 << 4, 0, 0, 4, 0, 0, 0, 1, 1, 4, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 1;
627 
628  sol1.middleRows(dim_obj_lsq, dim_eq) *= 2;
629  sol1.middleRows(dim_obj_lsq + dim_eq, dim_ineq) *= 3;
630  sol1.middleRows(dim_obj_lsq + dim_eq + dim_ineq, dim_bounds) *= 4;
631  EXPECT_EQ_MATRIX(jacobian, sol1, 1e-5);
632 
633  optim.computeCombinedSparseJacobian(jacobian, true, true, true, true, false, 2, 3, 4);
634 
635  Eigen::MatrixXd sol2 = sol1;
636  sol2(4, 1) = 1 * 3;
637  sol2(4, 2) = 1 * 3;
638  EXPECT_EQ_MATRIX(jacobian, sol2, 1e-5);
639 
640  // test triplet version
641  Eigen::MatrixXd sol3(5, 3);
642  sol3 << 4, 0, 0, 4, 0, 0, 0, 1, 1, 4, 0, 0, 0, 1, 1;
643 
644  int jac_nnz = optim.computeCombinedSparseJacobiansNNZ();
645 
646  Eigen::VectorXi jac_irow(jac_nnz);
647  Eigen::VectorXi jac_icol(jac_nnz);
648  Eigen::VectorXd jac_values(jac_nnz);
649  optim.computeCombinedSparseJacobiansStructure(jac_irow, jac_icol);
650  optim.computeCombinedSparseJacobiansValues(jac_values);
651 
652  // convert to Eigen type for convenient comparison
653  std::vector<Eigen::Triplet<double>> triplet_list;
654  corbo::convert_triplet(jac_irow, jac_icol, jac_values, triplet_list);
655  Eigen::SparseMatrix<double> sparse_hessian(dim_obj_lsq + dim_eq + dim_ineq, optim.getParameterDimension());
656  sparse_hessian.setFromTriplets(triplet_list.begin(), triplet_list.end());
657  EXPECT_EQ_MATRIX(sparse_hessian, sol3, 1e-5);
658 
659  // now with multipliers
660  double multiplier_obj_lsq = 5;
661  Eigen::Vector2d multiplier_eq(2, 3);
662  Eigen::Vector2d multiplier_ineq(3, 4);
663 
664  sol3.row(0) *= multiplier_obj_lsq;
665  sol3.row(1) *= multiplier_eq(0);
666  sol3.row(2) *= multiplier_eq(1);
667  sol3.row(3) *= multiplier_ineq(0);
668  sol3.row(4) *= multiplier_ineq(1);
669 
670  jac_values.setZero();
671  optim.computeCombinedSparseJacobiansValues(jac_values, true, true, true, &multiplier_obj_lsq, multiplier_eq.data(), multiplier_ineq.data());
672  corbo::convert_triplet(jac_irow, jac_icol, jac_values, triplet_list);
673  sparse_hessian.setZero();
674  sparse_hessian.setFromTriplets(triplet_list.begin(), triplet_list.end());
675  EXPECT_EQ_MATRIX(sparse_hessian, sol3, 1e-5);
676 }
corbo::SimpleOptimizationProblemWithCallbacks
Simple optimization problem formulation (callback based configuration)
Definition: simple_optimization_problem.h:225
Eigen::SparseMatrix< double >
TestStandardOptimizationProblem
Definition: test_simple_optimization_problem.cpp:42
simple_optimization_problem.h
EXPECT_ZERO_MATRIX
#define EXPECT_ZERO_MATRIX(A, tol)
Definition: macros.h:75
macros.h
console.h
corbo::CORBO_INF_DBL
constexpr const double CORBO_INF_DBL
Representation for infinity (double version)
Definition: core/include/corbo-core/types.h:75
utilities.h
value_comparison.h
TestStandardOptimizationProblem::TestStandardOptimizationProblem
TestStandardOptimizationProblem()
Definition: test_simple_optimization_problem.cpp:46
misc.h
x
Scalar * x
Definition: level1_cplx_impl.h:89
TEST_F
TEST_F(TestStandardOptimizationProblem, compute_objective)
Definition: test_simple_optimization_problem.cpp:62
EXPECT_EQ_MATRIX
#define EXPECT_EQ_MATRIX(A, B, tol)
Definition: macros.h:61
Eigen::Ref
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:192
c
RealScalar c
Definition: level1_cplx_impl.h:103
Eigen::SparseMatrix::setFromTriplets
void setFromTriplets(const InputIterators &begin, const InputIterators &end)
Definition: SparseMatrix.h:993
types.h
TestStandardOptimizationProblem::optim
SimpleOptimizationProblemWithCallbacks optim
Definition: test_simple_optimization_problem.cpp:59
Eigen::SparseMatrix::setZero
void setZero()
Definition: SparseMatrix.h:251
TestStandardOptimizationProblem::~TestStandardOptimizationProblem
virtual ~TestStandardOptimizationProblem()
Definition: test_simple_optimization_problem.cpp:48
corbo::convert_triplet
void convert_triplet(const Eigen::Ref< const Eigen::VectorXi > &i_row, const Eigen::Ref< const Eigen::VectorXi > &j_col, const Eigen::Ref< const Eigen::VectorXd > &values, std::vector< Eigen::Triplet< double >> &triplets)
Definition: misc.cpp:51
TestStandardOptimizationProblem::SetUp
void SetUp() override
Definition: test_simple_optimization_problem.cpp:54


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Wed Mar 2 2022 00:07:06