ipqp.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi 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 GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "ipqp.hpp"
27 #include "casadi/core/nlpsol.hpp"
28 
29 namespace casadi {
30 
31  extern "C"
32  int CASADI_CONIC_IPQP_EXPORT
33  casadi_register_conic_ipqp(Conic::Plugin* plugin) {
34  plugin->creator = Ipqp::creator;
35  plugin->name = "ipqp";
36  plugin->doc = Ipqp::meta_doc.c_str();
37  plugin->version = CASADI_VERSION;
38  plugin->options = &Ipqp::options_;
39  plugin->deserialize = &Ipqp::deserialize;
40  return 0;
41  }
42 
43  extern "C"
44  void CASADI_CONIC_IPQP_EXPORT casadi_load_conic_ipqp() {
46  }
47 
48  void print_vec(const std::string& str, const double* v, casadi_int n) {
49  uout() << str << " =";
50  for (casadi_int k = 0; k < n; ++k) uout() << " " << v[k];
51  uout() << "\n";
52  }
53 
54  Ipqp::Ipqp(const std::string& name, const std::map<std::string, Sparsity> &st)
55  : Conic(name, st) {
56  }
57 
59  clear_mem();
60  }
61 
63  = {{&Conic::options_},
64  {{"max_iter",
65  {OT_INT,
66  "Maximum number of iterations [1000]."}},
67  {"constr_viol_tol",
68  {OT_DOUBLE,
69  "Constraint violation tolerance [1e-8]."}},
70  {"dual_inf_tol",
71  {OT_DOUBLE,
72  "Dual feasibility violation tolerance [1e-8]"}},
73  {"print_header",
74  {OT_BOOL,
75  "Print header [true]."}},
76  {"print_iter",
77  {OT_BOOL,
78  "Print iterations [true]."}},
79  {"print_info",
80  {OT_BOOL,
81  "Print info [true]."}},
82  {"linear_solver",
83  {OT_STRING,
84  "A custom linear solver creator function [default: ldl]"}},
85  {"linear_solver_options",
86  {OT_DICT,
87  "Options to be passed to the linear solver"}},
88  {"min_lam",
89  {OT_DOUBLE,
90  "Smallest multiplier treated as inactive for the initial active set [0]."}}
91  }
92  };
93 
94  void Ipqp::init(const Dict& opts) {
95  // Initialize the base classes
96  Conic::init(opts);
97  // Assemble KKT system sparsity
98  kkt_ = Sparsity::kkt(H_, A_, true, true);
99  // Setup memory structure
100  set_qp_prob();
101  // Default options
102  print_iter_ = true;
103  print_header_ = true;
104  print_info_ = true;
105  linear_solver_ = "ldl";
106  // Read user options
107  for (auto&& op : opts) {
108  if (op.first=="max_iter") {
109  p_.max_iter = op.second;
110  } else if (op.first=="pr_tol") {
111  p_.pr_tol = op.second;
112  } else if (op.first=="du_tol") {
113  p_.du_tol = op.second;
114  } else if (op.first=="co_tol") {
115  p_.co_tol = op.second;
116  } else if (op.first=="mu_tol") {
117  p_.mu_tol = op.second;
118  } else if (op.first=="print_iter") {
119  print_iter_ = op.second;
120  } else if (op.first=="print_header") {
121  print_header_ = op.second;
122  } else if (op.first=="print_info") {
123  print_info_ = op.second;
124  } else if (op.first=="linear_solver") {
125  linear_solver_ = op.second.to_string();
126  } else if (op.first=="linear_solver_options") {
127  linear_solver_options_ = op.second;
128  }
129  }
130  // Memory for IP solver
131  alloc_w(casadi_ipqp_sz_w(&p_), true);
132  // Memory for KKT formation
133  alloc_w(kkt_.nnz(), true);
134  alloc_iw(A_.size2());
135  alloc_w(nx_ + na_);
136  // KKT solver
138  // Print summary
139  if (print_header_) {
140  print("-------------------------------------------\n");
141  print("This is casadi::Ipqp\n");
142  print("Linear solver: %12s\n", linear_solver_.c_str());
143  print("Number of variables: %12d\n", nx_);
144  print("Number of constraints: %12d\n", na_);
145  print("Number of nonzeros in H: %12d\n", H_.nnz());
146  print("Number of nonzeros in A: %12d\n", A_.nnz());
147  print("Number of nonzeros in KKT: %12d\n", kkt_.nnz());
148  }
149  }
150 
151  void Ipqp::set_qp_prob() {
152  casadi_ipqp_setup(&p_, nx_, na_);
153  }
154 
155  int Ipqp::init_mem(void* mem) const {
156  if (Conic::init_mem(mem)) return 1;
157  auto m = static_cast<IpqpMemory*>(mem);
158  m->return_status = "";
159  return 0;
160  }
161 
162  int Ipqp::
163  solve(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
164  auto m = static_cast<IpqpMemory*>(mem);
165  // Message buffer
166  char buf[121];
167  // Setup KKT system
168  double* nz_kkt = w; w += kkt_.nnz();
169  // Checkout a linear solver instance
170  int linsol_mem = linsol_.checkout();
171  // Setup IP solver
173  d.prob = &p_;
174  casadi_ipqp_init(&d, &iw, &w);
175  casadi_ipqp_bounds(&d, arg[CONIC_G],
176  arg[CONIC_LBX], arg[CONIC_UBX], arg[CONIC_LBA], arg[CONIC_UBA]);
177  casadi_ipqp_guess(&d, arg[CONIC_X0], arg[CONIC_LAM_X0], arg[CONIC_LAM_A0]);
178  // Reverse communication loop
179  while (casadi_ipqp(&d)) {
180  switch (d.task) {
181  case IPQP_MV:
182  // Matrix-vector multiplication
183  casadi_mv(arg[CONIC_H], H_, d.z, d.rz, 0);
184  casadi_mv(arg[CONIC_A], A_, d.lam + p_.nx, d.rz, 1);
185  casadi_mv(arg[CONIC_A], A_, d.z, d.rz + p_.nx, 0);
186  break;
187  case IPQP_PROGRESS:
188  // Print progress
189  if (print_iter_) {
190  if (d.iter % 10 == 0) {
191  // Print header
192  if (casadi_ipqp_print_header(&d, buf, sizeof(buf))) break;
193  uout() << buf << "\n";
194  }
195  // Print iteration
196  if (casadi_ipqp_print_iteration(&d, buf, sizeof(buf))) break;
197  uout() << buf << "\n";
198  // User interrupt?
200  }
201  break;
202  case IPQP_FACTOR:
203  // Form KKT
204  casadi_kkt(kkt_, nz_kkt, H_, arg[CONIC_H], A_, arg[CONIC_A],
205  d.S, d.D, w, iw);
206  // Factorize KKT
207  if (linsol_.nfact(nz_kkt, linsol_mem))
208  d.status = IPQP_FACTOR_ERROR;
209  break;
210  case IPQP_SOLVE:
211  // Solve KKT
212  if (linsol_.solve(nz_kkt, d.linsys, 1, false, linsol_mem))
213  d.status = IPQP_SOLVE_ERROR;
214  break;
215  }
216  }
217  // Release linear solver instance
218  linsol_.release(linsol_mem);
219  // Read return status
220  m->return_status = casadi_ipqp_return_status(d.status);
221  if (d.status == IPQP_MAX_ITER)
222  m->d_qp.unified_return_status = SOLVER_RET_LIMITED;
223  // Get solution
224  casadi_ipqp_solution(&d, res[CONIC_X], res[CONIC_LAM_X], res[CONIC_LAM_A]);
225  if (res[CONIC_COST]) {
226  *res[CONIC_COST] = .5 * casadi_bilin(arg[CONIC_H], H_, d.z, d.z)
227  + casadi_dot(p_.nx, d.z, d.g);
228  }
229  // Return
230  if (verbose_) casadi_warning(m->return_status);
231  m->d_qp.success = d.status == IPQP_SUCCESS;
232  return 0;
233  }
234 
235  Dict Ipqp::get_stats(void* mem) const {
236  Dict stats = Conic::get_stats(mem);
237  auto m = static_cast<IpqpMemory*>(mem);
238  stats["return_status"] = m->return_status;
239  return stats;
240  }
241 
243  s.version("Ipqp", 1);
244  s.unpack("Ipqp::kkt", kkt_);
245  s.unpack("Ipqp::print_iter", print_iter_);
246  s.unpack("Ipqp::print_header", print_header_);
247  s.unpack("Ipqp::print_info", print_info_);
248  s.unpack("Ipqp::linear_solver", linear_solver_);
249  s.unpack("Ipqp::linear_solver_options", linear_solver_options_);
250  set_qp_prob();
251  s.unpack("Ipqp::max_iter", p_.max_iter);
252  s.unpack("Ipqp::pr_tol", p_.pr_tol);
253  s.unpack("Ipqp::du_tol", p_.du_tol);
254  s.unpack("Ipqp::co_tol", p_.co_tol);
255  s.unpack("Ipqp::mu_tol", p_.mu_tol);
256  }
257 
260 
261  s.version("Ipqp", 1);
262  s.pack("Ipqp::kkt", kkt_);
263  s.pack("Ipqp::print_iter", print_iter_);
264  s.pack("Ipqp::print_header", print_header_);
265  s.pack("Ipqp::print_info", print_info_);
266  s.pack("Ipqp::linear_solver", linear_solver_);
267  s.pack("Ipqp::linear_solver_options", linear_solver_options_);
268  s.pack("Ipqp::max_iter", p_.max_iter);
269  s.pack("Ipqp::pr_tol", p_.pr_tol);
270  s.pack("Ipqp::du_tol", p_.du_tol);
271  s.pack("Ipqp::co_tol", p_.co_tol);
272  s.pack("Ipqp::mu_tol", p_.mu_tol);
273  }
274 
275 } // namespace casadi
Internal class.
Definition: conic_impl.hpp:44
static const Options options_
Options.
Definition: conic_impl.hpp:83
casadi_int nx_
Number of decision variables.
Definition: conic_impl.hpp:169
int init_mem(void *mem) const override
Initalize memory block.
Definition: conic.cpp:451
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
Definition: conic_impl.hpp:172
Sparsity H_
Problem structure.
Definition: conic_impl.hpp:166
void init(const Dict &opts) override
Initialize.
Definition: conic.cpp:412
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: conic.cpp:738
Dict get_stats(void *mem) const override
Get all statistics.
Definition: conic.cpp:711
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
static void check()
Raises an error if an interrupt was captured.
Dict get_stats(void *mem) const override
Get all statistics.
Definition: ipqp.cpp:235
bool print_iter_
Definition: ipqp.hpp:112
void init(const Dict &opts) override
Initialize.
Definition: ipqp.cpp:94
int init_mem(void *mem) const override
Initalize memory block.
Definition: ipqp.cpp:155
Sparsity kkt_
Definition: ipqp.hpp:107
bool print_info_
Definition: ipqp.hpp:112
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
Definition: ipqp.hpp:120
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Solve the QP.
Definition: ipqp.cpp:163
~Ipqp() override
Destructor.
Definition: ipqp.cpp:58
Ipqp(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new Solver.
Definition: ipqp.cpp:54
static const Options options_
Options.
Definition: ipqp.hpp:88
std::string linear_solver_
Definition: ipqp.hpp:113
casadi_ipqp_prob< double > p_
Definition: ipqp.hpp:105
bool print_header_
Definition: ipqp.hpp:112
Dict linear_solver_options_
Definition: ipqp.hpp:114
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: ipqp.cpp:258
static const std::string meta_doc
A documentation string.
Definition: ipqp.hpp:103
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
Definition: ipqp.hpp:63
Linsol linsol_
Definition: ipqp.hpp:109
Linear solver.
Definition: linsol.hpp:55
casadi_int checkout() const
Checkout a memory object.
Definition: linsol.cpp:197
void nfact(const DM &A) const
Numeric factorization of the linear system.
Definition: linsol.cpp:127
DM solve(const DM &A, const DM &B, bool tr=false) const
Definition: linsol.cpp:73
void release(int mem) const
Release a memory object.
Definition: linsol.cpp:201
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
void print(const char *fmt,...) const
C-style formatted printing during evaluation.
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
static Sparsity kkt(const Sparsity &H, const Sparsity &J, bool with_x_diag=true, bool with_lam_g_diag=true)
Get KKT system sparsity.
Definition: sparsity.cpp:1961
The casadi namespace.
Definition: archiver.cpp:28
@ CONIC_UBA
dense, (nc x 1)
Definition: conic.hpp:181
@ CONIC_X0
dense, (n x 1)
Definition: conic.hpp:187
@ CONIC_A
The matrix A: sparse, (nc x n) - product with x must be dense.
Definition: conic.hpp:177
@ CONIC_G
The vector g: dense, (n x 1)
Definition: conic.hpp:175
@ CONIC_LBA
dense, (nc x 1)
Definition: conic.hpp:179
@ CONIC_UBX
dense, (n x 1)
Definition: conic.hpp:185
@ CONIC_H
Definition: conic.hpp:173
@ CONIC_LAM_A0
dense
Definition: conic.hpp:191
@ CONIC_LBX
dense, (n x 1)
Definition: conic.hpp:183
@ CONIC_LAM_X0
dense
Definition: conic.hpp:189
T1 casadi_bilin(const T1 *A, const casadi_int *sp_A, const T1 *x, const T1 *y)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
T1 casadi_dot(casadi_int n, const T1 *x, const T1 *y)
Inner product.
void CASADI_CONIC_IPQP_EXPORT casadi_load_conic_ipqp()
Definition: ipqp.cpp:44
void print_vec(const std::string &str, const double *v, casadi_int n)
Definition: ipqp.cpp:48
void casadi_mv(const T1 *x, const casadi_int *sp_x, const T1 *y, T1 *z, casadi_int tr)
Sparse matrix-vector multiplication: z <- z + x*y.
int CASADI_CONIC_IPQP_EXPORT casadi_register_conic_ipqp(Conic::Plugin *plugin)
Definition: ipqp.cpp:33
std::ostream & uout()
@ SOLVER_RET_LIMITED
@ CONIC_X
The primal solution.
Definition: conic.hpp:201
@ CONIC_LAM_A
The dual solution corresponding to linear bounds.
Definition: conic.hpp:205
@ CONIC_COST
The optimal cost.
Definition: conic.hpp:203
@ CONIC_LAM_X
The dual solution corresponding to simple bounds.
Definition: conic.hpp:207
const char * return_status
Definition: ipqp.hpp:45
Options metadata for a class.
Definition: options.hpp:40
const casadi_ipqp_prob< T1 > * prob
Definition: casadi_ipqp.hpp:97
casadi_ipqp_flag_t status
casadi_ipqp_task_t task
casadi_int max_iter
Definition: casadi_ipqp.hpp:36