GCC Code Coverage Report


Directory: ./
File: src/solvers/solver-HQP-eiquadprog-fast.cpp
Date: 2025-05-10 01:12:46
Exec Total Coverage
Lines: 0 136 0.0%
Branches: 0 368 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-fast.hpp"
19 #include "tsid/math/utils.hpp"
20 #include "eiquadprog/eiquadprog-fast.hpp"
21 #include "tsid/utils/stop-watch.hpp"
22
23 // #define PROFILE_EIQUADPROG_FAST
24
25 using namespace eiquadprog::solvers;
26
27 namespace tsid {
28 namespace solvers {
29
30 using namespace math;
31 ✗ SolverHQuadProgFast::SolverHQuadProgFast(const std::string& name)
32 : SolverHQPBase(name),
33 ✗ m_hessian_regularization(DEFAULT_HESSIAN_REGULARIZATION) {
34 ✗ m_n = 0;
35 ✗ m_neq = 0;
36 ✗ m_nin = 0;
37 }
38
39 ✗ void SolverHQuadProgFast::sendMsg(const std::string& s) {
40 ✗ std::cout << "[SolverHQuadProgFast." << m_name << "] " << s << std::endl;
41 }
42
43 ✗ void SolverHQuadProgFast::resize(unsigned int n, unsigned int neq,
44 unsigned int nin) {
45 ✗ const bool resizeVar = n != m_n;
46 ✗ const bool resizeEq = (resizeVar || neq != m_neq);
47 ✗ const bool resizeIn = (resizeVar || nin != m_nin);
48
49 ✗ if (resizeEq) {
50 #ifndef NDEBUG
51 ✗ sendMsg("Resizing equality constraints from " + toString(m_neq) + " to " +
52 ✗ toString(neq));
53 #endif
54 ✗ m_qpData.CE.resize(neq, n);
55 ✗ m_qpData.ce0.resize(neq);
56 }
57 ✗ if (resizeIn) {
58 #ifndef NDEBUG
59 ✗ sendMsg("Resizing inequality constraints from " + toString(m_nin) + " to " +
60 ✗ toString(nin));
61 #endif
62 ✗ m_qpData.CI.resize(2 * nin, n);
63 ✗ m_qpData.ci0.resize(2 * nin);
64 }
65 ✗ if (resizeVar) {
66 #ifndef NDEBUG
67 ✗ sendMsg("Resizing Hessian from " + toString(m_n) + " to " + toString(n));
68 #endif
69 ✗ m_qpData.H.resize(n, n);
70 ✗ m_qpData.g.resize(n);
71 ✗ m_output.x.resize(n);
72 }
73
74 ✗ if (resizeVar || resizeIn || resizeEq) {
75 ✗ m_solver.reset(n, neq, nin * 2);
76 ✗ m_output.resize(n, neq, 2 * nin);
77 }
78
79 ✗ m_n = n;
80 ✗ m_neq = neq;
81 ✗ m_nin = nin;
82 }
83
84 ✗ void SolverHQuadProgFast::retrieveQPData(const HQPData& problemData,
85 const bool hessianRegularization) {
86 ✗ if (problemData.size() > 2) {
87 ✗ PINOCCHIO_CHECK_INPUT_ARGUMENT(
88 false, "Solver not implemented for more than 2 hierarchical levels.");
89 }
90
91 // Compute the constraint matrix sizes
92 ✗ unsigned int neq = 0, nin = 0;
93 ✗ const ConstraintLevel& cl0 = problemData[0];
94 ✗ if (cl0.size() > 0) {
95 ✗ const unsigned int n = cl0[0].second->cols();
96 ✗ for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
97 ✗ it++) {
98 ✗ auto constr = it->second;
99 ✗ assert(n == constr->cols());
100 ✗ if (constr->isEquality())
101 ✗ neq += constr->rows();
102 else
103 ✗ nin += constr->rows();
104 }
105 // If necessary, resize the constraint matrices
106 ✗ resize(n, neq, nin);
107
108 ✗ unsigned int i_eq = 0, i_in = 0;
109 ✗ for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
110 ✗ it++) {
111 ✗ auto constr = it->second;
112 ✗ if (constr->isEquality()) {
113 ✗ m_qpData.CE.middleRows(i_eq, constr->rows()) = constr->matrix();
114 ✗ m_qpData.ce0.segment(i_eq, constr->rows()) = -constr->vector();
115 ✗ i_eq += constr->rows();
116
117 ✗ } else if (constr->isInequality()) {
118 ✗ m_qpData.CI.middleRows(i_in, constr->rows()) = constr->matrix();
119 ✗ m_qpData.ci0.segment(i_in, constr->rows()) = -constr->lowerBound();
120 ✗ i_in += constr->rows();
121 ✗ m_qpData.CI.middleRows(i_in, constr->rows()) = -constr->matrix();
122 ✗ m_qpData.ci0.segment(i_in, constr->rows()) = constr->upperBound();
123 ✗ i_in += constr->rows();
124 ✗ } else if (constr->isBound()) {
125 ✗ m_qpData.CI.middleRows(i_in, constr->rows()).setIdentity();
126 ✗ m_qpData.ci0.segment(i_in, constr->rows()) = -constr->lowerBound();
127 ✗ i_in += constr->rows();
128 ✗ m_qpData.CI.middleRows(i_in, constr->rows()) =
129 ✗ -Matrix::Identity(m_n, m_n);
130 ✗ m_qpData.ci0.segment(i_in, constr->rows()) = constr->upperBound();
131 ✗ i_in += constr->rows();
132 }
133 }
134 } else
135 ✗ resize(m_n, neq, nin);
136
137 ✗ EIGEN_MALLOC_NOT_ALLOWED;
138
139 // Compute the cost
140 ✗ if (problemData.size() > 1) {
141 ✗ const ConstraintLevel& cl1 = problemData[1];
142 ✗ m_qpData.H.setZero();
143 ✗ m_qpData.g.setZero();
144
145 ✗ for (ConstraintLevel::const_iterator it = cl1.begin(); it != cl1.end();
146 ✗ it++) {
147 ✗ const double& w = it->first;
148 ✗ auto constr = it->second;
149 ✗ if (!constr->isEquality())
150 ✗ PINOCCHIO_CHECK_INPUT_ARGUMENT(
151 false, "Inequalities in the cost function are not implemented yet");
152
153 ✗ EIGEN_MALLOC_ALLOWED;
154 ✗ m_qpData.H.noalias() +=
155 ✗ w * constr->matrix().transpose() * constr->matrix();
156 ✗ EIGEN_MALLOC_NOT_ALLOWED;
157
158 ✗ m_qpData.g.noalias() -=
159 ✗ w * constr->matrix().transpose() * constr->vector();
160 }
161
162 ✗ if (hessianRegularization) {
163 ✗ double m_hessian_regularization(DEFAULT_HESSIAN_REGULARIZATION);
164 ✗ m_qpData.H.diagonal().array() += m_hessian_regularization;
165 }
166 }
167 }
168
169 ✗ const HQPOutput& SolverHQuadProgFast::solve(const HQPData& problemData) {
170 ✗ SolverHQuadProgFast::retrieveQPData(problemData);
171
172 START_PROFILER_EIQUADPROG_FAST(PROFILE_EIQUADPROG_SOLUTION);
173 // min 0.5 * x G x + g0 x
174 // s.t.
175 // CE x + ce0 = 0
176 // CI x + ci0 >= 0
177 ✗ EIGEN_MALLOC_ALLOWED
178 eiquadprog::solvers::EiquadprogFast_status status =
179 ✗ m_solver.solve_quadprog(m_qpData.H, m_qpData.g, m_qpData.CE, m_qpData.ce0,
180 ✗ m_qpData.CI, m_qpData.ci0, m_output.x);
181
182 STOP_PROFILER_EIQUADPROG_FAST(PROFILE_EIQUADPROG_SOLUTION);
183
184 ✗ if (status == EIQUADPROG_FAST_OPTIMAL) {
185 ✗ m_output.status = HQP_STATUS_OPTIMAL;
186 ✗ m_output.lambda = m_solver.getLagrangeMultipliers();
187 ✗ m_output.iterations = m_solver.getIteratios();
188 // m_output.activeSet =
189 // m_solver.getActiveSet().tail(2*m_nin).head(m_solver.getActiveSetSize()-m_neq);
190 ✗ m_output.activeSet = m_solver.getActiveSet().segment(
191 ✗ m_neq, m_solver.getActiveSetSize() - m_neq);
192 #ifndef NDEBUG
193 ✗ const Vector& x = m_output.x;
194
195 ✗ const ConstraintLevel& cl0 = problemData[0];
196 ✗ if (cl0.size() > 0) {
197 ✗ for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
198 ✗ it++) {
199 ✗ auto constr = it->second;
200 ✗ if (constr->checkConstraint(x) == false) {
201 // m_output.status = HQP_STATUS_ERROR;
202 ✗ if (constr->isEquality()) {
203 ✗ sendMsg("Equality " + constr->name() + " violated: " +
204 ✗ toString((constr->matrix() * x - constr->vector()).norm()));
205 ✗ } else if (constr->isInequality()) {
206 ✗ sendMsg(
207 ✗ "Inequality " + constr->name() + " violated: " +
208 ✗ toString(
209 ✗ (constr->matrix() * x - constr->lowerBound()).minCoeff()) +
210 ✗ "\n" +
211 ✗ toString(
212 ✗ (constr->upperBound() - constr->matrix() * x).minCoeff()));
213 ✗ } else if (constr->isBound()) {
214 ✗ sendMsg("Bound " + constr->name() + " violated: " +
215 ✗ toString((x - constr->lowerBound()).minCoeff()) + "\n" +
216 ✗ toString((constr->upperBound() - x).minCoeff()));
217 }
218 }
219 }
220 }
221 #endif
222 ✗ } else if (status == EIQUADPROG_FAST_UNBOUNDED)
223 ✗ m_output.status = HQP_STATUS_INFEASIBLE;
224 ✗ else if (status == EIQUADPROG_FAST_MAX_ITER_REACHED)
225 ✗ m_output.status = HQP_STATUS_MAX_ITER_REACHED;
226 ✗ else if (status == EIQUADPROG_FAST_REDUNDANT_EQUALITIES)
227 ✗ m_output.status = HQP_STATUS_ERROR;
228
229 ✗ return m_output;
230 }
231
232 ✗ double SolverHQuadProgFast::getObjectiveValue() {
233 ✗ return m_solver.getObjValue();
234 }
235
236 ✗ bool SolverHQuadProgFast::setMaximumIterations(unsigned int maxIter) {
237 ✗ SolverHQPBase::setMaximumIterations(maxIter);
238 ✗ return m_solver.setMaxIter(maxIter);
239 }
240 } // namespace solvers
241 } // namespace tsid
242