blocksqp.hpp
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 #ifndef CASADI_BLOCKSQP_HPP
27 #define CASADI_BLOCKSQP_HPP
28 
29 #include <casadi/interfaces/blocksqp/casadi_nlpsol_blocksqp_export.h>
30 #include "casadi/core/linsol.hpp"
31 #include "casadi/core/nlpsol_impl.hpp"
32 #include "../qpoases/qpoases_interface.hpp"
33 
47 namespace casadi {
48  // Forward declaration
49  class Blocksqp;
50 
51  struct BlocksqpMemory : public NlpsolMemory {
53  BlocksqpMemory();
54 
56  ~BlocksqpMemory();
57 
58  qpOASES::SymSparseMat *H;
59  qpOASES::Matrix *A;
60  qpOASES::SQProblem* qp;
61 
62  // [Workaround] qpOASES memory block
63  QpoasesMemory* qpoases_mem;
64 
65  // Stats
66  casadi_int itCount; // iteration number
67  casadi_int qpIterations; // number of qp iterations in the current major iteration
68  casadi_int qpIterations2; // number of qp iterations for solving convexified QPs
69  casadi_int qpItTotal; // total number of qp iterations
70  casadi_int qpResolve; // how often has QP to be convexified and resolved?
71  casadi_int nFunCalls; // number of function calls
72  casadi_int nDerCalls; // number of derivative calls
73  casadi_int nRestHeurCalls; // number calls to feasibility restoration heuristic
74  casadi_int nRestPhaseCalls; // number calls to feasibility restoration phase
75  casadi_int rejectedSR1; // count how often the SR1 update is rejected
76  casadi_int hessSkipped; // number of block updates skipped in the current iteration
77  casadi_int hessDamped; // number of block updates damped in the current iteration
78  casadi_int nTotalUpdates;
79  casadi_int nTotalSkippedUpdates;
80  double averageSizingFactor; // average value (over all blocks) of COL sizing factor
81 
82  // Variables that are updated during one SQP iteration
83  double obj; // objective value
84  double qpObj; // objective value of last QP subproblem
85  double cNorm; // constraint violation
86  double cNormS; // scaled constraint violation
87  double gradNorm; // norm of Lagrangian gradient
88  double lambdaStepNorm; // norm of step in dual variables
89  double tol; // current optimality tolerance
90 
91  double *lam_xk, *lam_gk; // dual variables
92  double *gk; // constraint vector
93 
94  double *jac_g; // nonzero elements of Jacobian (length)
95 
96  double *deltaMat; // last m primal steps
97  double *dxk; // alias for current step
98  double *grad_fk; // gradient of objective
99  double *grad_lagk; // gradient of Lagrangian
100  double *gammaMat; // Lagrangian gradient differences for last m steps
101  double *gamma; // alias for current Lagrangian gradient
102 
103  double **hess; // [blockwise] pointer to current Hessian of the Lagrangian
104  double **hess1; // [blockwise] first Hessian approximation
105  double **hess2; // [blockwise] second Hessian approximation (convexified)
106  double *hess_lag; // nonzero elements of Hessian (length)
107  int *hessIndRow; // row indices (length)
108  int *hessIndCol; // indices to first entry of columns (nCols+1)
109  int *hessIndLo; // Indices to first entry of lower triangle (including diagonal) (nCols)
110 
111  /*
112  * Variables for QP solver
113  */
114  double *lbx_qp, *ubx_qp, *lba_qp, *uba_qp; // bounds for current step
115  double* lam_qp; // dual variables of QP
116  double* jac_times_dxk; // product of constraint Jacobian with dxk
117 
118  /*
119  * For modified BFGS updates
120  */
121  double *delta_norm; // sTs
122  double* delta_norm_old; // (from previous iteration)
123  double* delta_gamma; // sTy
124  double* delta_gamma_old; // (from previous iteration)
125  casadi_int *noUpdateCounter; // count skipped updates for each block
126 
127  /*
128  * Variables for globalization strategy
129  */
130  casadi_int steptype; // is current step a restoration step (1)?
131  double alpha; // stepsize for line search
132  casadi_int nSOCS; // number of second-order correction steps
133  casadi_int reducedStepCount; // count number of consecutive reduced steps,
134  double* delta_h; // inertia correction (filter line search w indef Hessian)
135  double* trial_xk; // new trial iterate (for line search)
136  std::set< std::pair<double, double> > filter; // Filter contains pairs (constrVio, objective)
137 
138  std::vector<int> colind, row;
139 
140  // Temporary memory
141  double* jac;
142  double* exact_hess_lag;
143 
144  casadi_int ret_; // return value (only needed for first iteration of restoration phase)
145  };
146 
151  class Blocksqp : public Nlpsol {
152  public:
153  explicit Blocksqp(const std::string& name, const Function& nlp);
154  ~Blocksqp() override;
155 
156  // Get name of the plugin
157  const char* plugin_name() const override { return "blocksqp";}
158 
159  // Get name of the class
160  std::string class_name() const override { return "Blocksqp";}
161 
163  static Nlpsol* creator(const std::string& name, const Function& nlp) {
164  return new Blocksqp(name, nlp);
165  }
166 
168 
169  static const Options options_;
170  const Options& get_options() const override { return options_;}
172 
173  // Initialize the solver
174  void init(const Dict& opts) override;
175 
177  void* alloc_mem() const override { return new BlocksqpMemory();}
178 
180  int init_mem(void* mem) const override;
181 
183  void free_mem(void *mem) const override { delete static_cast<BlocksqpMemory*>(mem);}
184 
186  void set_work(void* mem, const double**& arg, double**& res,
187  casadi_int*& iw, double*& w) const override;
188 
189  // Solve the NLP
190  int solve(void* mem) const override;
191 
193  static const std::string meta_doc;
194 
195  // Block partitioning
196  casadi_int nblocks_;
197  std::vector<casadi_int> blocks_;
198  std::vector<casadi_int> dim_;
199  casadi_int nnz_H_;
200 
201  // Jacobian/Hessian sparsity
202  Sparsity Asp_, Hsp_;
203  Sparsity exact_hess_lag_sp_;
204 
206  casadi_int run(BlocksqpMemory* m, casadi_int maxIt, casadi_int warmStart = 0) const;
208  void calcLagrangeGradient(BlocksqpMemory* m,
209  const double* lam_x, const double* lam_g,
210  const double* grad_f, const double *jacNz,
211  double *grad_lag, casadi_int flag) const;
212 
214  void calcLagrangeGradient(BlocksqpMemory* m, double* grad_lag, casadi_int flag) const;
216  void printInfo(BlocksqpMemory* m) const;
218  bool calcOptTol(BlocksqpMemory* m) const;
219 
220  /*
221  * Solve QP subproblem
222  */
223  // Update the bounds on the current step, i.e. the QP variables
224  void updateStepBounds(BlocksqpMemory* m, bool soc) const;
225  // Solve a QP with QPOPT or qpOASES to obtain a step deltaXi and estimates
226  // for the Lagrange multipliers
227  casadi_int solveQP(BlocksqpMemory* m, double* deltaXi, double* lambdaQP,
228  bool matricesChanged = true) const;
229  // Compute the next Hessian in the inner loop of increasingly convexified
230  // QPs and store it in vars->hess2
231  void computeNextHessian(BlocksqpMemory* m, casadi_int idx, casadi_int maxQP) const;
232 
233  /*
234  * Globalization Strategy
235  */
237  casadi_int fullstep(BlocksqpMemory* m) const;
239  void acceptStep(BlocksqpMemory* m, const double* deltaXi,
240  const double* lambdaQP, double alpha, casadi_int nSOCS) const;
241  // Overloaded function for convenience, uses current variables of SQPiterate vars
242  void acceptStep(BlocksqpMemory* m, double alpha) const;
243  // Reduce stepsize if a step is rejected
244  void reduceStepsize(BlocksqpMemory* m, double *alpha) const;
245  // Determine steplength alpha by a filter based line search similar to IPOPT
246  casadi_int filterLineSearch(BlocksqpMemory* m) const;
247  // Remove all entries from filter
248  void initializeFilter(BlocksqpMemory* m) const;
249  // Is a pair (cNorm, obj) in the current filter?
250  bool pairInFilter(BlocksqpMemory* m, double cNorm, double obj) const;
251  // Augment current filter by pair (cNorm, obj)
252  void augmentFilter(BlocksqpMemory* m, double cNorm, double obj) const;
253  // Perform a second order correction step (solve QP)
254  bool secondOrderCorrection(BlocksqpMemory* m, double cNorm, double cNormTrial,
255  double dfTdeltaXi, bool swCond, casadi_int it) const;
256  // Reduce stepsize if a second order correction step is rejected
257  void reduceSOCStepsize(BlocksqpMemory* m, double *alphaSOC) const;
258  // Start feasibility restoration heuristic
259  casadi_int feasibilityRestorationHeuristic(BlocksqpMemory* m) const;
260  // Start feasibility restoration phase (solve NLP)
261  casadi_int feasibilityRestorationPhase(BlocksqpMemory* m) const;
262  // Check if full step reduces KKT error
263  casadi_int kktErrorReduction(BlocksqpMemory* m) const;
264 
265  /*
266  * Hessian Approximation
267  */
268  // Set initial Hessian: Identity matrix
269  void calcInitialHessian(BlocksqpMemory* m) const;
270  // [blockwise] Set initial Hessian: Identity matrix
271  void calcInitialHessian(BlocksqpMemory* m, casadi_int b) const;
272  // Reset Hessian to identity and remove past information on Lagrange gradient and steps
273  void resetHessian(BlocksqpMemory* m) const;
274  // [blockwise] Reset Hessian to identity and remove past information on
275  // Lagrange gradient and steps
276  void resetHessian(BlocksqpMemory* m, casadi_int b) const;
277  // Compute full memory Hessian approximations based on update formulas
278  void calcHessianUpdate(BlocksqpMemory* m, casadi_int updateType, casadi_int hessScaling) const;
279  // Compute limited memory Hessian approximations based on update formulas
280  void calcHessianUpdateLimitedMemory(BlocksqpMemory* m,
281  casadi_int updateType, casadi_int hessScaling) const;
282  // Compute exact Hessian update
283  void calcHessianUpdateExact(BlocksqpMemory* m) const;
284  // [blockwise] Compute new approximation for Hessian by SR1 update
285  void calcSR1(BlocksqpMemory* m, const double* gamma, const double* delta,
286  casadi_int b) const;
287  // [blockwise] Compute new approximation for Hessian by BFGS update with Powell modification
288  void calcBFGS(BlocksqpMemory* m, const double* gamma, const double* delta,
289  casadi_int b) const;
290  // Set pointer to correct step and Lagrange gradient difference in a limited memory context
291  void updateDeltaGamma(BlocksqpMemory* m) const;
292 
293  /*
294  * Scaling of Hessian Approximation
295  */
296  // [blockwise] Size Hessian using SP, OL, or mean sizing factor
297  void sizeInitialHessian(BlocksqpMemory* m, const double* gamma,
298  const double* delta, casadi_int b, casadi_int option) const;
299  // [blockwise] Size Hessian using the COL scaling factor
300  void sizeHessianCOL(BlocksqpMemory* m, const double* gamma,
301  const double* delta, casadi_int b) const;
302 
303  /*
304  * STATS
305  */
306  void initStats(BlocksqpMemory* m) const;
307  void updateStats(BlocksqpMemory* m) const;
309  void printProgress(BlocksqpMemory* m) const;
311  void reset_sqp(BlocksqpMemory* m) const;
313  void convertHessian(BlocksqpMemory* m) const;
315  void initIterate(BlocksqpMemory* m) const;
316 
318  casadi_int evaluate(BlocksqpMemory* m, double *f, double *g,
319  double *grad_f, double *jac_g) const;
320 
322  casadi_int evaluate(BlocksqpMemory* m, const double *xk,
323  double *f, double *g) const;
324 
326  casadi_int evaluate(BlocksqpMemory* m,
327  double *exact_hess_lag) const;
328 
329  // Declaration of general purpose routines for matrix and vector computations
330  double lInfConstraintNorm(BlocksqpMemory* m, const double* xk, const double* g) const;
331 
333  //Function qpsol_;
334 
335  // Linear solver in qpOASES
336  std::string linsol_plugin_;
337 
338  // General options
339  bool print_header_;
340  bool print_iteration_;
341  double eps_; // values smaller than this are regarded as numerically zero
342  double opttol_; // optimality tolerance
343  double nlinfeastol_; // nonlinear feasibility tolerance
344 
345  // Algorithmic options
346  bool schur_; // Use qpOASES schur compliment approach
347  bool globalization_; // Globalization strategy
348  bool restore_feas_;// Use feasibility restoration phase
349  casadi_int max_line_search_; // Maximum number of steps in line search
350  casadi_int max_consec_reduced_steps_;// Maximum number of consecutive reduced steps
351  casadi_int max_consec_skipped_updates_; // Maximum number of consecutive skipped updates
352  casadi_int max_it_qp_; // Maximum number of QP iterations per SQP iteration
353  casadi_int max_iter_; // Maximum number of SQP steps
354  bool warmstart_; // Use warmstarting
355  bool qp_init_;
356  bool block_hess_; // Blockwise Hessian approximation?
357  casadi_int hess_scaling_;// Scaling strategy for Hessian approximation
358  casadi_int fallback_scaling_; // If indefinite update is used, the type of fallback strategy
359  double max_time_qp_; // Maximum number of time in seconds per QP solve per SQP iteration
360  double ini_hess_diag_; // Initial Hessian guess: diagonal matrix diag(iniHessDiag)
361  double col_eps_; // epsilon for COL scaling strategy
362  double col_tau1_; // tau1 for COL scaling strategy
363  double col_tau2_; // tau2 for COL scaling strategy
364  casadi_int hess_damp_; // activate Powell damping for BFGS
365  double hess_damp_fac_; // damping factor for BFGS Powell modification
366  casadi_int hess_update_; // Type of Hessian approximation
367  casadi_int fallback_update_; // If indefinite update is used, the type of fallback strategy
368  casadi_int hess_lim_mem_; // Full or limited memory
369  casadi_int hess_memsize_; // Memory size for L-BFGS updates
370  casadi_int which_second_derv_; // For which block should second derivatives be provided
371  // by the user
372  bool skip_first_globalization_; // No globalization strategy in first iteration
373  casadi_int conv_strategy_; // Convexification strategy
374  casadi_int max_conv_qp_; // How many additional QPs may be solved for convexification
375  // per iteration?
376 
377  // Filter line search parameters, cf. IPOPT paper
378  casadi_int max_soc_iter_; // Maximum number of SOC line search iterations
379  double gamma_theta_;
380  double gamma_f_;
381  double kappa_soc_;
382  double kappa_f_;
383  double theta_max_;
384  double theta_min_;
385  double delta_;
386  double s_theta_;
387  double s_f_;
388  double kappa_minus_;
389  double kappa_plus_;
390  double kappa_plus_max_;
391  double delta_h0_;
392  double eta_;
393  double obj_lo_;
394  double obj_up_;
395 
396  // feasibility restoration phase
397  double rho_; // Regularization factor for first part of objective
398  double zeta_; // Regularization factor for second part of objective
399  Function rp_solver_; // restoration phase Solver
400  bool print_maxit_reached_;
401 
403  void serialize_body(SerializingStream &s) const override;
404 
406  static ProtoFunction* deserialize(DeserializingStream& s) { return new Blocksqp(s); }
407 
408  protected:
410  explicit Blocksqp(DeserializingStream& s);
411  };
412 
413 } // namespace casadi
414 
416 #endif // CASADI_BLOCKSQP_HPP
The casadi namespace.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.