GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/tsid/solvers/solver-HQP-eiquadprog-rt.hxx Lines: 74 111 66.7 %
Date: 2024-02-02 08:47:34 Branches: 104 352 29.5 %

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
#ifndef __invdyn_solvers_hqp_eiquadprog_rt_hxx__
19
#define __invdyn_solvers_hqp_eiquadprog_rt_hxx__
20
21
#include "tsid/solvers/solver-HQP-eiquadprog-rt.hpp"
22
#include "eiquadprog/eiquadprog-rt.hxx"
23
#include "tsid/utils/stop-watch.hpp"
24
#include "tsid/math/utils.hpp"
25
26
namespace eisol = eiquadprog::solvers;
27
28
#define PROFILE_EIQUADPROG_PREPARATION "EiquadprogRT problem preparation"
29
#define PROFILE_EIQUADPROG_SOLUTION "EiquadprogRT problem solution"
30
31
namespace tsid {
32
namespace solvers {
33
34
template <int nVars, int nEqCon, int nIneqCon>
35
2
SolverHQuadProgRT<nVars, nEqCon, nIneqCon>::SolverHQuadProgRT(
36
    const std::string& name)
37
    : SolverHQPBase(name),
38




2
      m_hessian_regularization(DEFAULT_HESSIAN_REGULARIZATION) {
39
2
  m_n = nVars;
40
2
  m_neq = nEqCon;
41
2
  m_nin = nIneqCon;
42
2
  m_output.resize(nVars, nEqCon, 2 * nIneqCon);
43
2
}
44
45
template <int nVars, int nEqCon, int nIneqCon>
46
void SolverHQuadProgRT<nVars, nEqCon, nIneqCon>::sendMsg(const std::string& s) {
47
  std::cout << "[SolverHQuadProgRT." << m_name << "] " << s << std::endl;
48
}
49
50
template <int nVars, int nEqCon, int nIneqCon>
51
611
void SolverHQuadProgRT<nVars, nEqCon, nIneqCon>::resize(unsigned int n,
52
                                                        unsigned int neq,
53
                                                        unsigned int nin) {
54
611
  assert(n == nVars);
55
611
  assert(neq == nEqCon);
56
611
  assert(nin == nIneqCon);
57

611
  if ((n != nVars) || (neq != nEqCon) || (nin != nIneqCon))
58
    std::cerr
59
        << "[SolverHQuadProgRT] (n!=nVars) || (neq!=nEqCon) || (nin!=nIneqCon)"
60
        << std::endl;
61
611
}
62
63
template <int nVars, int nEqCon, int nIneqCon>
64
610
const HQPOutput& SolverHQuadProgRT<nVars, nEqCon, nIneqCon>::solve(
65
    const HQPData& problemData) {
66
  using namespace tsid::math;
67
68
  // #ifndef EIGEN_RUNTIME_NO_MALLOC
69
  //   Eigen::internal::set_is_malloc_allowed(false);
70
  // #endif
71
72
  START_PROFILER_EIQUADPROG_RT(PROFILE_EIQUADPROG_PREPARATION);
73
74
610
  if (problemData.size() > 2) {
75
    PINOCCHIO_CHECK_INPUT_ARGUMENT(
76
        false, "Solver not implemented for more than 2 hierarchical levels.");
77
  }
78
79
  // Compute the constraint matrix sizes
80
610
  unsigned int neq = 0, nin = 0;
81
610
  const ConstraintLevel& cl0 = problemData[0];
82
610
  if (cl0.size() > 0) {
83
610
    const unsigned int n = cl0[0].second->cols();
84
1870
    for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
85
1260
         it++) {
86
2520
      auto constr = it->second;
87

1260
      assert(n == constr->cols());
88

1260
      if (constr->isEquality())
89
630
        neq += constr->rows();
90
      else
91
630
        nin += constr->rows();
92
    }
93
    // If necessary, resize the constraint matrices
94
610
    resize(n, neq, nin);
95
96
610
    int i_eq = 0, i_in = 0;
97
1870
    for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
98
1260
         it++) {
99
2520
      auto constr = it->second;
100

1260
      if (constr->isEquality()) {
101


630
        m_CE.middleRows(i_eq, constr->rows()) = constr->matrix();
102


630
        m_ce0.segment(i_eq, constr->rows()) = -constr->vector();
103
630
        i_eq += constr->rows();
104

630
      } else if (constr->isInequality()) {
105


630
        m_CI.middleRows(i_in, constr->rows()) = constr->matrix();
106


630
        m_ci0.segment(i_in, constr->rows()) = -constr->lowerBound();
107
630
        i_in += constr->rows();
108


630
        m_CI.middleRows(i_in, constr->rows()) = -constr->matrix();
109


630
        m_ci0.segment(i_in, constr->rows()) = constr->upperBound();
110
630
        i_in += constr->rows();
111
      } else if (constr->isBound()) {
112
        m_CI.middleRows(i_in, constr->rows()).setIdentity();
113
        m_ci0.segment(i_in, constr->rows()) = -constr->lowerBound();
114
        i_in += constr->rows();
115
        m_CI.middleRows(i_in, constr->rows()) = -Matrix::Identity(m_n, m_n);
116
        m_ci0.segment(i_in, constr->rows()) = constr->upperBound();
117
        i_in += constr->rows();
118
      }
119
    }
120
  } else
121
    resize(m_n, neq, nin);
122
123
610
  if (problemData.size() > 1) {
124
610
    const ConstraintLevel& cl1 = problemData[1];
125
610
    m_H.setZero();
126
610
    m_g.setZero();
127
1250
    for (ConstraintLevel::const_iterator it = cl1.begin(); it != cl1.end();
128
640
         it++) {
129
640
      const double& w = it->first;
130
640
      auto constr = it->second;
131

640
      if (!constr->isEquality())
132
        PINOCCHIO_CHECK_INPUT_ARGUMENT(
133
            false, "Inequalities in the cost function are not implemented yet");
134
135
640
      EIGEN_MALLOC_ALLOWED
136



640
      m_H.noalias() += w * constr->matrix().transpose() * constr->matrix();
137
640
      EIGEN_MALLOC_NOT_ALLOWED
138



640
      m_g.noalias() -= w * (constr->matrix().transpose() * constr->vector());
139
    }
140


610
    m_H.diagonal().noalias() += m_hessian_regularization * Vector::Ones(m_n);
141
  }
142
143
  STOP_PROFILER_EIQUADPROG_RT(PROFILE_EIQUADPROG_PREPARATION);
144
145
  //  // eliminate equality constraints
146
  //  if(m_neq>0)
147
  //  {
148
  //    m_CE_lu.compute(m_CE);
149
  //    sendMsg("The rank of CD is "+toString(m_CE_lu.rank());
150
  //    const MatrixXd & Z = m_CE_lu.kernel();
151
152
  //  }
153
154
  START_PROFILER_EIQUADPROG_RT(PROFILE_EIQUADPROG_SOLUTION);
155
156
  //  min 0.5 * x G x + g0 x
157
  //  s.t.
158
  //  CE x + ce0 = 0
159
  //  CI x + ci0 >= 0
160
610
  typename RtVectorX<nVars>::d sol(m_n);
161
610
  EIGEN_MALLOC_ALLOWED
162
1220
  eisol::RtEiquadprog_status status =
163
610
      m_solver.solve_quadprog(m_H, m_g, m_CE, m_ce0, m_CI, m_ci0, sol);
164
  STOP_PROFILER_EIQUADPROG_RT(PROFILE_EIQUADPROG_SOLUTION);
165
166
610
  m_output.x = sol;
167
168
  // #ifndef EIGEN_RUNTIME_NO_MALLOC
169
  //   Eigen::internal::set_is_malloc_allowed(true);
170
  // #endif
171
172
610
  if (status == eisol::RT_EIQUADPROG_OPTIMAL) {
173
610
    m_output.status = HQP_STATUS_OPTIMAL;
174
610
    m_output.lambda = m_solver.getLagrangeMultipliers();
175
    //    m_output.activeSet = m_solver.getActiveSet().template tail< 2*nIneqCon
176
    //    >().head(m_solver.getActiveSetSize());
177
1220
    m_output.activeSet = m_solver.getActiveSet().segment(
178
610
        m_neq, m_solver.getActiveSetSize() - m_neq);
179
610
    m_output.iterations = m_solver.getIteratios();
180
181
#ifndef NDEBUG
182
610
    const Vector& x = m_output.x;
183
184
610
    if (cl0.size() > 0) {
185
1870
      for (ConstraintLevel::const_iterator it = cl0.begin(); it != cl0.end();
186
1260
           it++) {
187
2520
        auto constr = it->second;
188

1260
        if (constr->checkConstraint(x) == false) {
189
          if (constr->isEquality()) {
190
            sendMsg("Equality " + constr->name() + " violated: " +
191
                    toString((constr->matrix() * x - constr->vector()).norm()));
192
          } else if (constr->isInequality()) {
193
            sendMsg(
194
                "Inequality " + constr->name() + " violated: " +
195
                toString(
196
                    (constr->matrix() * x - constr->lowerBound()).minCoeff()) +
197
                "\n" +
198
                toString(
199
                    (constr->upperBound() - constr->matrix() * x).minCoeff()));
200
          } else if (constr->isBound()) {
201
            sendMsg("Bound " + constr->name() + " violated: " +
202
                    toString((x - constr->lowerBound()).minCoeff()) + "\n" +
203
                    toString((constr->upperBound() - x).minCoeff()));
204
          }
205
        }
206
      }
207
    }
208
#endif
209
  } else if (status == eisol::RT_EIQUADPROG_UNBOUNDED)
210
    m_output.status = HQP_STATUS_INFEASIBLE;
211
  else if (status == eisol::RT_EIQUADPROG_MAX_ITER_REACHED)
212
    m_output.status = HQP_STATUS_MAX_ITER_REACHED;
213
  else if (status == eisol::RT_EIQUADPROG_REDUNDANT_EQUALITIES)
214
    m_output.status = HQP_STATUS_ERROR;
215
216
610
  return m_output;
217
}
218
219
template <int nVars, int nEqCon, int nIneqCon>
220
double SolverHQuadProgRT<nVars, nEqCon, nIneqCon>::getObjectiveValue() {
221
  return m_solver.getObjValue();
222
}
223
224
template <int nVars, int nEqCon, int nIneqCon>
225
bool SolverHQuadProgRT<nVars, nEqCon, nIneqCon>::setMaximumIterations(
226
    unsigned int maxIter) {
227
  SolverHQPBase::setMaximumIterations(maxIter);
228
  return m_solver.setMaxIter(maxIter);
229
}
230
231
}  // namespace solvers
232
}  // namespace tsid
233
234
#endif  // ifndef __invdyn_solvers_hqp_eiquadprog_rt_hxx__