conopt_interface.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 #ifndef CASADI_CONOPT_INTERFACE_HPP
26 #define CASADI_CONOPT_INTERFACE_HPP
27 
28 #include "casadi/core/nlpsol_impl.hpp"
29 #include <casadi/interfaces/conopt/casadi_nlpsol_conopt_export.h>
30 #include <conopt.h>
31 #include <atomic>
32 #include <vector>
33 #include <string>
34 #include <utility>
35 
36 namespace casadi {
37  class ConoptInterface;
38 
39  enum class ConoptModelStatus {
40  Unset = 0,
41  Optimal = 1,
42  LocallyOptimal = 2,
43  Unbounded = 3,
44  Infeasible = 4,
48  UnknownError = 12,
49  ErrorNoSolution = 13
50  };
51 
52  // CONOPT's TYPE row-type convention (used for both TYPEX[] passed to CONOPT
53  // and the interface's own conopt_type bookkeeping).
54  enum class ConoptRowType {
55  Equality = 0, // an equality constraint
56  GreaterEqual = 1, // a greater than or equal constraint
57  LessEqual = 2, // a less than or equal constraint
58  Free = 3 // a free row
59  };
60 
61  // CONOPT's IniStat=2 basis-status convention, shared by VSTA (variables) and
62  // ESTA (constraint slacks) in the ReadMatrix callback.
63  enum class ConoptBasisStatus {
64  AtLower = 0, // initialized at lower bound
65  AtUpper = 1, // initialized at upper bound
66  Basic = 2, // initialized basic
67  SuperBasic = 3 // initialized superbasic
68  };
69 
70  enum class ConoptSolverStatus {
71  Unset = 0,
72  NormalCompletion = 1,
73  IterationLimit = 2,
74  TimeLimit = 3,
76  EvalErrorLimit = 5,
77  UserInterrupt = 8,
78  SetupFailure = 9,
79  MajorSolverError = 10,
81  SystemError = 13,
83  };
84 
85  struct ConoptMemory : public NlpsolMemory {
86  const ConoptInterface& self;
87  coiHandle_t cntvect;
88 
91  int iter;
92  std::string return_status;
93 
94  // Caching state for the evaluation block
95  std::vector<double> cached_x;
96  double cached_f;
97  std::vector<double> cached_grad_f;
98  std::vector<double> cached_g;
99  std::vector<double> cached_jac_g;
100  std::atomic<bool> cache_valid{false};
101  std::atomic<bool> cache_valid_jac{false}; // true only when cached_jac_g was computed
103 
104  // Options handling
105  std::vector<std::pair<std::string, GenericType>> custom_options;
106 
107  // Constant Jacobian values for linear (NLFLAG=0) entries (populated in solve())
108  std::vector<double> const_jac_vals;
109  // Constant objective gradient values for linear (NLFLAG=0) entries
110  std::vector<double> gradf_const_vals;
111  // Scratch buffer for the linear part of G at x0 per row.
112  std::vector<double> linear_at_x0;
113 
114  // Range-constraint expansion state (recomputed each solve)
117  std::vector<int> row_nnz; // nnz per CasADi row, persistent across solves
118  std::vector<int> conopt_to_casadi; // CONOPT constraint row (0-indexed) → CasADi index
119  std::vector<int> casadi_to_conopt_lb_row; // CasADi index → CONOPT row for lb (or only) side
120  // CasADi index -> CONOPT row for ub side (range only); -1 otherwise
121  std::vector<int> casadi_to_conopt_ub_row;
122  std::vector<ConoptRowType> conopt_type; // CONOPT TYPE for each expanded row
123  std::vector<double> conopt_rhs; // CONOPT RHS for each expanded row
124 
125  // Multiplier buffer for Hessian callback (avoids per-call allocation)
126  std::vector<double> hess_lam_g_;
127 
128  // Stored constant objective value when the interface is in feasibility mode
129  double obj_const_;
130  // Per-row constant absorbed into conopt_rhs, restored in the reported g.
131  std::vector<double> row_const_;
132  // Affine objective constant, restored in the reported f.
134 
135  ConoptMemory(const ConoptInterface& interface);
137  };
138 
139  class ConoptInterface : public Nlpsol {
140  public:
141  explicit ConoptInterface(const std::string& name, const Function& nlp);
142  ~ConoptInterface() override;
143 
144  const char* plugin_name() const override { return "conopt"; }
145  std::string class_name() const override { return "ConoptInterface"; }
146 
147  static Nlpsol* creator(const std::string& name, const Function& nlp) {
148  return new ConoptInterface(name, nlp);
149  }
150 
151  static const Options options_;
152  const Options& get_options() const override { return options_; }
153 
155  static const std::string meta_doc;
156 
157  void init(const Dict& opts) override;
158  void* alloc_mem() const override { return new ConoptMemory(*this); }
159  int init_mem(void* mem) const override;
160  void free_mem(void* mem) const override;
161  void set_work(void* mem, const double**& arg, double**& res,
162  casadi_int*& iw, double*& w) const override;
163  int solve(void* mem) const override;
164  Dict get_stats(void* mem) const override;
165 
166  // Grows conopt_to_casadi/conopt_type/conopt_rhs (which always stay the same
167  // size as each other) when they are full, used by solve()'s row-expansion loop.
168  void ensure_row_capacity(ConoptMemory* m, casadi_int remaining_rows) const;
169 
170  // Sparsities for the problem components
176  bool debug_;
177  Dict opts_; // CONOPT specific options
178  std::string optfile_; // Path to CONOPT option file (for string-valued CR-cells)
179 
180  // Per-column flag: true if the objective gradient has a nonzero in that column
181  std::vector<bool> gradf_col_flag_;
182 
183  // Row-indexed structure for fast Jacobian scatter in cb_fd_eval (nonlinear entries only)
184  std::vector<int> jacg_rowstart_; // size ng_+1; nonlinear nnz prefix sums per row
185  std::vector<int> jacg_nzidx_; // CCS indices of nonlinear entries, in row-major order
186  std::vector<int> jacg_col_; // column (variable) index of each entry in jacg_nzidx_
187 
188  // Per-nonzero linearity flag (0 = constant/linear, 1 = nonlinear), CCS order
189  std::vector<int> jacg_nlflag_;
191 
192  // Objective gradient linearity: flag per nonzero of gradf_sp_
193  std::vector<int> gradf_nlflag_;
195  // Maps variable index → nonzero index in gradf_sp_ (-1 if absent)
196  std::vector<casadi_int> gradf_col_to_nz_;
197 
198  // Serialization and Deserialization
199  void serialize_body(SerializingStream &s) const override;
200  static ProtoFunction* deserialize(DeserializingStream& s) { return new ConoptInterface(s); }
201 
202  // CONOPT Mandatory Callbacks
203  static int COI_CALLCONV cb_read_matrix(double LOWER[], double CURR[], double UPPER[],
204  int VSTA[], int TYPEX[], double RHS[],
205  int ESTA[], int COLSTA[], int ROWNO[],
206  double VALUE[], int NLFLAG[], int NUMVAR,
207  int NUMCON, int NUMNZ, void* USRMEM);
208  static int COI_CALLCONV cb_fdevalini(const double X[], const int ROWLIST[], int MODE,
209  int LISTSIZE, int NUMTHREAD, int IGNERR,
210  int* ERRCNT, int NUMVAR, void* USRMEM);
211  static int COI_CALLCONV cb_fd_eval(const double X[], double* G, double JAC[],
212  int ROWNO, const int JACNUM[], int MODE, int IGNERR,
213  int* ERRCNT, int NUMVAR, int NUMJAC, int THREAD,
214  void* USRMEM);
215  static int COI_CALLCONV cb_fdevalend(int IGNERR, int* ERRCNT, void* USRMEM);
216  static int COI_CALLCONV cb_status(int MODSTA, int SOLSTA, int ITER, double OBJVAL,
217  void* USRMEM);
218  static int COI_CALLCONV cb_solution(const double XVAL[], const double XMAR[],
219  const int XBAS[], const int XSTA[],
220  const double YVAL[], const double YMAR[],
221  const int YBAS[], const int YSTA[],
222  int NUMVAR, int NUMCON, void* USRMEM);
223 
224  // Logging and Messages
225  static int COI_CALLCONV cb_message(int SMSG, int DMSG, int NMSG, char* MSGV[],
226  void* USRMEM);
227  static int COI_CALLCONV cb_errmsg(int ROWNO, int COLNO, int POSNO, const char* MSG,
228  void* USRMEM);
229 
230  // Options and Progress
231  static int COI_CALLCONV cb_option(int NCALL, double* RVAL, int* IVAL, int* LVAL,
232  char* NAME, void* USRMEM);
233  static int COI_CALLCONV cb_progress(int LEN_INT, const int INTX[], int LEN_RL,
234  const double RL[], const double X[], void* USRMEM);
235 
236  // CONOPT 2nd Order Callbacks
237  static int COI_CALLCONV cb_2dlagrstr(int HSRW[], int HSCL[], int* NODRV, int NUMVAR,
238  int NUMCON, int NHESS, void* USRMEM);
239  static int COI_CALLCONV cb_2dlagrval(const double X[], const double U[],
240  const int HSRW[], const int HSCL[], double HSVL[],
241  int* NODRV, int NUMVAR, int NUMCON, int NHESS,
242  void* USRMEM);
243 
244  protected:
246  };
247 } // namespace casadi
248 #endif // CASADI_CONOPT_INTERFACE_HPP
static int COI_CALLCONV cb_fd_eval(const double X[], double *G, double JAC[], int ROWNO, const int JACNUM[], int MODE, int IGNERR, int *ERRCNT, int NUMVAR, int NUMJAC, int THREAD, void *USRMEM)
std::vector< int > jacg_nlflag_
int init_mem(void *mem) const override
void * alloc_mem() const override
static int COI_CALLCONV cb_read_matrix(double LOWER[], double CURR[], double UPPER[], int VSTA[], int TYPEX[], double RHS[], int ESTA[], int COLSTA[], int ROWNO[], double VALUE[], int NLFLAG[], int NUMVAR, int NUMCON, int NUMNZ, void *USRMEM)
void ensure_row_capacity(ConoptMemory *m, casadi_int remaining_rows) const
const Options & get_options() const override
void serialize_body(SerializingStream &s) const override
static int COI_CALLCONV cb_progress(int LEN_INT, const int INTX[], int LEN_RL, const double RL[], const double X[], void *USRMEM)
static int COI_CALLCONV cb_2dlagrstr(int HSRW[], int HSCL[], int *NODRV, int NUMVAR, int NUMCON, int NHESS, void *USRMEM)
std::vector< bool > gradf_col_flag_
static int COI_CALLCONV cb_fdevalend(int IGNERR, int *ERRCNT, void *USRMEM)
std::vector< int > jacg_rowstart_
Dict get_stats(void *mem) const override
std::string class_name() const override
std::vector< casadi_int > gradf_col_to_nz_
static int COI_CALLCONV cb_option(int NCALL, double *RVAL, int *IVAL, int *LVAL, char *NAME, void *USRMEM)
static int COI_CALLCONV cb_fdevalini(const double X[], const int ROWLIST[], int MODE, int LISTSIZE, int NUMTHREAD, int IGNERR, int *ERRCNT, int NUMVAR, void *USRMEM)
ConoptInterface(const std::string &name, const Function &nlp)
std::vector< int > gradf_nlflag_
ConoptInterface(DeserializingStream &s)
static int COI_CALLCONV cb_status(int MODSTA, int SOLSTA, int ITER, double OBJVAL, void *USRMEM)
static const Options options_
std::vector< int > jacg_nzidx_
int solve(void *mem) const override
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
static Nlpsol * creator(const std::string &name, const Function &nlp)
void free_mem(void *mem) const override
static const std::string meta_doc
A documentation string.
static ProtoFunction * deserialize(DeserializingStream &s)
void init(const Dict &opts) override
const char * plugin_name() const override
std::vector< int > jacg_col_
static int COI_CALLCONV cb_2dlagrval(const double X[], const double U[], const int HSRW[], const int HSCL[], double HSVL[], int *NODRV, int NUMVAR, int NUMCON, int NHESS, void *USRMEM)
static int COI_CALLCONV cb_solution(const double XVAL[], const double XMAR[], const int XBAS[], const int XSTA[], const double YVAL[], const double YMAR[], const int YBAS[], const int YSTA[], int NUMVAR, int NUMCON, void *USRMEM)
static int COI_CALLCONV cb_errmsg(int ROWNO, int COLNO, int POSNO, const char *MSG, void *USRMEM)
static int COI_CALLCONV cb_message(int SMSG, int DMSG, int NMSG, char *MSGV[], void *USRMEM)
Helper class for Serialization.
Function object.
Definition: function.hpp:60
Helper class for Serialization.
General sparsity class.
Definition: sparsity.hpp:106
The casadi namespace.
Definition: archiver.hpp:32
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
std::vector< int > casadi_to_conopt_lb_row
std::vector< double > cached_grad_f
std::vector< int > casadi_to_conopt_ub_row
std::vector< std::pair< std::string, GenericType > > custom_options
std::vector< double > cached_g
std::vector< double > row_const_
std::vector< int > row_nnz
std::vector< double > hess_lam_g_
std::vector< double > const_jac_vals
std::vector< double > conopt_rhs
ConoptModelStatus modsta
std::vector< double > gradf_const_vals
std::vector< ConoptRowType > conopt_type
std::vector< int > conopt_to_casadi
std::vector< double > cached_x
ConoptMemory(const ConoptInterface &interface)
ConoptSolverStatus solsta
std::vector< double > linear_at_x0
std::vector< double > cached_jac_g