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 "newton.hpp"
27 #include <iomanip>
28 
29 namespace casadi {
30 
31  extern "C"
32  int CASADI_ROOTFINDER_NEWTON_EXPORT
33  casadi_register_rootfinder_newton(Rootfinder::Plugin* plugin) {
34  plugin->creator = Newton::creator;
35  plugin->name = "newton";
36  plugin->doc = Newton::meta_doc.c_str();
37  plugin->version = CASADI_VERSION;
38  plugin->options = &Newton::options_;
39  plugin->deserialize = &Newton::deserialize;
40  return 0;
41  }
42 
43  extern "C"
44  void CASADI_ROOTFINDER_NEWTON_EXPORT casadi_load_rootfinder_newton() {
46  }
47 
48  Newton::Newton(const std::string& name, const Function& f)
49  : Rootfinder(name, f) {
50  }
51 
53  clear_mem();
54  }
55 
58  {{"abstol",
59  {OT_DOUBLE,
60  "Stopping criterion tolerance on max(|F|)"}},
61  {"abstolStep",
62  {OT_DOUBLE,
63  "Stopping criterion tolerance on step size"}},
64  {"max_iter",
65  {OT_INT,
66  "Maximum number of Newton iterations to perform before returning."}},
67  {"print_iteration",
68  {OT_BOOL,
69  "Print information about each iteration"}},
70  {"line_search",
71  {OT_BOOL,
72  "Enable line-search (default: true)"}}
73  }
74  };
75 
76  void Newton::init(const Dict& opts) {
77 
78  // Call the base class initializer
79  Rootfinder::init(opts);
80 
81  // Default options
82  max_iter_ = 1000;
83  abstol_ = 1e-12;
84  abstolStep_ = 1e-12;
85  print_iteration_ = false;
86  line_search_ = true;
87 
88  // Read options
89  for (auto&& op : opts) {
90  if (op.first=="max_iter") {
91  max_iter_ = op.second;
92  } else if (op.first=="abstol") {
93  abstol_ = op.second;
94  } else if (op.first=="abstolStep") {
95  abstolStep_ = op.second;
96  } else if (op.first=="print_iteration") {
97  print_iteration_ = op.second;
98  } else if (op.first=="line_search") {
99  line_search_ = op.second;
100  }
101  }
102 
103  casadi_assert(oracle_.n_in()>0,
104  "Newton: the supplied f must have at least one input.");
105  casadi_assert(!linsol_.is_null(),
106  "Newton::init: linear_solver must be supplied");
107 
108  set_function(oracle_, "g");
109 
110 
111  // Allocate memory
112  alloc_w(n_, true); // x
113  alloc_w(n_, true); // F
114  alloc_w(n_, true); // dx trial
115  alloc_w(n_, true); // F trial
116  alloc_w(sp_jac_.nnz(), true); // J
117  }
118 
119  void Newton::set_work(void* mem, const double**& arg, double**& res,
120  casadi_int*& iw, double*& w) const {
121  Rootfinder::set_work(mem, arg, res, iw, w);
122  auto m = static_cast<NewtonMemory*>(mem);
123  m->x = w; w += n_;
124  m->f = w; w += n_;
125  m->x_trial = w; w += n_;
126  m->f_trial = w; w += n_;
127  m->jac = w; w += sp_jac_.nnz();
128  }
129 
130  int Newton::solve(void* mem) const {
131  auto m = static_cast<NewtonMemory*>(mem);
132 
133  scoped_checkout<Linsol> mem_linsol(linsol_);
134 
135  // Get the initial guess
136  casadi_copy(m->iarg[iin_], n_, m->x);
137 
138  // Perform the Newton iterations
139  m->iter=0;
140  bool success = true;
141  while (true) {
142  // Break if maximum number of iterations already reached
143  if (m->iter >= max_iter_) {
144  if (verbose_) casadi_message("Max iterations reached.");
145  m->return_status = "max_iteration_reached";
146  m->unified_return_status = SOLVER_RET_LIMITED;
147  success = false;
148  break;
149  }
150 
151  // Start a new iteration
152  m->iter++;
153 
154  // Use x to evaluate g and J
155  std::copy_n(m->iarg, n_in_, m->arg);
156  m->arg[iin_] = m->x;
157  m->res[0] = m->jac;
158  std::copy_n(m->ires, n_out_, m->res+1);
159  m->res[1+iout_] = m->f;
160  calc_function(m, "jac_g_x");
161 
162  // Check convergence
163  double abstol = 0;
164  if (abstol_ != std::numeric_limits<double>::infinity()) {
165  for (casadi_int i=0; i<n_; ++i) {
166  abstol = std::max(abstol, fabs(m->f[i]));
167  }
168  if (abstol <= abstol_) {
169  if (verbose_) casadi_message("Converged to acceptable tolerance: " + str(abstol_));
170  break;
171  }
172  }
173 
174  // Factorize the linear solver with J
175  linsol_.nfact(m->jac, mem_linsol);
176  linsol_.solve(m->jac, m->f, 1, false, mem_linsol);
177 
178  // Check convergence again
179  double abstolStep=0;
180  if (std::numeric_limits<double>::infinity() != abstolStep_) {
181  for (casadi_int i=0; i<n_; ++i) {
182  abstolStep = std::max(abstolStep, fabs(m->f[i]));
183  }
184  if (abstolStep <= abstolStep_) {
185  if (verbose_) casadi_message("Minimal step size reached: " + str(abstolStep_));
186  break;
187  }
188  }
189 
190  double alpha = 1;
191  if (line_search_) {
192  std::copy_n(m->iarg, n_in_, m->arg);
193  m->arg[iin_] = m->x_trial;
194  std::copy_n(m->ires, n_out_, m->res);
195  m->res[iout_] = m->f_trial;
196  while (1) {
197  // Xtrial = Xk - alpha*J^(-1) F
198  std::copy_n(m->x, n_, m->x_trial);
199  casadi_axpy(n_, -alpha, m->f, m->x_trial);
200  calc_function(m, "g");
201 
202  double abstol_trial = casadi_norm_inf(n_, m->f_trial);
203  if (abstol_trial<=(1-alpha/2)*abstol) {
204  std::copy_n(m->x_trial, n_, m->x);
205  break;
206  }
207  if (alpha*abstolStep <= abstolStep_) {
208  if (verbose_) casadi_message("Linesearch did not find a descent step "
209  "for step size " + str(alpha*abstolStep));
210  success = false;
211  break;
212  }
213  alpha*= 0.5;
214  }
215  if (!success) break;
216  } else {
217  // X = Xk - J^(-1) F
218  casadi_axpy(n_, -alpha, m->f, m->x);
219  }
220 
221  if (print_iteration_) {
222  // Only print iteration header once in a while
223  if ((m->iter-1) % 10 ==0) {
224  printIteration(uout());
225  }
226 
227  // Print iteration information
228  printIteration(uout(), m->iter, abstol, abstolStep, alpha);
229  }
230 
231  }
232 
233  // Get the solution
234  casadi_copy(m->x, n_, m->ires[iout_]);
235 
236  // Store the iteration count
237  if (success) m->return_status = "success";
238  if (verbose_) casadi_message("Newton algorithm took " + str(m->iter) + " steps");
239 
240  m->success = success;
241 
242  return 0;
243  }
244 
245  void Newton::printIteration(std::ostream &stream) const {
246  stream << std::setw(5) << "iter";
247  stream << std::setw(10) << "res";
248  stream << std::setw(10) << "step";
249  if (line_search_) stream << std::setw(10) << "alpha";
250  stream << std::endl;
251  stream.unsetf(std::ios::floatfield);
252  }
253 
254  void Newton::printIteration(std::ostream &stream, casadi_int iter,
255  double abstol, double abstolStep, double alpha) const {
256 
257  std::ios_base::fmtflags f = stream.flags();
258  stream << std::setw(5) << iter;
259  stream << std::setw(10) << std::scientific << std::setprecision(2) << abstol;
260  stream << std::setw(10) << std::scientific << std::setprecision(2) << abstolStep;
261  if (line_search_) stream << std::setw(10) << std::scientific << std::setprecision(2) << alpha;
262 
263  stream << std::fixed;
264  stream << std::endl;
265  stream.flags(f);
266  }
267 
268  int Newton::init_mem(void* mem) const {
269  if (Rootfinder::init_mem(mem)) return 1;
270  auto m = static_cast<NewtonMemory*>(mem);
271  m->return_status = "";
272  m->iter = 0;
273  return 0;
274  }
275 
276  Dict Newton::get_stats(void* mem) const {
277  Dict stats = Rootfinder::get_stats(mem);
278  auto m = static_cast<NewtonMemory*>(mem);
279  stats["return_status"] = m->return_status;
280  stats["iter_count"] = m->iter;
281  return stats;
282  }
283 
284 
286  s.version("Newton", 1);
287  s.unpack("Newton::max_iter", max_iter_);
288  s.unpack("Newton::abstol", abstol_);
289  s.unpack("Newton::abstolStep", abstolStep_);
290  s.unpack("Newton::print_iteration", print_iteration_);
291  s.unpack("Newton::line_search", line_search_);
292  }
293 
296  s.version("Newton", 1);
297  s.pack("Newton::max_iter", max_iter_);
298  s.pack("Newton::abstol", abstol_);
299  s.pack("Newton::abstolStep", abstolStep_);
300  s.pack("Newton::print_iteration", print_iteration_);
301  s.pack("Newton::line_search", line_search_);
302  }
303 
304 } // namespace casadi
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
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?
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 set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: newton.cpp:119
static const std::string meta_doc
A documentation string.
Definition: newton.hpp:116
void printIteration(std::ostream &stream) const
Print iteration header.
Definition: newton.cpp:245
static const Options options_
Options.
Definition: newton.hpp:92
int solve(void *mem) const override
Solve the system of equations and calculate derivatives.
Definition: newton.cpp:130
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: newton.cpp:294
bool print_iteration_
If true, each iteration will be printed.
Definition: newton.hpp:141
void init(const Dict &opts) override
Initialize.
Definition: newton.cpp:76
bool line_search_
Definition: newton.hpp:146
Dict get_stats(void *mem) const override
Get all statistics.
Definition: newton.cpp:276
int init_mem(void *mem) const override
Initalize memory block.
Definition: newton.cpp:268
double abstolStep_
Absolute tolerance that should be met on step.
Definition: newton.hpp:138
Newton(const std::string &name, const Function &f)
Constructor.
Definition: newton.cpp:48
static Rootfinder * creator(const std::string &name, const Function &f)
Create a new Rootfinder.
Definition: newton.hpp:86
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into MX.
Definition: newton.hpp:125
casadi_int max_iter_
Maximum number of Newton iterations.
Definition: newton.hpp:132
double abstol_
Absolute tolerance that should be met on residual.
Definition: newton.hpp:135
~Newton() override
Destructor.
Definition: newton.cpp:52
void set_function(const Function &fcn, const std::string &fname, bool jit=false)
Function oracle_
Oracle: Used to generate other functions.
int calc_function(OracleMemory *m, const std::string &fcn, const double *const *arg=nullptr, int thread_id=0) const
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool verbose_
Verbose printout.
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 nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
The casadi namespace.
Definition: archiver.cpp:28
int CASADI_ROOTFINDER_NEWTON_EXPORT casadi_register_rootfinder_newton(Rootfinder::Plugin *plugin)
Definition: newton.cpp:33
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void casadi_axpy(casadi_int n, T1 alpha, const T1 *x, T1 *y)
AXPY: y <- a*x + y.
void CASADI_ROOTFINDER_NEWTON_EXPORT casadi_load_rootfinder_newton()
Definition: newton.cpp:44
T1 casadi_norm_inf(casadi_int n, const T1 *x)
std::ostream & uout()
@ SOLVER_RET_LIMITED
const char * return_status
Definition: newton.hpp:58
Options metadata for a class.
Definition: options.hpp:40