9 #include "crocoddyl/core/solvers/box-qp.hpp"
13 #include "crocoddyl/core/utils/exception.hpp"
18 const double th_acceptstep,
const double th_grad,
const double reg)
21 th_acceptstep_(th_acceptstep),
35 if (0. >= th_acceptstep && th_acceptstep >= 0.5) {
36 std::cerr <<
"Warning: th_acceptstep value should between 0 and 0.5"
40 std::cerr <<
"Warning: th_grad value has to be positive." << std::endl;
43 std::cerr <<
"Warning: reg value has to be positive." << std::endl;
57 solution_.
x = Eigen::VectorXd::Zero(nx);
60 const std::size_t n_alphas_ = 10;
61 alphas_.resize(n_alphas_);
62 for (std::size_t n = 0; n < n_alphas_; ++n) {
63 alphas_[n] = 1. / pow(2.,
static_cast<double>(n));
70 const Eigen::VectorXd& q,
71 const Eigen::VectorXd& lb,
72 const Eigen::VectorXd& ub,
73 const Eigen::VectorXd& xinit) {
74 if (
static_cast<std::size_t
>(H.rows()) != nx_ ||
75 static_cast<std::size_t
>(H.cols()) != nx_) {
76 throw_pretty(
"Invalid argument: "
77 <<
"H has wrong dimension (it should be " +
78 std::to_string(nx_) +
"," + std::to_string(nx_) +
")");
80 if (
static_cast<std::size_t
>(q.size()) != nx_) {
81 throw_pretty(
"Invalid argument: "
82 <<
"q has wrong dimension (it should be " +
83 std::to_string(nx_) +
")");
85 if (
static_cast<std::size_t
>(lb.size()) != nx_) {
86 throw_pretty(
"Invalid argument: "
87 <<
"lb has wrong dimension (it should be " +
88 std::to_string(nx_) +
")");
90 if (
static_cast<std::size_t
>(ub.size()) != nx_) {
91 throw_pretty(
"Invalid argument: "
92 <<
"ub has wrong dimension (it should be " +
93 std::to_string(nx_) +
")");
95 if (
static_cast<std::size_t
>(xinit.size()) != nx_) {
96 throw_pretty(
"Invalid argument: "
97 <<
"xinit has wrong dimension (it should be " +
98 std::to_string(nx_) +
")");
102 for (std::size_t i = 0; i < nx_; ++i) {
103 x_(i) = std::max(std::min(xinit(i), ub(i)), lb(i));
107 for (std::size_t k = 0; k < maxiter_; ++k) {
112 g_.noalias() += H * x_;
113 for (std::size_t j = 0; j < nx_; ++j) {
114 const double gj = g_(j);
115 const double xj = x_(j);
116 const double lbj = lb(j);
117 const double ubj = ub(j);
118 if ((xj == lbj && gj > 0.) || (xj == ubj && gj < 0.)) {
128 Eigen::VectorBlock<Eigen::VectorXd> xf = xo_.head(nf_);
129 Eigen::VectorBlock<Eigen::VectorXd> xc = xo_.tail(nc_);
130 Eigen::VectorBlock<Eigen::VectorXd> dxf = dxo_.head(nf_);
131 Eigen::VectorBlock<Eigen::VectorXd> qf = qo_.head(nf_);
132 Eigen::Block<Eigen::MatrixXd> Hff = Ho_.topLeftCorner(nf_, nf_);
133 Eigen::Block<Eigen::MatrixXd> Hfc = Ho_.topRightCorner(nf_, nc_);
134 for (std::size_t i = 0; i < nf_; ++i) {
135 const std::size_t fi = solution_.
free_idx[i];
138 for (std::size_t j = 0; j < nf_; ++j) {
139 Hff(i, j) = H(fi, solution_.
free_idx[j]);
141 for (std::size_t j = 0; j < nc_; ++j) {
144 Hfc(i, j) = H(fi, cj);
148 Hff.diagonal().array() += reg_;
150 Hff_inv_llt_.compute(Hff);
151 const Eigen::ComputationInfo& info = Hff_inv_llt_.info();
152 if (info != Eigen::Success) {
153 throw_pretty(
"backward_error");
155 solution_.
Hff_inv.setIdentity(nf_, nf_);
156 Hff_inv_llt_.solveInPlace(solution_.
Hff_inv);
157 qf.noalias() += Hff * xf;
159 qf.noalias() += Hfc * xc;
162 Hff_inv_llt_.solveInPlace(dxf);
164 for (std::size_t i = 0; i < nf_; ++i) {
165 dx_(solution_.
free_idx[i]) = dxf(i);
170 fold_ = 0.5 * x_.dot(H * x_) + q.dot(x_);
171 for (std::vector<double>::const_iterator it = alphas_.begin();
172 it != alphas_.end(); ++it) {
173 double steplength = *it;
174 for (std::size_t i = 0; i < nx_; ++i) {
176 std::max(std::min(x_(i) + steplength * dx_(i), ub(i)), lb(i));
178 fnew_ = 0.5 * xnew_.dot(H * xnew_) + q.dot(xnew_);
179 if (fold_ - fnew_ > th_acceptstep_ * g_.dot(x_ - xnew_)) {
186 if (qf.lpNorm<Eigen::Infinity>() <= th_grad_) {
211 x_.conservativeResize(nx);
212 xnew_.conservativeResize(nx);
213 g_.conservativeResize(nx);
214 dx_.conservativeResize(nx);
215 xo_.conservativeResize(nx);
216 dxo_.conservativeResize(nx);
217 qo_.conservativeResize(nx);
218 Ho_.conservativeResize(nx, nx);
224 if (0. >= th_acceptstep && th_acceptstep >= 0.5) {
225 throw_pretty(
"Invalid argument: "
226 <<
"th_acceptstep value should between 0 and 0.5");
228 th_acceptstep_ = th_acceptstep;
233 throw_pretty(
"Invalid argument: "
234 <<
"th_grad value has to be positive.");
241 throw_pretty(
"Invalid argument: "
242 <<
"reg value has to be positive.");
248 double prev_alpha = alphas[0];
249 if (prev_alpha != 1.) {
250 std::cerr <<
"Warning: alpha[0] should be 1" << std::endl;
252 for (std::size_t i = 1; i < alphas.size(); ++i) {
253 double alpha = alphas[i];
255 throw_pretty(
"Invalid argument: "
256 <<
"alpha values has to be positive.");
258 if (alpha >= prev_alpha) {
259 throw_pretty(
"Invalid argument: "
260 <<
"alpha values are monotonously decreasing.");
void set_reg(const double reg)
Modify the regularization value.
void set_th_grad(const double th_grad)
Modify the gradient tolerance threshold.
const BoxQPSolution & get_solution() const
Return the stored solution.
double get_th_grad() const
Return the gradient tolerance threshold.
const BoxQPSolution & solve(const Eigen::MatrixXd &H, const Eigen::VectorXd &q, const Eigen::VectorXd &lb, const Eigen::VectorXd &ub, const Eigen::VectorXd &xinit)
Compute the solution of bound-constrained QP based on Newton projection.
std::size_t get_maxiter() const
Return the maximum allowed number of iterations.
void set_maxiter(const std::size_t maxiter)
Modify the maximum allowed number of iterations.
~BoxQP()
Destroy the Projected-Newton QP solver.
void set_nx(const std::size_t nx)
Modify the decision vector dimension.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW BoxQP(const std::size_t nx, const std::size_t maxiter=100, const double th_acceptstep=0.1, const double th_grad=1e-9, const double reg=1e-9)
Initialize the Projected-Newton QP for bound constraints.
const std::vector< double > & get_alphas() const
Return the stack of step lengths using by the line-search procedure.
void set_alphas(const std::vector< double > &alphas)
Modify the stack of step lengths using by the line-search procedure.
void set_th_acceptstep(const double th_acceptstep)
Modify the acceptance step threshold.
double get_th_acceptstep() const
Return the acceptance step threshold.
double get_reg() const
Return the regularization value.
std::size_t get_nx() const
Return the decision vector dimension.
std::vector< size_t > free_idx
Free space indexes.
Eigen::MatrixXd Hff_inv
Inverse of the free space Hessian.
Eigen::VectorXd x
Decision vector.
std::vector< size_t > clamped_idx
Clamped space indexes.