GCC Code Coverage Report


Directory: ./
File: src/solvers/solver-HQP-eiquadprog.cpp
Date: 2024-08-26 20:29:39
Exec Total Coverage
Lines: 0 114 0.0%
Branches: 0 354 0.0%

Line Branch Exec Source
1 //
2 // Copyright (c) 2017 CNRS
3 //
4 // This file is part of tsid
5 // tsid is free software: you can redistribute it
6 // and/or modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation, either version
8 // 3 of the License, or (at your option) any later version.
9 // tsid is distributed in the hope that it will be
10 // useful, but WITHOUT ANY WARRANTY; without even the implied warranty
11 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Lesser Public License for more details. You should have
13 // received a copy of the GNU Lesser General Public License along with
14 // tsid If not, see
15 // <http://www.gnu.org/licenses/>.
16 //
17
18 #include "tsid/solvers/solver-HQP-eiquadprog.hpp"
19 #include "tsid/math/utils.hpp"
20 #include "eiquadprog/eiquadprog.hpp"
21 #include "tsid/utils/stop-watch.hpp"
22
23 using namespace tsid::math;
24 using namespace tsid::solvers;
25 using namespace Eigen;
26
27 SolverHQuadProg::SolverHQuadProg(const std::string& name)
28 : SolverHQPBase(name),
29 m_hessian_regularization(DEFAULT_HESSIAN_REGULARIZATION) {
30 m_n = 0;
31 m_neq = 0;
32 m_nin = 0;
33 }
34
35 void SolverHQuadProg::sendMsg(const std::string& s) {
36 std::cout << "[SolverHQuadProg." << m_name << "] " << s << std::endl;
37 }
38
39 void SolverHQuadProg::resize(unsigned int n, unsigned int neq,
40 unsigned int nin) {
41 const bool resizeVar = n != m_n;
42 const bool resizeEq = (resizeVar || neq != m_neq);
43 const bool resizeIn = (resizeVar || nin != m_nin);
44
45 if (resizeEq) {
46 #ifndef NDEBUG
47 sendMsg("Resizing equality constraints from " + toString(m_neq) + " to " +
48 toString(neq));
49 #endif
50 m_qpData.CE.resize(neq, n);
51 m_qpData.ce0.resize(neq);
52 }
53 if (resizeIn) {
54 #ifndef NDEBUG
55 sendMsg("Resizing inequality constraints from " + toString(m_nin) + " to " +
56 toString(nin));
57 #endif
58 m_qpData.CI.resize(2 * nin, n);
59 m_qpData.ci0.resize(2 * nin);
60 }
61 if (resizeVar) {
62 #ifndef NDEBUG
63 sendMsg("Resizing Hessian from " + toString(m_n) + " to " + toString(n));
64 #endif
65 m_qpData.H.resize(n, n);
66 m_qpData.g.resize(n);
67 m_output.x.resize(n);
68 }
69
70 m_n = n;
71 m_neq = neq;
72 m_nin = nin;
73 }
74
75 void SolverHQuadProg::retrieveQPData(const HQPData& problemData,
76 const bool /*hessianRegularization*/) {
77 if (problemData.size() > 2) {
78 PINOCCHIO_CHECK_INPUT_ARGUMENT(
79 false, "Solver not implemented for more than 2 hierarchical levels.");
80 }
81
82 // Compute the constraint matrix sizes
83 unsigned int neq = 0, nin = 0;
84 const ConstraintLevel& cl0 = problemData[0];
85 if (cl0.size() > 0) {
86 const unsigned int n = cl0[0].second->cols();
87 for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
88 it++) {
89 auto constr = it->second;
90 assert(n == constr->cols());
91 if (constr->isEquality())
92 neq += constr->rows();
93 else
94 nin += constr->rows();
95 }
96 // If necessary, resize the constraint matrices
97 resize(n, neq, nin);
98
99 int i_eq = 0, i_in = 0;
100 for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
101 it++) {
102 auto constr = it->second;
103 if (constr->isEquality()) {
104 m_qpData.CE.middleRows(i_eq, constr->rows()) = constr->matrix();
105 m_qpData.ce0.segment(i_eq, constr->rows()) = -constr->vector();
106 i_eq += constr->rows();
107 } else if (constr->isInequality()) {
108 m_qpData.CI.middleRows(i_in, constr->rows()) = constr->matrix();
109 m_qpData.ci0.segment(i_in, constr->rows()) = -constr->lowerBound();
110 i_in += constr->rows();
111 m_qpData.CI.middleRows(i_in, constr->rows()) = -constr->matrix();
112 m_qpData.ci0.segment(i_in, constr->rows()) = constr->upperBound();
113 i_in += constr->rows();
114 } else if (constr->isBound()) {
115 m_qpData.CI.middleRows(i_in, constr->rows()).setIdentity();
116 m_qpData.ci0.segment(i_in, constr->rows()) = -constr->lowerBound();
117 i_in += constr->rows();
118 m_qpData.CI.middleRows(i_in, constr->rows()) =
119 -Matrix::Identity(m_n, m_n);
120 m_qpData.ci0.segment(i_in, constr->rows()) = constr->upperBound();
121 i_in += constr->rows();
122 }
123 }
124 } else
125 resize(m_n, neq, nin);
126
127 if (problemData.size() > 1) {
128 const ConstraintLevel& cl1 = problemData[1];
129 m_qpData.H.setZero();
130 m_qpData.g.setZero();
131 for (ConstraintLevel::const_iterator it = cl1.begin(); it != cl1.end();
132 it++) {
133 const double& w = it->first;
134 auto constr = it->second;
135 if (!constr->isEquality())
136 PINOCCHIO_CHECK_INPUT_ARGUMENT(
137 false, "Inequalities in the cost function are not implemented yet");
138
139 m_qpData.H += w * constr->matrix().transpose() * constr->matrix();
140 m_qpData.g -= w * (constr->matrix().transpose() * constr->vector());
141 }
142 m_qpData.H.diagonal() += m_hessian_regularization * Vector::Ones(m_n);
143 }
144
145 #ifdef ELIMINATE_EQUALITY_CONSTRAINTS
146
147 // eliminate equality constraints
148 const int r = m_neq;
149 Matrix Z(m_n, m_n - m_neq);
150 Matrix ZT(m_n, m_n);
151 m_ZT_H_Z.resize(m_n - r, m_n - r);
152
153 START_PROFILER("Eiquadprog eliminate equalities");
154 if (m_neq > 0) {
155 START_PROFILER("Eiquadprog CE decomposition");
156 // m_qpData.CE_dec.compute(m_qpData.CE, ComputeThinU | ComputeThinV);
157 m_qpData.CE_dec.compute(m_qpData.CE);
158 STOP_PROFILER("Eiquadprog CE decomposition");
159
160 START_PROFILER("Eiquadprog get CE null-space basis");
161 // get nullspace basis from SVD
162 // const int r = m_qpData.CE_dec.nonzeroSingularValues();
163 // const Matrix Z = m_qpData.CE_dec.matrixV().rightCols(m_n-r);
164
165 // get null space basis from ColPivHouseholderQR
166 // Matrix Z = m_qpData.CE_dec.householderQ();
167 // Z = Z.rightCols(m_n-r);
168
169 // get null space basis from COD
170 // P^{-1} * y => colsPermutation() * y;
171 // Z = m_qpData.CE_dec.matrixZ(); // * m_qpData.CE_dec.colsPermutation();
172 ZT.setIdentity();
173 // m_qpData.CE_dec.applyZAdjointOnTheLeftInPlace(ZT);
174 typedef tsid::math::Index Index;
175 const Index rank = m_qpData.CE_dec.rank();
176 Vector temp(m_n);
177 for (Index k = 0; k < rank; ++k) {
178 if (k != rank - 1) ZT.row(k).swap(ZT.row(rank - 1));
179 ZT.middleRows(rank - 1, m_n - rank + 1)
180 .applyHouseholderOnTheLeft(
181 m_qpData.CE_dec.matrixQTZ().row(k).tail(m_n - rank).adjoint(),
182 m_qpData.CE_dec.zCoeffs()(k), &temp(0));
183 if (k != rank - 1) ZT.row(k).swap(ZT.row(rank - 1));
184 }
185 STOP_PROFILER("Eiquadprog get CE null-space basis");
186
187 // find a solution for the equalities
188 Vector x0 = m_qpData.CE_dec.solve(m_qpData.ce0);
189 x0 = -x0;
190
191 // START_PROFILER("Eiquadprog project Hessian full");
192 // m_ZT_H_Z.noalias() = Z.transpose()*m_qpData.H*Z; // this is too slow
193 // STOP_PROFILER("Eiquadprog project Hessian full");
194
195 START_PROFILER("Eiquadprog project Hessian incremental");
196 const ConstraintLevel& cl1 = problemData[1];
197 m_ZT_H_Z.setZero();
198 // m_qpData.g.setZero();
199 Matrix AZ;
200 for (ConstraintLevel::const_iterator it = cl1.begin(); it != cl1.end();
201 it++) {
202 const double& w = it->first;
203 auto constr = it->second;
204 if (!constr->isEquality())
205 PINOCCHIO_CHECK_INPUT_ARGUMENT(
206 false, "Inequalities in the cost function are not implemented yet");
207
208 AZ.noalias() = constr->matrix() * Z.rightCols(m_n - r);
209 m_ZT_H_Z += w * AZ.transpose() * AZ;
210 // m_qpData.g -= w*(constr->matrix().transpose()*constr->vector());
211 }
212 // m_ZT_H_Z.diagonal() += 1e-8*Vector::Ones(m_n);
213 m_qpData.CI_Z.noalias() = m_qpData.CI * Z.rightCols(m_n - r);
214 STOP_PROFILER("Eiquadprog project Hessian incremental");
215 }
216 STOP_PROFILER("Eiquadprog eliminate equalities");
217 #endif
218 }
219
220 const HQPOutput& SolverHQuadProg::solve(const HQPData& problemData) {
221 // #ifndef NDEBUG
222 // PRINT_MATRIX(m_qpData.H);
223 // PRINT_VECTOR(m_qpData.g);
224 // PRINT_MATRIX(m_qpData.CE);
225 // PRINT_VECTOR(m_qpData.ce0);
226 // PRINT_MATRIX(m_qpData.CI);
227 // PRINT_VECTOR(m_qpData.ci0);
228 // #endif
229 SolverHQuadProg::retrieveQPData(problemData);
230
231 // min 0.5 * x G x + g0 x
232 // s.t.
233 // CE^T x + ce0 = 0
234 // CI^T x + ci0 >= 0
235 m_objValue = eiquadprog::solvers::solve_quadprog(
236 m_qpData.H, m_qpData.g, m_qpData.CE.transpose(), m_qpData.ce0,
237 m_qpData.CI.transpose(), m_qpData.ci0, m_output.x, m_activeSet,
238 m_activeSetSize);
239
240 if (m_objValue == std::numeric_limits<double>::infinity())
241 m_output.status = HQP_STATUS_INFEASIBLE;
242 else {
243 m_output.status = HQP_STATUS_OPTIMAL;
244 #ifndef NDEBUG
245 const Vector& x = m_output.x;
246 const ConstraintLevel& cl0 = problemData[0];
247 if (cl0.size() > 0) {
248 for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
249 it++) {
250 auto constr = it->second;
251 if (constr->checkConstraint(x) == false) {
252 if (constr->isEquality()) {
253 sendMsg("Equality " + constr->name() + " violated: " +
254 toString((constr->matrix() * x - constr->vector()).norm()));
255 } else if (constr->isInequality()) {
256 sendMsg(
257 "Inequality " + constr->name() + " violated: " +
258 toString(
259 (constr->matrix() * x - constr->lowerBound()).minCoeff()) +
260 "\n" +
261 toString(
262 (constr->upperBound() - constr->matrix() * x).minCoeff()));
263 } else if (constr->isBound()) {
264 sendMsg("Bound " + constr->name() + " violated: " +
265 toString((x - constr->lowerBound()).minCoeff()) + "\n" +
266 toString((constr->upperBound() - x).minCoeff()));
267 }
268 }
269 }
270 }
271 #endif
272 }
273
274 return m_output;
275 }
276
277 double SolverHQuadProg::getObjectiveValue() { return m_objValue; }
278