implicit_to_nlp.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 "implicit_to_nlp.hpp"
27 #include "casadi/core/nlpsol.hpp"
28 #include "casadi/core/nlpsol_impl.hpp"
29 
30 namespace casadi {
31 
32  extern "C"
33  int CASADI_ROOTFINDER_NLPSOL_EXPORT
34  casadi_register_rootfinder_nlpsol(Rootfinder::Plugin* plugin) {
35  plugin->creator = ImplicitToNlp::creator;
36  plugin->name = "nlpsol";
37  plugin->doc = ImplicitToNlp::meta_doc.c_str();
38  plugin->version = CASADI_VERSION;
39  plugin->options = &ImplicitToNlp::options_;
40  return 0;
41  }
42 
43  extern "C"
44  void CASADI_ROOTFINDER_NLPSOL_EXPORT casadi_load_rootfinder_nlpsol() {
46  }
47 
48  ImplicitToNlp::ImplicitToNlp(const std::string& name, const Function& f)
49  : Rootfinder(name, f) {
50  }
51 
53  clear_mem();
54  }
55 
58  {{"nlpsol",
59  {OT_STRING,
60  "Name of solver."}},
61  {"nlpsol_options",
62  {OT_DICT,
63  "Options to be passed to solver."}}
64  }
65  };
66 
67  void ImplicitToNlp::init(const Dict& opts) {
68  // Call the base class initializer
69  Rootfinder::init(opts);
70 
71  // Default options
72  std::string nlpsol_plugin;
74 
75  // Read user options
76  for (auto&& op : opts) {
77  if (op.first=="nlpsol") {
78  nlpsol_plugin = op.second.to_string();
79  } else if (op.first=="nlpsol_options") {
80  nlpsol_options = op.second;
81  }
82  }
83 
84  // Free variable in the NLP
85  MX u = MX::sym("u", sparsity_in_.at(iin_));
86 
87  // So that we can pass it on to createParent
88  std::vector<MX> inputs;
89  for (casadi_int i=0; i<n_in_; ++i) {
90  if (i!=iin_) {
91  std::stringstream ss;
92  ss << "p" << i;
93  inputs.push_back(MX::sym(ss.str(), sparsity_in_[i]));
94  }
95  }
96  MX p = veccat(inputs);
97 
98  // Dummy NLP objective
99  MX nlp_f = 0;
100 
101  // NLP constraints
102  std::vector< MX > args_call(n_in_);
103  args_call[iin_] = u;
104  for (casadi_int i=0, i2=0; i<n_in_; ++i)
105  if (i!=iin_) args_call[i] = inputs[i2++];
106  MX nlp_g = oracle_(args_call).at(iout_);
107 
108  // We're going to use two-argument objective and constraints to allow the use of parameters
109  MXDict nlp = {{"x", u}, {"p", p}, {"f", nlp_f}, {"g", nlp_g}};
110 
111  // Create an Nlpsol instance
112  casadi_assert(!nlpsol_plugin.empty(), "'nlpsol' option has not been set");
113  solver_ = nlpsol("nlpsol", nlpsol_plugin, nlp, nlpsol_options);
114  alloc(solver_);
115 
116  // Allocate storage for variable bounds
117  alloc_w(n_, true); // lbx
118  alloc_w(n_, true); // ubx
119 
120  // Allocate storage for NLP solver parameters
121  alloc_w(oracle_.nnz_in() - nnz_in(iin_), true);
122 
123  // Allocate storage for NLP primal solution
124  alloc_w(n_, true);
125  }
126 
127  void ImplicitToNlp::set_work(void* mem, const double**& arg, double**& res,
128  casadi_int*& iw, double*& w) const {
129  Rootfinder::set_work(mem, arg, res, iw, w);
130  auto *m = static_cast<ImplicitToNlpMemory*>(mem);
131  m->lbx = w; w += n_;
132  m->ubx = w; w += n_;
133  m->p = w; w += oracle_.nnz_in() - nnz_in(iin_);
134  m->x = w; w += n_;
135  }
136 
137  int ImplicitToNlp::solve(void* mem) const {
138  auto *m = static_cast<ImplicitToNlpMemory*>(mem);
139 
140  // Buffers for calling the NLP solver
141  std::fill_n(m->arg, static_cast<casadi_int>(NLPSOL_NUM_IN), nullptr);
142  std::fill_n(m->res, static_cast<casadi_int>(NLPSOL_NUM_OUT), nullptr);
143 
144  // Initial guess
145  m->arg[NLPSOL_X] = m->iarg[iin_];
146 
147  // Nonlinear bounds
148  m->arg[NLPSOL_LBG] = nullptr;
149  m->arg[NLPSOL_UBG] = nullptr;
150 
151  // Variable bounds
152  std::fill_n(m->lbx, n_, -std::numeric_limits<double>::infinity());
153  m->arg[NLPSOL_LBX] = m->lbx;
154  std::fill_n(m->ubx, n_, std::numeric_limits<double>::infinity());
155  m->arg[NLPSOL_UBX] = m->ubx;
156  for (casadi_int k=0; k<u_c_.size(); ++k) {
157  if (u_c_[k] > 0) m->lbx[k] = 0;
158  if (u_c_[k] < 0) m->ubx[k] = 0;
159  }
160 
161  // NLP parameters
162  m->arg[NLPSOL_P] = m->p;
163  double* pi = m->p;
164  for (casadi_int i=0; i<n_in_; ++i) {
165  if (i!=iin_) {
166  casadi_int n = oracle_.nnz_in(i);
167  casadi_copy(m->iarg[i], n, pi);
168  pi += n;
169  }
170  }
171 
172  // Primal solution
173  m->res[NLPSOL_X] = m->x;
174 
175  // Solve the NLP
176  solver_(m->arg, m->res, m->iw, m->w, 0);
177 
178  // Get the implicit variable
179  casadi_copy(m->x, n_, m->ires[iout_]);
180 
181  // Check if any auxilary outputs to evaluate
182  bool has_aux = false;
183  for (casadi_int i=0; i<n_out_; ++i) {
184  if (i!=iout_ && m->ires[i]) {
185  has_aux = true;
186  break;
187  }
188  }
189 
190  // Evaluate auxilary outputs, if necessary
191  if (has_aux) {
192  std::copy_n(m->iarg, n_in_, m->arg);
193  m->arg[iin_] = m->x;
194  std::copy_n(m->ires, n_out_, m->res);
195  m->res[iout_] = nullptr;
196  oracle_(m->arg, m->res, m->iw, m->w, 0);
197  }
198 
199  // Shared-object free access to nlpsol return status
200  void* nlpsol_mem = solver_.memory(0);
201  auto *nlpsol_m = static_cast<NlpsolMemory*>(nlpsol_mem);
202  m->success = nlpsol_m->success;
203  m->unified_return_status = nlpsol_m->unified_return_status;
204 
205  return 0;
206  }
207 
208  Dict ImplicitToNlp::get_stats(void* mem) const {
209  Dict stats = Rootfinder::get_stats(mem);
210  stats["nlpsol"] = solver_.stats();
211  return stats;
212  }
213 
214 } // namespace casadi
std::vector< Sparsity > sparsity_in_
Input and output sparsity.
size_t n_in_
Number of inputs and outputs.
casadi_int nnz_in() const
Number of input/output nonzeros.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
void alloc(const Function &f, bool persistent=false, int num_threads=1)
Ensure work vectors long enough to evaluate function.
Function object.
Definition: function.hpp:60
void * memory(int ind) const
Get memory object.
Definition: function.cpp:1781
casadi_int nnz_in() const
Get number of input nonzeros.
Definition: function.cpp:851
Dict stats(int mem=0) const
Get all statistics obtained at the end of the last evaluate call.
Definition: function.cpp:928
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
ImplicitToNlp(const std::string &name, const Function &f)
Constructor.
Dict get_stats(void *mem) const override
Get all statistics.
static const Options options_
Options.
static const std::string meta_doc
A documentation string.
~ImplicitToNlp() override
Destructor.
int solve(void *mem) const override
Solve the system of equations and calculate derivatives.
Function solver_
NLP solver.
static Rootfinder * creator(const std::string &name, const Function &f)
Create a new Rootfinder.
void init(const Dict &opts) override
Initialize.
MX - Matrix expression.
Definition: mx.hpp:92
Function oracle_
Oracle: Used to generate other functions.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
void clear_mem()
Clear all memory (called from destructor)
Internal class.
casadi_int n_
Number of equations.
std::vector< casadi_int > u_c_
Constraints on decision variables.
Dict get_stats(void *mem) const override
Get all statistics.
Definition: rootfinder.cpp:567
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: rootfinder.cpp:298
casadi_int iin_
Indices of the input and output that correspond to the actual root-finding.
static const Options options_
Options.
void init(const Dict &opts) override
Initialize.
Definition: rootfinder.cpp:197
std::vector< std::string > nlpsol_options(const std::string &name)
Get all options for a plugin.
Definition: nlpsol.cpp:830
Function nlpsol(const std::string &name, const std::string &solver, const SXDict &nlp, const Dict &opts)
Definition: nlpsol.cpp:118
The casadi namespace.
Definition: archiver.cpp:28
@ NLPSOL_P
Value of fixed parameters (np x 1)
Definition: nlpsol.hpp:198
@ NLPSOL_UBX
Decision variables upper bound (nx x 1), default +inf.
Definition: nlpsol.hpp:202
@ NLPSOL_UBG
Constraints upper bound (ng x 1), default +inf.
Definition: nlpsol.hpp:206
@ NLPSOL_NUM_IN
Definition: nlpsol.hpp:211
@ NLPSOL_LBG
Constraints lower bound (ng x 1), default -inf.
Definition: nlpsol.hpp:204
@ NLPSOL_LBX
Decision variables lower bound (nx x 1), default -inf.
Definition: nlpsol.hpp:200
void CASADI_ROOTFINDER_NLPSOL_EXPORT casadi_load_rootfinder_nlpsol()
std::map< std::string, MX > MXDict
Definition: mx.hpp:1009
@ NLPSOL_X
Decision variables at the optimal solution (nx x 1)
Definition: nlpsol.hpp:217
@ NLPSOL_NUM_OUT
Definition: nlpsol.hpp:228
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
int CASADI_ROOTFINDER_NLPSOL_EXPORT casadi_register_rootfinder_nlpsol(Rootfinder::Plugin *plugin)
const double pi
Define pi.
Definition: calculus.hpp:46
Integrator memory.
Definition: nlpsol_impl.hpp:40
Options metadata for a class.
Definition: options.hpp:40