fast_newton.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 "fast_newton.hpp"
27 #include <iomanip>
28 #include "../core/linsol_internal.hpp"
29 
30 namespace casadi {
31 
32  extern "C"
33  int CASADI_ROOTFINDER_FAST_NEWTON_EXPORT
34  casadi_register_rootfinder_fast_newton(Rootfinder::Plugin* plugin) {
35  plugin->creator = FastNewton::creator;
36  plugin->name = "fast_newton";
37  plugin->doc = FastNewton::meta_doc.c_str();
38  plugin->version = CASADI_VERSION;
39  plugin->options = &FastNewton::options_;
40  plugin->deserialize = &FastNewton::deserialize;
41  return 0;
42  }
43 
44  extern "C"
45  void CASADI_ROOTFINDER_FAST_NEWTON_EXPORT casadi_load_rootfinder_fast_newton() {
47  }
48 
49  FastNewton::FastNewton(const std::string& name, const Function& f)
50  : Rootfinder(name, f) {
51  }
52 
54  clear_mem();
55  }
56 
59  {{"abstol",
60  {OT_DOUBLE,
61  "Stopping criterion tolerance on ||g||__inf)"}},
62  {"abstolStep",
63  {OT_DOUBLE,
64  "Stopping criterion tolerance on step size"}},
65  {"max_iter",
66  {OT_INT,
67  "Maximum number of Newton iterations to perform before returning."}}
68  }
69  };
70 
71  void FastNewton::init(const Dict& opts) {
72 
73  // Call the base class initializer
74  Rootfinder::init(opts);
75 
76  // Default options
77  max_iter_ = 1000;
78  abstol_ = 1e-12;
79  abstolStep_ = 1e-12;
80 
81  // Read options
82  for (auto&& op : opts) {
83  if (op.first=="max_iter") {
84  max_iter_ = op.second;
85  } else if (op.first=="abstol") {
86  abstol_ = op.second;
87  } else if (op.first=="abstolStep") {
88  abstolStep_ = op.second;
89  }
90  }
91 
92  casadi_assert(oracle_.n_in()>0,
93  "Newton: the supplied f must have at least one input.");
94  casadi_assert(!linsol_.is_null(),
95  "Newton::init: linear_solver must be supplied");
96 
97  jac_g_x_ = get_function("jac_g_x");
98 
99  // Symbolic factorization
101 
102  // Allocate memory
103  alloc_w(n_, true); // x
104  alloc_w(n_, true); // F
105  alloc_w(sp_jac_.nnz(), true); // J
106 
107  alloc_w(sp_jac_.size1() + sp_jac_.size2(), true); // w
108  alloc_w(sp_v_.nnz(), true); // v
109  alloc_w(sp_r_.nnz(), true); // r
110  alloc_w(sp_jac_.size2(), true); // beta
111 
112  }
113 
114  void FastNewton::set_work(void* mem, const double**& arg, double**& res,
115  casadi_int*& iw, double*& w) const {
116  Rootfinder::set_work(mem, arg, res, iw, w);
117  auto m = static_cast<FastNewtonMemory*>(mem);
118  casadi_newton_mem<double>* M = &(m->M);
119 
120  M->n = n_;
121  M->abstol = abstol_;
122  M->abstol_step = abstolStep_;
123 
124  M->x = w; w += n_;
125  M->g = w; w += n_;
126  M->jac_g_x = w; w += sp_jac_.nnz();
127 
128  M->sp_a = sp_jac_;
129  M->sp_v = sp_v_;
130  M->sp_r = sp_r_;
131  M->prinv = get_ptr(prinv_);
132  M->pc = get_ptr(pc_);
133 
134  M->lin_w = w; w+= sp_jac_.size1()+sp_jac_.size2();
135  M->lin_v = w; w+= sp_v_.nnz();
136  M->lin_r = w; w+= sp_r_.nnz();
137  M->lin_beta = w; w+= sp_jac_.size2();
138 
139  }
140 
141  int FastNewton::solve(void* mem) const {
142  auto m = static_cast<FastNewtonMemory*>(mem);
143  casadi_newton_mem<double>* M = &(m->M);
144 
145  // Get the initial guess
146  casadi_copy(m->iarg[iin_], n_, M->x);
147 
148  for (m->iter=0; m->iter<max_iter_; ++m->iter) {
149  /* (re)calculate f and J */
150  // Use x to evaluate J
151  for (casadi_int i=0;i<n_in_;++i) m->arg[i] = m->iarg[i];
152  m->arg[iin_] = M->x;
153  for (casadi_int i=0;i<n_out_;++i) m->res[i+1] = m->ires[i];
154  m->res[0] = M->jac_g_x;
155  m->res[1+iout_] = M->g;
156  jac_g_x_(m->arg, m->res, m->iw, m->w);
157 
158  m->return_status = casadi_newton(M);
159  if (m->return_status) break;
160  }
161  // Get the solution
162  casadi_copy(M->x, n_, m->ires[iout_]);
163 
164  m->success = m->return_status>0;
165  if (m->return_status==0) m->unified_return_status = SOLVER_RET_LIMITED;
166 
167  return 0;
168  }
169 
172 
173  g.local("m", "struct casadi_newton_mem");
174 
175  g << "m.n = " << n_ << ";\n";
176  g << "m.abstol = " << abstol_ << ";\n";
177  g << "m.abstol_step = " << abstolStep_ << ";\n";
178 
179  casadi_int w_offset = 0;
180  g << "m.x = w;\n"; w_offset+=n_;
181  g << "m.g = w+" + str(w_offset) + ";\n"; w_offset+=n_;
182  g << "m.jac_g_x = w+" + str(w_offset) + ";\n"; w_offset+=sp_jac_.nnz();
183 
184  g << "m.sp_a = " + g.sparsity(sp_jac_)+ ";\n";
185  g << "m.sp_v = " + g.sparsity(sp_v_)+ ";\n";
186  g << "m.sp_r = " + g.sparsity(sp_r_)+ ";\n";
187  g << "m.prinv = " + g.constant(prinv_)+ ";\n";
188  g << "m.pc = " + g.constant(pc_)+ ";\n";
189 
190  g << "m.lin_w = w+" + str(w_offset) + ";\n"; w_offset+=sp_jac_.size1()+sp_jac_.size2();
191  g << "m.lin_v = w+" + str(w_offset) + ";\n"; w_offset+=sp_v_.nnz();
192  g << "m.lin_r = w+" + str(w_offset) + ";\n"; w_offset+=sp_r_.nnz();
193  g << "m.lin_beta = w+" + str(w_offset) + ";\n"; w_offset+=sp_jac_.size2();
194 
195  g.comment("Get the initial guess");
196  g << g.copy(g.arg(iin_), n_, "m.x") << "\n";
197 
198  g.local("iter", "casadi_int");
199  g << "for (iter=0; iter<" + str(max_iter_) + "; ++iter) {\n";
200 
201  g.comment("(re)calculate f and J");
202  // Use x to evaluate J
203  for (casadi_int i=0;i<n_in_;++i) {
204  g << g.arg(i+n_in_) << " = " << (i==iin_? "m.x" : g.arg(i)) << ";\n";
205  }
206  g << g.res(n_out_) + " = m.jac_g_x;\n";
207  for (casadi_int i=0;i<n_out_;++i) {
208  g << g.res(i+n_out_+1) + " = " << (i==iout_? "m.g" : g.res(i)) << ";\n";
209  }
210  std::string flag = g(get_function("jac_g_x"),
211  "arg+" + str(n_in_), "res+" + str(n_out_), "iw", "w+" + str(w_offset));
212  g << "if (" << flag << ") return 1;\n";
213  g << "if (casadi_newton(&m)) break;\n";
214  g << "}\n";
215 
216  // Get the solution
217  g.comment("Get the solution");
218  g << g.copy("m.x", n_, g.res(iout_)) << "\n";
219  }
220 
222  g.add_dependency(get_function("jac_g_x"));
223  }
224 
225  int FastNewton::init_mem(void* mem) const {
226  if (Rootfinder::init_mem(mem)) return 1;
227  auto m = static_cast<FastNewtonMemory*>(mem);
228  m->return_status = 0;
229  m->iter = 0;
230  return 0;
231  }
232 
233  std::string return_code(casadi_int status) {
234  switch (status) {
235  case 0:
236  return "max_iteration_reached";
237  case 1:
238  return "converged_abstol";
239  case 2:
240  return "converged_abstol_step";
241  default:
242  return "unknown";
243  }
244  }
245 
246  Dict FastNewton::get_stats(void* mem) const {
247  Dict stats = Rootfinder::get_stats(mem);
248  auto m = static_cast<FastNewtonMemory*>(mem);
249  stats["return_status"] = return_code(m->return_status);
250  stats["iter_count"] = m->iter;
251  return stats;
252  }
253 
254 
256  s.version("Newton", 1);
257  s.unpack("Newton::max_iter", max_iter_);
258  s.unpack("Newton::abstol", abstol_);
259  s.unpack("Newton::abstolStep", abstolStep_);
260  s.unpack("Newton::jac_g_x", jac_g_x_);
261  s.unpack("Newton::sp_v", sp_v_);
262  s.unpack("Newton::sp_r", sp_r_);
263  s.unpack("Newton::prinv", prinv_);
264  s.unpack("Newton::pc", pc_);
265  }
266 
269  s.version("Newton", 1);
270  s.pack("Newton::max_iter", max_iter_);
271  s.pack("Newton::abstol", abstol_);
272  s.pack("Newton::abstolStep", abstolStep_);
273  s.pack("Newton::jac_g_x", jac_g_x_);
274  s.pack("Newton::sp_v", sp_v_);
275  s.pack("Newton::sp_r", sp_r_);
276  s.pack("Newton::prinv", prinv_);
277  s.pack("Newton::pc", pc_);
278  }
279 
280 } // namespace casadi
Helper class for C code generation.
std::string add_dependency(const Function &f)
Add a function dependency.
std::string arg(casadi_int i) const
Refer to argument.
std::string copy(const std::string &arg, std::size_t n, const std::string &res)
Create a copy operation.
void comment(const std::string &s)
Write a comment line (ignored if not verbose)
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
std::string res(casadi_int i) const
Refer to resuly.
std::string sparsity(const Sparsity &sp, bool canonical=true)
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
int solve(void *mem) const override
Solve the system of equations and calculate derivatives.
static Rootfinder * creator(const std::string &name, const Function &f)
Create a new Rootfinder.
Definition: fast_newton.hpp:78
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Sparsity sp_v_
Data for qr.
static const std::string meta_doc
A documentation string.
int init_mem(void *mem) const override
Initalize memory block.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into MX.
Function jac_g_x_
Reference to jacobian function.
double abstol_
Absolute tolerance that should be met on residual.
static const Options options_
Options.
Definition: fast_newton.hpp:84
void codegen_declarations(CodeGenerator &g) const override
Generate code for the declarations of the C function.
FastNewton(const std::string &name, const Function &f)
Constructor.
Definition: fast_newton.cpp:49
casadi_int max_iter_
Maximum number of Newton iterations.
void init(const Dict &opts) override
Initialize.
Definition: fast_newton.cpp:71
Dict get_stats(void *mem) const override
Get all statistics.
double abstolStep_
Absolute tolerance that should be met on step.
void codegen_body(CodeGenerator &g) const override
Generate code for the function body.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
~FastNewton() override
Destructor.
Definition: fast_newton.cpp:53
std::vector< casadi_int > pc_
std::vector< casadi_int > prinv_
size_t n_in_
Number of inputs and outputs.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
Function object.
Definition: function.hpp:60
casadi_int n_in() const
Get the number of function inputs.
Definition: function.cpp:819
bool is_null() const
Is a null pointer?
Function oracle_
Oracle: Used to generate other functions.
std::vector< std::string > get_function() const override
Get list of dependency 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.
int init_mem(void *mem) const override
Initalize memory block.
Definition: rootfinder.cpp:271
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: rootfinder.cpp:575
casadi_int n_
Number of equations.
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.
Linsol linsol_
Linear solver.
void init(const Dict &opts) override
Initialize.
Definition: rootfinder.cpp:197
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 size1() const
Get the number of rows.
Definition: sparsity.cpp:124
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
void qr_sparse(Sparsity &V, Sparsity &R, std::vector< casadi_int > &prinv, std::vector< casadi_int > &pc, bool amd=true) const
Symbolic QR factorization.
Definition: sparsity.cpp:654
The casadi namespace.
Definition: archiver.cpp:28
int CASADI_ROOTFINDER_FAST_NEWTON_EXPORT casadi_register_rootfinder_fast_newton(Rootfinder::Plugin *plugin)
Definition: fast_newton.cpp:34
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
void CASADI_ROOTFINDER_FAST_NEWTON_EXPORT casadi_load_rootfinder_fast_newton()
Definition: fast_newton.cpp:45
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
std::string return_code(casadi_int status)
int casadi_newton(const casadi_newton_mem< T1 > *m)
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
@ SOLVER_RET_LIMITED
Options metadata for a class.
Definition: options.hpp:40