|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: solving nonlinear systems of equations
|
|
Packit |
67cb25 |
single: nonlinear systems of equations, solution of
|
|
Packit |
67cb25 |
single: systems of equations, nonlinear
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
*****************************
|
|
Packit |
67cb25 |
Multidimensional Root-Finding
|
|
Packit |
67cb25 |
*****************************
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. include:: include.rst
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This chapter describes functions for multidimensional root-finding
|
|
Packit |
67cb25 |
(solving nonlinear systems with :math:`n` equations in :math:`n`
|
|
Packit |
67cb25 |
unknowns). The library provides low level components for a variety of
|
|
Packit |
67cb25 |
iterative solvers and convergence tests. These can be combined by the
|
|
Packit |
67cb25 |
user to achieve the desired solution, with full access to the
|
|
Packit |
67cb25 |
intermediate steps of the iteration. Each class of methods uses the
|
|
Packit |
67cb25 |
same framework, so that you can switch between solvers at runtime
|
|
Packit |
67cb25 |
without needing to recompile your program. Each instance of a solver
|
|
Packit |
67cb25 |
keeps track of its own state, allowing the solvers to be used in
|
|
Packit |
67cb25 |
multi-threaded programs. The solvers are based on the original Fortran
|
|
Packit |
67cb25 |
library |minpack|.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The header file :file:`gsl_multiroots.h` contains prototypes for the
|
|
Packit |
67cb25 |
multidimensional root finding functions and related declarations.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: multidimensional root finding, overview
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Overview
|
|
Packit |
67cb25 |
========
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The problem of multidimensional root finding requires the simultaneous
|
|
Packit |
67cb25 |
solution of :math:`n` equations, :math:`f_i`, in :math:`n` variables,
|
|
Packit |
67cb25 |
:math:`x_i`,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: f_i (x_1, \dots, x_n) = 0 \qquad\hbox{for}~i = 1 \dots n.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
f_i (x_1, ..., x_n) = 0 for i = 1 ... n.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
In general there are no bracketing methods available for :math:`n`
|
|
Packit |
67cb25 |
dimensional systems, and no way of knowing whether any solutions
|
|
Packit |
67cb25 |
exist. All algorithms proceed from an initial guess using a variant of
|
|
Packit |
67cb25 |
the Newton iteration,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: x \to x' = x - J^{-1} f(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
x -> x' = x - J^{-1} f(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where :math:`x`, :math:`f` are vector quantities and :math:`J` is the
|
|
Packit |
67cb25 |
Jacobian matrix :math:`J_{ij} = \partial f_i / \partial x_j`.
|
|
Packit |
67cb25 |
Additional strategies can be used to enlarge the region of
|
|
Packit |
67cb25 |
convergence. These include requiring a decrease in the norm :math:`|f|` on
|
|
Packit |
67cb25 |
each step proposed by Newton's method, or taking steepest-descent steps in
|
|
Packit |
67cb25 |
the direction of the negative gradient of :math:`|f|`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Several root-finding algorithms are available within a single framework.
|
|
Packit |
67cb25 |
The user provides a high-level driver for the algorithms, and the
|
|
Packit |
67cb25 |
library provides the individual functions necessary for each of the
|
|
Packit |
67cb25 |
steps. There are three main phases of the iteration. The steps are,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* initialize solver state, :data:`s`, for algorithm :data:`T`
|
|
Packit |
67cb25 |
* update :data:`s` using the iteration :data:`T`
|
|
Packit |
67cb25 |
* test :data:`s` for convergence, and repeat iteration if necessary
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The evaluation of the Jacobian matrix can be problematic, either because
|
|
Packit |
67cb25 |
programming the derivatives is intractable or because computation of the
|
|
Packit |
67cb25 |
:math:`n^2` terms of the matrix becomes too expensive. For these reasons
|
|
Packit |
67cb25 |
the algorithms provided by the library are divided into two classes according
|
|
Packit |
67cb25 |
to whether the derivatives are available or not.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: Jacobian matrix, root finding
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The state for solvers with an analytic Jacobian matrix is held in a
|
|
Packit |
67cb25 |
:type:`gsl_multiroot_fdfsolver` struct. The updating procedure requires
|
|
Packit |
67cb25 |
both the function and its derivatives to be supplied by the user.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The state for solvers which do not use an analytic Jacobian matrix is
|
|
Packit |
67cb25 |
held in a :type:`gsl_multiroot_fsolver` struct. The updating procedure
|
|
Packit |
67cb25 |
uses only function evaluations (not derivatives). The algorithms
|
|
Packit |
67cb25 |
estimate the matrix :math:`J` or :math:`J^{-1}`
|
|
Packit |
67cb25 |
by approximate methods.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Initializing the Solver
|
|
Packit |
67cb25 |
=======================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following functions initialize a multidimensional solver, either
|
|
Packit |
67cb25 |
with or without derivatives. The solver itself depends only on the
|
|
Packit |
67cb25 |
dimension of the problem and the algorithm and can be reused for
|
|
Packit |
67cb25 |
different problems.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_multiroot_fsolver
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This is a workspace for multidimensional root-finding without derivatives.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_multiroot_fdfsolver
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This is a workspace for multidimensional root-finding with derivatives.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_multiroot_fsolver * gsl_multiroot_fsolver_alloc (const gsl_multiroot_fsolver_type * T, size_t n)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function returns a pointer to a newly allocated instance of a
|
|
Packit |
67cb25 |
solver of type :data:`T` for a system of :data:`n` dimensions.
|
|
Packit |
67cb25 |
For example, the following code creates an instance of a hybrid solver,
|
|
Packit |
67cb25 |
to solve a 3-dimensional system of equations::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_multiroot_fsolver_type * T = gsl_multiroot_fsolver_hybrid;
|
|
Packit |
67cb25 |
gsl_multiroot_fsolver * s = gsl_multiroot_fsolver_alloc (T, 3);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
If there is insufficient memory to create the solver then the function
|
|
Packit |
67cb25 |
returns a null pointer and the error handler is invoked with an error
|
|
Packit |
67cb25 |
code of :macro:`GSL_ENOMEM`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_multiroot_fdfsolver * gsl_multiroot_fdfsolver_alloc (const gsl_multiroot_fdfsolver_type * T, size_t n)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function returns a pointer to a newly allocated instance of a
|
|
Packit |
67cb25 |
derivative solver of type :data:`T` for a system of :data:`n` dimensions.
|
|
Packit |
67cb25 |
For example, the following code creates an instance of a Newton-Raphson solver,
|
|
Packit |
67cb25 |
for a 2-dimensional system of equations::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_multiroot_fdfsolver_type * T = gsl_multiroot_fdfsolver_newton;
|
|
Packit |
67cb25 |
gsl_multiroot_fdfsolver * s = gsl_multiroot_fdfsolver_alloc (T, 2);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
If there is insufficient memory to create the solver then the function
|
|
Packit |
67cb25 |
returns a null pointer and the error handler is invoked with an error
|
|
Packit |
67cb25 |
code of :macro:`GSL_ENOMEM`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_multiroot_fsolver_set (gsl_multiroot_fsolver * s, gsl_multiroot_function * f, const gsl_vector * x)
|
|
Packit |
67cb25 |
int gsl_multiroot_fdfsolver_set (gsl_multiroot_fdfsolver * s, gsl_multiroot_function_fdf * fdf, const gsl_vector * x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions set, or reset, an existing solver :data:`s` to use the
|
|
Packit |
67cb25 |
function :data:`f` or function and derivative :data:`fdf`, and the initial
|
|
Packit |
67cb25 |
guess :data:`x`. Note that the initial position is copied from :data:`x`, this
|
|
Packit |
67cb25 |
argument is not modified by subsequent iterations.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: void gsl_multiroot_fsolver_free (gsl_multiroot_fsolver * s)
|
|
Packit |
67cb25 |
void gsl_multiroot_fdfsolver_free (gsl_multiroot_fdfsolver * s)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions free all the memory associated with the solver :data:`s`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: const char * gsl_multiroot_fsolver_name (const gsl_multiroot_fsolver * s)
|
|
Packit |
67cb25 |
const char * gsl_multiroot_fdfsolver_name (const gsl_multiroot_fdfsolver * s)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions return a pointer to the name of the solver. For example::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
printf ("s is a '%s' solver\n", gsl_multiroot_fdfsolver_name (s));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
would print something like :code:`s is a 'newton' solver`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: multidimensional root finding, providing a function to solve
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Providing the function to solve
|
|
Packit |
67cb25 |
===============================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
You must provide :math:`n` functions of :math:`n` variables for the root
|
|
Packit |
67cb25 |
finders to operate on. In order to allow for general parameters the
|
|
Packit |
67cb25 |
functions are defined by the following data types:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_multiroot_function
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This data type defines a general system of functions with parameters.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`int (* f) (const gsl_vector * x, void * params, gsl_vector * f)`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
this function should store the vector result
|
|
Packit |
67cb25 |
:math:`f(x,params)` in :data:`f` for argument :data:`x` and parameters :data:`params`,
|
|
Packit |
67cb25 |
returning an appropriate error code if the function cannot be computed.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`size_t n`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
the dimension of the system, i.e. the number of components of the
|
|
Packit |
67cb25 |
vectors :data:`x` and :data:`f`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`void * params`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
a pointer to the parameters of the function.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Here is an example using Powell's test function,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
f_1(x) &= A x_0 x_1 - 1 \\
|
|
Packit |
67cb25 |
f_2(x) &= \exp(-x_0) + \exp(-x_1) - (1 + 1/A)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
f_1(x) = A x_0 x_1 - 1,
|
|
Packit |
67cb25 |
f_2(x) = exp(-x_0) + exp(-x_1) - (1 + 1/A)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
with :math:`A = 10^4`. The following code defines a
|
|
Packit |
67cb25 |
:type:`gsl_multiroot_function` system :code:`F` which you could pass to a
|
|
Packit |
67cb25 |
solver::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
struct powell_params { double A; };
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
powell (gsl_vector * x, void * p, gsl_vector * f) {
|
|
Packit |
67cb25 |
struct powell_params * params
|
|
Packit |
67cb25 |
= (struct powell_params *)p;
|
|
Packit |
67cb25 |
const double A = (params->A);
|
|
Packit |
67cb25 |
const double x0 = gsl_vector_get(x,0);
|
|
Packit |
67cb25 |
const double x1 = gsl_vector_get(x,1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_vector_set (f, 0, A * x0 * x1 - 1);
|
|
Packit |
67cb25 |
gsl_vector_set (f, 1, (exp(-x0) + exp(-x1)
|
|
Packit |
67cb25 |
- (1.0 + 1.0/A)));
|
|
Packit |
67cb25 |
return GSL_SUCCESS
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_multiroot_function F;
|
|
Packit |
67cb25 |
struct powell_params params = { 10000.0 };
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
F.f = &powell;
|
|
Packit |
67cb25 |
F.n = 2;
|
|
Packit |
67cb25 |
F.params = ¶m;;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_multiroot_function_fdf
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This data type defines a general system of functions with parameters and
|
|
Packit |
67cb25 |
the corresponding Jacobian matrix of derivatives,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`int (* f) (const gsl_vector * x, void * params, gsl_vector * f)`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
this function should store the vector result
|
|
Packit |
67cb25 |
:math:`f(x,params)` in :data:`f` for argument :data:`x` and parameters :data:`params`,
|
|
Packit |
67cb25 |
returning an appropriate error code if the function cannot be computed.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`int (* df) (const gsl_vector * x, void * params, gsl_matrix * J)`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
this function should store the :data:`n`-by-:data:`n` matrix result
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: J_{ij} = \partial f_i(x,\hbox{\it params}) / \partial x_j
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
J_ij = d f_i(x,params) / d x_j
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
in :data:`J` for argument :data:`x`
|
|
Packit |
67cb25 |
and parameters :data:`params`, returning an appropriate error code if the
|
|
Packit |
67cb25 |
function cannot be computed.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`int (* fdf) (const gsl_vector * x, void * params, gsl_vector * f, gsl_matrix * J)`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function should set the values of the :data:`f` and :data:`J` as above,
|
|
Packit |
67cb25 |
for arguments :data:`x` and parameters :data:`params`. This function
|
|
Packit |
67cb25 |
provides an optimization of the separate functions for :math:`f(x)` and
|
|
Packit |
67cb25 |
:math:`J(x)`---it is always faster to compute the function and its
|
|
Packit |
67cb25 |
derivative at the same time.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`size_t n`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
the dimension of the system, i.e. the number of components of the
|
|
Packit |
67cb25 |
vectors :data:`x` and :data:`f`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:code:`void * params`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
a pointer to the parameters of the function.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The example of Powell's test function defined above can be extended to
|
|
Packit |
67cb25 |
include analytic derivatives using the following code::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
powell_df (gsl_vector * x, void * p, gsl_matrix * J)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
struct powell_params * params
|
|
Packit |
67cb25 |
= (struct powell_params *)p;
|
|
Packit |
67cb25 |
const double A = (params->A);
|
|
Packit |
67cb25 |
const double x0 = gsl_vector_get(x,0);
|
|
Packit |
67cb25 |
const double x1 = gsl_vector_get(x,1);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 0, 0, A * x1);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 0, 1, A * x0);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 1, 0, -exp(-x0));
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 1, 1, -exp(-x1));
|
|
Packit |
67cb25 |
return GSL_SUCCESS
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
powell_fdf (gsl_vector * x, void * p,
|
|
Packit |
67cb25 |
gsl_matrix * f, gsl_matrix * J) {
|
|
Packit |
67cb25 |
struct powell_params * params
|
|
Packit |
67cb25 |
= (struct powell_params *)p;
|
|
Packit |
67cb25 |
const double A = (params->A);
|
|
Packit |
67cb25 |
const double x0 = gsl_vector_get(x,0);
|
|
Packit |
67cb25 |
const double x1 = gsl_vector_get(x,1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const double u0 = exp(-x0);
|
|
Packit |
67cb25 |
const double u1 = exp(-x1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_vector_set (f, 0, A * x0 * x1 - 1);
|
|
Packit |
67cb25 |
gsl_vector_set (f, 1, u0 + u1 - (1 + 1/A));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 0, 0, A * x1);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 0, 1, A * x0);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 1, 0, -u0);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 1, 1, -u1);
|
|
Packit |
67cb25 |
return GSL_SUCCESS
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_multiroot_function_fdf FDF;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
FDF.f = &powell_f;
|
|
Packit |
67cb25 |
FDF.df = &powell_df;
|
|
Packit |
67cb25 |
FDF.fdf = &powell_fdf;
|
|
Packit |
67cb25 |
FDF.n = 2;
|
|
Packit |
67cb25 |
FDF.params = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Note that the function :code:`powell_fdf` is able to reuse existing terms
|
|
Packit |
67cb25 |
from the function when calculating the Jacobian, thus saving time.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Iteration
|
|
Packit |
67cb25 |
=========
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following functions drive the iteration of each algorithm. Each
|
|
Packit |
67cb25 |
function performs one iteration to update the state of any solver of the
|
|
Packit |
67cb25 |
corresponding type. The same functions work for all solvers so that
|
|
Packit |
67cb25 |
different methods can be substituted at runtime without modifications to
|
|
Packit |
67cb25 |
the code.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_multiroot_fsolver_iterate (gsl_multiroot_fsolver * s)
|
|
Packit |
67cb25 |
int gsl_multiroot_fdfsolver_iterate (gsl_multiroot_fdfsolver * s)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions perform a single iteration of the solver :data:`s`. If the
|
|
Packit |
67cb25 |
iteration encounters an unexpected problem then an error code will be
|
|
Packit |
67cb25 |
returned,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:macro:`GSL_EBADFUNC`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
the iteration encountered a singular point where the function or its
|
|
Packit |
67cb25 |
derivative evaluated to :code:`Inf` or :code:`NaN`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:macro:`GSL_ENOPROG`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
the iteration is not making any progress, preventing the algorithm from
|
|
Packit |
67cb25 |
continuing.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The solver maintains a current best estimate of the root :code:`s->x`
|
|
Packit |
67cb25 |
and its function value :code:`s->f` at all times. This information can
|
|
Packit |
67cb25 |
be accessed with the following auxiliary functions,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_vector * gsl_multiroot_fsolver_root (const gsl_multiroot_fsolver * s)
|
|
Packit |
67cb25 |
gsl_vector * gsl_multiroot_fdfsolver_root (const gsl_multiroot_fdfsolver * s)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions return the current estimate of the root for the solver :data:`s`, given by :code:`s->x`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_vector * gsl_multiroot_fsolver_f (const gsl_multiroot_fsolver * s)
|
|
Packit |
67cb25 |
gsl_vector * gsl_multiroot_fdfsolver_f (const gsl_multiroot_fdfsolver * s)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions return the function value :math:`f(x)` at the current
|
|
Packit |
67cb25 |
estimate of the root for the solver :data:`s`, given by :code:`s->f`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_vector * gsl_multiroot_fsolver_dx (const gsl_multiroot_fsolver * s)
|
|
Packit |
67cb25 |
gsl_vector * gsl_multiroot_fdfsolver_dx (const gsl_multiroot_fdfsolver * s)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions return the last step :math:`dx` taken by the solver
|
|
Packit |
67cb25 |
:data:`s`, given by :code:`s->dx`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: root finding, stopping parameters
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Search Stopping Parameters
|
|
Packit |
67cb25 |
==========================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
A root finding procedure should stop when one of the following conditions is
|
|
Packit |
67cb25 |
true:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* A multidimensional root has been found to within the user-specified precision.
|
|
Packit |
67cb25 |
* A user-specified maximum number of iterations has been reached.
|
|
Packit |
67cb25 |
* An error has occurred.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The handling of these conditions is under user control. The functions
|
|
Packit |
67cb25 |
below allow the user to test the precision of the current result in
|
|
Packit |
67cb25 |
several standard ways.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_multiroot_test_delta (const gsl_vector * dx, const gsl_vector * x, double epsabs, double epsrel)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function tests for the convergence of the sequence by comparing the
|
|
Packit |
67cb25 |
last step :data:`dx` with the absolute error :data:`epsabs` and relative
|
|
Packit |
67cb25 |
error :data:`epsrel` to the current position :data:`x`. The test returns
|
|
Packit |
67cb25 |
:macro:`GSL_SUCCESS` if the following condition is achieved,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: |dx_i| < \hbox{\it epsabs} + \hbox{\it epsrel\/}\, |x_i|
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
|dx_i| < epsabs + epsrel |x_i|
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for each component of :data:`x` and returns :macro:`GSL_CONTINUE` otherwise.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: residual, in nonlinear systems of equations
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_multiroot_test_residual (const gsl_vector * f, double epsabs)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function tests the residual value :data:`f` against the absolute
|
|
Packit |
67cb25 |
error bound :data:`epsabs`. The test returns :macro:`GSL_SUCCESS` if the
|
|
Packit |
67cb25 |
following condition is achieved,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: \sum_i |f_i| < \hbox{\it epsabs}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
\sum_i |f_i| < epsabs
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
and returns :macro:`GSL_CONTINUE` otherwise. This criterion is suitable
|
|
Packit |
67cb25 |
for situations where the precise location of the root, :math:`x`, is
|
|
Packit |
67cb25 |
unimportant provided a value can be found where the residual is small
|
|
Packit |
67cb25 |
enough.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Algorithms using Derivatives
|
|
Packit |
67cb25 |
============================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The root finding algorithms described in this section make use of both
|
|
Packit |
67cb25 |
the function and its derivative. They require an initial guess for the
|
|
Packit |
67cb25 |
location of the root, but there is no absolute guarantee of
|
|
Packit |
67cb25 |
convergence---the function must be suitable for this technique and the
|
|
Packit |
67cb25 |
initial guess must be sufficiently close to the root for it to work.
|
|
Packit |
67cb25 |
When the conditions are satisfied then convergence is quadratic.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_multiroot_fdfsolver_type
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following are available algorithms for minimizing functions using
|
|
Packit |
67cb25 |
derivatives.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index:: HYBRID algorithms for nonlinear systems
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: HYBRIDSJ algorithm
|
|
Packit |
67cb25 |
single: MINPACK, minimization algorithms
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fdfsolver_hybridsj
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This is a modified version of Powell's Hybrid method as implemented in
|
|
Packit |
67cb25 |
the HYBRJ algorithm in |minpack|. Minpack was written by Jorge
|
|
Packit |
67cb25 |
J. |More|, Burton S. Garbow and Kenneth E. Hillstrom. The Hybrid
|
|
Packit |
67cb25 |
algorithm retains the fast convergence of Newton's method but will also
|
|
Packit |
67cb25 |
reduce the residual when Newton's method is unreliable.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The algorithm uses a generalized trust region to keep each step under
|
|
Packit |
67cb25 |
control. In order to be accepted a proposed new position :math:`x'` must
|
|
Packit |
67cb25 |
satisfy the condition :math:`|D (x' - x)| < \delta`, where :math:`D` is a
|
|
Packit |
67cb25 |
diagonal scaling matrix and :math:`\delta` is the size of the trust
|
|
Packit |
67cb25 |
region. The components of :math:`D` are computed internally, using the
|
|
Packit |
67cb25 |
column norms of the Jacobian to estimate the sensitivity of the residual
|
|
Packit |
67cb25 |
to each component of :math:`x`. This improves the behavior of the
|
|
Packit |
67cb25 |
algorithm for badly scaled functions.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
On each iteration the algorithm first determines the standard Newton
|
|
Packit |
67cb25 |
step by solving the system :math:`J dx = - f`. If this step falls inside
|
|
Packit |
67cb25 |
the trust region it is used as a trial step in the next stage. If not,
|
|
Packit |
67cb25 |
the algorithm uses the linear combination of the Newton and gradient
|
|
Packit |
67cb25 |
directions which is predicted to minimize the norm of the function while
|
|
Packit |
67cb25 |
staying inside the trust region,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: dx = - \alpha J^{-1} f(x) - \beta \nabla |f(x)|^2
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This combination of Newton and gradient directions is referred to as a
|
|
Packit |
67cb25 |
*dogleg step*.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The proposed step is now tested by evaluating the function at the
|
|
Packit |
67cb25 |
resulting point, :math:`x'`. If the step reduces the norm of the function
|
|
Packit |
67cb25 |
sufficiently then it is accepted and size of the trust region is
|
|
Packit |
67cb25 |
increased. If the proposed step fails to improve the solution then the
|
|
Packit |
67cb25 |
size of the trust region is decreased and another trial step is
|
|
Packit |
67cb25 |
computed.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The speed of the algorithm is increased by computing the changes to the
|
|
Packit |
67cb25 |
Jacobian approximately, using a rank-1 update. If two successive
|
|
Packit |
67cb25 |
attempts fail to reduce the residual then the full Jacobian is
|
|
Packit |
67cb25 |
recomputed. The algorithm also monitors the progress of the solution
|
|
Packit |
67cb25 |
and returns an error if several steps fail to make any improvement,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:macro:`GSL_ENOPROG`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
the iteration is not making any progress, preventing the algorithm from
|
|
Packit |
67cb25 |
continuing.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
:macro:`GSL_ENOPROGJ`
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
re-evaluations of the Jacobian indicate that the iteration is not
|
|
Packit |
67cb25 |
making any progress, preventing the algorithm from continuing.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index:: HYBRIDJ algorithm
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fdfsolver_hybridj
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This algorithm is an unscaled version of HYBRIDSJ. The steps are
|
|
Packit |
67cb25 |
controlled by a spherical trust region :math:`|x' - x| < \delta`, instead
|
|
Packit |
67cb25 |
of a generalized region. This can be useful if the generalized region
|
|
Packit |
67cb25 |
estimated by HYBRIDSJ is inappropriate.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index:: Newton's method for systems of nonlinear equations
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fdfsolver_newton
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Newton's Method is the standard root-polishing algorithm. The algorithm
|
|
Packit |
67cb25 |
begins with an initial guess for the location of the solution. On each
|
|
Packit |
67cb25 |
iteration a linear approximation to the function :math:`F` is used to
|
|
Packit |
67cb25 |
estimate the step which will zero all the components of the residual.
|
|
Packit |
67cb25 |
The iteration is defined by the following sequence,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: x \to x' = x - J^{-1} f(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
x -> x' = x - J^{-1} f(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where the Jacobian matrix :math:`J` is computed from the derivative
|
|
Packit |
67cb25 |
functions provided by :data:`f`. The step :math:`dx` is obtained by solving
|
|
Packit |
67cb25 |
the linear system,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: J dx = - f(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
using LU decomposition. If the Jacobian matrix is singular, an error
|
|
Packit |
67cb25 |
code of :macro:`GSL_EDOM` is returned.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: Modified Newton's method for nonlinear systems
|
|
Packit |
67cb25 |
single: Newton algorithm, globally convergent
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fdfsolver_gnewton
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This is a modified version of Newton's method which attempts to improve
|
|
Packit |
67cb25 |
global convergence by requiring every step to reduce the Euclidean norm
|
|
Packit |
67cb25 |
of the residual, :math:`|f(x)|`. If the Newton step leads to an increase
|
|
Packit |
67cb25 |
in the norm then a reduced step of relative size,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: t = (\sqrt{1 + 6 r} - 1) / (3 r)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
is proposed, with :math:`r` being the ratio of norms
|
|
Packit |
67cb25 |
:math:`|f(x')|^2/|f(x)|^2`. This procedure is repeated until a suitable step
|
|
Packit |
67cb25 |
size is found.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Algorithms without Derivatives
|
|
Packit |
67cb25 |
==============================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The algorithms described in this section do not require any derivative
|
|
Packit |
67cb25 |
information to be supplied by the user. Any derivatives needed are
|
|
Packit |
67cb25 |
approximated by finite differences. Note that if the
|
|
Packit |
67cb25 |
finite-differencing step size chosen by these routines is inappropriate,
|
|
Packit |
67cb25 |
an explicit user-supplied numerical derivative can always be used with
|
|
Packit |
67cb25 |
the algorithms described in the previous section.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_multiroot_fsolver_type
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following are available algorithms for minimizing functions without
|
|
Packit |
67cb25 |
derivatives.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: HYBRIDS algorithm, scaled without derivatives
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fsolver_hybrids
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This is a version of the Hybrid algorithm which replaces calls to the
|
|
Packit |
67cb25 |
Jacobian function by its finite difference approximation. The finite
|
|
Packit |
67cb25 |
difference approximation is computed using :func:`gsl_multiroots_fdjac`
|
|
Packit |
67cb25 |
with a relative step size of :macro:`GSL_SQRT_DBL_EPSILON`. Note that
|
|
Packit |
67cb25 |
this step size will not be suitable for all problems.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: HYBRID algorithm, unscaled without derivatives
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fsolver_hybrid
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This is a finite difference version of the Hybrid algorithm without
|
|
Packit |
67cb25 |
internal scaling.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: Discrete Newton algorithm for multidimensional roots
|
|
Packit |
67cb25 |
single: Newton algorithm, discrete
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fsolver_dnewton
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The *discrete Newton algorithm* is the simplest method of solving a
|
|
Packit |
67cb25 |
multidimensional system. It uses the Newton iteration
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: x \to x - J^{-1} f(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
x -> x - J^{-1} f(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where the Jacobian matrix :math:`J` is approximated by taking finite
|
|
Packit |
67cb25 |
differences of the function :data:`f`. The approximation scheme used by
|
|
Packit |
67cb25 |
this implementation is,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: J_{ij} = (f_i(x + \delta_j) - f_i(x)) / \delta_j
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where :math:`\delta_j` is a step of size :math:`\sqrt\epsilon |x_j|` with
|
|
Packit |
67cb25 |
:math:`\epsilon` being the machine precision
|
|
Packit |
67cb25 |
(:math:`\epsilon \approx 2.22 \times 10^{-16}`).
|
|
Packit |
67cb25 |
The order of convergence of Newton's algorithm is quadratic, but the
|
|
Packit |
67cb25 |
finite differences require :math:`n^2` function evaluations on each
|
|
Packit |
67cb25 |
iteration. The algorithm may become unstable if the finite differences
|
|
Packit |
67cb25 |
are not a good approximation to the true derivatives.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: Broyden algorithm for multidimensional roots
|
|
Packit |
67cb25 |
single: multidimensional root finding, Broyden algorithm
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. var:: gsl_multiroot_fsolver_broyden
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The *Broyden algorithm* is a version of the discrete Newton
|
|
Packit |
67cb25 |
algorithm which attempts to avoids the expensive update of the Jacobian
|
|
Packit |
67cb25 |
matrix on each iteration. The changes to the Jacobian are also
|
|
Packit |
67cb25 |
approximated, using a rank-1 update,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: J^{-1} \to J^{-1} - (J^{-1} df - dx) dx^T J^{-1} / dx^T J^{-1} df
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where the vectors :math:`dx` and :math:`df` are the changes in :math:`x`
|
|
Packit |
67cb25 |
and :math:`f`. On the first iteration the inverse Jacobian is estimated
|
|
Packit |
67cb25 |
using finite differences, as in the discrete Newton algorithm.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This approximation gives a fast update but is unreliable if the changes
|
|
Packit |
67cb25 |
are not small, and the estimate of the inverse Jacobian becomes worse as
|
|
Packit |
67cb25 |
time passes. The algorithm has a tendency to become unstable unless it
|
|
Packit |
67cb25 |
starts close to the root. The Jacobian is refreshed if this instability
|
|
Packit |
67cb25 |
is detected (consult the source for details).
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This algorithm is included only for demonstration purposes, and is not
|
|
Packit |
67cb25 |
recommended for serious use.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Examples
|
|
Packit |
67cb25 |
========
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The multidimensional solvers are used in a similar way to the
|
|
Packit |
67cb25 |
one-dimensional root finding algorithms. This first example
|
|
Packit |
67cb25 |
demonstrates the HYBRIDS scaled-hybrid algorithm, which does not
|
|
Packit |
67cb25 |
require derivatives. The program solves the Rosenbrock system of equations,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
f_1 (x, y) &= a (1 - x) \\
|
|
Packit |
67cb25 |
f_2 (x, y) &= b (y - x^2)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
f_1 (x, y) = a (1 - x)
|
|
Packit |
67cb25 |
f_2 (x, y) = b (y - x^2)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
with :math:`a = 1, b = 10`. The solution of this system lies at
|
|
Packit |
67cb25 |
:math:`(x,y) = (1,1)` in a narrow valley.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The first stage of the program is to define the system of equations::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <stdlib.h>
|
|
Packit |
67cb25 |
#include <stdio.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_vector.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_multiroots.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
struct rparams
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double a;
|
|
Packit |
67cb25 |
double b;
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
rosenbrock_f (const gsl_vector * x, void *params,
|
|
Packit |
67cb25 |
gsl_vector * f)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double a = ((struct rparams *) params)->a;
|
|
Packit |
67cb25 |
double b = ((struct rparams *) params)->b;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const double x0 = gsl_vector_get (x, 0);
|
|
Packit |
67cb25 |
const double x1 = gsl_vector_get (x, 1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const double y0 = a * (1 - x0);
|
|
Packit |
67cb25 |
const double y1 = b * (x1 - x0 * x0);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_vector_set (f, 0, y0);
|
|
Packit |
67cb25 |
gsl_vector_set (f, 1, y1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The main program begins by creating the function object :code:`f`, with
|
|
Packit |
67cb25 |
the arguments :code:`(x,y)` and parameters :code:`(a,b)`. The solver
|
|
Packit |
67cb25 |
:code:`s` is initialized to use this function, with the :data:`gsl_multiroot_fsolver_hybrids`
|
|
Packit |
67cb25 |
method::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
main (void)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const gsl_multiroot_fsolver_type *T;
|
|
Packit |
67cb25 |
gsl_multiroot_fsolver *s;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
size_t i, iter = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const size_t n = 2;
|
|
Packit |
67cb25 |
struct rparams p = {1.0, 10.0};
|
|
Packit |
67cb25 |
gsl_multiroot_function f = {&rosenbrock_f, n, &p};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double x_init[2] = {-10.0, -5.0};
|
|
Packit |
67cb25 |
gsl_vector *x = gsl_vector_alloc (n);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_vector_set (x, 0, x_init[0]);
|
|
Packit |
67cb25 |
gsl_vector_set (x, 1, x_init[1]);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
T = gsl_multiroot_fsolver_hybrids;
|
|
Packit |
67cb25 |
s = gsl_multiroot_fsolver_alloc (T, 2);
|
|
Packit |
67cb25 |
gsl_multiroot_fsolver_set (s, &f, x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
print_state (iter, s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
do
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
iter++;
|
|
Packit |
67cb25 |
status = gsl_multiroot_fsolver_iterate (s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
print_state (iter, s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (status) /* check if solver is stuck */
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status =
|
|
Packit |
67cb25 |
gsl_multiroot_test_residual (s->f, 1e-7);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
while (status == GSL_CONTINUE && iter < 1000);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
printf ("status = %s\n", gsl_strerror (status));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_multiroot_fsolver_free (s);
|
|
Packit |
67cb25 |
gsl_vector_free (x);
|
|
Packit |
67cb25 |
return 0;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Note that it is important to check the return status of each solver
|
|
Packit |
67cb25 |
step, in case the algorithm becomes stuck. If an error condition is
|
|
Packit |
67cb25 |
detected, indicating that the algorithm cannot proceed, then the error
|
|
Packit |
67cb25 |
can be reported to the user, a new starting point chosen or a different
|
|
Packit |
67cb25 |
algorithm used.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The intermediate state of the solution is displayed by the following
|
|
Packit |
67cb25 |
function. The solver state contains the vector :code:`s->x` which is the
|
|
Packit |
67cb25 |
current position, and the vector :code:`s->f` with corresponding function
|
|
Packit |
67cb25 |
values::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
print_state (size_t iter, gsl_multiroot_fsolver * s)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
printf ("iter = %3u x = % .3f % .3f "
|
|
Packit |
67cb25 |
"f(x) = % .3e % .3e\n",
|
|
Packit |
67cb25 |
iter,
|
|
Packit |
67cb25 |
gsl_vector_get (s->x, 0),
|
|
Packit |
67cb25 |
gsl_vector_get (s->x, 1),
|
|
Packit |
67cb25 |
gsl_vector_get (s->f, 0),
|
|
Packit |
67cb25 |
gsl_vector_get (s->f, 1));
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Here are the results of running the program. The algorithm is started at
|
|
Packit |
67cb25 |
:math:`(-10,-5)` far from the solution. Since the solution is hidden in
|
|
Packit |
67cb25 |
a narrow valley the earliest steps follow the gradient of the function
|
|
Packit |
67cb25 |
downhill, in an attempt to reduce the large value of the residual. Once
|
|
Packit |
67cb25 |
the root has been approximately located, on iteration 8, the Newton
|
|
Packit |
67cb25 |
behavior takes over and convergence is very rapid::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
iter = 0 x = -10.000 -5.000 f(x) = 1.100e+01 -1.050e+03
|
|
Packit |
67cb25 |
iter = 1 x = -10.000 -5.000 f(x) = 1.100e+01 -1.050e+03
|
|
Packit |
67cb25 |
iter = 2 x = -3.976 24.827 f(x) = 4.976e+00 9.020e+01
|
|
Packit |
67cb25 |
iter = 3 x = -3.976 24.827 f(x) = 4.976e+00 9.020e+01
|
|
Packit |
67cb25 |
iter = 4 x = -3.976 24.827 f(x) = 4.976e+00 9.020e+01
|
|
Packit |
67cb25 |
iter = 5 x = -1.274 -5.680 f(x) = 2.274e+00 -7.302e+01
|
|
Packit |
67cb25 |
iter = 6 x = -1.274 -5.680 f(x) = 2.274e+00 -7.302e+01
|
|
Packit |
67cb25 |
iter = 7 x = 0.249 0.298 f(x) = 7.511e-01 2.359e+00
|
|
Packit |
67cb25 |
iter = 8 x = 0.249 0.298 f(x) = 7.511e-01 2.359e+00
|
|
Packit |
67cb25 |
iter = 9 x = 1.000 0.878 f(x) = 1.268e-10 -1.218e+00
|
|
Packit |
67cb25 |
iter = 10 x = 1.000 0.989 f(x) = 1.124e-11 -1.080e-01
|
|
Packit |
67cb25 |
iter = 11 x = 1.000 1.000 f(x) = 0.000e+00 0.000e+00
|
|
Packit |
67cb25 |
status = success
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Note that the algorithm does not update the location on every
|
|
Packit |
67cb25 |
iteration. Some iterations are used to adjust the trust-region
|
|
Packit |
67cb25 |
parameter, after trying a step which was found to be divergent, or to
|
|
Packit |
67cb25 |
recompute the Jacobian, when poor convergence behavior is detected.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The next example program adds derivative information, in order to
|
|
Packit |
67cb25 |
accelerate the solution. There are two derivative functions
|
|
Packit |
67cb25 |
:code:`rosenbrock_df` and :code:`rosenbrock_fdf`. The latter computes both
|
|
Packit |
67cb25 |
the function and its derivative simultaneously. This allows the
|
|
Packit |
67cb25 |
optimization of any common terms. For simplicity we substitute calls to
|
|
Packit |
67cb25 |
the separate :code:`f` and :code:`df` functions at this point in the code
|
|
Packit |
67cb25 |
below::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
rosenbrock_df (const gsl_vector * x, void *params,
|
|
Packit |
67cb25 |
gsl_matrix * J)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const double a = ((struct rparams *) params)->a;
|
|
Packit |
67cb25 |
const double b = ((struct rparams *) params)->b;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const double x0 = gsl_vector_get (x, 0);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const double df00 = -a;
|
|
Packit |
67cb25 |
const double df01 = 0;
|
|
Packit |
67cb25 |
const double df10 = -2 * b * x0;
|
|
Packit |
67cb25 |
const double df11 = b;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 0, 0, df00);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 0, 1, df01);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 1, 0, df10);
|
|
Packit |
67cb25 |
gsl_matrix_set (J, 1, 1, df11);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
rosenbrock_fdf (const gsl_vector * x, void *params,
|
|
Packit |
67cb25 |
gsl_vector * f, gsl_matrix * J)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
rosenbrock_f (x, params, f);
|
|
Packit |
67cb25 |
rosenbrock_df (x, params, J);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The main program now makes calls to the corresponding :code:`fdfsolver`
|
|
Packit |
67cb25 |
versions of the functions::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
main (void)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const gsl_multiroot_fdfsolver_type *T;
|
|
Packit |
67cb25 |
gsl_multiroot_fdfsolver *s;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
size_t i, iter = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const size_t n = 2;
|
|
Packit |
67cb25 |
struct rparams p = {1.0, 10.0};
|
|
Packit |
67cb25 |
gsl_multiroot_function_fdf f = {&rosenbrock_f,
|
|
Packit |
67cb25 |
&rosenbrock_df,
|
|
Packit |
67cb25 |
&rosenbrock_fdf,
|
|
Packit |
67cb25 |
n, &p};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double x_init[2] = {-10.0, -5.0};
|
|
Packit |
67cb25 |
gsl_vector *x = gsl_vector_alloc (n);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_vector_set (x, 0, x_init[0]);
|
|
Packit |
67cb25 |
gsl_vector_set (x, 1, x_init[1]);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
T = gsl_multiroot_fdfsolver_gnewton;
|
|
Packit |
67cb25 |
s = gsl_multiroot_fdfsolver_alloc (T, n);
|
|
Packit |
67cb25 |
gsl_multiroot_fdfsolver_set (s, &f, x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
print_state (iter, s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
do
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
iter++;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = gsl_multiroot_fdfsolver_iterate (s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
print_state (iter, s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = gsl_multiroot_test_residual (s->f, 1e-7);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
while (status == GSL_CONTINUE && iter < 1000);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
printf ("status = %s\n", gsl_strerror (status));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_multiroot_fdfsolver_free (s);
|
|
Packit |
67cb25 |
gsl_vector_free (x);
|
|
Packit |
67cb25 |
return 0;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The addition of derivative information to the :data:`gsl_multiroot_fsolver_hybrids` solver does
|
|
Packit |
67cb25 |
not make any significant difference to its behavior, since it able to
|
|
Packit |
67cb25 |
approximate the Jacobian numerically with sufficient accuracy. To
|
|
Packit |
67cb25 |
illustrate the behavior of a different derivative solver we switch to
|
|
Packit |
67cb25 |
:data:`gsl_multiroot_fdfsolver_gnewton`. This is a traditional Newton solver with the constraint
|
|
Packit |
67cb25 |
that it scales back its step if the full step would lead "uphill". Here
|
|
Packit |
67cb25 |
is the output for the :data:`gsl_multiroot_fdfsolver_gnewton` algorithm::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
iter = 0 x = -10.000 -5.000 f(x) = 1.100e+01 -1.050e+03
|
|
Packit |
67cb25 |
iter = 1 x = -4.231 -65.317 f(x) = 5.231e+00 -8.321e+02
|
|
Packit |
67cb25 |
iter = 2 x = 1.000 -26.358 f(x) = -8.882e-16 -2.736e+02
|
|
Packit |
67cb25 |
iter = 3 x = 1.000 1.000 f(x) = -2.220e-16 -4.441e-15
|
|
Packit |
67cb25 |
status = success
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The convergence is much more rapid, but takes a wide excursion out to
|
|
Packit |
67cb25 |
the point :math:`(-4.23,-65.3)`. This could cause the algorithm to go
|
|
Packit |
67cb25 |
astray in a realistic application. The hybrid algorithm follows the
|
|
Packit |
67cb25 |
downhill path to the solution more reliably.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
References and Further Reading
|
|
Packit |
67cb25 |
==============================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The original version of the Hybrid method is described in the following
|
|
Packit |
67cb25 |
articles by Powell,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* M.J.D. Powell, "A Hybrid Method for Nonlinear Equations" (Chap 6, p
|
|
Packit |
67cb25 |
87--114) and "A Fortran Subroutine for Solving systems of Nonlinear
|
|
Packit |
67cb25 |
Algebraic Equations" (Chap 7, p 115--161), in *Numerical Methods for
|
|
Packit |
67cb25 |
Nonlinear Algebraic Equations*, P. Rabinowitz, editor. Gordon and
|
|
Packit |
67cb25 |
Breach, 1970.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following papers are also relevant to the algorithms described in
|
|
Packit |
67cb25 |
this section,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* J.J. |More|, M.Y. Cosnard, "Numerical Solution of Nonlinear Equations",
|
|
Packit |
67cb25 |
*ACM Transactions on Mathematical Software*, Vol 5, No 1, (1979), p 64--85
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* C.G. Broyden, "A Class of Methods for Solving Nonlinear
|
|
Packit |
67cb25 |
Simultaneous Equations", *Mathematics of Computation*, Vol 19 (1965),
|
|
Packit |
67cb25 |
p 577--593
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* J.J. |More|, B.S. Garbow, K.E. Hillstrom, "Testing Unconstrained
|
|
Packit |
67cb25 |
Optimization Software", ACM Transactions on Mathematical Software, Vol
|
|
Packit |
67cb25 |
7, No 1 (1981), p 17--41
|