@raggedbottom
@dircategory Scientific software * gsl-ref: (gsl-ref). GNU Scientific Library -- Reference
Los Alamos National Laboratory
Department of Computer Science, Georgia Institute of Technology
Astrophysics and Radiation Measurements Group, Los Alamos National Laboratory
Network Theory Limited
Theoretical Fluid Dynamics Group, Los Alamos National Laboratory
Department of Physics and Astronomy, The Johns Hopkins University
University of Paris-Dauphine Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 The GSL Team.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
The GNU Scientific Library (GSL) is a collection of routines for numerical computing. The routines have been written from scratch in C, and are meant to present a modern Applications Programming Interface (API) for C programmers, while allowing wrappers to be written for very high level languages. The source code is distributed under the GNU General Public License.
The library covers a wide range of topics in numerical computing. Routines are available for the following areas,
Complex Numbers | Roots of Polynomials | |
Special Functions | Vectors and Matrices | |
Permutations | Sorting | |
BLAS Support | Linear Algebra | |
Eigensystems | Fast Fourier Transforms | |
Quadrature | Random Numbers | |
Quasi-Random Sequences | Random Distributions | |
Statistics | Histograms | |
N-Tuples | Monte Carlo Integration | |
Simulated Annealing | Differential Equations | |
Interpolation | Numerical Differentiation | |
Chebyshev Approximations | Series Acceleration | |
Discrete Hankel Transforms | Root-Finding | |
Minimization | Least-Squares Fitting | |
Physical Constants | IEEE Floating-Point |
The use of these routines is described in this manual. Each chapter provides detailed definitions of the functions, followed by example programs and references to the articles on which the algorithms are based.
The subroutines in the GNU Scientific Library are "free software"; this means that everyone is free to use them, and to redistribute them in other free programs. The library is not in the public domain; it is copyrighted and there are conditions on its distribution. These conditions are designed to permit everything that a good cooperating citizen would want to do. What is not allowed is to try to prevent others from further sharing any version of the software that they might get from you.
Specifically, we want to make sure that you have the right to give away copies of any programs related to the GNU Scientific Library, that you receive their source code or else can get it if you want it, that you can change these programs or use pieces of them in new free programs, and that you know you can do these things. The library should not be redistributed in proprietary programs.
To make sure that everyone has such rights, we have to forbid you to deprive anyone else of these rights. For example, if you distribute copies of any related code, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights.
Also, for our own protection, we must make certain that everyone finds out that there is no warranty for the GNU Scientific Library. If these programs are modified by someone else and passed on, we want their recipients to know that what they have is not what we distributed, so that any problems introduced by others will not reflect on our reputation.
The precise conditions for the distribution of software related to the GNU Scientific Library are found in the GNU General Public License (see section GNU General Public License). Further information about this license is available from the GNU Project webpage Frequently Asked Questions about the GNU GPL,
The source code for the library can be obtained in different ways, by copying it from a friend, purchasing it on CDROM or downloading it from the internet. A list of public ftp servers which carry the source code can be found on the development website,
The preferred platform for the library is a GNU system, which allows it to take advantage of additional features. The library is portable and compiles on most Unix platforms. It is also available for Microsoft Windows. Precompiled versions of the library can be purchased from commercial redistributors listed on the website.
Announcements of new releases, updates and other relevant events are
made on the gsl-announce
mailing list. To subscribe to this
low-volume list, send an email of the following form,
To: gsl-announce-request@sources.redhat.com Subject: subscribe
You will receive a response asking to you to reply in order to confirm your subscription.
The following short program demonstrates the use of the library by computing the value of the Bessel function J_0(x) for x=5,
#include <stdio.h> #include <gsl/gsl_sf_bessel.h> int main (void) { double x = 5.0; double y = gsl_sf_bessel_J0 (x); printf("J0(%g) = %.18e\n", x, y); return 0; }
The output is shown below, and should be correct to double-precision accuracy,
J0(5) = -1.775967713143382920e-01
The steps needed to compile programs which use the library are described in the next chapter.
The software described in this manual has no warranty, it is provided "as is". It is your responsibility to validate the behavior of the routines and their accuracy using the source code provided. Consult the GNU General Public license for further details (see section GNU General Public License).
Additional information, including online copies of this manual, links to related projects, and mailing list archives are available from the development website mentioned above. The developers of the library can be reached via the project's public mailing list,
gsl-discuss@sources.redhat.com
This mailing list can be used to report bugs or to ask questions not covered by this manual.
This chapter describes how to compile programs that use GSL, and introduces its conventions.
The library is written in ANSI C and is intended to conform to the ANSI C standard. It should be portable to any system with a working ANSI C compiler.
The library does not rely on any non-ANSI extensions in the interface it exports to the user. Programs you write using GSL can be ANSI compliant. Extensions which can be used in a way compatible with pure ANSI C are supported, however, via conditional compilation. This allows the library to take advantage of compiler extensions on those platforms which support them.
When an ANSI C feature is known to be broken on a particular system the library will exclude any related functions at compile-time. This should make it impossible to link a program that would use these functions and give incorrect results.
To avoid namespace conflicts all exported function names and variables
have the prefix gsl_
, while exported macros have the prefix
GSL_
.
The library header files are installed in their own `gsl' directory. You should write any preprocessor include statements with a `gsl/' directory prefix thus,
#include <gsl/gsl_math.h>
If the directory is not installed on the standard search path of your compiler you will also need to provide its location to the preprocessor as a command line flag. The default location of the `gsl' directory is `/usr/local/include/gsl'.
The library is installed as a single file, `libgsl.a'. A shared version of the library is also installed on systems that support shared libraries. The default location of these files is `/usr/local/lib'. To link against the library you need to specify both the main library and a supporting CBLAS library, which provides standard basic linear algebra subroutines. A suitable CBLAS implementation is provided in the library `libgslcblas.a' if your system does not provide one. The following example shows how to link an application with the library,
gcc app.o -lgsl -lgslcblas -lm
The following command line shows how you would link the same application with an alternative blas library called `libcblas',
gcc app.o -lgsl -lcblas -lm
For the best performance an optimized platform-specific CBLAS
library should be used for -lcblas
. The library must conform to
the CBLAS standard. The ATLAS package provides a portable
high-performance BLAS library with a CBLAS interface. It is
free software and should be installed for any work requiring fast vector
and matrix operations. The following command line will link with the
ATLAS library and its CBLAS interface,
gcc app.o -lgsl -lcblas -latlas -lm
For more information see section BLAS Support.
The program gsl-config
provides information on the local version
of the library. For example, the following command shows that the
library has been installed under the directory `/usr/local',
bash$ gsl-config --prefix /usr/local
Further information is available using the command gsl-config --help
.
To run a program linked with the shared version of the library it may be
necessary to define the shell variable LD_LIBRARY_PATH
to include
the directory where the library is installed. For example,
LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH ./app
To compile a statically linked version of the program instead, use the
-static
flag in gcc
,
gcc -static app.o -lgsl -lgslcblas -lm
For applications using autoconf
the standard macro
AC_CHECK_LIB
can be used to link with the library automatically
from a configure
script. The library itself depends on the
presence of a CBLAS and math library as well, so these must also be
located before linking with the main libgsl
file. The following
commands should be placed in the `configure.in' file to perform
these tests,
AC_CHECK_LIB(m,main) AC_CHECK_LIB(gslcblas,main) AC_CHECK_LIB(gsl,main)
Assuming the libraries are found the output during the configure stage looks like this,
checking for main in -lm... yes checking for main in -lgslcblas... yes checking for main in -lgsl... yes
If the library is found then the tests will define the macros
HAVE_LIBGSL
, HAVE_LIBGSLCBLAS
, HAVE_LIBM
and add
the options -lgsl -lgslcblas -lm
to the variable LIBS
.
The tests above will find any version of the library. They are suitable for general use, where the versions of the functions are not important. An alternative macro is available in the file `gsl.m4' to test for a specific version of the library. To use this macro simply add the following line to your `configure.in' file instead of the tests above:
AM_PATH_GSL(GSL_VERSION, [action-if-found], [action-if-not-found])
The argument GSL_VERSION
should be the two or three digit
MAJOR.MINOR or MAJOR.MINOR.MICRO version number of the release
you require. A suitable choice for action-if-not-found
is,
AC_MSG_ERROR(could not find required version of GSL)
Then you can add the variables GSL_LIBS
and GSL_CFLAGS
to
your Makefile.am files to obtain the correct compiler flags.
GSL_LIBS
is equal to the output of the gsl-config --libs
command and GSL_CFLAGS
is equal to gsl-config --cflags
command. For example,
libgsdv_la_LDFLAGS = \ $(GTK_LIBDIR) \ $(GTK_LIBS) -lgsdvgsl $(GSL_LIBS) -lgslcblas
Note that the macro AM_PATH_GSL
needs to use the C compiler so it
should appear in the `configure.in' file before the macro
AC_LANG_CPLUSPLUS
for programs that use C++.
The inline
keyword is not part of ANSI C and the library does not
export any inline function definitions by default. However, the library
provides optional inline versions of performance-critical functions by
conditional compilation. The inline versions of these functions can be
included by defining the macro HAVE_INLINE
when compiling an
application.
gcc -c -DHAVE_INLINE app.c
If you use autoconf
this macro can be defined automatically.
The following test should be placed in your `configure.in' file,
AC_C_INLINE if test "$ac_cv_c_inline" != no ; then AC_DEFINE(HAVE_INLINE,1) AC_SUBST(HAVE_INLINE) fi
and the macro will then be defined in the compilation flags or by
including the file `config.h' before any library headers. If you
do not define the macro HAVE_INLINE
then the slower non-inlined
versions of the functions will be used instead.
Note that the actual usage of the inline keyword is extern
inline
, which eliminates unnecessary function definitions in GCC.
If the form extern inline
causes problems with other compilers a
stricter autoconf test can be used, see section Autoconf Macros.
The extended numerical type long double
is part of the ANSI C
standard and should be available in every modern compiler. However, the
precision of long double
is platform dependent, and this should
be considered when using it. The IEEE standard only specifies the
minimum precision of extended precision numbers, while the precision of
double
is the same on all platforms.
In some system libraries the stdio.h
formatted input/output
functions printf
and scanf
are not implemented correctly
for long double
. Undefined or incorrect results are avoided by
testing these functions during the configure
stage of library
compilation and eliminating certain GSL functions which depend on them
if necessary. The corresponding line in the configure
output
looks like this,
checking whether printf works with long double... no
Consequently when long double
formatted input/output does not
work on a given system it should be impossible to link a program which
uses GSL functions dependent on this.
If it is necessary to work on a system which does not support formatted
long double
input/output then the options are to use binary
formats or to convert long double
results into double
for
reading and writing.
To help in writing portable applications GSL provides some
implementations of functions that are found in other libraries, such as
the BSD math library. You can write your application to use the native
versions of these functions, and substitute the GSL versions via a
preprocessor macro if they are unavailable on another platform. The
substitution can be made automatically if you use autoconf
. For
example, to test whether the BSD function hypot
is available you
can include the following line in the configure file `configure.in'
for your application,
AC_CHECK_FUNCS(hypot)
and place the following macro definitions in the file `config.h.in',
/* Substitute gsl_hypot for missing system hypot */ #ifndef HAVE_HYPOT #define hypot gsl_hypot #endif
The application source files can then use the include command
#include <config.h>
to substitute gsl_hypot
for each
occurrence of hypot
when hypot
is not available.
In most circumstances the best strategy is to use the native versions of these functions when available, and fall back to GSL versions otherwise, since this allows your application to take advantage of any platform-specific optimizations in the system library. This is the strategy used within GSL itself.
The main implementation of some functions in the library will not be optimal on all architectures. For example, there are several ways to compute a Gaussian random variate and their relative speeds are platform-dependent. In cases like this the library provides alternate implementations of these functions with the same interface. If you write your application using calls to the standard implementation you can select an alternative version later via a preprocessor definition. It is also possible to introduce your own optimized functions this way while retaining portability. The following lines demonstrate the use of a platform-dependent choice of methods for sampling from the Gaussian distribution,
#ifdef SPARC #define gsl_ran_gaussian gsl_ran_gaussian_ratio_method #endif #ifdef INTEL #define gsl_ran_gaussian my_gaussian #endif
These lines would be placed in the configuration header file `config.h' of the application, which should then be included by all the source files. Note that the alternative implementations will not produce bit-for-bit identical results, and in the case of random number distributions will produce an entirely different stream of random variates.
Many functions in the library are defined for different numeric types.
This feature is implemented by varying the name of the function with a
type-related modifier -- a primitive form of C++ templates. The
modifier is inserted into the function name after the initial module
prefix. The following table shows the function names defined for all
the numeric types of an imaginary module gsl_foo
with function
fn
,
gsl_foo_fn double gsl_foo_long_double_fn long double gsl_foo_float_fn float gsl_foo_long_fn long gsl_foo_ulong_fn unsigned long gsl_foo_int_fn int gsl_foo_uint_fn unsigned int gsl_foo_short_fn short gsl_foo_ushort_fn unsigned short gsl_foo_char_fn char gsl_foo_uchar_fn unsigned char
The normal numeric precision double
is considered the default and
does not require a suffix. For example, the function
gsl_stats_mean
computes the mean of double precision numbers,
while the function gsl_stats_int_mean
computes the mean of
integers.
A corresponding scheme is used for library defined types, such as
gsl_vector
and gsl_matrix
. In this case the modifier is
appended to the type name. For example, if a module defines a new
type-dependent struct or typedef gsl_foo
it is modified for other
types in the following way,
gsl_foo double gsl_foo_long_double long double gsl_foo_float float gsl_foo_long long gsl_foo_ulong unsigned long gsl_foo_int int gsl_foo_uint unsigned int gsl_foo_short short gsl_foo_ushort unsigned short gsl_foo_char char gsl_foo_uchar unsigned char
When a module contains type-dependent definitions the library provides individual header files for each type. The filenames are modified as shown in the below. For convenience the default header includes the definitions for all the types. To include only the double precision header, or any other specific type, file use its individual filename.
#include <gsl/gsl_foo.h> All types #include <gsl/gsl_foo_double.h> double #include <gsl/gsl_foo_long_double.h> long double #include <gsl/gsl_foo_float.h> float #include <gsl/gsl_foo_long.h> long #include <gsl/gsl_foo_ulong.h> unsigned long #include <gsl/gsl_foo_int.h> int #include <gsl/gsl_foo_uint.h> unsigned int #include <gsl/gsl_foo_short.h> short #include <gsl/gsl_foo_ushort.h> unsigned short #include <gsl/gsl_foo_char.h> char #include <gsl/gsl_foo_uchar.h> unsigned char
The library header files automatically define functions to have
extern "C"
linkage when included in C++ programs.
The library assumes that arrays, vectors and matrices passed as
modifiable arguments are not aliased and do not overlap with each other.
This removes the need for the library to handle overlapping memory
regions as a special case, and allows additional optimizations to be
used. If overlapping memory regions are passed as modifiable arguments
then the results of such functions will be undefined. If the arguments
will not be modified (for example, if a function prototype declares them
as const
arguments) then overlapping or aliased memory regions
can be safely used.
Where possible the routines in the library have been written to avoid
dependencies between modules and files. This should make it possible to
extract individual functions for use in your own applications, without
needing to have the whole library installed. You may need to define
certain macros such as GSL_ERROR
and remove some #include
statements in order to compile the files as standalone units. Reuse of
the library code in this way is encouraged, subject to the terms of the
GNU General Public License.
This chapter describes the way that GSL functions report and handle errors. By examining the status information returned by every function you can determine whether it succeeded or failed, and if it failed you can find out what the precise cause of failure was. You can also define your own error handling functions to modify the default behavior of the library.
The functions described in this section are declared in the header file `gsl_errno.h'.
The library follows the thread-safe error reporting conventions of the
POSIX Threads library. Functions return a non-zero error code to
indicate an error and 0
to indicate success.
int status = gsl_function(...) if (status) { /* an error occurred */ ..... /* status value specifies the type of error */ }
The routines report an error whenever they cannot perform the task requested of them. For example, a root-finding function would return a non-zero error code if could not converge to the requested accuracy, or exceeded a limit on the number of iterations. Situations like this are a normal occurrence when using any mathematical library and you should check the return status of the functions that you call.
Whenever a routine reports an error the return value specifies the
type of error. The return value is analogous to the value of the
variable errno
in the C library. However, the C library's
errno
is a global variable, which is not thread-safe (There can
be only one instance of a global variable per program. Different
threads of execution may overwrite errno
simultaneously).
Returning the error number directly avoids this problem. The caller can
examine the return code and decide what action to take, including
ignoring the error if it is not considered serious.
The error code numbers are defined in the file `gsl_errno.h'. They
all have the prefix GSL_
and expand to non-zero constant integer
values. Many of the error codes use the same base name as a
corresponding error code in C library. Here are some of the most common
error codes,
malloc
.
int status = gsl_fft_complex_radix2_forward (data, n); if (status) { if (status == GSL_EINVAL) { fprintf (stderr, "invalid argument, n=%d\n", n); } else { fprintf (stderr, "failed, gsl_errno=%d\n", status); } exit (-1); }
The function gsl_fft_complex_radix2
only accepts integer lengths
which are a power of two. If the variable n
is not a power
of two then the call to the library function will return
GSL_EINVAL
, indicating that the length argument is invalid. The
else
clause catches any other possible errors.
The error codes can be converted into an error message using the
function gsl_strerror
.
printf("error: %s\n", gsl_strerror (status));
would print an error message like error: output range error
for a
status value of GSL_ERANGE
.
In addition to reporting errors the library also provides an optional error handler. The error handler is called by library functions when they report an error, just before they return to the caller. The purpose of the handler is to provide a function where a breakpoint can be set that will catch library errors when running under the debugger. It is not intended for use in production programs, which should handle any errors using the error return codes described above.
The default behavior of the error handler is to print a short message
and call abort()
whenever an error is reported by the library.
If this default is not turned off then any program using the library
will stop with a core-dump whenever a library routine reports an error.
This is intended as a fail-safe default for programs which do not check
the return status of library routines (we don't encourage you to write
programs this way). If you turn off the default error handler it is
your responsibility to check the return values of the GSL routines. You
can customize the error behavior by providing a new error handler. For
example, an alternative error handler could log all errors to a file,
ignore certain error conditions (such as underflows), or start the
debugger and attach it to the current process when an error occurs.
All GSL error handlers have the type gsl_error_handler_t
, which is
defined in `gsl_errno.h',
This is the type of GSL error handler functions. An error handler will
be passed four arguments which specify the reason for the error (a
string), the name of the source file in which it occurred (also a
string), the line number in that file (an integer) and the error number
(an integer). The source file and line number are set at compile time
using the __FILE__
and __LINE__
directives in the
preprocessor. An error handler function returns type void
.
Error handler functions should be defined like this,
void handler (const char * reason, const char * file, int line, int gsl_errno)
To request the use of your own error handler you need to call the
function gsl_set_error_handler
which is also declared in
`gsl_errno.h',
This functions sets a new error handler, new_handler, for the GSL library routines. The previous handler is returned (so that you can restore it later). Note that the pointer to a user defined error handler function is stored in a static variable, so there can only be one error handler per program. This function should be not be used in multi-threaded programs except to set up a program-wide error handler from a master thread. The following example shows how to set and restore a new error handler,
/* save original handler, install new handler */ old_handler = gsl_set_error_handler (&my_handler); /* code uses new handler */ ..... /* restore original handler */ gsl_set_error_handler (old_handler);
To use the default behavior (abort
on error) set the error
handler to NULL
,
old_handler = gsl_set_error_handler (NULL);
The error behavior can be changed for specific applications by
recompiling the library with a customized definition of the
GSL_ERROR
macro in the file `gsl_errno.h'.
If you are writing numerical functions in a program which also uses GSL code you may find it convenient to adopt the same error reporting conventions as in the library.
To report an error you need to call the function gsl_error
with a
string describing the error and then return an appropriate error code
from gsl_errno.h
, or a special value, such as NaN
. For
convenience the file `gsl_errno.h' defines two macros which carry
out these steps:
This macro reports an error using the GSL conventions and returns a
status value of gsl_errno
. It expands to the following code fragment,
gsl_error (reason, __FILE__, __LINE__, gsl_errno); return gsl_errno;
The macro definition in `gsl_errno.h' actually wraps the code
in a do { ... } while (0)
block to prevent possible
parsing problems.
Here is an example of how the macro could be used to report that a
routine did not achieve a requested tolerance. To report the error the
routine needs to return the error code GSL_ETOL
.
if (residual > tolerance) { GSL_ERROR("residual exceeds tolerance", GSL_ETOL); }
This macro is the same as GSL_ERROR
but returns a user-defined
status value of value instead of an error code. It can be used for
mathematical functions that return a floating point value.
Here is an example where a function needs to return a NaN
because
of a mathematical singularity,
if (x == 0) { GSL_ERROR_VAL("argument lies on singularity", GSL_ERANGE, GSL_NAN); }
This chapter describes basic mathematical functions. Some of these functions are present in system libraries, but the alternative versions given here can be used as a substitute when the system functions are not available.
The functions and macros described in this chapter are defined in the header file `gsl_math.h'.
The library ensures that the standard BSD mathematical constants are defined. For reference here is a list of the constants.
M_E
M_LOG2E
M_LOG10E
M_SQRT2
M_SQRT1_2
M_SQRT3
M_PI
M_PI_2
M_PI_4
M_SQRTPI
M_2_SQRTPI
M_1_PI
M_2_PI
M_LN10
M_LN2
M_LNPI
M_EULER
+1.0/0.0
.
-1.0/0.0
.
NaN
. It is computed from the ratio 0.0/0.0
.
The following routines provide portable implementations of functions
found in the BSD math library. When native versions are not available
the functions described here can be used instead. The substitution can
be made automatically if you use autoconf
to compile your
application (see section Portability functions).
log1p(x)
.
expm1(x)
.
hypot(x,y)
.
acosh(x)
.
asinh(x)
.
atanh(x)
.
A common complaint about the standard C library is its lack of a function for calculating (small) integer powers. GSL provides a simple functions to fill this gap. For reasons of efficiency, these functions do not check for overflow or underflow conditions.
gsl_sf_pow_int_e
.
#include <gsl/gsl_math.h> double y = gsl_pow_4 (3.141) /* compute 3.141**4 */
((x) >= 0
? 1 : -1)
. Note that with this definition the sign of zero is positive
(regardless of its IEEE sign bit).
GSL_IS_ODD(n)
. It evaluates to 1 if
n is even and 0 if n is odd. The argument n must be of
integer type.
((a) > (b) ? (a):(b))
.
((a) < (b) ? (a):(b))
.
GSL_MAX
will be automatically substituted.
GSL_MIN
will be automatically substituted.
GSL_MAX
or GSL_MIN
will be automatically substituted.
GSL_MAX
or GSL_MIN
will be automatically substituted.
The functions described in this chapter provide support for complex numbers. The algorithms take care to avoid unnecessary intermediate underflows and overflows, allowing the functions to evaluated over the as much of the complex plane as possible.
For multiple-valued functions the branch cuts have been chosen to follow the conventions of Abramowitz and Stegun in the Handbook of Mathematical Functions. The functions return principal values which are the same as those in GNU Calc, which in turn are the same as those in Common Lisp, The Language (Second Edition) (n.b. The second edition uses different definitions from the first edition) and the HP-28/48 series of calculators.
The complex types are defined in the header file `gsl_complex.h', while the corresponding complex functions and arithmetic operations are defined in `gsl_complex_math.h'.
Complex numbers are represented using the type gsl_complex
. The
internal representation of this type may vary across platforms and
should not be accessed directly. The functions and macros described
below allow complex numbers to be manipulated in a portable way.
For reference, the default form of the gsl_complex
type is
given by the following struct,
typedef struct { double dat[2]; } gsl_complex;
The real and imaginary part are stored in contiguous elements of a two
element array. This eliminates any padding between the real and
imaginary parts, dat[0]
and dat[1]
, allowing the struct to
be mapped correctly onto packed complex arrays.
GSL_SET_COMPLEX(&z, 3, 4)
sets z to be 3 + 4i.
log(gsl_complex_abs(z))
would lead to a loss of
precision in this case.
The implementations of the elementary and trigonometric functions are based on the following papers,
The general formulas and details of branch cuts can be found in the following books,
This chapter describes functions for evaluating and solving polynomials.
There are routines for finding real and complex roots of quadratic and
cubic equations using analytic methods. An iterative polynomial solver
is also available for finding the roots of general polynomials with real
coefficients (of any order). The functions are declared in the header
file gsl_poly.h
.
The number of real roots (either zero or two) is returned, and their locations are stored in x0 and x1. If no real roots are found then x0 and x1 are not modified. When two real roots are found they are stored in x0 and x1 in ascending order. The case of coincident roots is not considered special. For example (x-1)^2=0 will have two roots, which happen to have exactly equal values.
The number of roots found depends on the sign of the discriminant b^2 - 4 a c. This will be subject to rounding and cancellation errors when computed in double precision, and will also be subject to errors if the coefficients of the polynomial are inexact. These errors may cause a discrete change in the number of roots. However, for polynomials with small integer coefficients the discriminant can always be computed exactly.
This function finds the complex roots of the quadratic equation,
The number of complex roots is returned (always two) and the locations of the roots are stored in z0 and z1. The roots are returned in ascending order, sorted first by their real components and then by their imaginary components.
This function finds the real roots of the cubic equation,
with a leading coefficient of unity. The number of real roots (either one or three) is returned, and their locations are stored in x0, x1 and x2. If one real root is found then only x0 is modified. When three real roots are found they are stored in x0, x1 and x2 in ascending order. The case of coincident roots is not considered special. For example, the equation (x-1)^3=0 will have three roots with exactly equal values.
This function finds the complex roots of the cubic equation,
The number of complex roots is returned (always three) and the locations of the roots are stored in z0, z1 and z2. The roots are returned in ascending order, sorted first by their real components and then by their imaginary components.
The roots of polynomial equations cannot be found analytically beyond the special cases of the quadratic, cubic and quartic equation. The algorithm described in this section uses an iterative method to find the approximate locations of roots of higher order polynomials.
gsl_poly_complex_workspace
struct and a workspace suitable for solving a polynomial with n
coefficients using the routine gsl_poly_complex_solve
.
The function returns a pointer to the newly allocated
gsl_poly_complex_workspace
if no errors were detected, and a null
pointer in the case of error.
The function returns GSL_SUCCESS
if all the roots are found and
GSL_EFAILED
if the QR reduction does not converge.
To demonstrate the use of the general polynomial solver we will take the polynomial P(x) = x^5 - 1 which has the following roots,
The following program will find these roots.
#include <stdio.h> #include <gsl/gsl_poly.h> int main (void) { int i; /* coefficient of P(x) = -1 + x^5 */ double a[6] = { -1, 0, 0, 0, 0, 1 }; double z[10]; gsl_poly_complex_workspace * w = gsl_poly_complex_workspace_alloc (6); gsl_poly_complex_solve (a, 6, w, z); gsl_poly_complex_workspace_free (w); for (i = 0; i < 5; i++) { printf("z%d = %+.18f %+.18f\n", i, z[2*i], z[2*i+1]); } return 0; }
The output of the program is,
bash$ ./a.out z0 = -0.809016994374947451 +0.587785252292473137 z1 = -0.809016994374947451 -0.587785252292473137 z2 = +0.309016994374947451 +0.951056516295153642 z3 = +0.309016994374947451 -0.951056516295153642 z4 = +1.000000000000000000 +0.000000000000000000
which agrees with the analytic result, z_n = \exp(2 \pi n i/5).
The balanced-QR method and its error analysis is described in the following papers.
This chapter describes the GSL special function library. The library includes routines for calculating the values of Airy functions, Bessel functions, Clausen functions, Coulomb wave functions, Coupling coefficients, the Dawson function, Debye functions, Dilogarithms, Elliptic integrals, Jacobi elliptic functions, Error functions, Exponential integrals, Fermi-Dirac functions, Gamma functions, Gegenbauer functions, Hypergeometric functions, Laguerre functions, Legendre functions and Spherical Harmonics, the Psi (Digamma) Function, Synchrotron functions, Transport functions, Trigonometric functions and Zeta functions. Each routine also computes an estimate of the numerical error in the calculated value of the function.
The functions are declared in individual header files, such as `gsl_sf_airy.h', `gsl_sf_bessel.h', etc. The complete set of header files can be included using the file `gsl_sf.h'.
The special functions are available in two calling conventions, a natural form which returns the numerical value of the function and an error-handling form which returns an error code. The two types of function provide alternative ways of accessing the same underlying code.
The natural form returns only the value of the function and can be used directly in mathematical expressions.. For example, the following function call will compute the value of the Bessel function J_0(x),
double y = gsl_sf_bessel_J0 (x);
There is no way to access an error code or to estimate the error using this method. To allow access to this information the alternative error-handling form stores the value and error in a modifiable argument,
gsl_sf_result result; int status = gsl_sf_bessel_J0_e (x, &result);
The error-handling functions have the suffix _e
. The returned
status value indicates error conditions such as overflow, underflow or
loss of precision. If there are no errors the error-handling functions
return GSL_SUCCESS
.
The error handling form of the special functions always calculate an error estimate along with the value of the result. Therefore, structures are provided for amalgamating a value and error estimate. These structures are declared in the header file `gsl_sf_result.h'.
The gsl_sf_result
struct contains value and error fields.
typedef struct { double val; double err; } gsl_sf_result;
The field val contains the value and the field err contains an estimate of the absolute error in the value.
In some cases, an overflow or underflow can be detected and handled by a
function. In this case, it may be possible to return a scaling exponent
as well as an error/value pair in order to save the result from
exceeding the dynamic range of the built-in types. The
gsl_sf_result_e10
struct contains value and error fields as well
as an exponent field such that the actual result is obtained as
result * 10^(e10)
.
typedef struct { double val; double err; int e10; } gsl_sf_result_e10;
The goal of the library is to achieve double precision accuracy wherever
possible. However the cost of evaluating some special functions to
double precision can be significant, particularly where very high order
terms are required. In these cases a mode
argument allows the
accuracy of the function to be reduced in order to improve performance.
The following precision levels are available for the mode argument,
GSL_PREC_DOUBLE
GSL_PREC_SINGLE
GSL_PREC_APPROX
The approximate mode provides the fastest evaluation at the lowest accuracy.
The Airy functions Ai(x) and Bi(x) are defined by the integral representations,
For further information see Abramowitz & Stegun, Section 10.4. The Airy functions are defined in the header file `gsl_sf_airy.h'.
The routines described in this section compute the Cylindrical Bessel functions J_n(x), Y_n(x), Modified cylindrical Bessel functions I_n(x), K_n(x), Spherical Bessel functions j_l(x), y_l(x), and Modified Spherical Bessel functions i_l(x), k_l(x). For more information see Abramowitz & Stegun, Chapters 9 and 10. The Bessel functions are defined in the header file `gsl_sf_bessel.h'.
The regular modified spherical Bessel functions i_l(x) are related to the modified Bessel functions of fractional order, i_l(x) = \sqrt{\pi/(2x)} I_{l+1/2}(x)
The irregular modified spherical Bessel functions k_l(x) are related to the irregular modified Bessel functions of fractional order, k_l(x) = \sqrt{\pi/(2x)} K_{l+1/2}(x).
The Clausen function is defined by the following integral, It is related to the dilogarithm by Cl_2(\theta) = \Im Li_2(\exp(i \theta)). The Clausen functions are declared in the header file `gsl_sf_clausen.h'.
The Coulomb functions are declared in the header file `gsl_sf_coulomb.h'. Both bound state and scattering solutions are available.
The Coulomb wave functions F_L(\eta,x), G_L(\eta,x) are
described in Abramowitz & Stegun, Chapter 14. Because there can be a
large dynamic range of values for these functions, overflows are handled
gracefully. If an overflow occurs, GSL_EOVRFLW
is signalled and
exponent(s) are returned through the modifiable parameters exp_F,
exp_G. The full solution can be reconstructed from the following
relations,
GSL_EOVRFLW
is returned and scaling exponents are stored in
the modifiable parameters exp_F, exp_G.
The Coulomb wave function normalization constant is defined in Abramowitz 14.1.7.
The Wigner 3-j, 6-j and 9-j symbols give the coupling coefficients for combined angular momentum vectors. Since the arguments of the standard coupling coefficient functions are integer or half-integer, the arguments of the following functions are, by convention, integers equal to twice the actual spin value. For information on the 3-j coefficients see Abramowitz & Stegun, Section 27.9. The functions described in this section are declared in the header file `gsl_sf_coupling.h'.
where the arguments are given in half-integer units, ja = two_ja/2, ma = two_ma/2, etc.
where the arguments are given in half-integer units, ja = two_ja/2, ma = two_ma/2, etc.
where the arguments are given in half-integer units, ja = two_ja/2, ma = two_ma/2, etc.
The Dawson integral is defined by \exp(-x^2) \int_0^x dt \exp(t^2). A table of Dawson's integral can be found in Abramowitz & Stegun, Table 7.5. The Dawson functions are declared in the header file `gsl_sf_dawson.h'.
The Debye functions are defined by the integral D_n(x) = n/x^n \int_0^x dt (t^n/(e^t - 1)). For further information see Abramowitz & Stegun, Section 27.1. The Debye functions are declared in the header file `gsl_sf_debye.h'.
The functions described in this section are declared in the header file `gsl_sf_dilog.h'.
The following functions allow for the propagation of errors when combining quantities by multiplication. The functions are declared in the header file `gsl_sf_elementary.h'.
The functions described in this section are declared in the header file `gsl_sf_ellint.h'.
The Legendre forms of elliptic integrals F(\phi,k), E(\phi,k) and P(\phi,k,n) are defined by,
The complete Legendre forms are denoted by K(k) = F(\pi/2, k) and E(k) = E(\pi/2, k). Further information on the Legendre forms of elliptic integrals can be found in Abramowitz & Stegun, Chapter 17. The notation used here is based on Carlson, Numerische Mathematik 33 (1979) 1 and differs slightly from that used by Abramowitz & Stegun.
The Carlson symmetric forms of elliptical integrals RC(x,y), RD(x,y,z), RF(x,y,z) and RJ(x,y,z,p) are defined by,
The Jacobian Elliptic functions are defined in Abramowitz & Stegun, Chapter 16. The functions are declared in the header file `gsl_sf_elljac.h'.
The error function is described in Abramowitz & Stegun, Chapter 7. The functions in this section are declared in the header file `gsl_sf_erf.h'.
The probability functions for the Normal or Gaussian distribution are described in Abramowitz & Stegun, Section 26.2.
The functions described in this section are declared in the header file `gsl_sf_exp.h'.
gsl_sf_result_e10
type to return a result with extended range.
This function may be useful if the value of \exp(x) would
overflow the numeric range of double
.
gsl_sf_result_e10
type to return a result with extended numeric
range.
gsl_sf_exprel
and
gsl_sf_exprel2
. The N-relative exponential is given by,
gsl_sf_result_e10
type to return a result with
extended range.
gsl_sf_result_e10
type to return a result with extended range.
Information on the exponential integrals can be found in Abramowitz & Stegun, Chapter 5. These functions are declared in the header file `gsl_sf_expint.h'.
M_EULER
).
The functions described in this section are declared in the header file `gsl_sf_fermi_dirac.h'.
The complete Fermi-Dirac integral F_j(x) is given by,
The incomplete Fermi-Dirac integral F_j(x,b) is given by,
The Gamma function is defined by the following integral,
Further information on the Gamma function can be found in Abramowitz & Stegun, Chapter 6. The functions described in this section are declared in the header file `gsl_sf_gamma.h'.
GSL_SF_GAMMA_XMAX
and is 171.0.
and is a useful suggestion of Temme.
GSL_ELOSS
error when it occurs. The absolute
value part (lnr), however, never suffers from loss of precision.
gsl_sf_lngamma
for n < 170,
but defers for larger n.
n choose m
= n!/(m!(n-m)!)
n choose m
. This is
equivalent to the sum \log(n!) - \log(m!) - \log((n-m)!).
The Gegenbauer polynomials are defined in Abramowitz & Stegun, Chapter 22, where they are known as Ultraspherical polynomials. The functions described in this section are declared in the header file `gsl_sf_gegenbauer.h'.
Hypergeometric functions are described in Abramowitz & Stegun, Chapters 13 and 15. These functions are declared in the header file `gsl_sf_hyperg.h'.
gsl_sf_result_e10
type to return a result with extended range.
gsl_sf_result_e10
type to return a
result with extended range.
The Laguerre polynomials are defined in terms of confluent hypergeometric functions as L^a_n(x) = ((a+1)_n / n!) 1F1(-n,a+1,x). These functions are declared in the header file `gsl_sf_laguerre.h'.
Lambert's W functions, W(x), are defined to be solutions of the equation W(x) \exp(W(x)) = x. This function has multiple branches for x < 0; however, it has only two real-valued branches. We define W_0(x) to be the principal branch, where W > -1 for x < 0, and W_{-1}(x) to be the other real branch, where W < -1 for x < 0. The Lambert functions are declared in the header file `gsl_sf_lambert.h'.
The Legendre Functions and Legendre Polynomials are described in Abramowitz & Stegun, Chapter 8. These functions are declared in the header file `gsl_sf_legendre.h'.
The following functions compute the associated Legendre Polynomials
P_l^m(x). Note that this function grows combinatorially with
l and can overflow for l larger than about 150. There is
no trouble for small m, but overflow occurs when m and
l are both large. Rather than allow overflows, these functions
refuse to calculate P_l^m(x) and return GSL_EOVRFLW
when
they can sense that l and m are too big.
If you want to calculate a spherical harmonic, then do not use
these functions. Instead use gsl_sf_legendre_sphPlm()
below,
which uses a similar recursion, but with the normalized functions.
The Conical Functions @c{$P^\mu_{-(1/2)+i\lambda}(x)$} P^\mu_{-(1/2)+i\lambda}(x), @c{$Q^\mu_{-(1/2)+i\lambda}$} Q^\mu_{-(1/2)+i\lambda} are described in Abramowitz & Stegun, Section 8.12.
The following spherical functions are specializations of Legendre functions which give the regular eigenfunctions of the Laplacian on a 3-dimensional hyperbolic space H3d. Of particular interest is the flat limit, \lambda \to \infty, \eta \to 0, \lambda\eta fixed.
Information on the properties of the Logarithm function can be found in Abramowitz & Stegun, Chapter 4. The functions described in this section are declared in the header file `gsl_sf_log.h'.
The following functions are equivalent to the function gsl_pow_int
(see section Small integer powers) with an error estimate. These functions are
declared in the header file `gsl_sf_pow_int.h'.
#include <gsl/gsl_sf_pow_int.h> /* compute 3.0**12 */ double y = gsl_sf_pow_int(3.0, 12);
The polygamma functions of order m defined by \psi^{(m)}(x) = (d/dx)^m \psi(x) = (d/dx)^{m+1} \log(\Gamma(x)), where \psi(x) = \Gamma'(x)/\Gamma(x) is known as the digamma function. These functions are declared in the header file `gsl_sf_psi.h'.
The functions described in this section are declared in the header file `gsl_sf_synchroton.h'.
The transport functions J(n,x) are defined by the integral representations J(n,x) := \int_0^x dt t^n e^t /(e^t - 1)^2. They are declared in the header file `gsl_sf_transport.h'.
The library includes its own trigonometric functions in order to provide consistency across platforms and reliable error estimates. These functions are declared in the header file `gsl_sf_trig.h'.
The Riemann zeta function is defined in Abramowitz & Stegun, Section 23.2. The functions described in this section are declared in the header file `gsl_sf_zeta.h'.
The Riemann zeta function is defined by the infinite sum \zeta(s) = \sum_{k=1}^\infty k^{-s}.
The Hurwitz zeta function is defined by \zeta(s,q) = \sum_0^\infty (k+q)^{-s}.
The eta function is defined by \eta(s) = (1-2^{1-s}) \zeta(s).
The following example demonstrates the use of the error handling form of the special functions, in this case to compute the Bessel function J_0(5.0),
#include <stdio.h> #include <gsl/gsl_sf_bessel.h> int main (void) { double x = 5.0; gsl_sf_result result; double expected = -0.17759677131433830434739701; int status = gsl_sf_bessel_J0_e (x, &result); printf("status = %s\n", gsl_strerror(status)); printf("J0(5.0) = %.18f\n" " +/- % .18f\n", result.val, result.err); printf("exact = %.18f\n", expected); return status; }
Here are the results of running the program,
$ ./a.out status = success J0(5.0) = -0.177596771314338292 +/- 0.000000000000000193 exact = -0.177596771314338292
The next program computes the same quantity using the natural form of the function. In this case the error term result.err and return status are not accessible.
#include <stdio.h> #include <gsl/gsl_sf_bessel.h> int main (void) { double x = 5.0; double expected = -0.17759677131433830434739701; double y = gsl_sf_bessel_J0 (x); printf("J0(5.0) = %.18f\n", y); printf("exact = %.18f\n", expected); return 0; }
The results of the function are the same,
$ ./a.out J0(5.0) = -0.177596771314338292 exact = -0.177596771314338292
The library follows the conventions of Abramowitz & Stegun where possible,
The following papers contain information on the algorithms used to compute the special functions,
The functions described in this chapter provide a simple vector and matrix interface to ordinary C arrays. The memory management of these arrays is implemented using a single underlying type, known as a block. By writing your functions in terms of vectors and matrices you can pass a single structure containing both data and dimensions as an argument without needing additional function parameters. The structures are compatible with the vector and matrix formats used by BLAS routines.
All the functions are available for each of the standard data-types.
The versions for double
have the prefix gsl_block
,
gsl_vector
and gsl_matrix
. Similarly the versions for
single-precision float
arrays have the prefix
gsl_block_float
, gsl_vector_float
and
gsl_matrix_float
. The full list of available types is given
below,
gsl_block double gsl_block_float float gsl_block_long_double long double gsl_block_int int gsl_block_uint unsigned int gsl_block_long long gsl_block_ulong unsigned long gsl_block_short short gsl_block_ushort unsigned short gsl_block_char char gsl_block_uchar unsigned char gsl_block_complex complex double gsl_block_complex_float complex float gsl_block_complex_long_double complex long double
Corresponding types exist for the gsl_vector
and
gsl_matrix
functions.
For consistency all memory is allocated through a gsl_block
structure. The structure contains two components, the size of an area of
memory and a pointer to the memory. The gsl_block
structure looks
like this,
typedef struct { size_t size; double * data; } gsl_block;
Vectors and matrices are made by slicing an underlying block. A slice is a set of elements formed from an initial offset and a combination of indices and step-sizes. In the case of a matrix the step-size for the column index represents the row-length. The step-size for a vector is known as the stride.
The functions for allocating and deallocating blocks are defined in `gsl_block.h'
The functions for allocating memory to a block follow the style of
malloc
and free
. In addition they also perform their own
error checking. If there is insufficient memory available to allocate a
block then the functions call the GSL error handler (with an error
number of GSL_ENOMEM
) in addition to returning a null
pointer. Thus if you use the library error handler to abort your program
then it isn't necessary to check every alloc
.
gsl_block_calloc
if you want to ensure that all the
elements are initialized to zero.
A null pointer is returned if insufficient memory is available to create the block.
gsl_block_alloc
or gsl_block_calloc
.
The library provides functions for reading and writing blocks to a file as binary data or formatted text.
GSL_EFAILED
if there was a problem writing to the file. Since the
data is written in the native binary format it may not be portable
between different architectures.
GSL_EFAILED
if there was a problem reading from the file. The
data is assumed to have been written in the native binary format on the
same architecture.
%g
, %e
or %f
formats for
floating point numbers and %d
for integers. The function returns
0 for success and GSL_EFAILED
if there was a problem writing to
the file.
GSL_EFAILED
if there was a problem reading from the file.
The following program shows how to allocate a block,
#include <stdio.h> #include <gsl/gsl_block.h> int main (void) { gsl_block * b = gsl_block_alloc (100); printf("length of block = %u\n", b->size); printf("block data address = %#x\n", b->data); gsl_block_free (b); return 0; }
Here is the output from the program,
length of block = 100 block data address = 0x804b0d8
Vectors are defined by a gsl_vector
structure which describes a
slice of a block. Different vectors can be created which point to the
same block. A vector slice is a set of equally-spaced elements of an
area of memory.
The gsl_vector
structure contains five components, the
size, the stride, a pointer to the memory where the elements
are stored, data, a pointer to the block owned by the vector,
block, if any, and an ownership flag, owner. The structure
is very simple and looks like this,
typedef struct { size_t size; size_t stride; double * data; gsl_block * block; int owner; } gsl_vector;
The size is simply the number of vector elements. The range of
valid indices runs from 0 to size-1
. The stride is the
step-size from one element to the next in physical memory, measured in
units of the appropriate datatype. The pointer data gives the
location of the first element of the vector in memory. The pointer
block stores the location of the memory block in which the vector
elements are located (if any). If the vector owns this block then the
owner field is set to one and the block will be deallocated when the
vector is freed. If the vector points to a block owned by another
object then the owner field is zero and any underlying block will not be
deallocated.
The functions for allocating and accessing vectors are defined in `gsl_vector.h'
The functions for allocating memory to a vector follow the style of
malloc
and free
. In addition they also perform their own
error checking. If there is insufficient memory available to allocate a
vector then the functions call the GSL error handler (with an error
number of GSL_ENOMEM
) in addition to returning a null
pointer. Thus if you use the library error handler to abort your program
then it isn't necessary to check every alloc
.
gsl_vector_alloc
then the block
underlying the vector will also be deallocated. If the vector has been
created from another object then the memory is still owned by that
object and will not be deallocated.
Unlike FORTRAN compilers, C compilers do not usually provide
support for range checking of vectors and matrices. Range checking is
available in the GNU C Compiler extension checkergcc
but it is
not available on every platform. The functions gsl_vector_get
and gsl_vector_set
can perform portable range checking for you
and report an error if you attempt to access elements outside the
allowed range.
The functions for accessing the elements of a vector or matrix are
defined in `gsl_vector.h' and declared extern inline
to
eliminate function-call overhead. If necessary you can turn off range
checking completely without modifying any source files by recompiling
your program with the preprocessor definition
GSL_RANGE_CHECK_OFF
. Provided your compiler supports inline
functions the effect of turning off range checking is to replace calls
to gsl_vector_get(v,i)
by v->data[i*v->stride]
and and
calls to gsl_vector_set(v,i,x)
by v->data[i*v->stride]=x
.
Thus there should be no performance penalty for using the range checking
functions when range checking is turned off.
The library provides functions for reading and writing vectors to a file as binary data or formatted text.
GSL_EFAILED
if there was a problem writing to the file. Since the
data is written in the native binary format it may not be portable
between different architectures.
GSL_EFAILED
if there was a problem reading from the file. The
data is assumed to have been written in the native binary format on the
same architecture.
%g
, %e
or %f
formats for
floating point numbers and %d
for integers. The function returns
0 for success and GSL_EFAILED
if there was a problem writing to
the file.
GSL_EFAILED
if there was a problem reading from the file.
In addition to creating vectors from slices of blocks it is also possible to slice vectors and create vector views. For example, a subvector of another vector can be described with a view, or two views can be made which provide access to the even and odd elements of a vector.
A vector view is a temporary object, stored on the stack, which can be
used to operate on a subset of vector elements. Vector views can be
defined for both constant and non-constant vectors, using separate types
that preserve constness. A vector view has the type
gsl_vector_view
and a constant vector view has the type
gsl_vector_const_view
. In both cases the elements of the view
can be accessed as a gsl_vector
using the vector
component
of the view object. A pointer to a vector of type gsl_vector *
or const gsl_vector *
can be obtained by taking the address of
this component with the &
operator.
v'(i) = v->data[(offset + i)*v->stride]
where the index i runs from 0 to n-1
.
The data
pointer of the returned vector struct is set to null if
the combined parameters (offset,n) overrun the end of the
original vector.
The new vector is only a view of the block underlying the original vector, v. The block containing the elements of v is not owned by the new vector. When the view goes out of scope the original vector v and its block will continue to exist. The original memory can only be deallocated by freeing the original vector. Of course, the original vector should not be deallocated while the view is still in use.
The function gsl_vector_const_subvector
is equivalent to
gsl_vector_subvector
but can be used for vectors which are
declared const
.
gsl_vector_subvector
but the new vector has
n elements with a step-size of stride from one element to
the next in the original vector. Mathematically, the i-th element
of the new vector v' is given by,
v'(i) = v->data[(offset + i*stride)*v->stride]
where the index i runs from 0 to n-1
.
Note that subvector views give direct access to the underlying elements
of the original vector. For example, the following code will zero the
even elements of the vector v
of length n
, while leaving the
odd elements untouched,
gsl_vector_view v_even = gsl_vector_subvector_with_stride (v, 0, 2, n/2); gsl_vector_set_zero (&v_even.vector);
A vector view can be passed to any subroutine which takes a vector
argument just as a directly allocated vector would be, using
&
view.vector
. For example, the following code
computes the norm of odd elements of v
using the BLAS
routine DNRM2,
gsl_vector_view v_odd = gsl_vector_subvector_with_stride (v, 1, 2, n/2); double r = gsl_blas_dnrm2 (&v_odd.vector);
The function gsl_vector_const_subvector_with_stride
is equivalent
to gsl_vector_subvector_with_stride
but can be used for vectors
which are declared const
.
The function gsl_vector_complex_const_real
is equivalent to
gsl_vector_complex_real
but can be used for vectors which are
declared const
.
The function gsl_vector_complex_const_imag
is equivalent to
gsl_vector_complex_imag
but can be used for vectors which are
declared const
.
v'(i) = base[i]
where the index i runs from 0 to n-1
.
The array containing the elements of v is not owned by the new vector view. When the view goes out of scope the original array will continue to exist. The original memory can only be deallocated by freeing the original pointer base. Of course, the original array should not be deallocated while the view is still in use.
The function gsl_vector_const_view_array
is equivalent to
gsl_vector_view_array
but can be used for arrays which are
declared const
.
gsl_vector_view_array
but the new vector has n elements
with a step-size of stride from one element to the next in the
original array. Mathematically, the i-th element of the new
vector v' is given by,
v'(i) = base[i*stride]
where the index i runs from 0 to n-1
.
Note that the view gives direct access to the underlying elements of the
original array. A vector view can be passed to any subroutine which
takes a vector argument just as a directly allocated vector would be,
using &
view.vector
.
The function gsl_vector_const_view_array_with_stride
is
equivalent to gsl_vector_view_array_with_stride
but can be used
for arrays which are declared const
.
Common operations on vectors such as addition and multiplication are available in the BLAS part of the library (see section BLAS Support). However, it is useful to have a small number of utility functions which do not require the full BLAS code. The following functions fall into this category.
The following function can be used to exchange, or permute, the elements of a vector.
The following operations are only defined for real vectors.
This program shows how to allocate, initialize and read from a vector
using the functions gsl_vector_alloc
, gsl_vector_set
and
gsl_vector_get
.
#include <stdio.h> #include <gsl/gsl_vector.h> int main (void) { int i; gsl_vector * v = gsl_vector_alloc (3); for (i = 0; i < 3; i++) { gsl_vector_set (v, i, 1.23 + i); } for (i = 0; i < 100; i++) { printf("v_%d = %g\n", i, gsl_vector_get (v, i)); } return 0; }
Here is the output from the program. The final loop attempts to read
outside the range of the vector v
, and the error is trapped by
the range-checking code in gsl_vector_get
.
v_0 = 1.23 v_1 = 2.23 v_2 = 3.23 gsl: vector_source.c:12: ERROR: index out of range IOT trap/Abort (core dumped)
The next program shows how to write a vector to a file.
#include <stdio.h> #include <gsl/gsl_vector.h> int main (void) { int i; gsl_vector * v = gsl_vector_alloc (100); for (i = 0; i < 100; i++) { gsl_vector_set (v, i, 1.23 + i); } { FILE * f = fopen("test.dat", "w"); gsl_vector_fprintf (f, v, "%.5g"); fclose (f); } return 0; }
After running this program the file `test.dat' should contain the
elements of v
, written using the format specifier
%.5g
. The vector could then be read back in using the function
gsl_vector_fscanf (f, v)
.
Matrices are defined by a gsl_matrix
structure which describes a
generalized slice of a block. Like a vector it represents a set of
elements in an area of memory, but uses two indices instead of one.
The gsl_matrix
structure contains six components, the two
dimensions of the matrix, a physical dimension, a pointer to the memory
where the elements of the matrix are stored, data, a pointer to
the block owned by the matrix block, if any, and an ownership
flag, owner. The physical dimension determines the memory layout
and can differ from the matrix dimension to allow the use of
submatrices. The gsl_matrix
structure is very simple and looks
like this,
typedef struct { size_t size1; size_t size2; size_t tda; double * data; gsl_block * block; int owner; } gsl_matrix;
Matrices are stored in row-major order, meaning that each row of
elements forms a contiguous block in memory. This is the standard
"C-language ordering" of two-dimensional arrays. Note that FORTRAN
stores arrays in column-major order. The number of rows is size1.
The range of valid row indices runs from 0 to size1-1
. Similarly
size2 is the number of columns. The range of valid column indices
runs from 0 to size2-1
. The physical row dimension tda, or
trailing dimension, specifies the size of a row of the matrix as
laid out in memory.
For example, in the following matrix size1 is 3, size2 is 4, and tda is 8. The physical memory layout of the matrix begins in the top left hand-corner and proceeds from left to right along each row in turn.
00 01 02 03 XX XX XX XX 10 11 12 13 XX XX XX XX 20 21 22 23 XX XX XX XX
Each unused memory location is represented by "XX
". The
pointer data gives the location of the first element of the matrix
in memory. The pointer block stores the location of the memory
block in which the elements of the matrix are located (if any). If the
matrix owns this block then the owner field is set to one and the
block will be deallocated when the matrix is freed. If the matrix is
only a slice of a block owned by another object then the owner field is
zero and any underlying block will not be freed.
The functions for allocating and accessing matrices are defined in `gsl_matrix.h'
The functions for allocating memory to a matrix follow the style of
malloc
and free
. They also perform their own error
checking. If there is insufficient memory available to allocate a vector
then the functions call the GSL error handler (with an error number of
GSL_ENOMEM
) in addition to returning a null pointer. Thus if you
use the library error handler to abort your program then it isn't
necessary to check every alloc
.
gsl_matrix_alloc
then the block
underlying the matrix will also be deallocated. If the matrix has been
created from another object then the memory is still owned by that
object and will not be deallocated.
The functions for accessing the elements of a matrix use the same range
checking system as vectors. You turn off range checking by recompiling
your program with the preprocessor definition
GSL_RANGE_CHECK_OFF
.
The elements of the matrix are stored in "C-order", where the second
index moves continuously through memory. More precisely, the element
accessed by the function gsl_matrix_get(m,i,j)
and
gsl_matrix_set(m,i,j,x)
is
m->data[i * m->tda + j]
where tda is the physical row-length of the matrix.
The library provides functions for reading and writing matrices to a file as binary data or formatted text.
GSL_EFAILED
if there was a problem writing to the file. Since the
data is written in the native binary format it may not be portable
between different architectures.
GSL_EFAILED
if there was a problem reading from the file. The
data is assumed to have been written in the native binary format on the
same architecture.
%g
, %e
or %f
formats for
floating point numbers and %d
for integers. The function returns
0 for success and GSL_EFAILED
if there was a problem writing to
the file.
GSL_EFAILED
if there was a problem reading from the file.
A matrix view is a temporary object, stored on the stack, which can be
used to operate on a subset of matrix elements. Matrix views can be
defined for both constant and non-constant matrices using separate types
that preserve constness. A matrix view has the type
gsl_matrix_view
and a constant matrix view has the type
gsl_matrix_const_view
. In both cases the elements of the view
can by accessed using the matrix
component of the view object. A
pointer gsl_matrix *
or const gsl_matrix *
can be obtained
by taking the address of the matrix
component with the &
operator. In addition to matrix views it is also possible to create
vector views of a matrix, such as row or column views.
m'(i,j) = m->data[(k1*m->tda + k2) + i*m->tda + j]
where the index i runs from 0 to n1-1
and the index j
runs from 0 to n2-1
.
The data
pointer of the returned matrix struct is set to null if
the combined parameters (k1,k2,n1,n2,tda)
overrun the ends of the original matrix.
The new matrix view is only a view of the block underlying the existing matrix, m. The block containing the elements of m is not owned by the new matrix view. When the view goes out of scope the original matrix m and its block will continue to exist. The original memory can only be deallocated by freeing the original matrix. Of course, the original matrix should not be deallocated while the view is still in use.
The function gsl_matrix_const_submatrix
is equivalent to
gsl_matrix_submatrix
but can be used for matrices which are
declared const
.
m'(i,j) = base[i*n2 + j]
where the index i runs from 0 to n1-1
and the index j
runs from 0 to n2-1
.
The new matrix is only a view of the array base. When the view goes out of scope the original array base will continue to exist. The original memory can only be deallocated by freeing the original array. Of course, the original array should not be deallocated while the view is still in use.
The function gsl_matrix_const_view_array
is equivalent to
gsl_matrix_view_array
but can be used for matrices which are
declared const
.
m'(i,j) = base[i*tda + j]
where the index i runs from 0 to n1-1
and the index j
runs from 0 to n2-1
.
The new matrix is only a view of the array base. When the view goes out of scope the original array base will continue to exist. The original memory can only be deallocated by freeing the original array. Of course, the original array should not be deallocated while the view is still in use.
The function gsl_matrix_const_view_array_with_tda
is equivalent
to gsl_matrix_view_array_with_tda
but can be used for matrices
which are declared const
.
m'(i,j) = v->data[i*n2 + j]
where the index i runs from 0 to n1-1
and the index j
runs from 0 to n2-1
.
The new matrix is only a view of the vector v. When the view goes out of scope the original vector v will continue to exist. The original memory can only be deallocated by freeing the original vector. Of course, the original vector should not be deallocated while the view is still in use.
The function gsl_matrix_const_view_vector
is equivalent to
gsl_matrix_view_vector
but can be used for matrices which are
declared const
.
m'(i,j) = v->data[i*tda + j]
where the index i runs from 0 to n1-1
and the index j
runs from 0 to n2-1
.
The new matrix is only a view of the vector v. When the view goes out of scope the original vector v will continue to exist. The original memory can only be deallocated by freeing the original vector. Of course, the original vector should not be deallocated while the view is still in use.
The function gsl_matrix_const_view_vector_with_tda
is equivalent
to gsl_matrix_view_vector_with_tda
but can be used for matrices
which are declared const
.
In general there are two ways to access an object, by reference or by copying. The functions described in this section create vector views which allow access to a row or column of a matrix by reference. Modifying elements of the view is equivalent to modifying the matrix, since both the vector view and the matrix point to the same memory block.
data
pointer of the new vector is set to null if
i is out of range.
The function gsl_vector_const_row
is equivalent to
gsl_matrix_row
but can be used for matrices which are declared
const
.
data
pointer of the new vector is set to
null if j is out of range.
The function gsl_vector_const_column
equivalent to
gsl_matrix_column
but can be used for matrices which are declared
const
.
The function gsl_matrix_const_diagonal
is equivalent to
gsl_matrix_diagonal
but can be used for matrices which are
declared const
.
The function gsl_matrix_const_subdiagonal
is equivalent to
gsl_matrix_subdiagonal
but can be used for matrices which are
declared const
.
The function gsl_matrix_const_superdiagonal
is equivalent to
gsl_matrix_superdiagonal
but can be used for matrices which are
declared const
.
The functions described in this section copy a row or column of a matrix
into a vector. This allows the elements of the vector and the matrix to
be modified independently. Note that if the matrix and the vector point
to overlapping regions of memory then the result will be undefined. The
same effect can be achieved with more generality using
gsl_vector_memcpy
with vector views of rows and columns.
The following functions can be used to exchange the rows and columns of a matrix.
The following operations are only defined for real matrices.
The program below shows how to allocate, initialize and read from a matrix
using the functions gsl_matrix_alloc
, gsl_matrix_set
and
gsl_matrix_get
.
#include <stdio.h> #include <gsl/gsl_matrix.h> int main (void) { int i, j; gsl_matrix * m = gsl_matrix_alloc (10, 3); for (i = 0; i < 10; i++) for (j = 0; j < 3; j++) gsl_matrix_set (m, i, j, 0.23 + 100*i + j); for (i = 0; i < 100; i++) for (j = 0; j < 3; j++) printf("m(%d,%d) = %g\n", i, j, gsl_matrix_get (m, i, j)); return 0; }
Here is the output from the program. The final loop attempts to read
outside the range of the matrix m
, and the error is trapped by
the range-checking code in gsl_matrix_get
.
m(0,0) = 0.23 m(0,1) = 1.23 m(0,2) = 2.23 m(1,0) = 100.23 m(1,1) = 101.23 m(1,2) = 102.23 ... m(9,2) = 902.23 gsl: matrix_source.c:13: ERROR: first index out of range IOT trap/Abort (core dumped)
The next program shows how to write a matrix to a file.
#include <stdio.h> #include <gsl/gsl_matrix.h> int main (void) { int i, j, k = 0; gsl_matrix * m = gsl_matrix_alloc (100, 100); gsl_matrix * a = gsl_matrix_alloc (100, 100); for (i = 0; i < 100; i++) for (j = 0; j < 100; j++) gsl_matrix_set (m, i, j, 0.23 + i + j); { FILE * f = fopen("test.dat", "w"); gsl_matrix_fwrite (f, m); fclose (f); } { FILE * f = fopen("test.dat", "r"); gsl_matrix_fread (f, a); fclose (f); } for (i = 0; i < 100; i++) for (j = 0; j < 100; j++) { double mij = gsl_matrix_get(m, i, j); double aij = gsl_matrix_get(a, i, j); if (mij != aij) k++; } printf("differences = %d (should be zero)\n", k); return (k > 0); }
After running this program the file `test.dat' should contain the
elements of m
, written in binary format. The matrix which is read
back in using the function gsl_matrix_fread
should be exactly
equal to the original matrix.
The following program demonstrates the use of vector views. The program computes the column-norms of a matrix.
#include <math.h> #include <stdio.h> #include <gsl/gsl_matrix.h> #include <gsl/gsl_blas.h> int main (void) { size_t i,j; gsl_matrix *m = gsl_matrix_alloc (10, 10); for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) gsl_matrix_set (m, i, j, sin (i) + cos (j)); for (j = 0; j < 10; j++) { gsl_vector_view column = gsl_matrix_column (m, j); double d; d = gsl_blas_dnrm2 (&column.vector); printf ("matrix column %d, norm = %g\n", j, d); } gsl_matrix_free (m); }
Here is the output of the program, which can be confirmed using GNU OCTAVE,
$ ./a.out matrix column 0, norm = 4.31461 matrix column 1, norm = 3.1205 matrix column 2, norm = 2.19316 matrix column 3, norm = 3.26114 matrix column 4, norm = 2.53416 matrix column 5, norm = 2.57281 matrix column 6, norm = 4.20469 matrix column 7, norm = 3.65202 matrix column 8, norm = 2.08524 matrix column 9, norm = 3.07313 octave> m = sin(0:9)' * ones(1,10) + ones(10,1) * cos(0:9); octave> sqrt(sum(m.^2)) ans = 4.3146 3.1205 2.1932 3.2611 2.5342 2.5728 4.2047 3.6520 2.0852 3.0731
The block, vector and matrix objects in GSL follow the valarray
model of C++. A description of this model can be found in the following
reference,
This chapter describes functions for creating and manipulating permutations. A permutation p is represented by an array of n integers in the range 0 .. n-1, where each value p_i occurs once and only once. The application of a permutation p to a vector v yields a new vector v' where v'_i = v_{p_i}. For example, the array (0,1,3,2) represents a permutation which exchanges the last two elements of a four element vector. The corresponding identity permutation is (0,1,2,3).
Note that the permutations produced by the linear algebra routines correspond to the exchange of matrix columns, and so should be considered as applying to row-vectors in the form v' = v P rather than column-vectors, when permuting the elements of a vector.
The functions described in this chapter are defined in the header file `gsl_permutation.h'.
A permutation is stored by a structure containing two components, the size
of the permutation and a pointer to the permutation array. The elements
of the permutation array are all of type size_t
. The
gsl_permutation
structure looks like this,
typedef struct { size_t size; size_t * data; } gsl_permutation;
gsl_permutation_calloc
if you want to create a
permutation which is initialized to the identity. A null pointer is
returned if insufficient memory is available to create the permutation.
The following functions can be used to access and manipulate permutations.
GSL_SUCCESS
. If no further
permutations are available it returns GSL_FAILURE
and leaves
p unmodified. Starting with the identity permutation and
repeatedly applying this function will iterate through all possible
permutations of a given order.
GSL_SUCCESS
. If no previous permutation is available it returns
GSL_FAILURE
and leaves p unmodified.
The library provides functions for reading and writing permutations to a file as binary data or formatted text.
GSL_EFAILED
if there was a problem writing to the file. Since the
data is written in the native binary format it may not be portable
between different architectures.
GSL_EFAILED
if there was a problem reading from the file. The
data is assumed to have been written in the native binary format on the
same architecture.
Z
represents size_t
, so
"%Zu\n"
is a suitable format. The function returns
GSL_EFAILED
if there was a problem writing to the file.
GSL_EFAILED
if there was a problem reading from the file.
The example program below creates a random permutation by shuffling and finds its inverse.
#include <stdio.h> #include <gsl/gsl_rng.h> #include <gsl/gsl_randist.h> #include <gsl/gsl_permutation.h> int main (void) { const size_t N = 10; const gsl_rng_type * T; gsl_rng * r; gsl_permutation * p = gsl_permutation_alloc (N); gsl_permutation * q = gsl_permutation_alloc (N); gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc (T); printf("initial permutation:"); gsl_permutation_init (p); gsl_permutation_fprintf (stdout, p, " %u"); printf("\n"); printf(" random permutation:"); gsl_ran_shuffle (r, p->data, N, sizeof(size_t)); gsl_permutation_fprintf (stdout, p, " %u"); printf("\n"); printf("inverse permutation:"); gsl_permutation_invert (q, p); gsl_permutation_fprintf (stdout, q, " %u"); printf("\n"); return 0; }
Here is the output from the program,
bash$ ./a.out initial permutation: 0 1 2 3 4 5 6 7 8 9 random permutation: 1 3 5 2 7 6 0 4 9 8 inverse permutation: 6 0 3 1 7 2 5 4 9 8
The random permutation p[i]
and its inverse q[i]
are
related through the identity p[q[i]] = i
, which can be verified
from the output.
The next example program steps forwards through all possible 3-rd order permutations, starting from the identity,
#include <stdio.h> #include <gsl/gsl_permutation.h> int main (void) { gsl_permutation * p = gsl_permutation_alloc (3); gsl_permutation_init (p); do { gsl_permutation_fprintf (stdout, p, " %u"); printf("\n"); } while (gsl_permutation_next(p) == GSL_SUCCESS); return 0; }
Here is the output from the program,
bash$ ./a.out 0 1 2 0 2 1 1 0 2 1 2 0 2 0 1 2 1 0
All 6 permutations are generated in lexicographic order. To reverse the
sequence, begin with the final permutation (which is the reverse of the
identity) and replace gsl_permutation_next
with
gsl_permutation_prev
.
The subject of permutations is covered extensively in Knuth's Sorting and Searching,
This chapter describes functions for sorting data, both directly and indirectly (using an index). All the functions use the heapsort algorithm. Heapsort is an O(N \log N) algorithm which operates in-place. It does not require any additional storage and provides consistent performance. The running time for its worst-case (ordered data) is not significantly longer than the average and best cases. Note that the heapsort algorithm does not preserve the relative ordering of equal elements -- it is an unstable sort. However the resulting order of equal elements will be consistent across different platforms when using these functions.
The following function provides a simple alternative to the standard
library function qsort
. It is intended for systems lacking
qsort
, not as a replacement for it. The function qsort
should be used whenever possible, as it will be faster and can provide
stable ordering of equal elements. Documentation for qsort
is
available in the GNU C Library Reference Manual.
The functions described in this section are defined in the header file `gsl_heapsort.h'.
This function sorts the count elements of the array array, each of size size, into ascending order using the comparison function compare. The type of the comparison function is defined by,
int (*gsl_comparison_fn_t) (const void * a, const void * b)
A comparison function should return a negative integer if the first
argument is less than the second argument, 0
if the two arguments
are equal and a positive integer if the first argument is greater than
the second argument.
For example, the following function can be used to sort doubles into ascending numerical order.
int compare_doubles (const double * a, const double * b) { return (int) (*a - *b); }
The appropriate function call to perform the sort is,
gsl_heapsort (array, count, sizeof(double), compare_doubles);
Note that unlike qsort
the heapsort algorithm cannot be made into
a stable sort by pointer arithmetic. The trick of comparing pointers for
equal elements in the comparison function does not work for the heapsort
algorithm. The heapsort algorithm performs an internal rearrangement of
the data which destroys its initial ordering.
This function indirectly sorts the count elements of the array array, each of size size, into ascending order using the comparison function compare. The resulting permutation is stored in p, an array of length n. The elements of p give the index of the array element which would have been stored in that position if the array had been sorted in place. The first element of p gives the index of the least element in array, and the last element of p gives the index of the greatest element in array. The array itself is not changed.
The following functions will sort the elements of an array or vector,
either directly or indirectly. They are defined for all real and integer
types using the normal suffix rules. For example, the float
versions of the array functions are gsl_sort_float
and
gsl_sort_float_index
. The corresponding vector functions are
gsl_sort_vector_float
and gsl_sort_vector_float_index
. The
prototypes are available in the header files `gsl_sort_float.h'
`gsl_sort_vector_float.h'. The complete set of prototypes can be
included using the header files `gsl_sort.h' and
`gsl_sort_vector.h'.
There are no functions for sorting complex arrays or vectors, since the ordering of complex numbers is not uniquely defined. To sort a complex vector by magnitude compute a real vector containing the the magnitudes of the complex elements, and sort this vector indirectly. The resulting index gives the appropriate ordering of the original complex vector.
The functions described in this section select the k-th smallest or largest elements of a data set of size N. The routines use an O(kN) direct insertion algorithm which is suited to subsets that are small compared with the total size of the dataset. For example, the routines are useful for selecting the 10 largest values from one million data points, but not for selecting the largest 100,000 values. If the subset is a significant part of the total dataset it may be faster to sort all the elements of the dataset directly with an O(N \log N) algorithm and obtain the smallest or largest values that way.
The following functions find the indices of the k-th smallest or largest elements of a dataset,
The rank of an element is its order in the sorted data. The rank is the inverse of the index permutation, p. It can be computed using the following algorithm,
for (i = 0; i < p->size; i++) { size_t pi = p->data[i]; rank->data[pi] = i; }
This can be computed directly from the function
gsl_permutation_invert(rank,p)
.
The following function will print the rank of each element of the vector v,
void print_rank (gsl_vector * v) { size_t i; size_t n = v->size; gsl_permutation * perm = gsl_permutation_alloc(n); gsl_permutation * rank = gsl_permutation_alloc(n); gsl_sort_vector_index (perm, v); gsl_permutation_invert (rank, perm); for (i = 0; i < n; i++) { double vi = gsl_vector_get(v, i); printf("element = %d, value = %g, rank = %d\n", i, vi, rank->data[i]); } gsl_permutation_free (perm); gsl_permutation_free (rank); }
The following example shows how to use the permutation p to print the elements of the vector v in ascending order,
gsl_sort_vector_index (p, v); for (i = 0; i < v->size; i++) { double vpi = gsl_vector_get(v, p->data[i]); printf("order = %d, value = %g\n", i, vpi); }
The next example uses the function gsl_sort_smallest
to select
the 5 smallest numbers from 100000 uniform random variates stored in an
array,
#include <gsl/gsl_rng.h> #include <gsl/gsl_sort_double.h> int main (void) { const gsl_rng_type * T; gsl_rng * r; int i, k = 5, N = 100000; double * x = malloc (N * sizeof(double)); double * small = malloc (k * sizeof(double)); gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc (T); for (i = 0; i < N; i++) { x[i] = gsl_rng_uniform(r); } gsl_sort_smallest (small, k, x, 1, N); printf("%d smallest values from %d\n", k, N); for (i = 0; i < k; i++) { printf ("%d: %.18f\n", i, small[i]); } return 0; }
The output lists the 5 smallest values, in ascending order,
$ ./a.out 5 smallest values from 100000 0: 0.000005466630682349 1: 0.000012384494766593 2: 0.000017581274732947 3: 0.000025131041184068 4: 0.000031369971111417
The subject of sorting is covered extensively in Knuth's Sorting and Searching,
The Heapsort algorithm is described in the following book,
The Basic Linear Algebra Subprograms (BLAS) define a set of fundamental operations on vectors and matrices which can be used to create optimized higher-level linear algebra functionality.
The library provides a low-level layer which corresponds directly to the
C-language BLAS standard, referred to here as "CBLAS", and a
higher-level interface for operations on GSL vectors and matrices.
Users who are interested in simple operations on GSL vector and matrix
objects should use the high-level layer, which is declared in the file
gsl_blas.h
. This should satisfy the needs of most users. Note
that GSL matrices are implemented using dense-storage so the interface
only includes the corresponding dense-storage BLAS functions. The full
BLAS functionality for band-format and packed-format matrices is
available through the low-level CBLAS interface.
The interface for the gsl_cblas
layer is specified in the file
gsl_cblas.h
. This interface corresponds the BLAS Technical
Forum's draft standard for the C interface to legacy BLAS
implementations. Users who have access to other conforming CBLAS
implementations can use these in place of the version provided by the
library. Note that users who have only a Fortran BLAS library can
use a CBLAS conformant wrapper to convert it into a CBLAS
library. A reference CBLAS wrapper for legacy Fortran
implementations exists as part of the draft CBLAS standard and can
be obtained from Netlib. The complete set of CBLAS functions is
listed in an appendix (see section GSL CBLAS Library).
There are three levels of BLAS operations,
Each routine has a name which specifies the operation, the type of matrices involved and their precisions. Some of the most common operations and their names are given below,
The type of matrices are,
Each operation is defined for four precisions,
Thus, for example, the name SGEMM stands for "single-precision general matrix-matrix multiply" and ZGEMM stands for "double-precision complex matrix-matrix multiply".
GSL provides dense vector and matrix objects, based on the relevant
built-in types. The library provides an interface to the BLAS
operations which apply to these objects. The interface to this
functionality is given in the file gsl_blas.h
.
The variables a and b are overwritten by the routine.
CblasNoTrans
,
CblasTrans
, CblasConjTrans
.
CblasNoTrans
, CblasTrans
, CblasConjTrans
. When
Uplo is CblasUpper
then the upper triangle of A is
used, and when Uplo is CblasLower
then the lower triangle
of A is used. If Diag is CblasNonUnit
then the
diagonal of the matrix is used, but if Diag is CblasUnit
then the diagonal elements of the matrix A are taken as unity and
are not referenced.
CblasNoTrans
, CblasTrans
, CblasConjTrans
. When
Uplo is CblasUpper
then the upper triangle of A is
used, and when Uplo is CblasLower
then the lower triangle
of A is used. If Diag is CblasNonUnit
then the
diagonal of the matrix is used, but if Diag is CblasUnit
then the diagonal elements of the matrix A are taken as unity and
are not referenced.
CblasUpper
then the upper triangle
and diagonal of A are used, and when Uplo is
CblasLower
then the lower triangle and diagonal of A are
used.
CblasUpper
then the upper triangle
and diagonal of A are used, and when Uplo is
CblasLower
then the lower triangle and diagonal of A are
used. The imaginary elements of the diagonal are automatically assumed
to be zero and are not referenced.
CblasUpper
then the upper triangle and diagonal of
A are used, and when Uplo is CblasLower
then the
lower triangle and diagonal of A are used.
CblasUpper
then the upper triangle and diagonal of
A are used, and when Uplo is CblasLower
then the
lower triangle and diagonal of A are used. The imaginary elements
of the diagonal are automatically set to zero.
CblasUpper
then the upper triangle
and diagonal of A are used, and when Uplo is
CblasLower
then the lower triangle and diagonal of A are
used.
CblasUpper
then the upper triangle
and diagonal of A are used, and when Uplo is
CblasLower
then the lower triangle and diagonal of A are
used. The imaginary elements of the diagonal are automatically set to zero.
CblasNoTrans
, CblasTrans
,
CblasConjTrans
and similarly for the parameter TransB.
CblasLeft
and C =
\alpha B A + \beta C for Side is CblasRight
, where the
matrix A is symmetric. When Uplo is CblasUpper
then
the upper triangle and diagonal of A are used, and when Uplo
is CblasLower
then the lower triangle and diagonal of A are
used.
CblasLeft
and C =
\alpha B A + \beta C for Side is CblasRight
, where the
matrix A is hermitian. When Uplo is CblasUpper
then
the upper triangle and diagonal of A are used, and when Uplo
is CblasLower
then the lower triangle and diagonal of A are
used. The imaginary elements of the diagonal are automatically set to
zero.
CblasLeft
and B = \alpha B op(A) for
Side is CblasRight
. The matrix A is triangular and
op(A) = A, A^T, A^H for TransA =
CblasNoTrans
, CblasTrans
, CblasConjTrans
When
Uplo is CblasUpper
then the upper triangle of A is
used, and when Uplo is CblasLower
then the lower triangle
of A is used. If Diag is CblasNonUnit
then the
diagonal of A is used, but if Diag is CblasUnit
then
the diagonal elements of the matrix A are taken as unity and are
not referenced.
CblasLeft
and B = \alpha B op(inv(A)) for
Side is CblasRight
. The matrix A is triangular and
op(A) = A, A^T, A^H for TransA =
CblasNoTrans
, CblasTrans
, CblasConjTrans
When
Uplo is CblasUpper
then the upper triangle of A is
used, and when Uplo is CblasLower
then the lower triangle
of A is used. If Diag is CblasNonUnit
then the
diagonal of A is used, but if Diag is CblasUnit
then
the diagonal elements of the matrix A are taken as unity and are
not referenced.
CblasNoTrans
and C = \alpha A^T A + \beta C when
Trans is CblasTrans
. Since the matrix C is symmetric
only its upper half or lower half need to be stored. When Uplo is
CblasUpper
then the upper triangle and diagonal of C are
used, and when Uplo is CblasLower
then the lower triangle
and diagonal of C are used.
CblasNoTrans
and C = \alpha A^H A + \beta C when
Trans is CblasTrans
. Since the matrix C is hermitian
only its upper half or lower half need to be stored. When Uplo is
CblasUpper
then the upper triangle and diagonal of C are
used, and when Uplo is CblasLower
then the lower triangle
and diagonal of C are used. The imaginary elements of the
diagonal are automatically set to zero.
CblasNoTrans
and C = \alpha A^T B + \alpha B^T A + \beta C when
Trans is CblasTrans
. Since the matrix C is symmetric
only its upper half or lower half need to be stored. When Uplo is
CblasUpper
then the upper triangle and diagonal of C are
used, and when Uplo is CblasLower
then the lower triangle
and diagonal of C are used.
CblasNoTrans
and C = \alpha A^H B + \alpha^* B^H A + \beta C when
Trans is CblasTrans
. Since the matrix C is hermitian
only its upper half or lower half need to be stored. When Uplo is
CblasUpper
then the upper triangle and diagonal of C are
used, and when Uplo is CblasLower
then the lower triangle
and diagonal of C are used. The imaginary elements of the
diagonal are automatically set to zero.
The following program computes the product of two matrices using the Level-3 BLAS function DGEMM,
The matrices are stored in row major order, according to the C convention for arrays.
#include <stdio.h> #include <gsl/gsl_blas.h> int main (void) { double a[] = { 0.11, 0.12, 0.13, 0.21, 0.22, 0.23 }; double b[] = { 1011, 1012, 1021, 1022, 1031, 1032 }; double c[] = { 0.00, 0.00, 0.00, 0.00 }; gsl_matrix_view A = gsl_matrix_view_array(a, 2, 3); gsl_matrix_view B = gsl_matrix_view_array(b, 3, 2); gsl_matrix_view C = gsl_matrix_view_array(c, 2, 2); /* Compute C = A B */ gsl_blas_dgemm (CblasNoTrans, CblasNoTrans, 1.0, &A.matrix, &B.matrix, 0.0, &C.matrix); printf("[ %g, %g\n", c[0], c[1]); printf(" %g, %g ]\n", c[2], c[3]); return 0; }
Here is the output from the program,
$ ./a.out [ 367.76, 368.12 674.06, 674.72 ]
Information on the BLAS standards, including both the legacy and draft interface standards, is available online from the BLAS Homepage and BLAS Technical Forum web-site.
The following papers contain the specifications for Level 1, Level 2 and Level 3 BLAS.
Postscript versions of the latter two papers are available from http://www.netlib.org/blas/. A CBLAS wrapper for Fortran BLAS libraries is available from the same location.
This chapter describes functions for solving linear systems. The
library provides simple linear algebra operations which operate directly
on the gsl_vector
and gsl_matrix
objects. These are
intended for use with "small" systems where simple algorithms are
acceptable.
Anyone interested in large systems will want to use the sophisticated routines found in LAPACK. The Fortran version of LAPACK is recommended as the standard package for linear algebra. It supports blocked algorithms, specialized data representations and other optimizations.
The functions described in this chapter are declared in the header file `gsl_linalg.h'.
A general square matrix A has an LU decomposition into upper and lower triangular matrices,
where P is a permutation matrix, L is unit lower triangular matrix and U is upper triangular matrix. For square matrices this decomposition can be used to convert the linear system A x = b into a pair of triangular systems (L y = P b, U x = y), which can be solved by forward and back-substitution.
The permutation matrix P is encoded in the permutation p. The j-th column of the matrix P is given by the k-th column of the identity matrix, where k = p_j the j-th element of the permutation vector. The sign of the permutation is given by signum. It has the value (-1)^n, where n is the number of interchanges in the permutation.
The algorithm used in the decomposition is Gaussian Elimination with partial pivoting (Golub & Van Loan, Matrix Computations, Algorithm 3.4.1).
gsl_linalg_LU_decomp
or gsl_linalg_complex_LU_decomp
.
A general rectangular M-by-N matrix A has a QR decomposition into the product of an orthogonal M-by-M square matrix Q (where Q^T Q = I) and an M-by-N right-triangular matrix R,
This decomposition can be used to convert the linear system A x = b into the triangular system R x = Q^T b, which can be solved by back-substitution. Another use of the QR decomposition is to compute an orthonormal basis for a set of vectors. The first N columns of Q form an orthonormal basis for the range of A, ran(A), when A has full column rank.
The algorithm used to perform the decomposition is Householder QR (Golub & Van Loan, Matrix Computations, Algorithm 5.2.1).
gsl_linalg_QR_decomp
.
gsl_linalg_QR_decomp
. On input x should contain the
right-hand side b, which is replaced by the solution on output.
gsl_linalg_QR_decomp
. The solution is returned in x. The
residual is computed as a by-product and stored in residual.
gsl_linalg_QR_QTvec
.
gsl_linalg_QR_QTvec
.
The QR decomposition can be extended to the rank deficient case by introducing a column permutation P,
The first r columns of this Q form an orthonormal basis for the range of A for a matrix with column rank r. This decomposition can also be used to convert the linear system A x = b into the triangular system R y = Q^T b, x = P y, which can be solved by back-substitution and permutation. We denote the QR decomposition with column pivoting by QRP^T since A = Q R P^T.
The algorithm used to perform the decomposition is Householder QR with column pivoting (Golub & Van Loan, Matrix Computations, Algorithm 5.4.1).
gsl_linalg_QRPT_decomp
.
A general rectangular M-by-N matrix A has a singular value decomposition (SVD) into the product of an M-by-N orthogonal matrix U, an N-by-N diagonal matrix of singular values S and the transpose of an M-by-M orthogonal square matrix V,
The singular values \sigma_i = S_{ii} are all non-negative and are generally chosen to form a non-increasing sequence \sigma_1 >= \sigma_2 >= ... >= \sigma_N >= 0.
The singular value decomposition of a matrix has many practical uses. The condition number of the matrix is given by the ratio of the largest singular value to the smallest singular value. The presence of a zero singular value indicates that the matrix is singular. The number of non-zero singular values indicates the rank of the matrix. In practice singular value decomposition of a rank-deficient matrix will not produce exact zeroes for singular values, due to finite numerical precision. Small singular values should be edited by choosing a suitable tolerance.
This routine uses the Golub-Reinsch SVD algorithm.
gsl_linalg_SV_decomp
.
Only non-zero singular values are used in computing the solution. The parts of the solution corresponding to singular values of zero are ignored. Other singular values can be edited out by setting them to zero before calling this function.
In the over-determined case where A has more rows than columns the system is solved in the least squares sense, returning the solution x which minimizes ||A x - b||_2.
A symmetric, positive definite square matrix A has a Cholesky decomposition into a product of a lower triangular matrix L and its transpose L^T,
This is sometimes referred to as taking the square-root of a matrix. The Cholesky decomposition can only be carried out when all the eigenvalues of the matrix are positive. This decomposition can be used to convert the linear system A x = b into a pair of triangular systems (L y = b, L^T x = y), which can be solved by forward and back-substitution.
GSL_EDOM
.
gsl_linalg_cholesky_decomp
.
gsl_linalg_cholesky_decomp
. On input x should contain
the right-hand side b, which is replaced by the solution on
output.
A symmetric matrix A can be factorized by similarity transformations into the form,
where Q is an orthogonal matrix and T is a symmetric tridiagonal matrix.
gsl_linalg_symmtd_decomp
into
the orthogonal matrix Q, the vector of diagonal elements d
and the vector of subdiagonal elements sd.
gsl_linalg_symmtd_decomp
into the vectors d and sd.
A hermitian matrix A can be factorized by similarity transformations into the form,
where U is an unitary matrix and T is a real symmetric tridiagonal matrix.
gsl_linalg_hermtd_decomp
into the
unitary matrix U, the real vector of diagonal elements d and
the real vector of subdiagonal elements sd.
gsl_linalg_hermtd_decomp
into the real vectors d and
sd.
A general matrix A can be factorized by similarity transformations into the form,
where U and V are orthogonal matrices and B is a N-by-N bidiagonal matrix with non-zero entries only on the diagonal and superdiagonal. The size of U is M-by-N and the size of V is N-by-N.
gsl_linalg_bidiag_decomp
, (A, tau_U, tau_V)
into the separate orthogonal matrices U, V and the diagonal
vector diag and superdiagonal superdiag.
gsl_linalg_bidiag_decomp
, (A, tau_U, tau_V)
into the separate orthogonal matrices U, V and the diagonal
vector diag and superdiagonal superdiag. The matrix U
is stored in-place in A.
gsl_linalg_bidiag_decomp
, into
the diagonal vector diag and superdiagonal vector superdiag.
The following program solves the linear system A x = b. The system to be solved is,
and the solution is found using LU decomposition of the matrix A.
#include <stdio.h> #include <gsl/gsl_linalg.h> int main (void) { double a_data[] = { 0.18, 0.60, 0.57, 0.96, 0.41, 0.24, 0.99, 0.58, 0.14, 0.30, 0.97, 0.66, 0.51, 0.13, 0.19, 0.85 }; double b_data[] = { 1.0, 2.0, 3.0, 4.0 }; gsl_matrix_view m = gsl_matrix_view_array(a_data, 4, 4); gsl_vector_view b = gsl_vector_view_array(b_data, 4); gsl_vector *x = gsl_vector_alloc (4); int s; gsl_permutation * p = gsl_permutation_alloc (4); gsl_linalg_LU_decomp (&m.matrix, p, &s); gsl_linalg_LU_solve (&m.matrix, p, &b.vector, x); printf ("x = \n"); gsl_vector_fprintf(stdout, x, "%g"); gsl_permutation_free (p); return 0; }
Here is the output from the program,
x = -4.05205 -12.6056 1.66091 8.69377
This can be verified by multiplying the solution x by the original matrix A using GNU OCTAVE,
octave> A = [ 0.18, 0.60, 0.57, 0.96; 0.41, 0.24, 0.99, 0.58; 0.14, 0.30, 0.97, 0.66; 0.51, 0.13, 0.19, 0.85 ]; octave> x = [ -4.05205; -12.6056; 1.66091; 8.69377]; octave> A * x ans = 1.0000 2.0000 3.0000 4.0000
This reproduces the original right-hand side vector, b, in accordance with the equation A x = b.
Further information on the algorithms described in this section can be found in the following book,
The LAPACK library is described in,
The LAPACK source code can be found at the website above, along with an online copy of the users guide.
The Modified Golub-Reinsch algorithm is described in the following paper,
The Jacobi algorithm for singular value decomposition is described in the following papers,
lawns
or
lawnspdf
directories.
This chapter describes functions for computing eigenvalues and eigenvectors of matrices. There are routines for real symmetric and complex hermitian matrices, and eigenvalues can be computed with or without eigenvectors. The algorithms used are symmetric bidiagonalization followed by QR reduction.
These routines are intended for "small" systems where simple algorithms are acceptable. Anyone interested finding eigenvalues and eigenvectors of large matrices will want to use the sophisticated routines found in LAPACK. The Fortran version of LAPACK is recommended as the standard package for linear algebra.
The functions described in this chapter are declared in the header file `gsl_eigen.h'.
GSL_EIGEN_SORT_VAL_ASC
GSL_EIGEN_SORT_VAL_DESC
GSL_EIGEN_SORT_ABS_ASC
GSL_EIGEN_SORT_ABS_DESC
The following program computes the eigenvalues and eigenvectors of the 4-th order Hilbert matrix, H(i,j) = 1/(i + j + 1).
#include <stdio.h> #include <gsl/gsl_math.h> #include <gsl/gsl_eigen.h> int main (void) { double data[] = { 1.0 , 1/2.0, 1/3.0, 1/4.0, 1/2.0, 1/3.0, 1/4.0, 1/5.0, 1/3.0, 1/4.0, 1/5.0, 1/6.0, 1/4.0, 1/5.0, 1/6.0, 1/7.0 }; gsl_matrix_view m = gsl_matrix_view_array(data, 4, 4); gsl_vector *eval = gsl_vector_alloc (4); gsl_matrix *evec = gsl_matrix_alloc (4, 4); gsl_eigen_symmv_workspace * w = gsl_eigen_symmv_alloc (4); gsl_eigen_symmv (&m.matrix, eval, evec, w); gsl_eigen_symmv_free(w); gsl_eigen_symmv_sort (eval, evec, GSL_EIGEN_SORT_ABS_ASC); { int i; for (i = 0; i < 4; i++) { double eval_i = gsl_vector_get(eval, i); gsl_vector_view evec_i = gsl_matrix_column(evec, i); printf("eigenvalue = %g\n", eval_i); printf("eigenvector = \n"); gsl_vector_fprintf(stdout, &evec_i.vector, "%g"); } } return 0; }
Here is the beginning of the output from the program,
$ ./a.out eigenvalue = 9.67023e-05 eigenvector = -0.0291933 0.328712 -0.791411 0.514553 ...
This can be compared with the corresponding output from GNU OCTAVE,
octave> [v,d] = eig(hilb(4)); octave> diag(d) ans = 9.6702e-05 6.7383e-03 1.6914e-01 1.5002e+00 octave> v v = 0.029193 0.179186 -0.582076 0.792608 -0.328712 -0.741918 0.370502 0.451923 0.791411 0.100228 0.509579 0.322416 -0.514553 0.638283 0.514048 0.252161
Note that the eigenvectors can differ by a change of sign, since the sign of an eigenvector is arbitrary.
Further information on the algorithms described in this section can be found in the following book,
The LAPACK library is described in,
The LAPACK source code can be found at the website above along with an online copy of the users guide.
This chapter describes functions for performing Fast Fourier Transforms (FFTs). The library includes radix-2 routines (for lengths which are a power of two) and mixed-radix routines (which work for any length). For efficiency there are separate versions of the routines for real data and for complex data. The mixed-radix routines are a reimplementation of the FFTPACK library by Paul Swarztrauber. Fortran code for FFTPACK is available on Netlib (FFTPACK also includes some routines for sine and cosine transforms but these are currently not available in GSL). For details and derivations of the underlying algorithms consult the document GSL FFT Algorithms (see section References and Further Reading)
Fast Fourier Transforms are efficient algorithms for calculating the discrete fourier transform (DFT),
The DFT usually arises as an approximation to the continuous fourier transform when functions are sampled at discrete intervals in space or time. The naive evaluation of the discrete fourier transform is a matrix-vector multiplication W\vec{z}. A general matrix-vector multiplication takes O(N^2) operations for N data-points. Fast fourier transform algorithms use a divide-and-conquer strategy to factorize the matrix W into smaller sub-matrices, corresponding to the integer factors of the length N. If N can be factorized into a product of integers f_1 f_2 ... f_n then the DFT can be computed in O(N \sum f_i) operations. For a radix-2 FFT this gives an operation count of O(N \log_2 N).
All the FFT functions offer three types of transform: forwards, inverse and backwards, based on the same mathematical definitions. The definition of the forward fourier transform, x = FFT(z), is,
and the definition of the inverse fourier transform, x = IFFT(z), is,
The factor of 1/N makes this a true inverse. For example, a call
to gsl_fft_complex_forward
followed by a call to
gsl_fft_complex_inverse
should return the original data (within
numerical errors).
In general there are two possible choices for the sign of the exponential in the transform/ inverse-transform pair. GSL follows the same convention as FFTPACK, using a negative exponential for the forward transform. The advantage of this convention is that the inverse transform recreates the original function with simple fourier synthesis. Numerical Recipes uses the opposite convention, a positive exponential in the forward transform.
The backwards FFT is simply our terminology for an unscaled version of the inverse FFT,
When the overall scale of the result is unimportant it is often convenient to use the backwards FFT instead of the inverse to save unnecessary divisions.
The inputs and outputs for the complex FFT routines are packed arrays of floating point numbers. In a packed array the real and imaginary parts of each complex number are placed in alternate neighboring elements. For example, the following definition of a packed array of length 6,
gsl_complex_packed_array data[6];
can be used to hold an array of three complex numbers, z[3]
, in
the following way,
data[0] = Re(z[0]) data[1] = Im(z[0]) data[2] = Re(z[1]) data[3] = Im(z[1]) data[4] = Re(z[2]) data[5] = Im(z[2])
A stride parameter allows the user to perform transforms on the
elements z[stride*i]
instead of z[i]
. A stride greater
than 1 can be used to take an in-place FFT of the column of a matrix. A
stride of 1 accesses the array without any additional spacing between
elements.
The array indices have the same ordering as those in the definition of the DFT -- i.e. there are no index transformations or permutations of the data.
For physical applications it is important to remember that the index appearing in the DFT does not correspond directly to a physical frequency. If the time-step of the DFT is \Delta then the frequency-domain includes both positive and negative frequencies, ranging from -1/(2\Delta) through 0 to +1/(2\Delta). The positive frequencies are stored from the beginning of the array up to the middle, and the negative frequencies are stored backwards from the end of the array.
Here is a table which shows the layout of the array data, and the correspondence between the time-domain data z, and the frequency-domain data x.
index z x = FFT(z) 0 z(t = 0) x(f = 0) 1 z(t = 1) x(f = 1/(N Delta)) 2 z(t = 2) x(f = 2/(N Delta)) . ........ .................. N/2 z(t = N/2) x(f = +1/(2 Delta), -1/(2 Delta)) . ........ .................. N-3 z(t = N-3) x(f = -3/(N Delta)) N-2 z(t = N-2) x(f = -2/(N Delta)) N-1 z(t = N-1) x(f = -1/(N Delta))
When N is even the location N/2 contains the most positive and negative frequencies +1/(2 \Delta), -1/(2 \Delta)) which are equivalent. If N is odd then general structure of the table above still applies, but N/2 does not appear.
The radix-2 algorithms described in this section are simple and compact, although not necessarily the most efficient. They use the Cooley-Tukey algorithm to compute in-place complex FFTs for lengths which are a power of 2 -- no additional storage is required. The corresponding self-sorting mixed-radix routines offer better performance at the expense of requiring additional working space.
All these functions are declared in the header file `gsl_fft_complex.h'.
These functions compute forward, backward and inverse FFTs of length n with stride stride, on the packed complex array data using an in-place radix-2 decimation-in-time algorithm. The length of the transform n is restricted to powers of two.
The functions return a value of GSL_SUCCESS
if no errors were
detected, or GSL_EDOM
if the length of the data n is not a
power of two.
These are decimation-in-frequency versions of the radix-2 FFT functions.
Here is an example program which computes the FFT of a short pulse in a sample of length 128. To make the resulting fourier transform real the pulse is defined for equal positive and negative times (-10 ... 10), where the negative times wrap around the end of the array.
#include <stdio.h> #include <math.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_fft_complex.h> #define REAL(z,i) ((z)[2*(i)]) #define IMAG(z,i) ((z)[2*(i)+1]) int main (void) { int i; double data[2*128]; for (i = 0; i < 128; i++) { REAL(data,i) = 0.0; IMAG(data,i) = 0.0; } REAL(data,0) = 1.0; for (i = 1; i <= 10; i++) { REAL(data,i) = REAL(data,128-i) = 1.0; } for (i = 0; i < 128; i++) { printf ("%d %e %e\n", i, REAL(data,i), IMAG(data,i)); } printf ("\n"); gsl_fft_complex_radix2_forward (data, 1, 128); for (i = 0; i < 128; i++) { printf ("%d %e %e\n", i, REAL(data,i)/sqrt(128), IMAG(data,i)/sqrt(128)); } return 0; }
Note that we have assumed that the program is using the default error
handler (which calls abort
for any errors). If you are not using
a safe error handler you would need to check the return status of
gsl_fft_complex_radix2_forward
.
The transformed data is rescaled by 1/\sqrt N so that it fits on the same plot as the input. Only the real part is shown, by the choice of the input data the imaginary part is zero. Allowing for the wrap-around of negative times at t=128, and working in units of k/N, the DFT approximates the continuum fourier transform, giving a modulated \sin function.
A pulse and its discrete fourier transform, output from the example program.
This section describes mixed-radix FFT algorithms for complex data. The mixed-radix functions work for FFTs of any length. They are a reimplementation of the Fortran FFTPACK library by Paul Swarztrauber. The theory is explained in the review article Self-sorting Mixed-radix FFTs by Clive Temperton. The routines here use the same indexing scheme and basic algorithms as FFTPACK.
The mixed-radix algorithm is based on sub-transform modules -- highly optimized small length FFTs which are combined to create larger FFTs. There are efficient modules for factors of 2, 3, 4, 5, 6 and 7. The modules for the composite factors of 4 and 6 are faster than combining the modules for 2*2 and 2*3.
For factors which are not implemented as modules there is a fall-back to a general length-n module which uses Singleton's method for efficiently computing a DFT. This module is O(n^2), and slower than a dedicated module would be but works for any length n. Of course, lengths which use the general length-n module will still be factorized as much as possible. For example, a length of 143 will be factorized into 11*13. Large prime factors are the worst case scenario, e.g. as found in n=2*3*99991, and should be avoided because their O(n^2) scaling will dominate the run-time (consult the document GSL FFT Algorithms included in the GSL distribution if you encounter this problem).
The mixed-radix initialization function gsl_fft_complex_wavetable_alloc
returns the list of factors chosen by the library for a given length
N. It can be used to check how well the length has been
factorized, and estimate the run-time. To a first approximation the
run-time scales as N \sum f_i, where the f_i are the
factors of N. For programs under user control you may wish to
issue a warning that the transform will be slow when the length is
poorly factorized. If you frequently encounter data lengths which
cannot be factorized using the existing small-prime modules consult
GSL FFT Algorithms for details on adding support for other
factors.
All these functions are declared in the header file `gsl_fft_complex.h'.
gsl_fft_complex_wavetable
if no errors were detected, and a null
pointer in the case of error. The length n is factorized into a
product of subtransforms, and the factors and their trigonometric
coefficients are stored in the wavetable. The trigonometric coefficients
are computed using direct calls to sin
and cos
, for
accuracy. Recursion relations could be used to compute the lookup table
faster, but if an application performs many FFTs of the same length then
this computation is a one-off overhead which does not affect the final
throughput.
The wavetable structure can be used repeatedly for any transform of the same length. The table is not modified by calls to any of the other FFT functions. The same wavetable can be used for both forward and backward (or inverse) transforms of a given length.
gsl_fft_complex_wavetable
structure
which contains internal parameters for the FFT. It is not necessary to
set any of the components directly but it can sometimes be useful to
examine them. For example, the chosen factorization of the FFT length
is given and can be used to provide an estimate of the run-time or
numerical error.
The wavetable structure is declared in the header file `gsl_fft_complex.h'.
size_t n
size_t nf
n
was decomposed into.
size_t factor[64]
nf
elements are
used.
gsl_complex * trig
n
complex elements.
gsl_complex * twiddle[64]
trig
, giving the twiddle
factors for each pass.
The mixed radix algorithms require an additional working space to hold the intermediate steps of the transform.
The following functions compute the transform,
These functions compute forward, backward and inverse FFTs of length n with stride stride, on the packed complex array data, using a mixed radix decimation-in-frequency algorithm. There is no restriction on the length n. Efficient modules are provided for subtransforms of length 2, 3, 4, 5, 6 and 7. Any remaining factors are computed with a slow, O(n^2), general-n module. The caller must supply a wavetable containing the trigonometric lookup tables and a workspace work.
The functions return a value of 0
if no errors were detected. The
following gsl_errno
conditions are defined for these functions:
GSL_EDOM
GSL_EINVAL
Here is an example program which computes the FFT of a short pulse in a sample of length 630 (=2*3*3*5*7) using the mixed-radix algorithm.
#include <stdio.h> #include <math.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_fft_complex.h> #define REAL(z,i) ((z)[2*(i)]) #define IMAG(z,i) ((z)[2*(i)+1]) int main (void) { int i; const int n = 630; double data[2*n]; gsl_fft_complex_wavetable * wavetable; gsl_fft_complex_workspace * workspace; for (i = 0; i < n; i++) { REAL(data,i) = 0.0; IMAG(data,i) = 0.0; } data[0].real = 1.0; for (i = 1; i <= 10; i++) { REAL(data,i) = REAL(data,n-i) = 1.0; } for (i = 0; i < n; i++) { printf ("%d: %e %e\n", i, REAL(data,i), IMAG(data,i)); } printf ("\n"); wavetable = gsl_fft_complex_wavetable_alloc (n); workspace = gsl_fft_complex_workspace_alloc (n); for (i = 0; i < wavetable->nf; i++) { printf("# factor %d: %d\n", i, wavetable->factor[i]); } gsl_fft_complex_forward (data, 1, n, wavetable, workspace); for (i = 0; i < n; i++) { printf ("%d: %e %e\n", i, REAL(data,i), IMAG(data,i)); } gsl_fft_complex_wavetable_free (wavetable); gsl_fft_complex_workspace_free (workspace); return 0; }
Note that we have assumed that the program is using the default
gsl
error handler (which calls abort
for any errors). If
you are not using a safe error handler you would need to check the
return status of all the gsl
routines.
The functions for real data are similar to those for complex data. However, there is an important difference between forward and inverse transforms. The fourier transform of a real sequence is not real. It is a complex sequence with a special symmetry:
A sequence with this symmetry is called conjugate-complex or
half-complex. This different structure requires different
storage layouts for the forward transform (from real to half-complex)
and inverse transform (from half-complex back to real). As a
consequence the routines are divided into two sets: functions in
gsl_fft_real
which operate on real sequences and functions in
gsl_fft_halfcomplex
which operate on half-complex sequences.
Functions in gsl_fft_real
compute the frequency coefficients of a
real sequence. The half-complex coefficients c of a real sequence
x are given by fourier analysis,
Functions in gsl_fft_halfcomplex
compute inverse or backwards
transforms. They reconstruct real sequences by fourier synthesis from
their half-complex frequency coefficients, c,
The symmetry of the half-complex sequence implies that only half of the complex numbers in the output need to be stored. The remaining half can be reconstructed using the half-complex symmetry condition. (This works for all lengths, even and odd. When the length is even the middle value, where k=N/2, is also real). Thus only N real numbers are required to store the half-complex sequence, and the transform of a real sequence can be stored in the same size array as the original data.
The precise storage arrangements depend on the algorithm, and are different for radix-2 and mixed-radix routines. The radix-2 function operates in-place, which constrain the locations where each element can be stored. The restriction forces real and imaginary parts to be stored far apart. The mixed-radix algorithm does not have this restriction, and it stores the real and imaginary parts of a given term in neighboring locations. This is desirable for better locality of memory accesses.
This section describes radix-2 FFT algorithms for real data. They use the Cooley-Tukey algorithm to compute in-place FFTs for lengths which are a power of 2.
The radix-2 FFT functions for real data are declared in the header files `gsl_fft_real.h'
This function computes an in-place radix-2 FFT of length n and stride stride on the real array data. The output is a half-complex sequence, which is stored in-place. The arrangement of the half-complex terms uses the following scheme: for k < N/2 the real part of the k-th term is stored in location k, and the corresponding imaginary part is stored in location N-k. Terms with k > N/2 can be reconstructed using the symmetry z_k = z^*_{N-k}. The terms for k=0 and k=N/2 are both purely real, and count as a special case. Their real parts are stored in locations 0 and N/2 respectively, while their imaginary parts which are zero are not stored.
The following table shows the correspondence between the output data and the equivalent results obtained by considering the input data as a complex sequence with zero imaginary part,
complex[0].real = data[0] complex[0].imag = 0 complex[1].real = data[1] complex[1].imag = data[N-1] ............... ................ complex[k].real = data[k] complex[k].imag = data[N-k] ............... ................ complex[N/2].real = data[N/2] complex[N/2].real = 0 ............... ................ complex[k'].real = data[k] k' = N - k complex[k'].imag = -data[N-k] ............... ................ complex[N-1].real = data[1] complex[N-1].imag = -data[N-1]
The radix-2 FFT functions for halfcomplex data are declared in the header file `gsl_fft_halfcomplex.h'.
These functions compute the inverse or backwards in-place radix-2 FFT of
length n and stride stride on the half-complex sequence
data stored according the output scheme used by
gsl_fft_real_radix2
. The result is a real array stored in natural
order.
This section describes mixed-radix FFT algorithms for real data. The mixed-radix functions work for FFTs of any length. They are a reimplementation of the real-FFT routines in the Fortran FFTPACK library by Paul Swarztrauber. The theory behind the algorithm is explained in the article Fast Mixed-Radix Real Fourier Transforms by Clive Temperton. The routines here use the same indexing scheme and basic algorithms as FFTPACK.
The functions use the FFTPACK storage convention for half-complex sequences. In this convention the half-complex transform of a real sequence is stored with frequencies in increasing order, starting at zero, with the real and imaginary parts of each frequency in neighboring locations. When a value is known to be real the imaginary part is not stored. The imaginary part of the zero-frequency component is never stored. It is known to be zero (since the zero frequency component is simply the sum of the input data (all real)). For a sequence of even length the imaginary part of the frequency n/2 is not stored either, since the symmetry z_k = z_{N-k}^* implies that this is purely real too.
The storage scheme is best shown by some examples. The table below
shows the output for an odd-length sequence, n=5. The two columns
give the correspondence between the 5 values in the half-complex
sequence returned by gsl_fft_real_transform
, halfcomplex[] and the
values complex[] that would be returned if the same real input
sequence were passed to gsl_fft_complex_backward
as a complex
sequence (with imaginary parts set to 0
),
complex[0].real = halfcomplex[0] complex[0].imag = 0 complex[1].real = halfcomplex[1] complex[1].imag = halfcomplex[2] complex[2].real = halfcomplex[3] complex[2].imag = halfcomplex[4] complex[3].real = halfcomplex[3] complex[3].imag = -halfcomplex[4] complex[4].real = halfcomplex[1] complex[4].imag = -halfcomplex[2]
The upper elements of the complex array, complex[3]
and
complex[4]
are filled in using the symmetry condition. The
imaginary part of the zero-frequency term complex[0].imag
is
known to be zero by the symmetry.
The next table shows the output for an even-length sequence, n=5 In the even case both the there are two values which are purely real,
complex[0].real = halfcomplex[0] complex[0].imag = 0 complex[1].real = halfcomplex[1] complex[1].imag = halfcomplex[2] complex[2].real = halfcomplex[3] complex[2].imag = halfcomplex[4] complex[3].real = halfcomplex[5] complex[3].imag = 0 complex[4].real = halfcomplex[3] complex[4].imag = -halfcomplex[4] complex[5].real = halfcomplex[1] complex[5].imag = -halfcomplex[2]
The upper elements of the complex array, complex[4]
and
complex[5]
are filled in using the symmetry condition. Both
complex[0].imag
and complex[3].imag
are known to be zero.
All these functions are declared in the header files `gsl_fft_real.h' and `gsl_fft_halfcomplex.h'.
sin
and cos
, for accuracy.
Recursion relations could be used to compute the lookup table faster,
but if an application performs many FFTs of the same length then
computing the wavetable is a one-off overhead which does not affect the
final throughput.
The wavetable structure can be used repeatedly for any transform of the same length. The table is not modified by calls to any of the other FFT functions. The appropriate type of wavetable must be used for forward real or inverse half-complex transforms.
gsl_fft_real_transform
data is an array of
time-ordered real data. For gsl_fft_halfcomplex_transform
data contains fourier coefficients in the half-complex ordering
described above. There is no restriction on the length n.
Efficient modules are provided for subtransforms of length 2, 3, 4 and
5. Any remaining factors are computed with a slow, O(n^2),
general-n module. The caller must supply a wavetable containing
trigonometric lookup tables and a workspace work.
This function converts a single real array, real_coefficient into
an equivalent complex array, complex_coefficient, (with imaginary
part set to zero), suitable for gsl_fft_complex
routines. The
algorithm for the conversion is simply,
for (i = 0; i < n; i++) { complex_coefficient[i].real = real_coefficient[i]; complex_coefficient[i].imag = 0.0; }
This function converts halfcomplex_coefficient, an array of
half-complex coefficients as returned by gsl_fft_real_transform
, into an
ordinary complex array, complex_coefficient. It fills in the
complex array using the symmetry
z_k = z_{N-k}^*
to reconstruct the redundant elements. The algorithm for the conversion
is,
complex_coefficient[0].real = halfcomplex_coefficient[0]; complex_coefficient[0].imag = 0.0; for (i = 1; i < n - i; i++) { double hc_real = halfcomplex_coefficient[2 * i - 1]; double hc_imag = halfcomplex_coefficient[2 * i]; complex_coefficient[i].real = hc_real; complex_coefficient[i].imag = hc_imag; complex_coefficient[n - i].real = hc_real; complex_coefficient[n - i].imag = -hc_imag; } if (i == n - i) { complex_coefficient[i].real = halfcomplex_coefficient[n - 1]; complex_coefficient[i].imag = 0.0; }
Here is an example program using gsl_fft_real_transform
and
gsl_fft_halfcomplex_inverse
. It generates a real signal in the
shape of a square pulse. The pulse is fourier transformed to frequency
space, and all but the lowest ten frequency components are removed from
the array of fourier coefficients returned by
gsl_fft_real_transform
.
The remaining fourier coefficients are transformed back to the time-domain, to give a filtered version of the square pulse. Since fourier coefficients are stored using the half-complex symmetry both positive and negative frequencies are removed and the final filtered signal is also real.
#include <stdio.h> #include <math.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_fft_real.h> #include <gsl/gsl_fft_halfcomplex.h> int main (void) { int i, n = 100; double data[n]; gsl_fft_real_wavetable * real; gsl_fft_halfcomplex_wavetable * hc; gsl_fft_real_workspace * work; for (i = 0; i < n; i++) { data[i] = 0.0; } for (i = n / 3; i < 2 * n / 3; i++) { data[i] = 1.0; } for (i = 0; i < n; i++) { printf ("%d: %e\n", i, data[i]); } printf ("\n"); work = gsl_fft_real_workspace_alloc (n); real = gsl_fft_real_wavetable_alloc (n); gsl_fft_real_transform (data, 1, n, real, work); gsl_fft_real_wavetable_free (real); for (i = 11; i < n; i++) { data[i] = 0; } hc = gsl_fft_halfcomplex_wavetable_alloc (n); gsl_fft_halfcomplex_inverse (data, 1, n, hc, work); gsl_fft_halfcomplex_wavetable_free (hc); for (i = 0; i < n; i++) { printf ("%d: %e\n", i, data[i]); } gsl_fft_real_workspace_free (work); return 0; }
Low-pass filtered version of a real pulse, output from the example program.
A good starting point for learning more about the FFT is the review article Fast Fourier Transforms: A Tutorial Review and A State of the Art by Duhamel and Vetterli,
To find out about the algorithms used in the GSL routines you may want to consult the latex document GSL FFT Algorithms (it is included in GSL, as `doc/fftalgorithms.tex'). This has general information on FFTs and explicit derivations of the implementation for each routine. There are also references to the relevant literature. For convenience some of the more important references are reproduced below.
There are several introductory books on the FFT with example programs, such as The Fast Fourier Transform by Brigham and DFT/FFT and Convolution Algorithms by Burrus and Parks,
Both these introductory books cover the radix-2 FFT in some detail. The mixed-radix algorithm at the heart of the FFTPACK routines is reviewed in Clive Temperton's paper,
The derivation of FFTs for real-valued data is explained in the following two articles,
In 1979 the IEEE published a compendium of carefully-reviewed Fortran FFT programs in Programs for Digital Signal Processing. It is a useful reference for implementations of many different FFT algorithms,
For serious FFT work we recommend the use of the dedicated FFTW library by Frigo and Johnson. The FFTW library is self-optimizing -- it automatically tunes itself for each hardware platform in order to achieve maximum performance. It is available under the GNU GPL.
¤³¤Î¾Ï¤Ç¤Ï1¼¡¸µ´Ø¿ô¤Î¿ôÃÍÀÑÊ¬(µáÀÑ)¤ò¼Â¹Ô¤¹¤ë¥ë¡¼¥Á¥ó¤Ë¤Ä¤¤¤Æ½Ò¤Ù¤ë. °ìÈÌ ¤Î´Ø¿ô¤ÎÅ¬±þÅª¤ª¤è¤ÓÈóÅ¬±þÅªÀÑÊ¬¥ë¡¼¥Á¥ó¤¬¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë. Ìµ¸ÂÎÎ°è¤ª¤è¤Ó È¾Ìµ¸ÂÎÎ°è¤Ç¤ÎÀÑÊ¬, ÂÐ¿ôÆÃ°ÛÅÀ¤ò´Þ¤àÆÃ°ÛÅÀ¤ÎÀÑÊ¬, ¥³¡¼¥·¡¼¼çÃÍ¤Î·×»», ¤½ ¤·¤Æ¿¶Æ°ÀÑÊ¬¤â¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë. ¤³¤Î¥é¥¤¥Ö¥é¥ê¤ÏQUADPACK¤Ç»È¤ï¤ì¤Æ¤¤ ¤ëPiessens, Doncker-Kapenga, Uberhuber, Kahaner¤Ë¤è¤ê¼ÂÁõ¤µ¤ì¤¿¿ôÃÍÀÑÊ¬ ¥Ñ¥Ã¥±¡¼¥¸¤ò°Ü¿¢¤·¤¿¤â¤Î¤Ç¤¢¤ë. QUADPACK¤ÎFortran¥³¡¼¥É¤ÏNetlib¤ÇÍø ÍÑ¤Ç¤¤ë.
¤³¤Î¾Ï¤ÇÀâÌÀ¤µ¤ì¤ë´Ø¿ô¤Ï¥Ø¥Ã¥À¥Õ¥¡¥¤¥ë`gsl_integration.h'¤ÇÀë¸À¤µ¤ì ¤Æ¤¤¤ë.
³Æ¥¢¥ë¥´¥ê¥º¥à¤Ç¤Ï¼¡¤Î·Á¤ÎÍ³¦ÀÑÊ¬¤Î¶á»÷ÃÍ¤ò·×»»¤¹¤ë:
¤³¤³¤Ç ¤Ï½Å¤ß´Ø¿ô(°ìÈÌ¤ÎÈïÀÑÊ¬´Ø¿ô¤Ç¤Ï )¤Ç¤¢¤ë. ¥æ¡¼¥¶¡¼¤Ï¼¡¤ÎÀºÅÙÍ×µá¤òÆÃÄê¤¹¤ëÁêÂÐ¸íº¹ÈÏ°Ï (epsabs, epsrel)¤ò»ØÄê¤Ç¤¤ë:
¤³¤³¤Ç ¤Ï¥¢¥ë¥´¥ê¥º¥à¤Ë¤è¤êÆÀ¤é¤ì¤¿¿ôÃÍÅª¶á»÷ÃÍ¤Ç¤¢¤ë. ¥¢¥ë¥´¥ê¥º¥à¤ÏÀäÂÐ¸íº¹ ¤ò¼¡¤ÎÉÔÅù¼°¤ò¤ß¤¿¤¹·Á¤ÇÉ¾²Á¤¹¤ë:
¥ë¡¼¥Á¥ó¤Ï¸íº¹ÈÏ°Ï¤¬¸·Ì©¤¹¤®¤ë¤È¼ýÂ«¤·¤Ê¤¯¤Ê¤ë. ¤½¤Î¾ì¹çÅÓÃæ¤ÇÆÀ¤é¤ì¤ëºÇ ÎÉ¤Î¶á»÷ÃÍ¤¬ÊÖ¤µ¤ì¤ë.
QUADPACK¤Ë¤¢¤ë¥¢¥ë¥´¥ê¥º¥à¤Ï¼¡¤Î¤è¤¦¤ÊÌ¾Á°ÉÕ¤±µ¬Â§¤Ë½¾¤Ã¤Æ¤¤¤ë:
Q
- µáÀÑ¥ë¡¼¥Á¥óN
- ÈóÅ¬±þÅªÀÑÊ¬A
- Å¬±þÅªÀÑÊ¬G
- °ìÈÌÀÑÊ¬(¥æ¡¼¥¶¡¼ÄêµÁ)W
- ÈïÀÑÊ¬´Ø¿ô¤Ë½Å¤ß¤Å¤±´Ø¿ô¤ò¤«¤±¤ëS
- È¯»¶¤ò´Þ¤àÀÑÊ¬P
- ÆÃ°ÛÅÀ¤ò´Þ¤àÀÑÊ¬I
- Ìµ¸ÂÈÏ°Ï¤Ç¤ÎÀÑÊ¬O
- cos¤Þ¤¿¤Ïsin¤Ë¤è¤ë¿¶Æ°´Ø¿ô¤Ç¤Î½Å¤ß¤Å¤±F
- ¥Õ¡¼¥ê¥¨ÀÑÊ¬C
- ¥³¡¼¥·¡¼¼çÃÍ
¥¢¥ë¥´¥ê¥º¥à¤Ë¤Ï, ¹â¼¡¤ª¤è¤ÓÄã¼¡¤Îµ¬Â§¤Ë¤è¤ë1ÂÐ¤ÎÀÑÊ¬Â§¤¬»È¤ï¤ì¤Æ¤¤¤ë. ¹â¼¡µ¬Â§¤Ï¶¹¤¤ÈÏ°Ï¤ÎÀÑÊ¬¤Î¶á»÷ÃÍ¤ò·×»»¤¹¤ë¤Î¤Ë»È¤ï¤ì¤ë. ¹â¼¡¤ª¤è¤ÓÄã¼¡¤Î ·ë²Ì¤Îº¹¤Ï¶á»÷¤Î¸íº¹É¾²Á¤ËÍÑ¤¤¤é¤ì¤ë.
°ìÈÌ¤Î(½Å¤ß¤Î¤Ê¤¤)´Ø¿ô¤ÎÀÑÊ¬¥¢¥ë¥´¥ê¥º¥à¤ÏGauss-KronrodÂ§¤Ë´ð¤Å¤¤¤Æ¤¤¤ë. Gauss-KronrodÂ§¤Ïm¼¡¤Î¸ÅÅµÅª¥¬¥¦¥¹ÀÑÊ¬¤«¤é»Ï¤Þ¤ë. ¤³¤ì¤Ë 2m+1¼¡¤Î¹â¼¡KronrodÂ§¤òÍ¿¤¨¤ë¤è¤¦¤Ë²£¼´¤ËÅÀ¤òÉÕ²Ã¤¹¤ë. KornrodÂ§ ¤Ï¥¬¥¦¥¹Â§¤ÇÉ¾²Á¤·¤¿´Ø¿ôÃÍ¤òºÆÍøÍÑ¤¹¤ë¤Î¤Ç¸ú²ÌÅª¤Ç¤¢¤ë. ¹â¼¡KronrodÂ§¤Ï ÀÑÊ¬¤ÎºÇÎÉ¶á»÷ÃÍ¤È¤·¤ÆÍÑ¤¤¤é¤ì, ¤³¤ì¤é2¤Ä¤Îµ¬Â§¤Îº¹¤Ï¶á»÷¸íº¹¤È¤·¤ÆÉ¾²Á ¤µ¤ì¤ë.
½Å¤ß´Ø¿ô¤Î¤¢¤ëÈïÀÑÊ¬´Ø¿ô¤Î¾ì¹ç¤ÏClenshaw-CurtisµáÀÑË¡¤¬ÍÑ¤¤¤é¤ì¤ë. Clenshaw-CurtisÂ§¤Ïn¼¡ChebyschevÂ¿¹à¼°¶á»÷¤òÈïÀÑÊ¬´Ø¿ô¤ËÅ¬ÍÑ¤¹¤ë. ¤³¤ÎÂ¿¹à¼°¤Ï¸·Ì©¤ËÀÑÊ¬¤Ç¤¤ë¤Î¤Ç, ¸µ¤ÎÈïÀÑÊ¬´Ø¿ô¤ÎÀÑÊ¬¶á»÷ÃÍ¤¬µá¤á¤é¤ì¤ë. ChebyschevÅ¸³«¤Ï¶á»÷¤ò²þÎÉ¤¹¤ë¤¿¤á¤Ë¹â¼¡¤Ë³ÈÄ¥¤¹¤ë¤³¤È¤¬¤Ç¤¤ë. ÈïÀÑÊ¬´Ø ¿ô¤ÎÆÃ°ÛÅÀ(¤äÂ¾¤ÎÆÃÄ§)¤ÏChebyschev¶á»÷¤Ç¤Ï¼ýÂ«¤òÃÙ¤¯¤¹¤ë. QUADPACK ¤Ç»È¤ï¤ì¤Æ¤¤¤ë²þÎÉClenshaw-CurtisÂ§¤Ï¼ýÂ«¤òÃÙ¤¯¤¹¤ë¤¤¤¯¤Ä¤«¤ÎÈÆÍÑ½Å¤ß´Ø ¿ô¤òÊ¬Î¥¤·¤Æ¤¤¤ë. ¤³¤ì¤é¤Î½Å¤ß´Ø¿ô¤ÏChebyschevÂ¿¹à¼°¤ËÂÐ¤·¤ÆÊÑ·Á Chebyschev¥â¡¼¥á¥ó¥È¤È¤·¤Æ¤¢¤é¤«¤¸¤á²òÀÏÅª¤ËÀÑÊ¬¤µ¤ì¤Æ¤¤¤ë. ¤³¤Î¥â¡¼¥á ¥ó¥È¤È´Ø¿ô¤ÎChebyschev¶á»÷¤òÁÈ¤ß¤¢¤ï¤»¤ë¤³¤È¤Ë¤è¤ê¹¥¤¤ÊÀÑÊ¬¤ò¼Â¹Ô¤Ç¤¤ë. ´Ø¿ô¤ÎÆÃ°ÛÉôÊ¬¤Ë²òÀÏÅªÀÑÊ¬¤ò»È¤¦¤³¤È¤Ç¸·Ì©¤ËÁê»¦¤¹¤ë¤³¤È¤¬¤Ç¤, ÀÑÊ¬Á´ÂÎ ¤Î¼ýÂ«À¤ò¤«¤Ê¤ê²þÁ±¤Ç¤¤ë.
QNG¥¢¥ë¥´¥ê¥º¥à¤Ï, ºÇÂç¤Ç87ÅÀ¤ÇÈïÀÑÊ¬´Ø¿ô¤Î¥µ¥ó¥×¥ê¥ó¥°¤ò¹Ô¤¦¸ÇÄê Gauss-KronrodÂ§¤Ë¤è¤ëÈóÅ¬±þ¥×¥í¥·¡¼¥¸¥ã¤Ç¤¢¤ë. ¤³¤ì¤Ë¤è¤ê³ê¤é¤«¤Ê´Ø¿ô¤ò ¹âÂ®¤ËÀÑÊ¬¤Ç¤¤ë.
¤³¤Î´Ø¿ô¤Ï, (a,b)¾å¤Îf¤ÎÀÑÊ¬¤Î¶á»÷ÃÍ¤¬Í×µá¤µ¤ì¤ëÀäÂÐ¤ª¤è¤Ó ÁêÂÐ¸íº¹epsabs¤ª¤è¤Óepsrel¤ÎÈÏ°ÏÆâ¤Ë¤¢¤ë¸Â¤ê10ÅÀ, 21ÅÀ, 43ÅÀ ¤ª¤è¤Ó87ÅÀ¤ÎGauss-KronrodÀÑÊ¬¤ò¼Â¹Ô¤¹¤ë. ¤³¤Î´Ø¿ô¤ÏºÇ½ªÅª¤Ê¶á»÷ÃÍ result, ÀäÂÐ¸íº¹¤Î¸«ÀÑ¤êabserr, ÍÑ¤¤¤é¤ì¤¿´Ø¿ôÉ¾²Á¿ô neval¤òÊÖ¤¹. Gauss-KronrodÂ§¤Ï, ´Ø¿ôÉ¾²Á¤ÎÁí¿ô¤ò¸º¤é¤¹¤¿¤á, ³ÆÃÊ¤Ç Á°ÃÊ¤Î·ë²Ì¤òÍøÍÑ¤¹¤ë¤è¤¦Àß·×¤µ¤ì¤Æ¤¤¤ë.
QAG¥¢¥ë¥´¥ê¥º¥à¤Ï´ÊÃ±¤ÊÅ¬±þÅªÀÑÊ¬¥×¥í¥·¡¼¥¸¥ã¤Ç¤¢¤ë. ÀÑÊ¬¶è´Ö¤òÊ¬³ä¤·,
³ÆÃÊ¤ÇºÇÂç¸«ÀÑ¤ê¸íº¹¤òÍ¿¤¨¤ë¶è´Ö¤òÊ¬³ä¤¹¤ë. ¤³¤ì¤Ë¤è¤êÁ´ÂÎ¤Î¸íº¹¤¬µÞ¸º¤·,
¶è´Ö¤ÎÊ¬³ä¤ÏÈïÀÑÊ¬´Ø¿ô¤Î¶É½êÅªÆñÅÀ¤Ë½¸Ãæ¤¹¤ë¤³¤È¤Ë¤Ê¤ë. ¤³¤ÎÊ¬³ä¶è´Ö¤Ï
gsl_integration_workspace
¹½Â¤ÂÎ¤Ç´ÉÍý¤µ¤ì, ¶è´Ö, ·ë²Ì, ¤½¤·¤ÆÉ¾²Á
¸íº¹¤¬³ÊÇ¼¤µ¤ì¤ë.
¤³¤Î´Ø¿ô¤Ïn¸Ä¤ÎÇÜÀºÅÙ¤Î¶è´Ö, ÀÑÊ¬·ë²Ì, É¾²Á¸íº¹¤ò³ÊÇ¼¤¹¤ë¤Î¤Ë½½Ê¬ ¤Êºî¶È¶õ´Ö¤ò³ÎÊÝ¤¹¤ë.
¤³¤Î´Ø¿ô¤Ïºî¶È¶õ´Öw¤Ë³ä¤ê¤¢¤Æ¤é¤ì¤Æ¤¤¤¿¥á¥â¥ê¤ò²òÊü¤¹¤ë.
¤³¤Î´Ø¿ô¤Ï(a,b)¾å¤Îf¤ÎÀÑÊ¬¤Î¶á»÷ÃÍ¤¬Í×µá¤µ¤ì¤ëÀäÂÐ¤ª¤è¤ÓÁê ÂÐ¸íº¹epsabs¤ª¤è¤Óepsrel¤ÎÈÏ°ÏÆâ¤Ë¤¢¤ë¸Â¤êÅ¬±þÅª¤ËÀÑÊ¬¤ò¼Â¹Ô ¤¹¤ë. ¤³¤Î´Ø¿ô¤ÏºÇ½ªÅª¤Ê¶á»÷ÃÍresult, ÀäÂÐ¸íº¹¤ÎÉ¾²ÁÃÍabserr ¤òÊÖ¤¹. ÀÑÊ¬Â§¤Ïkey¤ÎÃÍ¤Ë¤è¤ê·èÄê¤µ¤ì¤ë. key¤Ï¼¡¤Î¥·¥ó¥Ü¥ëÌ¾ ¤«¤éÁª¤Ö:
GSL_INTEG_GAUSS15 (key = 1) GSL_INTEG_GAUSS21 (key = 2) GSL_INTEG_GAUSS31 (key = 3) GSL_INTEG_GAUSS41 (key = 4) GSL_INTEG_GAUSS51 (key = 5) GSL_INTEG_GAUSS61 (key = 6)
¤³¤ì¤Ï15, 21, 31, 41, 51, 61ÅÀGauss-KronrodÂ§¤ËÁêÅö¤¹¤ë. ¹â¼¡Â§¤Ï³ê¤é¤« ¤Ê´Ø¿ô¤Ç¤¢¤ì¤ÐÀºÅÙ¤¬¤è¤¯¤Ê¤ë. Äã¼¡Â§¤ÏÉÔÏ¢Â³¤Î¤è¤¦¤Ê¶É½êÅªÆñÅÀ¤ò´Þ¤à´Ø¿ô ¤Ç»þ´Ö¤òÀáÌó¤Ç¤¤ë.
ÀÑÊ¬¤Î³ÆÃÊ¤Ç, Å¬±þÅªÀÑÊ¬¥¹¥È¥é¥Æ¥¸¤Ë½¾¤¤É¾²Á¸íº¹¤¬ºÇÂç¤Î¶è´Ö¤òÊ¬³ä¤¹¤ë. ¶è´ÖÊ¬³ä¤È¤½¤Î·ë²Ì¤Ïworkspace¤Ç³ä¤ê¤¢¤Æ¤é¤ì¤ë¥á¥â¥ê¤Ë³ÊÇ¼¤µ¤ì¤ë. ¶è´ÖÊ¬³ä¿ô¤ÎºÇÂçÃÍ¤Ïlimit¤ÇÍ¿¤¨¤é¤ì¤ë. ¤³¤ì¤Ï³ä¤ê¤¢¤Æ¤¿ºî¶ÈÎÎ°è¤Î ¥µ¥¤¥º¤ò±Û¤¨¤Æ¤Ï¤Ê¤é¤Ê¤¤.
ÀÑÊ¬¶è´Ö¤Ë²ÄÀÑÊ¬¤ÎÆÃ°ÛÅÀ¤¬Â¸ºß¤¹¤ë¤È, Å¬±þÅª¥ë¡¼¥Á¥ó¤Î¶è´ÖÊ¬³ä¤¬ÆÃ°ÛÅÀ¤Î ¤Þ¤ï¤ê¤Ë½¸Ãæ¤·¤Æ¤·¤Þ¤¦. Ê¬³ä¤µ¤ì¤¿¶è´Ö¤ÎÉý¤¬¸º¾¯¤¹¤ë¤È¤½¤ì¤Ë¤è¤ëÀÑÊ¬¤Î¶á »÷ÃÍ¤Ï¸Â¤é¤ì¤¿·Á¤Ç¤·¤«¼ýÂ«¤·¤Ê¤¤. ¤³¤Î¼ýÂ«¤ò³°ÁÞ¤Ë¤è¤ê²ÃÂ®¤µ¤»¤ë. QAGS¥¢ ¥ë¥´¥ê¥º¥à¤ÏÅ¬±þÅª¶è´ÖÊ¬³ä¤ËWynn¦Å¥¢¥ë¥´¥ê¥º¥à¤òÍ»¹ç¤µ¤», ÍÍ¡¹¤Ê²ÄÀÑÊ¬ÆÃ °ÛÅÀ¤ÎÀÑÊ¬¤ò¥¹¥Ô¡¼¥É¥¢¥Ã¥×¤µ¤»¤ë.
¤³¤Î´Ø¿ô¤Ï(a,b)¾å¤Îf¤ÎÀÑÊ¬¤Î¶á»÷ÃÍ¤¬Í×µá¤µ¤ì¤ëÀäÂÐ¤ª¤è¤ÓÁê ÂÐ¸íº¹epsabs¤ª¤è¤Óepsrel¤ÎÈÏ°ÏÆâ¤Ë¤¢¤ë¸Â¤ê21ÅÀGauss-Kronrod ÀÑÊ¬Â§¤òÅ¬±þÅª¤Ë¼Â¹Ô¤¹¤ë. ·ë²Ì¤Ï ¥¢¥ë¥´¥ê¥º¥à¤Ë¤è¤ê³°ÁÞ¤µ¤ì, ÉÔÏ¢Â³¤ä²ÄÀÑÊ¬ÆÃ°ÛÅÀ¤ÎÂ¸ºß¤¹¤ëÀÑÊ¬¤Î¼ýÂ«¤ò²Ã Â®¤µ¤»¤ë. ¤³¤Î´Ø¿ô¤Ï³°ÁÞ¤Ë¤è¤ëºÇ½ª¶á»÷ÃÍresult, ÀäÂÐ¸íº¹¤ÎÉ¾²ÁÃÍ abserr¤òÊÖ¤¹. ¶è´ÖÊ¬³ä¤È¤½¤Î·ë²Ì¤Ïworkspace¤Ç³ä¤ê¤¢¤Æ¤é¤ì¤ë ¥á¥â¥ê¤Ë³ÊÇ¼¤µ¤ì¤ë. Ê¬³ä¶è´Ö¤ÎºÇÂç¿ô¤Ïlimit¤Ç»ØÄê¤¹¤ë. ºî¶È¶õ´Ö¤Î ³ä¤ê¤¢¤Æ¥µ¥¤¥º¤ò±Û¤¨¤Æ¤Ï¤Ê¤é¤Ê¤¤.
¤³¤Î´Ø¿ô¤Ï¥æ¡¼¥¶¡¼¤¬Äó¶¡¤¹¤ëÆÃ°ÛÅÀ¤Î¾ì½ê¤ò¹ÍÎ¸¤·¤Ê¤¬¤éÅ¬±þÅª¤ËÀÑÊ¬¤ò¼Â¹Ô ¤¹¤ë¥¢¥ë¥´¥ê¥º¥àQAGS¤Î¼ÂÁõ¤Ç¤¢¤ë. Ä¹¤µnpts¤ÎÇÛÎópts¤Ë¤ÏÀÑÊ¬ ¶è´Ö¤ÎÃ¼ÅÀ¤ÈÆÃ°ÛÅÀ¤Î°ÌÃÖ¤ò³ÊÇ¼¤¹¤ë. Îã¤¨¤Ð, ¶è´Ö(a,b)¾å¤Ç, ¤ËÆÃ°ÛÅÀ¤ò¤â¤ÄÀÑÊ¬¤ò¼Â¹Ô¤¹¤ë¾ì¹ç(¤¿¤À¤· ), ¼¡¤Î¤è¤¦¤ÊptsÇÛÎó¤òÍ¿¤¨¤ë:
pts[0] = a pts[1] = x_1 pts[2] = x_2 pts[3] = x_3 pts[4] = b
¤³¤³¤Ç npts = 5.
ÀÑÊ¬¶è´Ö¤Ç¤ÎÆÃ°ÛÅÀ¤Î°ÌÃÖ¤òÃÎ¤Ã¤Æ¤¤¤ë¾ì¹ç¤Ï, ¤³¤Î¥ë¡¼¥Á¥ó¤Î¤Û¤¦¤¬
QAGS
¤è¤êÁá¤¤.
¤³¤Î´Ø¿ô¤ÏÈóÍ³¦¶è´Ö ¤Ç´Ø¿ôf¤òÀÑÊ¬¤¹¤ë. ÀÑÊ¬¤ÏÊÑ¿ôÊÑ´¹ ¤Ë¤è¤ê¶è´Ö ¤Ë¼ÌÁü¤µ¤ì¤ë:
¤½¤·¤ÆQAGS¥¢¥ë¥´¥ê¥º¥à¤òÍÑ¤¤¤ÆÀÑÊ¬¤µ¤ì¤ë. ÊÑ¿ôÊÑ´¹¤Ë¤è¤ê¸¶ÅÀ¤Ë²ÄÀÑÊ¬¤ÎÆÃ °ÛÅÀ¤¬¤Ç¤¤Æ¤·¤Þ¤¦¤Î¤Ç, ÄÌ¾ï¤ÎQAGS¤Î21ÅÀGauss-KronrodÂ§¤ò15ÅÀÂ§¤Ë¤ª¤¤« ¤¨¤ë. ¤³¤Î¾ì¹çÄã¼¡Â§¤Î¤Û¤¦¤¬¤è¤ê¸ú²ÌÅª¤À¤«¤é¤Ç¤¢¤ë.
¤³¤Î´Ø¿ô¤ÏÈ¾ÈóÍ³¦¶è´Ö ¾å¤Ç´Ø¿ôf¤òÀÑÊ¬¤¹¤ë. ÀÑÊ¬¤ÏÊÑ¿ôÊÑ´¹ ¤Ë¤è¤ê¶è´Ö ¤Ë¼ÌÁü¤µ¤ì¤ë.
¤½¤·¤ÆQAGS¥¢¥ë¥´¥ê¥º¥à¤òÍÑ¤¤¤ÆÀÑÊ¬¤µ¤ì¤ë.
¤³¤Î´Ø¿ô¤ÏÈ¾ÈóÍ³¦¶è´Ö ¾å¤Ç´Ø¿ôf¤òÀÑÊ¬¤¹¤ë. ÀÑÊ¬¤ÏÊÑ¿ôÊÑ´¹ ¤Ë¤è¤ê¶è´Ö ¤Ë¼ÌÁü¤µ¤ì¤ë.
¤½¤·¤ÆQAGS¥¢¥ë¥´¥ê¥º¥à¤òÍÑ¤¤¤ÆÀÑÊ¬¤µ¤ì¤ë.
¤³¤Î´Ø¿ô¤Ï ¾å¤Îf¤ÎÀÑÊ¬¤Îc¤Ç¤ÎÆÃ°ÛÅÀ¤Î¥³¡¼¥·¡¼¼çÃÍ¤òµá¤á¤ë.
QAG¤ÎÅ¬±þÅªÊ¬³ä¥¢¥ë¥´¥ê¥º¥à¤¬»È¤ï¤ì¤ë¤¬, ÆÃ°ÛÅÀ ¤ÇÊ¬³ä¤µ¤ì¤Ê¤¤¤è¤¦¤Ë¹©É×¤µ¤ì¤Æ¤¤¤ë. Ê¬³ä¶è´Ö¤¬ÅÀ ¤ò´Þ¤ó¤Ç¤¤¤¿¤ê, ¤½¤ÎÅÀ¤Ë¶á¤¤¾ì¹ç¤ÏÆÃÊÌ¤Ê25ÅÀÊÑ·ÁClenshaw-CurtisÂ§¤¬ÆÃ°Û ÅÀ¤òÈò¤±¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë. ÆÃ°ÛÅÀ¤«¤éÎ¥¤ì¤¿¾ì½ê¤Ç¤ÏÄÌ¾ï¤Î15ÅÀ Gauss-KronrodÀÑÊ¬Â§¤¬»È¤ï¤ì¤ë.
QAWS¥¢¥ë¥´¥ê¥º¥à¤Ï, ÈïÀÑÊ¬´Ø¿ô¤¬ÀÑÊ¬ÎÎ°è¤ÎÃ¼ÅÀ¤ÇÂÐ¿ôÅª¤ÊÈ¯»¶¤ò¤¹¤ë¤È¤¤Ë ÍÑ¤¤¤é¤ì¤ë. ¸ú²ÌÅª¤Ë·×»»¤¹¤ë¤¿¤á, Chebyschev¥â¡¼¥á¥ó¥È¤ò¤¢¤é¤«¤¸¤á·×»»¤· ¤Æ¤ª¤¯.
¤³¤Î´Ø¿ô¤Ïgsl_integration_gaws_table
¤ÎÎÎ°è¤ò³ÎÊÝ¤·, ÆÃ°ÛÅÀ¤Î½Å¤ß
´Ø¿ô
¤ò¥Ñ¥é¥á¡¼¥¿
¤ÇÉ½¸½¤¹¤ë¤¿¤á¤Îºî¶ÈÎÎ°è¤ò³ä¤ê¤¢¤Æ¤ë:
¤³¤³¤Ç ¤Ç¤¢¤ë. ½Å¤ß´Ø¿ô¤Ï , ¤ÎÃÍ¤Ë¤è¤ê°Ê²¼¤Î4¤Ä¤Î·Á¤ò¤È¤ë:
ÆÃ°ÛÅÀ ¤ÏÀÑÊ¬¤¬·×»»¤µ¤ì¤ë¤Þ¤ÇÆÃÄê¤µ¤ì¤Ê¤¯¤Æ¤â¤è¤¤. ¤³¤ì¤é¤ÏÀÑÊ¬ÎÎ°è¤ÎÃ¼ÅÀ¤Ç¤¢¤ë.
¤³¤Î´Ø¿ô¤Ï, ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤Ê¤±¤ì¤Ð, ¿·¤·¤¯³ä¤ê¤¢¤Æ¤é¤ì¤¿
gsl_integration_qaws_table
¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹. ¥¨¥é¡¼¤Î¾ì¹ç¤Ë¤Ï0¤ò
ÊÖ¤¹.
gsl_integration_qaws_table
¹½Â¤ÂÎt¤Î¥Ñ¥é¥á¡¼¥¿
¤òÊÑ¹¹¤¹¤ë.
gsl_integration_qaws_table
¹½Â¤ÂÎt¤Ë³ä¤ê¤¢¤Æ¤é¤ì¤¿
¥á¥â¥ê¤ò²òÊü¤¹¤ë.
¤³¤Î´Ø¿ô¤ÏÆÃ°ÛÅÀ½Å¤ß´Ø¿ô ¤òÍÑ¤¤¤Æ¶è´Ö ¾å¤Ç´Ø¿ôf(x)¤òÀÑÊ¬¤¹¤ë. ¥Ñ¥é¥á¡¼¥¿ ¤Ï¥Æ¡¼¥Ö¥ët¤«¤é¼èÆÀ¤¹¤ë.
QAG¤ÎÅ¬±þÅªÊ¬³ä¥¢¥ë¥´¥ê¥º¥à¤¬ÍÑ¤¤¤é¤ì¤ë. Ê¬³ä¶è´Ö¤ËÃ¼ÅÀ¤¬´Þ¤Þ¤ì¤ë¤È¤¤Ï ÆÃ¼ì25ÅÀÊÑ·ÁClenshaw-CurtisÂ§¤¬ÆÃ°ÛÅÀ¤òÈò¤±¤ë¤¿¤á¤ËÍÑ¤¤¤é¤ì¤ë. Ã¼ÅÀ¤ò´Þ ¤Þ¤Ê¤¤¶è´Ö¤Ç¤ÏÄÌ¾ï¤Î15ÅÀGauss-KronrodÂ§¤¬ÍÑ¤¤¤é¤ì¤ë.
QAWO¥¢¥ë¥´¥ê¥º¥à¤Ï¿¶Æ°°ø»Ò ¤ò¤â¤ÄÀÑÊ¬¤ò·×»»¤¹¤ë. ¸ú²ÌÅª¤Ë·×»»¤¹¤ë¤¿¤á, ¤³¤Î°ø»Ò¤ò¤¢¤é¤«¤¸¤á·×»»¤·¤¿ Chebyschev¥â¡¼¥á¥ó¥È¤Î¥Æ¡¼¥Ö¥ë¤¬É¬Í×¤È¤Ê¤ë.
¤³¤Î´Ø¿ô¤Ïgsl_integration_qawo_table
¹½Â¤ÂÎ¤Î¤¿¤á¤Ë¶õ´Ö¤ò³ä¤ê¤¢¤Æ,
¥Ñ¥é¥á¡¼¥¿
¤ò¤â¤Ä¥µ¥¤¥ó¤Þ¤¿¤Ï¥³¥µ¥¤¥ó¤Î½Å¤ß´Ø¿ô
¤Î¤¿¤á¤Îºî¶ÈÎÎ°è¤È¤Ê¤ë.
¥Ñ¥é¥á¡¼¥¿L¤Ï´Ø¿ô¤ÎÀÑÊ¬ÎÎ°è¤ÎÄ¹¤µ ¤òÍ¿¤¨¤ë. ¥µ¥¤¥ó¤«¥³¥µ¥¤¥ó¤ÎÁªÂò¤Ï¥Ñ¥é¥á¡¼¥¿sine¤Ç¹Ô¤ï¤ì¤ë. ÃÍ¤Ë¤Ï °Ê²¼¤Î¥·¥ó¥Ü¥ëÃÍ¤òÍÑ¤¤¤ë:
GSL_INTEG_COSINE GSL_INTEG_SINE
gsl_integration_qawo_table
¤ÏÀÑÊ¬²áÄø¤ÇÉ¬Í×¤È¤Ê¤ë»°³Ñ´Ø¿ôÉ½¤Ç¤¢¤ë.
¥Ñ¥é¥á¡¼¥¿n¤Ë¤è¤ê·×»»¤µ¤ì¤ë·¸¿ô¤Î¥ì¥Ù¥ë¤¬»ØÄê¤µ¤ì¤ë. ³Æ¥ì¥Ù¥ë¤Ï´Ö
³ÖL¤ÎÊ¬³ä¤ËÁêÅö¤¹¤ë¤Î¤Ç, n¥ì¥Ù¥ë¤ÏÄ¹¤µ
¤Þ¤Ç¤ÎÊ¬³ä¶è´Ö¤ËÂÐ±þ¤Ç¤¤ë. ÀÑÊ¬¥ë¡¼¥Á¥ógsl_integration_qawo
¤Ï,
Í×µá¤µ¤ì¤¿ÀºÅÙ¤ËÂÐ¤·¤Æ¥ì¥Ù¥ë¿ô¤¬ÉÔÂ¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤ÏGSL_ETABLE
¤ò
ÊÖ¤¹.
¤³¤Î´Ø¿ô¤Ï, ¥Æ¡¼¥Ö¥ëwf¤ÇÄêµÁ¤µ¤ì¤ë½Å¤ß´Ø¿ô ¤òÍÑ¤¤, ¶è´Ö ¾å¤Ç´Ø¿ôf¤ÎÀÑÊ¬¤ò·×»»¤¹¤ëÅ¬±þÅª¥¢¥ë¥´¥ê¥º¥à¤Ç¤¢¤ë.
ÀÑÊ¬¤Î¼ýÂ«²ÃÂ®¤¹¤ë¤¿¤á, ·ë²Ì¤Ï ¥¢¥ë¥´¥ê¥º¥à¤òÍÑ¤¤¤Æ³°ÁÞ¤µ¤ì¤ë. ´Ø¿ô¤ÏºÇ½ªÅª¤Ê¿äÄêÃÍresult, ÀäÂÐ¸í º¹¤ÎÉ¾²ÁÃÍabserr¤òÊÖ¤¹. Ê¬³ä¶è´Ö¤È¤½¤Î·ë²Ì¤Ïworkspace¤Î¥á¥â ¥ê¤Ë³ÊÇ¼¤µ¤ì¤ë. Ê¬³ä¶è´Ö¤ÎºÇÂç¿ô¤Ïlimit¤ÇÍ¿¤¨¤é¤ì¤ë. ¤³¤ì¤Ïºî¶ÈÎÎ °è¤Î³ä¤ê¤¢¤Æ¥µ¥¤¥º¤ò±Û¤¨¤Æ¤Ï¤Ê¤é¤Ê¤¤.
¡ÖÂç¤¤Ê¡×Éý ¤ò¤â¤ÄÊ¬³ä¶è´Ö¤Ï25ÅÀClenshaw-CurtisÀÑÊ¬Â§¤òÍÑ¤¤¤Æ·×»»¤·, ¿¶Æ°¤ò½èÍý¤¹¤ë. ¡Ö¾®¤µ¤Ê¡×Éý ¤ò¤â¤ÄÊ¬³ä¶è´Ö¤Ç¤Ï15ÅÀGauss-KronrodÀÑÊ¬¤òÍÑ¤¤¤ë.
¤³¤Î´Ø¿ô¤ÏÈ¾ÈóÍ³¦¶è´Ö ¤Ç¤Î´Ø¿ôf¤ÎFourierÀÑÊ¬¤ò·×»»¤¹¤ë.
¥Ñ¥é¥á¡¼¥¿ ¤Ï¥Æ¡¼¥Ö¥ëwf(Ä¹¤µL¤Ï¹¥¤¤ÊÃÍ¤ò¤È¤ë¤³¤È¤¬¤Ç¤¤ë. FourierÀÑÊ¬ ¤ËÅ¬ÀÚ¤ÊÃÍ¤È¤Ê¤ë¤è¤¦¤Ë¤³¤Î´Ø¿ô¤Ë¤è¤ê¥ª¡¼¥Ð¡¼¥é¥¤¥É¤µ¤ì¤ë¤«¤é¤Ç¤¢¤ë)¤«¤é ¤È¤é¤ì¤ë. ÀÑÊ¬¤ÏQAWO¥¢¥ë¥´¥ê¥º¥à¤ò»È¤Ã¤Æ³ÆÊ¬³ä¶è´Ö¤Ç·×»»¤µ¤ì¤ë.
¤³¤³¤Ç ¤Ï¼þ´ü¤Î´ñ¿ôÇÜ¤ò¥«¥Ð¡¼¤¹¤ë¤è¤¦¤ËÁª¤Ð¤ì¤ë. ³Æ¶è´Ö¤«¤é¤Î´óÍ¿¤ÏÉä¹æ¤¬¸òÂå¤·, f¤¬Àµ¤ÇÃ±Ä´¸º¾¯¤¹¤ë¾ì¹ç¤Ë¤ÏÃ±Ä´¸º¾¯¤¹¤ë. ¤³¤Î¿ôÎó¤ÎÏÂ¤Ï ¥¢¥ë¥´¥ê¥º¥à¤Ç²ÃÂ®¤¹¤ë.
¤³¤Î´Ø¿ô¤ÏÁ´ÂÎ¤ÎÀäÂÐ¸íº¹abserr¤Ç²¡¤µ¤¨¤é¤ì¤ë. °Ê²¼¤Î¥¹¥È¥é¥Æ¥¸¤¬ÍÑ ¤¤¤é¤ì¤ë: ³Æ¶è´Ö ¤Ç¥¢¥ë¥´¥ê¥º¥à¤ÏµöÍÆ¸íº¹¤òÃ£À®¤·¤è¤¦¤È¤¹¤ë:
¤³¤³¤Ç ¤ª¤è¤Ó ¤Ç¤¢¤ë. ³Æ¶è´Ö¤Î»°³Ñ´Ø¿ôÎó¤Î´óÍ¿¤ÎÏÂ¤ÏÁ´ÂÎ¤ÎµöÍÆ¸íº¹abserr¤òÍ¿¤¨¤ë.
Ê¬³ä¶è´Ö¤ÎÀÑÊ¬¤Ëº¤Æñ¤¬È¯À¸¤·¤¿¾ì¹ç¤ÏÊ¬³ä¶è´Ö¤ËÂÐ¤¹¤ëÀºÅÙÍ×µá¤ò´ËÏÂ¤¹¤ë.
¤³¤³¤Ç ¤Ï¶è´Ö ¤Ç¤ÎÉ¾²Á¸íº¹¤Ç¤¢¤ë.
Ê¬³ä¶è´Ö¤È¤½¤Î·ë²Ì¤Ïworkspace¤Î¥á¥â¥ê¤Ë³ÊÇ¼¤µ¤ì¤ë. Ê¬³ä¶è´Ö¤ÎºÇÂç ¿ô¤Ïlimit¤ÇÍ¿¤¨¤é¤ì¤ë. ºî¶ÈÎÎ°è¤Î³ä¤ê¤¢¤Æ¥µ¥¤¥º¤è¤êÂç¤¤¯¤Ê¤¯¤Æ¤Ï ¤Ê¤é¤Ê¤¤. ³ÆÊ¬³ä¶è´Ö¤Ç¤ÎÀÑÊ¬¤Ë¤Ïcycle_workspace¤Î¥á¥â¥ê¤¬QAWO¥¢¥ë ¥´¥ê¥º¥à¤Î¤¿¤á¤Îºî¶ÈÎÎ°è¤È¤·¤ÆÍÑ¤¤¤é¤ì¤ë.
ÉÔÀµ°ú¿ô¤Ë´Ø¤¹¤ëÉ¸½à¥¨¥é¡¼¥³¡¼¥É¤ÎÂ¾, ¤³¤ì¤é¤Î´Ø¿ô¤Ï¼¡¤ÎÃÍ¤òÊÖ¤¹:
GSL_EMAXITER
GSL_EROUND
GSL_ESING
GSL_EDIVERGE
ÀÑÊ¬¥ë¡¼¥Á¥óQAGS
¤ÏÍ³¦ÀÑÊ¬¤ÎÂç¤¯¤Î¥¯¥é¥¹¤Ç·×»»¤Ç¤¤ë. Îã¤¨¤Ð, ¼¡
¤Î¤è¤¦¤ÊÀÑÊ¬¤ò¹Í¤¨¤ë. ¤³¤ì¤Ï¸¶ÅÀ¤ËÂÐ¿ôÅª¤ÊÆÃ°ÛÅÀ¤ò¤â¤Ä:
°Ê²¼¤Î¥×¥í¥°¥é¥à¤Ï¤³¤ÎÀÑÊ¬¤òÁêÂÐ¸íº¹1e-7
¤ÇÀÑÊ¬¤¹¤ë¤â¤Î¤Ç¤¢¤ë.
#include <stdio.h> #include <math.h> #include <gsl/gsl_integration.h> double f (double x, void * params) { double alpha = *(double *) params; double f = log(alpha*x) / sqrt(x); return f; } int main (void) { gsl_integration_workspace * w = gsl_integration_workspace_alloc(1000); double result, error; double expected = -4.0; double alpha = 1.0; gsl_function F; F.function = &f; F.params = α gsl_integration_qags (&F, 0, 1, 0, 1e-7, 1000, w, &result, &error); printf("result = % .18f\n", result); printf("exact result = % .18f\n", expected); printf("estimated error = % .18f\n", error); printf("actual error = % .18f\n", result - expected); printf("intervals = %d\n", w->size); return 0; }
°Ê²¼¤Ë¼¨¤¹·ë²Ì¤Ï, 8²óÊ¬³ä¤¹¤ë¤³¤È¤Ë¤è¤êË¾¤ó¤ÀÀºÅÙ¤¬Ã£À®¤Ç¤¤¿¤³¤È¤ò¼¨¤· ¤Æ¤¤¤ë.
bash$ ./a.out result = -3.999999999999973799 exact result = -4.000000000000000000 estimated error = 0.000000000000246025 actual error = 0.000000000000026201 intervals = 8
¼ÂºÝ, QAGS
¤ÇÍÑ¤¤¤é¤ì¤ë³°ÁÞ¥×¥í¥·¡¼¥¸¥ã¤ÏÍ×µáÀºÅÙ¤ÎÇÜ¶á¤¤ÀºÅÙ¤ò¤â
¤Ä. ³°ÁÞ¥×¥í¥·¡¼¥¸¥ã¤ÎÊÖ¤¹É¾²Á¸íº¹¤Ï¼ÂºÝ¤Î¸íº¹¤è¤ê¤âÂç¤¤¤. °ÂÁ´¤Î¤¿¤á1
·å¤Î¥Þ¡¼¥¸¥ó¤ò¤È¤Ã¤Æ¤¤¤ë¤«¤é¤Ç¤¢¤ë.
°Ê²¼¤ÎËÜ¤ÏQUADPACK¤Î»²¹Í½ñ¤Î·èÄêÈÇ¤Ç¤¢¤ê, ºî¼Ô¤Ë¤è¤ê½ñ¤«¤ì¤¿¤â¤Î¤Ç ¤¢¤ë. ¥¢¥ë¥´¥ê¥º¥à, ¥×¥í¥°¥é¥à°ìÍ÷, ¥Æ¥¹¥È¥×¥í¥°¥é¥à, ¤½¤·¤ÆÎãÂê¤¬ºÜ¤»¤é ¤ì¤Æ¤¤¤ë. ¿ôÃÍÀÑÊ¬¤Ë´Ø¤¹¤ëÍÍÑ¤Ê¥¢¥É¥Ð¥¤¥¹¤ä, QUADPACK¤Î³«È¯¤ËÍÑ¤¤ ¤é¤ì¤¿¿ôÃÍÀÑÊ¬¤Ë´Ø¤¹¤ë»²¹Í½ñ¤âºÜ¤»¤ì¤Æ¤¤¤ë.
The library provides a large collection of random number generators which can be accessed through a uniform interface. Environment variables allow you to select different generators and seeds at runtime, so that you can easily switch between generators without needing to recompile your program. Each instance of a generator keeps track of its own state, allowing the generators to be used in multi-threaded programs. Additional functions are available for transforming uniform random numbers into samples from continuous or discrete probability distributions such as the Gaussian, log-normal or Poisson distributions.
These functions are declared in the header file `gsl_rng.h'.
In 1988, Park and Miller wrote a paper entitled "Random number generators: good ones are hard to find." [Commun. ACM, 31, 1192--1201]. Fortunately, some excellent random number generators are available, though poor ones are still in common use. You may be happy with the system-supplied random number generator on your computer, but you should be aware that as computers get faster, requirements on random number generators increase. Nowadays, a simulation that calls a random number generator millions of times can often finish before you can make it down the hall to the coffee machine and back.
A very nice review of random number generators was written by Pierre L'Ecuyer, as Chapter 4 of the book: Handbook on Simulation, Jerry Banks, ed. (Wiley, 1997). The chapter is available in postscript from from L'Ecuyer's ftp site (see references). Knuth's volume on Seminumerical Algorithms (originally published in 1968) devotes 170 pages to random number generators, and has recently been updated in its 3rd edition (1997). It is brilliant, a classic. If you don't own it, you should stop reading right now, run to the nearest bookstore, and buy it.
A good random number generator will satisfy both theoretical and statistical properties. Theoretical properties are often hard to obtain (they require real math!), but one prefers a random number generator with a long period, low serial correlation, and a tendency not to "fall mainly on the planes." Statistical tests are performed with numerical simulations. Generally, a random number generator is used to estimate some quantity for which the theory of probability provides an exact answer. Comparison to this exact answer provides a measure of "randomness".
It is important to remember that a random number generator is not a "real" function like sine or cosine. Unlike real functions, successive calls to a random number generator yield different return values. Of course that is just what you want for a random number generator, but to achieve this effect, the generator must keep track of some kind of "state" variable. Sometimes this state is just an integer (sometimes just the value of the previously generated random number), but often it is more complicated than that and may involve a whole array of numbers, possibly with some indices thrown in. To use the random number generators, you do not need to know the details of what comprises the state, and besides that varies from algorithm to algorithm.
The random number generator library uses two special structs,
gsl_rng_type
which holds static information about each type of
generator and gsl_rng
which describes an instance of a generator
created from a given gsl_rng_type
.
The functions described in this section are declared in the header file `gsl_rng.h'.
gsl_rng * r = gsl_rng_alloc (gsl_rng_taus);
If there is insufficient memory to create the generator then the
function returns a null pointer and the error handler is invoked with an
error code of GSL_ENOMEM
.
The generator is automatically initialized with the default seed,
gsl_rng_default_seed
. This is zero by default but can be changed
either directly or by using the environment variable GSL_RNG_SEED
(see section Random number environment variables).
The details of the available generator types are described later in this chapter.
ranlux
generator used a seed
of 314159265, and so choosing s equal to zero reproduces this when
using gsl_rng_ranlux
.
The following functions return uniformly distributed random numbers, either as integers or double precision floating point numbers. To obtain non-uniform distributions see section Random Number Distributions.
gsl_rng_max (r)
and gsl_rng_min (r)
.
gsl_rng_get(r)
by gsl_rng_max(r) + 1.0
in double
precision. Some generators compute this ratio internally so that they
can provide floating point numbers with more than 32 bits of randomness
(the maximum number of bits that can be portably represented in a single
unsigned long int
).
gsl_rng_uniform
until a non-zero value is obtained. You can use
this function if you need to avoid a singularity at 0.0.
If n is larger than the range of the generator then the function
calls the error handler with an error code of GSL_EINVAL
and
returns zero.
The following functions provide information about an existing generator. You should use them in preference to hard-coding the generator parameters into your own code.
printf("r is a '%s' generator\n", gsl_rng_name (r));
would print something like r is a 'taus' generator
.
gsl_rng_max
returns the largest value that gsl_rng_get
can return.
gsl_rng_min
returns the smallest value that gsl_rng_get
can return. Usually this value is zero. There are some generators with
algorithms that cannot return zero, and for these generators the minimum
value is 1.
void * state = gsl_rng_state (r); size_t n = gsl_rng_size (r); fwrite (state, n, 1, stream);
const gsl_rng_type **t, **t0; t0 = gsl_rng_types_setup (); printf("Available generators:\n"); for (t = t0; *t != 0; t++) { printf("%s\n", (*t)->name); }
The library allows you to choose a default generator and seed from the
environment variables GSL_RNG_TYPE
and GSL_RNG_SEED
and
the function gsl_rng_env_setup
. This makes it easy try out
different generators and seeds without having to recompile your program.
GSL_RNG_TYPE
and
GSL_RNG_SEED
and uses their values to set the corresponding
library variables gsl_rng_default
and
gsl_rng_default_seed
. These global variables are defined as
follows,
extern const gsl_rng_type *gsl_rng_default extern unsigned long int gsl_rng_default_seed
The environment variable GSL_RNG_TYPE
should be the name of a
generator, such as taus
or mt19937
. The environment
variable GSL_RNG_SEED
should contain the desired seed value. It
is converted to an unsigned long int
using the C library function
strtoul
.
If you don't specify a generator for GSL_RNG_TYPE
then
gsl_rng_mt19937
is used as the default. The initial value of
gsl_rng_default_seed
is zero.
Here is a short program which shows how to create a global
generator using the environment variables GSL_RNG_TYPE
and
GSL_RNG_SEED
,
#include <stdio.h> #include <gsl/gsl_rng.h> gsl_rng * r; /* global generator */ int main (void) { const gsl_rng_type * T; gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc (T); printf("generator type: %s\n", gsl_rng_name (r)); printf("seed = %u\n", gsl_rng_default_seed); printf("first value = %u\n", gsl_rng_get (r)); return 0; }
Running the program without any environment variables uses the initial
defaults, an mt19937
generator with a seed of 0,
bash$ ./a.out generator type: mt19937 seed = 0 first value = 2867219139
By setting the two variables on the command line we can change the default generator and the seed,
bash$ GSL_RNG_TYPE="taus" GSL_RNG_SEED=123 ./a.out GSL_RNG_TYPE=taus GSL_RNG_SEED=123 generator type: taus seed = 123 first value = 2720986350
The above methods ignore the random number `state' which changes from call to call. It is often useful to be able to save and restore the state. To permit these practices, a few somewhat more advanced functions are supplied. These include:
stdout
. At the moment its only use is for debugging.
The functions described above make no reference to the actual algorithm used. This is deliberate so that you can switch algorithms without having to change any of your application source code. The library provides a large number of generators of different types, including simulation quality generators, generators provided for compatibility with other libraries and historical generators from the past.
The following generators are recommended for use in simulation. They have extremely long periods, low correlation and pass most statistical tests.
gsl_rng_set
reproduces this.
For more information see,
The generator gsl_rng_19937
uses the corrected version of the
seeding procedure published later by the two authors above. The
original seeding procedure suffered from low-order periodicity, but can
be used by selecting the alternate generator gsl_rng_mt19937_1998
.
The generator ranlxs0
is a second-generation version of the
RANLUX algorithm of L@"uscher, which produces "luxury random
numbers". This generator provides single precision output (24 bits) at
three luxury levels ranlxs0
, ranlxs1
and ranlxs2
.
It uses double-precision floating point arithmetic internally and can be
significantly faster than the integer version of ranlux
,
particularly on 64-bit architectures. The period of the generator is
about @c{$10^{171}$}
10^171. The algorithm has mathematically proven properties and
can provide truly decorrelated numbers at a known level of randomness.
The higher luxury levels provide additional decorrelation between samples
as an additional safety margin.
These generators produce double precision output (48 bits) from the
RANLXS generator. The library provides two luxury levels
ranlxd1
and ranlxd2
.
The ranlux
generator is an implementation of the original
algorithm developed by L@"uscher. It uses a
lagged-fibonacci-with-skipping algorithm to produce "luxury random
numbers". It is a 24-bit generator, originally designed for
single-precision IEEE floating point numbers. This implementation is
based on integer arithmetic, while the second-generation versions
RANLXS and RANLXD described above provide floating-point
implementations which will be faster on many platforms.
The period of the generator is about @c{$10^{171}$}
10^171. The algorithm has mathematically proven properties and
it can provide truly decorrelated numbers at a known level of
randomness. The default level of decorrelation recommended by L@"uscher
is provided by gsl_rng_ranlux
, while gsl_rng_ranlux389
gives the highest level of randomness, with all 24 bits decorrelated.
Both types of generator use 24 words of state per generator.
For more information see,
where the two underlying generators x_n and y_n are,
with coefficients a_1 = 0, a_2 = 63308, a_3 = -183326, b_1 = 86098, b_2 = 0, b_3 = -539608, and moduli m_1 = 2^31 - 1 = 2147483647 and m_2 = 2145483479.
The period of this generator is 2^205 (about 10^61). It uses 6 words of state per generator. For more information see,
with a_1 = 107374182, a_2 = a_3 = a_4 = 0, a_5 = 104480 and m = 2^31 - 1.
The period of this generator is about 10^46. It uses 5 words of state per generator. More information can be found in the following paper,
where,
computed modulo
2^32. In the formulas above
^^
denotes "exclusive-or". Note that the algorithm relies on the properties
of 32-bit unsigned integers and has been implemented using a bitmask
of 0xFFFFFFFF
to make it work on 64 bit machines.
The period of this generator is @c{$2^{88}$} 2^88 (about 10^26). It uses 3 words of state per generator. For more information see,
gfsr4
generator is like a lagged-fibonacci generator, and
produces each number as an xor
'd sum of four previous values.
Ziff (ref below) notes that "it is now widely known" that two-tap registers (such as R250, which is described below) have serious flaws, the most obvious one being the three-point correlation that comes from the definition of the generator. Nice mathematical properties can be derived for GFSR's, and numerics bears out the claim that 4-tap GFSR's with appropriately chosen offsets are as random as can be measured, using the author's test.
This implementation uses the values suggested the the example on p392 of Ziff's article: A=471, B=1586, C=6988, D=9689.
If the offsets are appropriately chosen (such the one ones in this implementation), then the sequence is said to be maximal. I'm not sure what that means, but I would guess that means all states are part of the same cycle, which would mean that the period for this generator is astronomical; it is (2^K)^D \approx 10^{93334} where K=32 is the number of bits in the word, and D is the longest lag. This would also mean that any one random number could easily be zero; ie 0 <= r < 2^32.
Ziff doesn't say so, but it seems to me that the bits are completely independent here, so one could use this as an efficient bit generator; each number supplying 32 random bits. The quality of the generated bits depends on the underlying seeding procedure, which may need to be improved in some circumstances.
For more information see,
The standard Unix random number generators rand
, random
and rand48
are provided as part of GSL. Although these
generators are widely available individually often they aren't all
available on the same platform. This makes it difficult to write
portable code using them and so we have included the complete set of
Unix generators in GSL for convenience. Note that these generators
don't produce high-quality randomness and aren't suitable for work
requiring accurate statistics. However, if you won't be measuring
statistical quantities and just want to introduce some variation into
your program then these generators are quite acceptable.
rand()
generator. Its sequence is
with a = 1103515245, c = 12345 and m = 2^31. The seed specifies the initial value, x_1. The period of this generator is 2^31, and it uses 1 word of storage per generator.
random()
family of functions, a
set of linear feedback shift register generators originally used in BSD
Unix. There are several versions of random()
in use today: the
original BSD version (e.g. on SunOS4), a libc5 version (found on
older GNU/Linux systems) and a glibc2 version. Each version uses a
different seeding procedure, and thus produces different sequences.
The original BSD routines accepted a variable length buffer for the
generator state, with longer buffers providing higher-quality
randomness. The random()
function implemented algorithms for
buffer lengths of 8, 32, 64, 128 and 256 bytes, and the algorithm with
the largest length that would fit into the user-supplied buffer was
used. To support these algorithms additional generators are available
with the following names,
gsl_rng_random8_bsd gsl_rng_random32_bsd gsl_rng_random64_bsd gsl_rng_random128_bsd gsl_rng_random256_bsd
where the numeric suffix indicates the buffer length. The original BSD
random
function used a 128-byte default buffer and so
gsl_rng_random_bsd
has been made equivalent to
gsl_rng_random128_bsd
. Corresponding versions of the libc5
and glibc2
generators are also available, with the names
gsl_rng_random8_libc5
, gsl_rng_random8_glibc2
, etc.
rand48
generator. Its sequence is
defined on 48-bit unsigned integers with
a = 25214903917,
c = 11 and
m = 2^48.
The seed specifies the upper 32 bits of the initial value, x_1,
with the lower 16 bits set to 0x330E
. The function
gsl_rng_get
returns the upper 32 bits from each term of the
sequence. This does not have a direct parallel in the original
rand48
functions, but forcing the result to type long int
reproduces the output of mrand48
. The function
gsl_rng_uniform
uses the full 48 bits of internal state to return
the double precision number x_n/m, which is equivalent to the
function drand48
. Note that some versions of the GNU C Library
contained a bug in mrand48
function which caused it to produce
different results (only the lower 16-bits of the return value were set).
The following generators are provided for compatibility with
Numerical Recipes. Note that the original Numerical Recipes
functions used single precision while we use double precision. This will
lead to minor discrepancies, but only at the level of single-precision
rounding error. If necessary you can force the returned values to single
precision by storing them in a volatile float
, which prevents the
value being held in a register with double or extended precision. Apart
from this difference the underlying algorithms for the integer part of
the generators are the same.
ran0
implements Park and Miller's MINSTD
algorithm with a modified seeding procedure.
ran1
implements Park and Miller's MINSTD
algorithm with a 32-element Bayes-Durham shuffle box.
ran2
implements a L'Ecuyer combined recursive
generator with a 32-element Bayes-Durham shuffle-box.
ran3
implements Knuth's portable
subtractive generator.
The generators in this section are provided for compatibility with existing libraries. If you are converting an existing program to use GSL then you can select these generators to check your new implementation against the original one, using the same random number generator. After verifying that your new program reproduces the original results you can then switch to a higher-quality generator.
Note that most of the generators in this section are based on single linear congruence relations, which are the least sophisticated type of generator. In particular, linear congruences have poor properties when used with a non-prime modulus, as several of these routines do (e.g. with a power of two modulus, 2^31 or 2^32). This leads to periodicity in the least significant bits of each number, with only the higher bits having any randomness. Thus if you want to produce a random bitstream it is best to avoid using the least significant bits.
RANF
. Its sequence is
defined on 48-bit unsigned integers with a = 44485709377909 and m = 2^48. The seed specifies the lower 32 bits of the initial value, x_1, with the lowest bit set to prevent the seed taking an even value. The upper 16 bits of x_1 are set to 0. A consequence of this procedure is that the pairs of seeds 2 and 3, 4 and 5, etc produce the same sequences.
The generator compatibile with the CRAY MATHLIB routine RANF. It produces double precision floating point numbers which should be identical to those from the original RANF.
There is a subtlety in the implementation of the seeding. The initial state is reversed through one step, by multiplying by the modular inverse of a mod m. This is done for compatibility with the original CRAY implementation.
Note that you can only seed the generator with integers up to 2^32, while the original CRAY implementation uses non-portable wide integers which can cover all 2^48 states of the generator.
The function gsl_rng_get
returns the upper 32 bits from each term
of the sequence. The function gsl_rng_uniform
uses the full 48
bits to return the double precision number x_n/m.
The period of this generator is @c{$2^{46}$} 2^46.
where ^^ denote "exclusive-or", defined on 32-bit words. The period of this generator is about @c{$2^{250}$} 2^250 and it uses 250 words of state per generator.
For more information see,
For more information see,
MTH$RANDOM
. Its sequence is,
with a = 69069, c = 1 and m = 2^32. The seed specifies the initial value, x_1. The period of this generator is 2^32 and it uses 1 word of storage per generator.
with a = 1664525 and m = 2^32. The seed specifies the initial value, x_1.
RANDU
generator. Its sequence is
with a = 65539 and m = 2^31. The seed specifies the initial value, x_1. The period of this generator was only 2^29. It has become a textbook example of a poor generator.
with a = 16807 and m = 2^31 - 1 = 2147483647. The seed specifies the initial value, x_1. The period of this generator is about 2^31.
This generator is used in the IMSL Library (subroutine RNUN) and in MATLAB (the RAND function). It is also sometimes known by the acronym "GGL" (I'm not sure what that stands for).
For more information see,
gsl_rng_uni32
. The original source code is available from NETLIB.
The original source code is available from NETLIB. For more information see,
The following table shows the relative performance of a selection the
available random number generators. The simulation quality generators
which offer the best performance are taus
, gfsr4
and
mt19937
.
1754 k ints/sec, 870 k doubles/sec, taus 1613 k ints/sec, 855 k doubles/sec, gfsr4 1370 k ints/sec, 769 k doubles/sec, mt19937 565 k ints/sec, 571 k doubles/sec, ranlxs0 400 k ints/sec, 405 k doubles/sec, ranlxs1 490 k ints/sec, 389 k doubles/sec, mrg 407 k ints/sec, 297 k doubles/sec, ranlux 243 k ints/sec, 254 k doubles/sec, ranlxd1 251 k ints/sec, 253 k doubles/sec, ranlxs2 238 k ints/sec, 215 k doubles/sec, cmrg 247 k ints/sec, 198 k doubles/sec, ranlux389 141 k ints/sec, 140 k doubles/sec, ranlxd2 1852 k ints/sec, 935 k doubles/sec, ran3 813 k ints/sec, 575 k doubles/sec, ran0 787 k ints/sec, 476 k doubles/sec, ran1 379 k ints/sec, 292 k doubles/sec, ran2
The subject of random number generation and testing is reviewed extensively in Knuth's Seminumerical Algorithms.
Further information is available in the review paper written by Pierre L'Ecuyer,
On the World Wide Web, see the pLab home page (http://random.mat.sbg.ac.at/) for a lot of information on the state-of-the-art in random number generation, and for numerous links to various "random" WWW sites.
The source code for the DIEHARD random number generator tests is also available online.
Thanks to Makoto Matsumoto, Takuji Nishimura and Yoshiharu Kurita for making the source code to their generators (MT19937, MM&TN; TT800, MM&YK) available under the GNU General Public License. Thanks to Martin L@"uscher for providing notes and source code for the RANLXS and RANLXD generators.
This chapter describes functions for generating quasi-random sequences in arbitrary dimensions. A quasi-random sequence progressively covers a d-dimensional space with a set of points that are uniformly distributed. Quasi-random sequences are also known as low-discrepancy sequences. The quasi-random sequence generators use an interface that is similar to the interface for random number generators.
The functions described in this section are declared in the header file `gsl_qrng.h'.
GSL_ENOMEM
.
void * state = gsl_qrng_state (q); size_t n = gsl_qrng_size (q); fwrite (state, n, 1, stream);
The following quasi-random sequence algorithms are available,
The following program prints the first 1024 points of the 2-dimensional Sobol sequence.
#include <stdio.h> #include <gsl/gsl_qrng.h> int main (void) { int i; gsl_qrng * q = gsl_qrng_alloc (gsl_qrng_sobol, 2); for (i = 0; i < 1024; i++) { double v[2]; gsl_qrng_get(q, v); printf("%.5f %.5f\n", v[0], v[1]); } gsl_qrng_free(q); return 0; }
Here is the output from the program,
$ ./a.out 0.50000 0.50000 0.75000 0.25000 0.25000 0.75000 0.37500 0.37500 0.87500 0.87500 0.62500 0.12500 0.12500 0.62500 ....
It can be seen that successive points progressively fill-in the spaces between previous points. The following plot shows the distribution in the x-y plane of the first 1024 points from the Sobol sequence, Distribution of the first 1024 points from the quasi-random Sobol sequence
The implementations of the quasi-random sequence routines are based on the algorithms described in the following paper,
This chapter describes functions for generating random variates and computing their probability distributions. Samples from the distributions described in this chapter can be obtained using any of the random number generators in the library as an underlying source of randomness. In the simplest cases a non-uniform distribution can be obtained analytically from the uniform distribution of a random number generator by applying an appropriate transformation. This method uses one call to the random number generator.
More complicated distributions are created by the acceptance-rejection method, which compares the desired distribution against a distribution which is similar and known analytically. This usually requires several samples from the generator.
The functions described in this section are declared in `gsl_randist.h'.
for x in the range -\infty to +\infty. Use the
transformation z = \mu + x on the numbers returned by
gsl_ran_gaussian
to obtain a Gaussian distribution with mean
\mu. This function uses the Box-Mueller algorithm which requires two
calls the random number generator r.
The probability distribution for Gaussian tail random variates is,
for x > a where N(a;\sigma) is the normalization constant,
for x,y in the range -\infty to +\infty. The correlation coefficient rho should lie between 1 and -1.
for @c{$x \ge 0$} x >= 0.
for -\infty < x < \infty.
for @c{$x \ge 0$} x >= 0. For b = 1 this reduces to the Laplace distribution. For b = 2 it has the same form as a gaussian distribution, but with @c{$a = \sqrt{2} \sigma$} a = \sqrt{2} \sigma.
for x in the range -\infty to +\infty. The Cauchy distribution is also known as the Lorentz distribution.
for x > 0.
for x > a.
For numerical purposes it is more convenient to use the following equivalent form of the integral,
There is no explicit solution for the form of p(x) and the
library does not define a corresponding pdf
function. For
\alpha = 1 the distribution reduces to the Cauchy distribution. For
\alpha = 2 it is a Gaussian distribution with @c{$\sigma = \sqrt{2} c$}
\sigma = \sqrt{2} c. For \alpha < 1 the tails of the
distribution become extremely wide.
The algorithm only works for @c{$0 < \alpha \le 2$} 0 < alpha <= 2.
When \alpha = 1 the term \tan(\pi \alpha/2) is replaced by
-(2/\pi)\log|t|. There is no explicit solution for the form of
p(x) and the library does not define a corresponding pdf
function. For \alpha = 2 the distribution reduces to a Gaussian
distribution with @c{$\sigma = \sqrt{2} c$}
\sigma = \sqrt{2} c and the skewness parameter has no effect.
For \alpha < 1 the tails of the distribution become extremely
wide. The symmetric distribution corresponds to \beta =
0.
The algorithm only works for @c{$0 < \alpha \le 2$} 0 < alpha <= 2.
The Levy alpha-stable distributions have the property that if N alpha-stable variates are drawn from the distribution p(c, \alpha, \beta) then the sum Y = X_1 + X_2 + \dots + X_N will also be distributed as an alpha-stable variate, p(N^(1/\alpha) c, \alpha, \beta).
for x > 0.
if @c{$a \le x < b$} a <= x < b and 0 otherwise.
for x > 0.
The chi-squared distribution arises in statistics If Y_i are n independent gaussian random variates with unit variance then the sum-of-squares,
has a chi-squared distribution with n degrees of freedom.
for @c{$x \ge 0$} x >= 0.
The F-distribution arises in statistics. If Y_1 and Y_2 are chi-squared deviates with \nu_1 and \nu_2 degrees of freedom then the ratio,
has an F-distribution F(x;\nu_1,\nu_2).
for @c{$x \ge 0$} x >= 0.
The t-distribution arises in statistics. If Y_1 has a normal distribution and Y_2 has a chi-squared distribution with \nu degrees of freedom then the ratio,
has a t-distribution t(x;\nu) with \nu degrees of freedom.
for -\infty < x < +\infty.
for @c{$0 \le x \le 1$} 0 <= x <= 1.
for -\infty < x < +\infty.
for @c{$x \ge b$} x >= b.
The spherical distributions generate random vectors, located on a spherical surface. They can be used as random directions, for example in the steps of a random walk.
This function returns a random direction vector v = (x_1,x_2,...,x_n) in n dimensions. The vector is normalized such that |v|^2 = x_1^2 + x_2^2 + ... + x_n^2 = 1. The method uses the fact that a multivariate gaussian distribution is spherically symmetric. Each component is generated to have a gaussian distribution, and then the components are normalized. The method is described by Knuth, v2, 3rd ed, p135-136, and attributed to G. W. Brown, Modern Mathematics for the Engineer (1956).
for @c{$x \ge 0$} x >= 0.
for -\infty < x < \infty.
for 0 < x < \infty.
Given K discrete events with different probabilities P[k], produce a random value k consistent with its probability.
The obvious way to do this is to preprocess the probability list by generating a cumulative probability array with K+1 elements:
Note that this construction produces C[K]=1. Now choose a uniform deviate u between 0 and 1, and find the value of k such that @c{$C[k] \le u < C[k+1]$} C[k] <= u < C[k+1]. Although this in principle requires of order \log K steps per random number generation, they are fast steps, and if you use something like \lfloor uK \rfloor as a starting point, you can often do pretty well.
But faster methods have been devised. Again, the idea is to preprocess the probability list, and save the result in some form of lookup table; then the individual calls for a random discrete event can go rapidly. An approach invented by G. Marsaglia (Generating discrete random numbers in a computer, Comm ACM 6, 37-38 (1963)) is very clever, and readers interested in examples of good algorithm design are directed to this short and well-written paper. Unfortunately, for large K, Marsaglia's lookup table can be quite large.
A much better approach is due to Alastair J. Walker (An efficient method for generating discrete random variables with general distributions, ACM Trans on Mathematical Software 3, 253-256 (1977); see also Knuth, v2, 3rd ed, p120-121,139). This requires two lookup tables, one floating point and one integer, but both only of size K. After preprocessing, the random numbers are generated in O(1) time, even for large K. The preprocessing suggested by Walker requires O(K^2) effort, but that is not actually necessary, and the implementation provided here only takes O(K) effort. In general, more preprocessing leads to faster generation of the individual random numbers, but a diminishing return is reached pretty early. Knuth points out that the optimal preprocessing is combinatorially difficult for large K.
This method can be used to speed up some of the discrete random number generators below, such as the binomial distribution. To use if for something like the Poisson Distribution, a modification would have to be made, since it only takes a finite set of K outcomes.
gsl_ran_discrete
function below.
for @c{$k \ge 0$} k >= 0.
for @c{$0 \le k \le n$} 0 <= k <= n.
Note that n is not required to be an integer.
for @c{$k \ge 0$} k >= 0
for @c{$k \ge 1$} k >= 1.
where C(a,b) = a!/(b!(a-b)!). The domain of k is max(0,t-n_2), ..., max(t,n_1).
for @c{$k \ge 1$} k >= 1.
The following functions allow the shuffling and sampling of a set of objects. The algorithms rely on a random number generator as source of randomness and a poor quality generator can lead to correlations in the output. In particular it is important to avoid generators with a short period. For more information see Knuth, v2, 3rd ed, Section 3.4.2, "Random Sampling and Shuffling".
This function randomly shuffles the order of n objects, each of size size, stored in the array base[0..n-1]. The output of the random number generator r is used to produce the permutation. The algorithm generates all possible n! permutations with equal probability, assuming a perfect source of random numbers.
The following code shows how to shuffle the numbers from 0 to 51,
int a[52]; for (i = 0; i < 52; i++) { a[i] = i; } gsl_ran_shuffle (r, a, 52, sizeof (int));
The objects are sampled without replacement, thus each object can
only appear once in dest[k]. It is required that k be less
than or equal to n
. The objects in dest will be in the
same relative order as those in src. You will need to call
gsl_ran_shuffle(r, dest, n, size)
if you want to randomize the
order.
The following code shows how to select a random sample of three unique numbers from the set 0 to 99,
double a[3], b[100]; for (i = 0; i < 100; i++) { b[i] = (double) i; } gsl_ran_choose (r, a, 3, b, 100, sizeof (double));
gsl_ran_choose
but samples k items
from the original array of n items src with replacement, so
the same object can appear more than once in the output sequence
dest. There is no requirement that k be less than n
in this case.
The following program demonstrates the use of a random number generator to produce variates from a distribution. It prints 10 samples from the Poisson distribution with a mean of 3.
#include <stdio.h> #include <gsl/gsl_rng.h> #include <gsl/gsl_randist.h> int main (void) { const gsl_rng_type * T; gsl_rng * r; int i, n = 10; double mu = 3.0; /* create a generator chosen by the environment variable GSL_RNG_TYPE */ gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc (T); /* print n random variates chosen from the poisson distribution with mean parameter mu */ for (i = 0; i < n; i++) { unsigned int k = gsl_ran_poisson (r, mu); printf(" %u", k); } printf("\n"); return 0; }
If the library and header files are installed under `/usr/local' (the default location) then the program can be compiled with these options,
gcc demo.c -lgsl -lgslcblas -lm
Here is the output of the program,
$ ./a.out 4 2 3 3 1 3 4 1 3 5
The variates depend on the seed used by the generator. The seed for the
default generator type gsl_rng_default
can be changed with the
GSL_RNG_SEED
environment variable to produce a different stream
of variates,
$ GSL_RNG_SEED=123 ./a.out GSL_RNG_SEED=123 1 1 2 1 2 6 2 1 8 7
The following program generates a random walk in two dimensions.
#include <stdio.h> #include <gsl/gsl_rng.h> #include <gsl/gsl_randist.h> int main (void) { const gsl_rng_type * T; gsl_rng * r; gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc (T); int i; double x = 0, y = 0, dx, dy; printf("%g %g\n", x, y); for (i = 0; i < 10; i++) { gsl_ran_dir_2d (r, &dx, &dy); x += dx; y += dy; printf("%g %g\n", x, y); } return 0; }
Example output from the program, three 10-step random walks from the origin.
For an encyclopaedic coverage of the subject readers are advised to consult the book Non-Uniform Random Variate Generation by Luc Devroye. It covers every imaginable distribution and provides hundreds of algorithms.
The subject of random variate generation is also reviewed by Knuth, who describes algorithms for all the major distributions.
The Particle Data Group provides a short review of techniques for generating distributions of random numbers in the "Monte Carlo" section of its Annual Review of Particle Physics.
The Review of Particle Physics is available online in postscript and pdf format.
This chapter describes the statistical functions in the library. The basic statistical functions include routines to compute the mean, variance and standard deviation. More advanced functions allow you to calculate absolute deviations, skewness, and kurtosis as well as the median and arbitrary percentiles. The algorithms use recurrence relations to compute average quantities in a stable way, without large intermediate values that might overflow.
The functions are available in versions for datasets in the standard
floating-point and integer types. The versions for double precision
floating-point data have the prefix gsl_stats
and are declared in
the header file `gsl_stats_double.h'. The versions for integer
data have the prefix gsl_stats_int
and are declared in the header
files `gsl_stats_int.h'.
where x_i are the elements of the dataset data. For samples drawn from a gaussian distribution the variance of \Hat\mu is \sigma^2 / N.
where x_i are the elements of the dataset data. Note that the normalization factor of 1/(N-1) results from the derivation of \Hat\sigma^2 as an unbiased estimator of the population variance \sigma^2. For samples drawn from a gaussian distribution the variance of \Hat\sigma^2 itself is 2 \sigma^4 / N.
This function computes the mean via a call to gsl_stats_mean
. If
you have already computed the mean then you can pass it directly to
gsl_stats_variance_m
.
where x_i are the elements of the dataset data. The
absolute deviation from the mean provides a more robust measure of the
width of a distribution than the variance. This function computes the
mean of data via a call to gsl_stats_mean
.
This function is useful if you have already computed the mean of data (and want to avoid recomputing it), or wish to calculate the absolute deviation relative to another value (such as zero, or the median).
where x_i are the elements of the dataset data. The skewness measures the asymmetry of the tails of a distribution.
The function computes the mean and estimated standard deviation of
data via calls to gsl_stats_mean
and gsl_stats_sd
.
These functions are useful if you have already computed the mean and standard deviation of data and want to avoid recomputing them.
The kurtosis measures how sharply peaked a distribution is, relative to its width. The kurtosis is normalized to zero for a gaussian distribution.
This function is useful if you have already computed the mean and standard deviation of data and want to avoid recomputing them.
The functions described in this section allow the computation of statistics for weighted samples. The functions accept an array of samples, x_i, with associated weights, w_i. Each sample x_i is considered as having been drawn from a Gaussian distribution with variance \sigma_i^2. The sample weight w_i is defined as the reciprocal of this variance, w_i = 1/\sigma_i^2. Setting a weight to zero corresponds to removing a sample from a dataset.
Note that this expression reduces to an unweighted variance with the familiar 1/(N-1) factor when there are N equal non-zero weights.
gsl_stats_wvariance
above.
gsl_stats_wvariance_m
above.
If you want instead to find the element with the largest absolute
magnitude you will need to apply fabs
or abs
to your data
before calling this function.
If you want instead to find the element with the smallest absolute
magnitude you will need to apply fabs
or abs
to your data
before calling this function.
The median and percentile functions described in this section operate on sorted data. For convenience we use quantiles, measured on a scale of 0 to 1, instead of percentiles (which use a scale of 0 to 100).
gsl_sort
should
always be used first.
When the dataset has an odd number of elements the median is the value of element (n-1)/2. When the dataset has an even number of elements the median is the mean of the two nearest middle values, elements (n-1)/2 and n/2. Since the algorithm for computing the median involves interpolation this function always returns a floating-point number, even for integer data types.
There are no checks to see whether the data are sorted, so the function
gsl_sort
should always be used first.
The quantile is found by interpolation, using the formula
where i is floor
((n - 1)f) and \delta is
(n-1)f - i.
Thus the minimum value of the array (data[0*stride]
) is given by
f equal to zero, the maximum value (data[(n-1)*stride]
) is
given by f equal to one and the median value is given by f
equal to 0.5. Since the algorithm for computing quantiles involves
interpolation this function always returns a floating-point number, even
for integer data types.
Here is a basic example of how to use the statistical functions:
#include <stdio.h> #include <gsl/gsl_statistics.h> int main(void) { double data[5] = {17.2, 18.1, 16.5, 18.3, 12.6}; double mean, variance, largest, smallest; mean = gsl_stats_mean(data, 1, 5); variance = gsl_stats_variance(data, 1, 5); largest = gsl_stats_max(data, 1, 5); smallest = gsl_stats_min(data, 1, 5); printf("The dataset is %g, %g, %g, %g, %g\n", data[0], data[1], data[2], data[3], data[4]); printf("The sample mean is %g\n", mean); printf("The estimated variance is %g\n", variance); printf("The largest value is %g\n", largest); printf("The smallest value is %g\n", smallest); return 0; }
The program should produce the following output,
The dataset is 17.2, 18.1, 16.5, 18.3, 12.6 The sample mean is 16.54 The estimated variance is 4.2984 The largest value is 18.3 The smallest value is 12.6
Here is an example using sorted data,
#include <stdio.h> #include <gsl/gsl_sort.h> #include <gsl/gsl_statistics.h> int main(void) { double data[5] = {17.2, 18.1, 16.5, 18.3, 12.6}; double median, upperq, lowerq; printf("Original dataset: %g, %g, %g, %g, %g\n", data[0], data[1], data[2], data[3], data[4]); gsl_sort (data, 1, 5); printf("Sorted dataset: %g, %g, %g, %g, %g\n", data[0], data[1], data[2], data[3], data[4]); median = gsl_stats_median_from_sorted_data (data, 1, 5); upperq = gsl_stats_quantile_from_sorted_data (data, 1, 5, 0.75); lowerq = gsl_stats_quantile_from_sorted_data (data, 1, 5, 0.25); printf("The median is %g\n", median); printf("The upper quartile is %g\n", upperq); printf("The lower quartile is %g\n", lowerq); return 0; }
This program should produce the following output,
Original dataset: 17.2, 18.1, 16.5, 18.3, 12.6 Sorted dataset: 12.6, 16.5, 17.2, 18.1, 18.3 The median is 17.2 The upper quartile is 18.1 The lower quartile is 16.5
The standard reference for almost any topic in statistics is the multi-volume Advanced Theory of Statistics by Kendall and Stuart.
Many statistical concepts can be more easily understood by a Bayesian approach. The following book by Gelman, Carlin, Stern and Rubin gives a comprehensive coverage of the subject.
For physicists the Particle Data Group provides useful reviews of Probability and Statistics in the "Mathematical Tools" section of its Annual Review of Particle Physics.
The Review of Particle Physics is available online at http://pdg.lbl.gov/.
This chapter describes functions for creating histograms. Histograms provide a convenient way of summarizing the distribution of a set of data. A histogram consists of a set of bins which count the number of events falling into a given range of a continuous variable x. In GSL the bins of a histogram contain floating-point numbers, so they can be used to record both integer and non-integer distributions. The bins can use arbitrary sets of ranges (uniformly spaced bins are the default). Both one and two-dimensional histograms are supported.
Once a histogram has been created it can also be converted into a probability distribution function. The library provides efficient routines for selecting random samples from probability distributions. This can be useful for generating simulations based real data.
The functions are declared in the header files `gsl_histogram.h' and `gsl_histogram2d.h'.
A histogram is defined by the following struct,
size_t n
double * range
double * bin
The range for bin[i] is given by range[i] to range[i+1]. For n bins there are n+1 entries in the array range. Each bin is inclusive at the lower end and exclusive at the upper end. Mathematically this means that the bins are defined by the following inequality,
Here is a diagram of the correspondence between ranges and bins on the number-line for x,
[ bin[0] )[ bin[1] )[ bin[2] )[ bin[3] )[ bin[5] ) ---|---------|---------|---------|---------|---------|--- x r[0] r[1] r[2] r[3] r[4] r[5]
In this picture the values of the range array are denoted by
r. On the left-hand side of each bin the square bracket
"[
" denotes an inclusive lower bound
(@c{$r \le x$}
r <= x), and the round parentheses ")
" on the right-hand
side denote an exclusive upper bound (x < r). Thus any samples
which fall on the upper end of the histogram are excluded. If you want
to include this value for the last bin you will need to add an extra bin
to your histogram.
The gsl_histogram
struct and its associated functions are defined
in the header file `gsl_histogram.h'.
The functions for allocating memory to a histogram follow the style of
malloc
and free
. In addition they also perform their own
error checking. If there is insufficient memory available to allocate a
histogram then the functions call the error handler (with an error
number of GSL_ENOMEM
) in addition to returning a null pointer.
Thus if you use the library error handler to abort your program then it
isn't necessary to check every histogram alloc
.
gsl_histogram
struct. If
insufficient memory is available a null pointer is returned and the
error handler is invoked with an error code of GSL_ENOMEM
. The
bins and ranges are not initialized, and should be prepared using one of
the range-setting functions below in order to make the histogram ready
for use.
range
array should contain the
desired bin limits. The ranges can be arbitrary, subject to the
restriction that they are monotonically increasing.
The following example shows how to create a histogram with logarithmic bins with ranges [1,10), [10,100) and [100,1000).
gsl_histogram * h = gsl_histogram_alloc (3); /* bin[0] covers the range 1 <= x < 10 */ /* bin[1] covers the range 10 <= x < 100 */ /* bin[2] covers the range 100 <= x < 1000 */ double range[4] = { 1.0, 10.0, 100.0, 1000.0 }; gsl_histogram_set_ranges (h, range, 4);
Note that the size of the range array should be defined to be one element bigger than the number of bins. The additional element is required for the upper value of the final bin.
where d is the bin spacing, d = (xmax-xmin)/n.
There are two ways to access histogram bins, either by specifying an x coordinate or by using the bin-index directly. The functions for accessing the histogram through x coordinates use a binary search to identify the bin which covers the appropriate range.
If x lies in the valid range of the histogram then the function
returns zero to indicate success. If x is less than the lower
limit of the histogram then the function returns GSL_EDOM
, and
none of bins are modified. Similarly, if the value of x is greater
than or equal to the upper limit of the histogram then the function
returns GSL_EDOM
, and none of the bins are modified. The error
handler is not called, however, since it is often necessary to compute
histogram for a small range of a larger dataset, ignoring the values
outside the range of interest.
gsl_histogram_increment
but increases
the value of the appropriate bin in the histogram h by the
floating-point number weight.
GSL_EDOM
and the function returns 0.
GSL_EDOM
.
gsl_histogram
struct directly.
The following functions are used by the access and update routines to locate the bin which corresponds to a given x coordinate.
GSL_SUCCESS
. If x lies outside the valid range of the
histogram then the function returns GSL_EDOM
and the error
handler is invoked.
The library provides functions for reading and writing histograms to a file as binary data or formatted text.
GSL_EFAILED
if there was a problem writing to the file. Since
the data is written in the native binary format it may not be portable
between different architectures.
GSL_EFAILED
if there was a problem reading from
the file. The data is assumed to have been written in the native binary
format on the same architecture.
%g
, %e
or %f
formats for floating point
numbers. The function returns 0 for success and GSL_EFAILED
if
there was a problem writing to the file. The histogram output is
formatted in three columns, and the columns are separated by spaces,
like this,
range[0] range[1] bin[0] range[1] range[2] bin[1] range[2] range[3] bin[2] .... range[n-1] range[n] bin[n-1]
The values of the ranges are formatted using range_format and the value of the bins are formatted using bin_format. Each line contains the lower and upper limit of the range of the bins and the value of the bin itself. Since the upper limit of one bin is the lower limit of the next there is duplication of these values between lines but this allows the histogram to be manipulated with line-oriented tools.
gsl_histogram_fprintf
. The histogram h must be
preallocated with the correct length since the function uses the size of
h to determine how many numbers to read. The function returns 0
for success and GSL_EFAILED
if there was a problem reading from
the file.
A histogram made by counting events can be regarded as a measurement of a probability distribution. Allowing for statistical error, the height of each bin represents the probability of an event where the value of x falls in the range of that bin. The probability distribution function has the one-dimensional form p(x)dx where,
In this equation n_i is the number of events in the bin which contains x, w_i is the width of the bin and N is the total number of events. The distribution of events within each bin is assumed to be uniform.
The probability distribution function for a histogram consists of a set of bins which measure the probability of an event falling into a given range of a continuous variable x. A probability distribution function is defined by the following struct, which actually stores the cumulative probability distribution function. This is the natural quantity for generating samples via the inverse transform method, because there is a one-to-one mapping between the cumulative probability distribution and the range [0,1]. It can be shown that by taking a uniform random number in this range and finding its corresponding coordinate in the cumulative probability distribution we obtain samples with the desired probability distribution.
size_t n
double * range
double * sum
The following functions allow you to create a gsl_histogram_pdf
struct which represents this probability distribution and generate
random samples from it.
gsl_histogram_pdf
struct. If insufficient memory is available a
null pointer is returned and the error handler is invoked with an error
code of GSL_ENOMEM
.
GSL_EDOM
because a probability distribution cannot contain
negative values.
where i is the index which satisfies sum[i] <= r < sum[i+1] and delta is (r - sum[i])/(sum[i+1] - sum[i]).
The following program shows how to make a simple histogram of a column
of numerical data supplied on stdin
. The program takes three
arguments, specifying the upper and lower bounds of the histogram and
the number of bins. It then reads numbers from stdin
, one line at
a time, and adds them to the histogram. When there is no more data to
read it prints out the accumulated histogram using
gsl_histogram_fprintf
.
#include <stdio.h> #include <stdlib.h> #include <gsl/gsl_histogram.h> int main (int argc, char **argv) { double a, b; size_t n; if (argc != 4) { printf ("Usage: gsl-histogram xmin xmax n\n" "Computes a histogram of the data " "on stdin using n bins from xmin " "to xmax\n"); exit (0); } a = atof (argv[1]); b = atof (argv[2]); n = atoi (argv[3]); { int status; double x; gsl_histogram * h = gsl_histogram_alloc (n); gsl_histogram_set_uniform (h, a, b); while (fscanf(stdin, "%lg", &x) == 1) { gsl_histogram_increment(h, x); } gsl_histogram_fprintf (stdout, h, "%g", "%g"); gsl_histogram_free (h); } exit (0); }
Here is an example of the program in use. We generate 10000 random samples from a Cauchy distribution with a width of 30 and histogram them over the range -100 to 100, using 200 bins.
$ gsl-randist 0 10000 cauchy 30 | gsl-histogram -100 100 200 > histogram.dat
A plot of the resulting histogram shows the familiar shape of the Cauchy distribution and the fluctuations caused by the finite sample size.
$ awk '{print $1, $3 ; print $2, $3}' histogram.dat | graph -T X
A two dimensional histogram consists of a set of bins which count the number of events falling in a given area of the (x,y) plane. The simplest way to use a two dimensional histogram is to record two-dimensional position information, n(x,y). Another possibility is to form a joint distribution by recording related variables. For example a detector might record both the position of an event (x) and the amount of energy it deposited E. These could be histogrammed as the joint distribution n(x,E).
Two dimensional histograms are defined by the following struct,
size_t nx, ny
double * xrange
double * yrange
double * bin
bin(i,j)
= bin[i * ny + j]
.
The range for bin(i,j)
is given by xrange[i]
to
xrange[i+1]
in the x-direction and yrange[j]
to
yrange[j+1]
in the y-direction. Each bin is inclusive at the lower
end and exclusive at the upper end. Mathematically this means that the
bins are defined by the following inequality,
Note that any samples which fall on the upper sides of the histogram are excluded. If you want to include these values for the side bins you will need to add an extra row or column to your histogram.
The gsl_histogram2d
struct and its associated functions are
defined in the header file `gsl_histogram2d.h'.
The functions for allocating memory to a 2D histogram follow the style
of malloc
and free
. In addition they also perform their
own error checking. If there is insufficient memory available to
allocate a histogram then the functions call the error handler (with
an error number of GSL_ENOMEM
) in addition to returning a null
pointer. Thus if you use the library error handler to abort your program
then it isn't necessary to check every 2D histogram alloc
.
gsl_histogram2d
struct. If insufficient memory is available a null pointer is returned
and the error handler is invoked with an error code of
GSL_ENOMEM
. The bins and ranges must be initialized with one of
the functions below before the histogram is ready for use.
You can access the bins of a two-dimensional histogram either by specifying a pair of (x,y) coordinates or by using the bin indices (i,j) directly. The functions for accessing the histogram through (x,y) coordinates use binary searches in the x and y directions to identify the bin which covers the appropriate range.
If the point (x,y) lies inside the valid ranges of the
histogram then the function returns zero to indicate success. If
(x,y) lies outside the limits of the histogram then the
function returns GSL_EDOM
, and none of bins are modified. The
error handler is not called, since it is often necessary to compute
histogram for a small range of a larger dataset, ignoring any
coordinates outside the range of interest.
gsl_histogram2d_increment
but increases
the value of the appropriate bin in the histogram h by the
floating-point number weight.
GSL_EDOM
and the function returns 0.
GSL_EDOM
.
gsl_histogram2d
struct directly.
The following functions are used by the access and update routines to locate the bin which corresponds to a given (x\,y) coordinate.
GSL_SUCCESS
. If
(x,y) lies outside the valid range of the histogram then the
function returns GSL_EDOM
and the error handler is invoked.
The library provides functions for reading and writing two dimensional histograms to a file as binary data or formatted text.
GSL_EFAILED
if there was a problem writing to the file. Since
the data is written in the native binary format it may not be portable
between different architectures.
GSL_EFAILED
if there was a problem
reading from the file. The data is assumed to have been written in the
native binary format on the same architecture.
%g
, %e
or %f
formats for floating point
numbers. The function returns 0 for success and GSL_EFAILED
if
there was a problem writing to the file. The histogram output is
formatted in five columns, and the columns are separated by spaces,
like this,
xrange[0] xrange[1] yrange[0] yrange[1] bin(0,0) xrange[0] xrange[1] yrange[1] yrange[2] bin(0,1) xrange[0] xrange[1] yrange[2] yrange[3] bin(0,2) .... xrange[0] xrange[1] yrange[ny-1] yrange[ny] bin(0,ny-1) xrange[1] xrange[2] yrange[0] yrange[1] bin(1,0) xrange[1] xrange[2] yrange[1] yrange[2] bin(1,1) xrange[1] xrange[2] yrange[1] yrange[2] bin(1,2) .... xrange[1] xrange[2] yrange[ny-1] yrange[ny] bin(1,ny-1) .... xrange[nx-1] xrange[nx] yrange[0] yrange[1] bin(nx-1,0) xrange[nx-1] xrange[nx] yrange[1] yrange[2] bin(nx-1,1) xrange[nx-1] xrange[nx] yrange[1] yrange[2] bin(nx-1,2) .... xrange[nx-1] xrange[nx] yrange[ny-1] yrange[ny] bin(nx-1,ny-1)
Each line contains the lower and upper limits of the bin and the contents of the bin. Since the upper limits of the each bin are the lower limits of the neighboring bins there is duplication of these values but this allows the histogram to be manipulated with line-oriented tools.
gsl_histogram_fprintf
. The histogram h must be
preallocated with the correct lengths since the function uses the sizes
of h to determine how many numbers to read. The function returns 0
for success and GSL_EFAILED
if there was a problem reading from
the file.
As in the one-dimensional case, a two-dimensional histogram made by counting events can be regarded as a measurement of a probability distribution. Allowing for statistical error, the height of each bin represents the probability of an event where (x,y) falls in the range of that bin. For a two-dimensional histogram the probability distribution takes the form p(x,y) dx dy where,
In this equation n_{ij} is the number of events in the bin which contains (x,y), A_{ij} is the area of the bin and N is the total number of events. The distribution of events within each bin is assumed to be uniform.
size_t nx, ny
double * xrange
double * yrange
double * sum
The following functions allow you to create a gsl_histogram2d_pdf
struct which represents a two dimensional probability distribution and
generate random samples from it.
gsl_histogram2d_pdf
struct. If insufficient
memory is available a null pointer is returned and the error handler is
invoked with an error code of GSL_ENOMEM
.
GSL_EDOM
because a probability distribution cannot
contain negative values.
This program demonstrates two features of two-dimensional histograms. First a 10 by 10 2d-histogram is created with x and y running from 0 to 1. Then a few sample points are added to the histogram, at (0.3,0.3) with a height of 1, at (0.8,0.1) with a height of 5 and at (0.7,0.9) with a height of 0.5. This histogram with three events is used to generate a random sample of 1000 simulated events, which are printed out.
#include <stdio.h> #include <gsl/gsl_rng.h> #include <gsl/gsl_histogram2d.h> int main (void) { const gsl_rng_type * T; gsl_rng * r; gsl_histogram2d * h = gsl_histogram2d_alloc (10, 10) gsl_histogram2d_set_ranges_uniform (h, 0.0, 1.0, 0.0, 1.0); gsl_histogram2d_accumulate (h, 0.3, 0.3, 1); gsl_histogram2d_accumulate (h, 0.8, 0.1, 5); gsl_histogram2d_accumulate (h, 0.7, 0.9, 0.5); gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc(T); { int i; gsl_histogram2d_pdf * p = gsl_histogram2d_pdf_alloc (h->n); gsl_histogram2d_pdf_init (p, h); for (i = 0; i < 1000; i++) { double x, y; double u = gsl_rng_uniform (r); double v = gsl_rng_uniform (r); int status = gsl_histogram2d_pdf_sample (p, u, v, &x, &y); printf("%g %g\n", x, y); } } return 0; }
The following plot shows the distribution of the simulated events. Using a higher resolution grid we can see the original underlying histogram and also the statistical fluctuations caused by the events being uniformly distributed over the the area of the original bins.
This chapter describes functions for creating and manipulating ntuples, sets of values associated with events. The ntuples are stored in files. Their values can be extracted in any combination and booked in an histogram using a selection function.
The values to be stored are held in a user-defined data structure, and an ntuple is created associating this data structure with a file. The values are then written to the file (normally inside a loop) using the ntuple functions described below.
A histogram can be created from ntuple data by providing a selection function and a value function. The selection function specifies whether an event should be included in the subset to be analyzed or not. The value function computes the entry to be added to the histogram entry for each event.
All the ntuple functions are defined in the header file `gsl_ntuple.h'
Ntuples are manipulated using the gsl_ntuple
struct. This struct
contains information on the file where the ntuple data is stored, a
pointer to the current ntuple data row and the size of the user-defined
ntuple data struct.
typedef struct { FILE * file; void * ntuple_data; size_t size; } gsl_ntuple;
gsl_ntuple_write
Once an ntuple has been created its contents can be histogrammed in
various ways using the function gsl_ntuple_project
. Two
user-defined functions must be provided, a function to select events and
a function to compute scalar values. The selection function and the
value function both accept the ntuple row as a first argument and other
parameters as a second argument.
The selection function determines which ntuple rows are selected for histogramming. It is defined by the following struct,
typedef struct { int (* function) (void * ntuple_data, void * params); void * params; } gsl_ntuple_select_fn;
The struct component function should return a non-zero value for each ntuple row that is to be included in the histogram.
The value function computes scalar values for those ntuple rows selected by the selection function,
typedef struct { double (* function) (void * ntuple_data, void * params); void * params; } gsl_ntuple_value_fn;
In this case the struct component function should return the value to be added to the histogram for the ntuple row.
The following example programs demonstrate the use of ntuples in managing a large dataset. The first program creates a set of 100,000 simulated "events", each with 3 associated values (x,y,z). These are generated from a gaussian distribution with unit variance, for demonstration purposes, and written to the ntuple file `test.dat'.
#include <config.h> #include <gsl/gsl_ntuple.h> #include <gsl/gsl_rng.h> #include <gsl/gsl_randist.h> struct data { double x; double y; double z; }; int main (void) { const gsl_rng_type * T; gsl_rng * r; struct data ntuple_row; int i; gsl_ntuple *ntuple = gsl_ntuple_create ("test.dat", &ntuple_row, sizeof (ntuple_row)); gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc (T); for (i = 0; i < 10000; i++) { ntuple_row.x = gsl_ran_ugaussian (r); ntuple_row.y = gsl_ran_ugaussian (r); ntuple_row.z = gsl_ran_ugaussian (r); gsl_ntuple_write (ntuple); } gsl_ntuple_close(ntuple); return 0; }
The next program analyses the ntuple data in the file `test.dat'. The analysis procedure is to compute the squared-magnitude of each event, E^2=x^2+y^2+z^2, and select only those which exceed a lower limit of 1.5. The selected events are then histogrammed using their E^2 values.
#include <config.h> #include <math.h> #include <gsl/gsl_ntuple.h> #include <gsl/gsl_histogram.h> struct data { double x; double y; double z; }; int sel_func (void *ntuple_data, void *params); double val_func (void *ntuple_data, void *params); int main (void) { struct data ntuple_row; int i; gsl_ntuple *ntuple = gsl_ntuple_open ("test.dat", &ntuple_row, sizeof (ntuple_row)); double lower = 1.5; gsl_ntuple_select_fn S; gsl_ntuple_value_fn V; gsl_histogram *h = gsl_histogram_alloc (100); gsl_histogram_set_ranges_uniform(h, 0.0, 10.0); S.function = &sel_func; S.params = &lower; V.function = &val_func; V.params = 0; gsl_ntuple_project (h, ntuple, &V, &S); gsl_histogram_fprintf (stdout, h, "%f", "%f"); gsl_histogram_free (h); gsl_ntuple_close (ntuple); return 0; } int sel_func (void *ntuple_data, void *params) { double x, y, z, E, scale; scale = *(double *) params; x = ((struct data *) ntuple_data)->x; y = ((struct data *) ntuple_data)->y; z = ((struct data *) ntuple_data)->z; E2 = x * x + y * y + z * z; return E2 > scale; } double val_func (void *ntuple_data, void *params) { double x, y, z; x = ((struct data *) ntuple_data)->x; y = ((struct data *) ntuple_data)->y; z = ((struct data *) ntuple_data)->z; return x * x + y * y + z * z; }
The following plot shows the distribution of the selected events. Note the cut-off at the lower bound.
Further information on the use of ntuples can be found in the documentation for the CERN packages PAW and HBOOK (available online).
This chapter describes routines for multidimensional Monte Carlo integration. These include the traditional Monte Carlo method and adaptive algorithms such as VEGAS and MISER which use importance sampling and stratified sampling techniques. Each algorithm computes an estimate of a multidimensional definite integral of the form,
over a hypercubic region ((x_l,x_u), (y_l,y_u), ...) using a fixed number of function calls. The routines also provide a statistical estimate of the error on the result. This error estimate should be taken as a guide rather than as a strict error bound --- random sampling of the region may not uncover all the important features of the function, resulting in an underestimate of the error.
The functions are defined in separate header files for each routine,
gsl_monte_plain.h
, `gsl_monte_miser.h' and
`gsl_monte_vegas.h'.
All of the Monte Carlo integration routines use the same interface. There is an allocator to allocate memory for control variables and workspace, a routine to initialize those control variables, the integrator itself, and a function to free the space when done.
Each integration function requires a random number generator to be supplied, and returns an estimate of the integral and its standard deviation. The accuracy of the result is determined by the number of function calls specified by the user. If a known level of accuracy is required this can be achieved by calling the integrator several times and averaging the individual results until the desired accuracy is obtained.
Random sample points used within the Monte Carlo routines are always chosen strictly within the integration region, so that endpoint singularities are automatically avoided.
The function to be integrated has its own datatype, defined in the header file `gsl_monte.h'.
This data type defines a general function with parameters for Monte Carlo integration.
double (* function) (double * x, size_t dim, void * params)
size_t dim
void * params
Here is an example for a quadratic function in two dimensions,
with a = 3, b = 2, c = 1. The following code
defines a gsl_monte_function
F
which you could pass to an
integrator:
struct my_f_params { double a; double b; double c; }; double my_f (double x, size_t dim, void * p) { struct my_f_params * fp = (struct my_f_params *)p; if (dim != 2) { fprintf(stderr, "error: dim != 2"); abort(); } return fp->a * x[0] * x[0] + fp->b * x[0] * x[1] + fp->c * x[1] * x[1]; } gsl_monte_function F; struct my_f_params params = { 3.0, 2.0, 1.0 }; F.function = &my_f; F.dim = 2; F.params = ¶ms;
The function f(x) can be evaluated using the following macro,
#define GSL_MONTE_FN_EVAL(F,x) (*((F)->function))(x,(F)->dim,(F)->params)
The plain Monte Carlo algorithm samples points randomly from the integration region to estimate the integral and its error. Using this algorithm the estimate of the integral E(f; N) for N randomly distributed points x_i is given by,
where V is the volume of the integration region. The error on this estimate \sigma(E;N) is calculated from the estimated variance of the mean,
For large N this variance decreases asymptotically as var(f)/N, where var(f) is the true variance of the function over the integration region. The error estimate itself should decrease as @c{$\sigma(f)/\sqrt{N}$} \sigma(f)/\sqrt{N}. The familiar law of errors decreasing as @c{$1/\sqrt{N}$} 1/\sqrt{N} applies -- to reduce the error by a factor of 10 requires a 100-fold increase in the number of sample points.
The functions described in this section are declared in the header file `gsl_monte_plain.h'.
The MISER algorithm of Press and Farrar is based on recursive stratified sampling. This technique aims to reduce the overall integration error by concentrating integration points in the regions of highest variance.
The idea of stratified sampling begins with the observation that for two disjoint regions a and b with Monte Carlo estimates of the integral E_a(f) and E_b(f) and variances \sigma_a^2(f) and \sigma_b^2(f), the variance Var(f) of the combined estimate E(f) = (1/2) (E_a(f) + E_b(f)) is given by,
It can be shown that this variance is minimized by distributing the points such that,
Hence the smallest error estimate is obtained by allocating sample points in proportion to the standard deviation of the function in each sub-region.
The MISER algorithm proceeds by bisecting the integration region along one coordinate axis to give two sub-regions at each step. The direction is chosen by examining all d possible bisections and selecting the one which will minimize the combined variance of the two sub-regions. The variance in the sub-regions is estimated by sampling with a fraction of the total number of points available to the current step. The same procedure is then repeated recursively for each of the two half-spaces from the best bisection. The remaining sample points are allocated to the sub-regions using the formula for N_a and N_b. This recursive allocation of integration points continues down to a user-specified depth where each sub-region is integrated using a plain Monte Carlo estimate. These individual values and their error estimates are then combined upwards to give an overall result and an estimate of its error.
The functions described in this section are declared in the header file `gsl_monte_miser.h'.
The MISER algorithm has several configurable parameters. The
following variables can be accessed through the
gsl_monte_miser_state
struct,
16 * dim
.
32 *
min_calls
.
The authors of the original paper describing MISER recommend the value \alpha = 2 as a good choice, obtained from numerical experiments, and this is used as the default value in this implementation.
The VEGAS algorithm of Lepage is based on importance sampling. It samples points from the probability distribution described by the function |f|, so that the points are concentrated in the regions that make the largest contribution to the integral.
In general, if the Monte Carlo integral of f is sampled with points distributed according to a probability distribution described by the function g, we obtain an estimate E_g(f; N),
with a corresponding variance,
If the probability distribution is chosen as g = |f|/I(|f|) then it can be shown that the variance V_g(f; N) vanishes, and the error in the estimate will be zero. In practice it is not possible to sample from the exact distribution g for an arbitrary function, so importance sampling algorithms aim to produce efficient approximations to the desired distribution.
The VEGAS algorithm approximates the exact distribution by making a number of passes over the integration region while histogramming the function f. Each histogram is used to define a sampling distribution for the next pass. Asymptotically this procedure converges to the desired distribution. In order to avoid the number of histogram bins growing like K^d the probability distribution is approximated by a separable function: g(x_1, x_2, ...) = g_1(x_1) g_2(x_2) ... so that the number of bins required is only Kd. This is equivalent to locating the peaks of the function from the projections of the integrand onto the coordinate axes. The efficiency of VEGAS depends on the validity of this assumption. It is most efficient when the peaks of the integrand are well-localized. If an integrand can be rewritten in a form which is approximately separable this will increase the efficiency of integration with VEGAS.
VEGAS incorporates a number of additional features, and combines both stratified sampling and importance sampling. The integration region is divided into a number of "boxes", with each box getting in fixed number of points (the goal is 2). Each box can then have a fractional number of bins, but if bins/box is less than two, Vegas switches to a kind variance reduction (rather than importance sampling).
The VEGAS algorithm computes a number of independent estimates of the
integral internally, according to the iterations
parameter
described below, and returns their weighted average. Random sampling of
the integrand can occasionally produce an estimate where the error is
zero, particularly if the function is constant in some regions. An
estimate with zero error causes the weighted average to break down and
must be handled separately. In the original Fortran implementations of
VEGAS the error estimate is made non-zero by substituting a small
value (typically 1e-30
). The implementation in GSL differs from
this and avoids the use of an arbitrary constant -- it either assigns
the value a weight which is the average weight of the preceding
estimates or discards it according to the following procedure,
The VEGAS algorithm is highly configurable. The following variables
can be accessed through the gsl_monte_vegas_state
struct,
alpha
controls the stiffness of the rebinning
algorithm. It is typically set between one and two. A value of zero
prevents rebinning of the grid. The default value is 1.5.
stage = 0
which begins with a new uniform grid and empty weighted
average. Calling vegas with stage = 1
retains the grid from the
previous run but discards the weighted average, so that one can "tune"
the grid using a relatively small number of points and then do a large
run with stage = 1
on the optimized grid. Setting stage =
2
keeps the grid and the weighted average from the previous run, but
may increase (or decrease) the number of histogram bins in the grid
depending on the number of calls available. Choosing stage = 3
enters at the main loop, so that nothing is changed, and is equivalent
to performing additional iterations in a previous call.
GSL_VEGAS_MODE_IMPORTANCE
,
GSL_VEGAS_MODE_STRATIFIED
, GSL_VEGAS_MODE_IMPORTANCE_ONLY
.
This determines whether VEGAS will use importance sampling or
stratified sampling, or whether it can pick on its own. In low
dimensions VEGAS uses strict stratified sampling (more precisely,
stratified sampling is chosen if there are fewer than 2 bins per box).
-1
, which turns off all output. A
verbose value of 0
prints summary information about the
weighted average and final result, while a value of 1
also
displays the grid coordinates. A value of 2
prints information
from the rebinning procedure for each iteration.
The example program below uses the Monte Carlo routines to estimate the value of the following 3-dimensional integral from the theory of random walks,
The analytic value of this integral can be shown to be I = \Gamma(1/4)^4/(4 \pi^3) = 1.393203929685676859.... The integral gives the mean time spent at the origin by a random walk on a body-centered cubic lattice in three dimensions.
For simplicity we will compute the integral over the region (0,0,0) to (\pi,\pi,\pi) and multiply by 8 to obtain the full result. The integral is slowly varying in the middle of the region but has integrable singularities at the corners (0,0,0), (0,\pi,\pi), (\pi,0,\pi) and (\pi,\pi,0). The Monte Carlo routines only select points which are strictly within the integration region and so no special measures are needed to avoid these singularities.
#include <stdlib.h> #include <gsl/gsl_math.h> #include <gsl/gsl_monte.h> #include <gsl/gsl_monte_plain.h> #include <gsl/gsl_monte_miser.h> #include <gsl/gsl_monte_vegas.h> /* Computation of the integral, I = int (dx dy dz)/(2pi)^3 1/(1-cos(x)cos(y)cos(z)) over (-pi,-pi,-pi) to (+pi, +pi, +pi). The exact answer is Gamma(1/4)^4/(4 pi^3). This example is taken from C.Itzykson, J.M.Drouffe, "Statistical Field Theory - Volume 1", Section 1.1, p21, which cites the original paper M.L.Glasser, I.J.Zucker, Proc.Natl.Acad.Sci.USA 74 1800 (1977) */ /* For simplicity we compute the integral over the region (0,0,0) -> (pi,pi,pi) and multiply by 8 */ double exact = 1.3932039296856768591842462603255; double g (double *k, size_t dim, void *params) { double A = 1.0 / (M_PI * M_PI * M_PI); return A / (1.0 - cos (k[0]) * cos (k[1]) * cos (k[2])); } void display_results (char *title, double result, double error) { printf ("%s ==================\n", title); printf ("result = % .6f\n", result); printf ("sigma = % .6f\n", error); printf ("exact = % .6f\n", exact); printf ("error = % .6f = %.1g sigma\n", result - exact, fabs (result - exact) / error); } int main (void) { double res, err; double xl[3] = { 0, 0, 0 }; double xu[3] = { M_PI, M_PI, M_PI }; const gsl_rng_type *T; gsl_rng *r; gsl_monte_function G = { &g, 3, 0 }; size_t calls = 500000; gsl_rng_env_setup (); T = gsl_rng_default; r = gsl_rng_alloc (T); { gsl_monte_plain_state *s = gsl_monte_plain_alloc (3); gsl_monte_plain_integrate (&G, xl, xu, 3, calls, r, s, &res, &err); gsl_monte_plain_free (s); display_results ("plain", res, err); } { gsl_monte_miser_state *s = gsl_monte_miser_alloc (3); gsl_monte_miser_integrate (&G, xl, xu, 3, calls, r, s, &res, &err); gsl_monte_miser_free (s); display_results ("miser", res, err); } { gsl_monte_vegas_state *s = gsl_monte_vegas_alloc (3); gsl_monte_vegas_integrate (&G, xl, xu, 3, 10000, r, s, &res, &err); display_results ("vegas warm-up", res, err); printf ("converging...\n"); do { gsl_monte_vegas_integrate (&G, xl, xu, 3, calls/5, r, s, &res, &err); printf ("result = % .6f sigma = % .6f " "chisq/dof = %.1f\n", res, err, s->chisq); } while (fabs (s->chisq - 1.0) > 0.5); display_results ("vegas final", res, err); gsl_monte_vegas_free (s); } return 0; }
With 500,000 function calls the plain Monte Carlo algorithm achieves a
fractional error of 0.6%. The estimated error sigma
is
consistent with the actual error, and the computed result differs from
the true result by about one standard deviation,
plain ================== result = 1.385867 sigma = 0.007938 exact = 1.393204 error = -0.007337 = 0.9 sigma
The MISER algorithm reduces the error by a factor of two, and also correctly estimates the error,
miser ================== result = 1.390656 sigma = 0.003743 exact = 1.393204 error = -0.002548 = 0.7 sigma
In the case of the VEGAS algorithm the program uses an initial warm-up run of 10,000 function calls to prepare, or "warm up", the grid. This is followed by a main run with five iterations of 100,000 function calls. The chi-squared per degree of freedom for the five iterations are checked for consistency with 1, and the run is repeated if the results have not converged. In this case the estimates are consistent on the first pass.
vegas warm-up ================== result = 1.386925 sigma = 0.002651 exact = 1.393204 error = -0.006278 = 2 sigma converging... result = 1.392957 sigma = 0.000452 chisq/dof = 1.1 vegas final ================== result = 1.392957 sigma = 0.000452 exact = 1.393204 error = -0.000247 = 0.5 sigma
If the value of chisq
had differed significantly from 1 it would
indicate inconsistent results, with a correspondingly underestimated
error. The final estimate from VEGAS (using a similar number of
function calls) is significantly more accurate than the other two
algorithms.
The MISER algorithm is described in the following article,
The VEGAS algorithm is described in the following papers,
Stochastic search techniques are used when the structure of a space is not well understood or is not smooth, so that techniques like Newton's method (which requires calculating Jacobian derivative matrices) cannot be used.
In particular, these techniques are frequently used to solve combinatorial optimization problems, such as the traveling salesman problem.
The basic problem layout is that we are looking for a point in the space at which a real valued energy function (or cost function) is minimized.
Simulated annealing is a technique which has given good results in avoiding local minima; it is based on the idea of taking a random walk through the space at successively lower temperatures, where the probability of taking a step is given by a Boltzmann distribution.
The functions described in this chapter are declared in the header file `gsl_siman.h'.
We take random walks through the problem space, looking for points with low energies; in these random walks, the probability of taking a step is determined by the Boltzmann distribution
if E_{i+1} > E_i, and p = 1 when E_{i+1} <= E_i.
In other words, a step will occur if the new energy is lower. If the new energy is higher, the transition can still occur, and its likelihood is proportional to the temperature T and inversely proportional to the energy difference E_{i+1} - E_i.
The temperature T is initially set to a high value, and a random walk is carried out at that temperature. Then the temperature is lowered very slightly (according to a cooling schedule) and another random walk is taken.
This slight probability of taking a step that gives higher energy is what allows simulated annealing to frequently get out of local minima.
An initial guess is supplied. At each step, a point is chosen at a random distance from the current one, where the random distance r is distributed according to a Boltzmann distribution After a few search steps using this distribution, the temperature T is lowered according to some scheme, for example where \mu_T is slightly greater than 1.
double (*gsl_siman_Efunc_t) (void *xp);
void (*gsl_siman_step_t) (void *xp, double step_size);
double (*gsl_siman_metric_t) (void *xp, void *yp);
void (*gsl_siman_print_t) (void *xp);
gsl_siman_solve
.
/* this structure contains all the information needed to structure the search, beyond the energy function, the step function and the initial guess. */ struct s_siman_params { /* how many points to try for each step */ int n_tries; /* how many iterations at each temperature? */ int iters_fixed_T; /* max step size in the random walk */ double step_size; /* the following parameters are for the Boltzmann distribution */ double k, t_initial, mu_t, t_min; }; typedef struct s_siman_params gsl_siman_params_t;
The params structure (described above) controls the run by
providing the temperature schedule and other tunable parameters to the
algorithm (see section Simulated Annealing algorithm).
p
The result (optimal point in the space) is placed in *x0_p
.
If print_position is not null, a log will be printed to the screen with the following columns:
number_of_iterations temperature x x-(*x0_p) Ef(x)
If print_position is null, no information is printed to the screen.
GSL's Simulated Annealing package is clumsy, and it has to be because it is written in C, for C callers, and tries to be polymorphic at the same time. But here we provide some examples which can be pasted into your application with little change and should make things easier.
The first example, in one dimensional cartesian space, sets up an energy function which is a damped sine wave; this has many local minima, but only one global minimum, somewhere between 1.0 and 1.5. The initial guess given is 15.5, which is several local minima away from the global minimum.
/* set up parameters for this simulated annealing run */ /* how many points do we try before stepping */ #define N_TRIES 200 /* how many iterations for each T? */ #define ITERS_FIXED_T 10 /* max step size in random walk */ #define STEP_SIZE 10 /* Boltzmann constant */ #define K 1.0 /* initial temperature */ #define T_INITIAL 0.002 /* damping factor for temperature */ #define MU_T 1.005 #define T_MIN 2.0e-6 gsl_siman_params_t params = {N_TRIES, ITERS_FIXED_T, STEP_SIZE, K, T_INITIAL, MU_T, T_MIN}; /* now some functions to test in one dimension */ double E1(void *xp) { double x = * ((double *) xp); return exp(-square(x-1))*sin(8*x); } double M1(void *xp, void *yp) { double x = *((double *) xp); double y = *((double *) yp); return fabs(x - y); } void S1(void *xp, double step_size) { double r; double old_x = *((double *) xp); double new_x; r = gsl_ran_uniform(); new_x = r*2*step_size - step_size + old_x; memcpy(xp, &new_x, sizeof(new_x)); } void P1(void *xp) { printf("%12g", *((double *) xp)); } int main(int argc, char *argv[]) { Element x0; /* initial guess for search */ double x_initial = 15.5; gsl_siman_solve(&x_initial, E1, S1, M1, P1, sizeof(double), params); return 0; }
Here are a couple of plots that are generated by running
siman_test
in the following way:
./siman_test | grep -v "^#" | xyplot -xyil -y -0.88 -0.83 -d "x...y" | xyps -d > siman-test.eps ./siman_test | grep -v "^#" | xyplot -xyil -xl "generation" -yl "energy" -d "x..y" | xyps -d > siman-energy.eps
Example of a simulated annealing run: at higher temperatures (early in the plot) you see that the solution can fluctuate, but at lower temperatures it converges.
The TSP (Traveling Salesman Problem) is the classic combinatorial optimization problem. I have provided a very simple version of it, based on the coordinates of twelve cities in the southwestern United States. This should maybe be called the Flying Salesman Problem, since I am using the great-circle distance between cities, rather than the driving distance. Also: I assume the earth is a sphere, so I don't use geoid distances.
The gsl_siman_solve()
routine finds a route which is 3490.62
Kilometers long; this is confirmed by an exhaustive search of all
possible routes with the same initial city.
The full code can be found in `siman/siman_tsp.c', but I include here some plots generated with in the following way:
./siman_tsp > tsp.output grep -v "^#" tsp.output | xyplot -xyil -d "x................y" -lx "generation" -ly "distance" -lt "TSP -- 12 southwest cities" | xyps -d > 12-cities.eps grep initial_city_coord tsp.output | awk '{print $2, $3, $4, $5}' | xyplot -xyil -lb0 -cs 0.8 -lx "longitude (- means west)" -ly "latitude" -lt "TSP -- initial-order" | xyps -d > initial-route.eps grep final_city_coord tsp.output | awk '{print $2, $3, $4, $5}' | xyplot -xyil -lb0 -cs 0.8 -lx "longitude (- means west)" -ly "latitude" -lt "TSP -- final-order" | xyps -d > final-route.eps
This is the output showing the initial order of the cities; longitude is negative, since it is west and I want the plot to look like a map.
# initial coordinates of cities (longitude and latitude) ###initial_city_coord: -105.95 35.68 Santa Fe ###initial_city_coord: -112.07 33.54 Phoenix ###initial_city_coord: -106.62 35.12 Albuquerque ###initial_city_coord: -103.2 34.41 Clovis ###initial_city_coord: -107.87 37.29 Durango ###initial_city_coord: -96.77 32.79 Dallas ###initial_city_coord: -105.92 35.77 Tesuque ###initial_city_coord: -107.84 35.15 Grants ###initial_city_coord: -106.28 35.89 Los Alamos ###initial_city_coord: -106.76 32.34 Las Cruces ###initial_city_coord: -108.58 37.35 Cortez ###initial_city_coord: -108.74 35.52 Gallup ###initial_city_coord: -105.95 35.68 Santa Fe
The optimal route turns out to be:
# final coordinates of cities (longitude and latitude) ###final_city_coord: -105.95 35.68 Santa Fe ###final_city_coord: -106.28 35.89 Los Alamos ###final_city_coord: -106.62 35.12 Albuquerque ###final_city_coord: -107.84 35.15 Grants ###final_city_coord: -107.87 37.29 Durango ###final_city_coord: -108.58 37.35 Cortez ###final_city_coord: -108.74 35.52 Gallup ###final_city_coord: -112.07 33.54 Phoenix ###final_city_coord: -106.76 32.34 Las Cruces ###final_city_coord: -96.77 32.79 Dallas ###final_city_coord: -103.2 34.41 Clovis ###final_city_coord: -105.92 35.77 Tesuque ###final_city_coord: -105.95 35.68 Santa Fe
Initial and final (optimal) route for the 12 southwestern cities Flying Salesman Problem.
Here's a plot of the cost function (energy) versus generation (point in the calculation at which a new temperature is set) for this problem:
Example of a simulated annealing run for the 12 southwestern cities Flying Salesman Problem.
This chapter describes functions for solving ordinary differential equation (ODE) initial value problems. The library provides a variety of low-level methods, such as Runge-Kutta and Bulirsch-Stoer routines, and higher-level components for adaptive step-size control. The components can be combined by the user to achieve the desired solution, with full access to any intermediate steps.
These functions are declared in the header file `gsl_odeiv.h'.
The routines solve the general n-dimensional first-order system,
for i = 1, \dots, n. The stepping functions rely on the vector
of derivatives f_i and the Jacobian matrix,
J_{ij} = df_i(t,y(t)) / dy_j.
A system of equations is defined using the gsl_odeiv_system
datatype.
int (* function) (double t, const double y[], double dydt[], void * params)
int (* jacobian) (double t, const double y[], double * dfdy, double dfdt[], void * params);
J(i,j) = dfdy[i * dim + j]
where dim
is the dimension of the system.
size_t dimension;
void * params
The lowest level components are the stepping functions which advance a solution from time t to t+h for a fixed step-size h and estimate the resulting local error.
printf("step method is '%s'\n", gsl_odeiv_step_name (s));
would print something like step method is 'rk4'
.
The following algorithms are available,
The control function examines the proposed change to the solution and its error estimate produced by a stepping function and attempts to determine the optimal step-size for a user-specified level of error.
The step-size adjustment procedure for this method begins by computing the desired error level D_i for each component,
and comparing it with the observed error E_i = |yerr_i|. If the observed error E exceeds the desired error level D by more than 10% for any component then the method reduces the step-size by an appropriate factor,
where q is the consistency order of method (e.g. q=4 for 4(5) embedded RK), and S is a safety factor of 0.9. The ratio D/E is taken to be the maximum of the ratios D_i/E_i.
If the observed error E is less than 50% of the desired error level D for the maximum ratio D_i/E_i then the algorithm takes the opportunity to increase the step-size to bring the error in line with the desired level,
This encompasses all the standard error scaling methods.
GSL_ODEIV_HADJ_DEC
. If the error is sufficiently small then
h may be increased and GSL_ODEIV_HADJ_INC
is returned. The
function returns GSL_ODEIV_HADJ_NIL
if the step-size is
unchanged. The goal of the function is to estimate the largest
step-size which satisfies the user-specified accuracy requirements for
the current point.
printf("control method is '%s'\n", gsl_odeiv_control_name (c));
would print something like control method is 'standard'
The highest level of the system is the evolution function which combines the results of a stepping function and control function to reliably advance the solution forward over an interval (t_0, t_1). If the control function signals that the step-size should be decreased the evolution function backs out of the current step and tries the proposed smaller step-size. This is process is continued until an acceptable step-size is found.
The following program solves the second-order nonlinear Van der Pol oscillator equation,
This can be converted into a first order system suitable for use with the library by introducing a separate variable for the velocity, y = x'(t),
The program begins by defining functions for these derivatives and their Jacobian,
#include <stdio.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_matrix.h> #include <gsl/gsl_odeiv.h> int func (double t, const double y[], double f[], void *params) { double mu = *(double *)params; f[0] = y[1]; f[1] = -y[0] - mu*y[1]*(y[0]*y[0] - 1); return GSL_SUCCESS; } int jac (double t, const double y[], double *dfdy, double dfdt[], void *params) { double mu = *(double *)params; gsl_matrix_view dfdy_mat = gsl_matrix_view_array (dfdy, 2, 2); gsl_matrix * m = &dfdy_mat.matrix; gsl_matrix_set (m, 0, 0, 0.0); gsl_matrix_set (m, 0, 1, 1.0); gsl_matrix_set (m, 1, 0, -2.0*mu*y[0]*y[1] - 1.0); gsl_matrix_set (m, 1, 1, -mu*(y[0]*y[0] - 1.0)); dfdt[0] = 0.0; dfdt[1] = 0.0; return GSL_SUCCESS; } int main (void) { const gsl_odeiv_step_type * T = gsl_odeiv_step_rk8pd; gsl_odeiv_step * s = gsl_odeiv_step_alloc (T, 2); gsl_odeiv_control * c = gsl_odeiv_control_y_new (1e-6, 0.0); gsl_odeiv_evolve * e = gsl_odeiv_evolve_alloc (2); double mu = 10; gsl_odeiv_system sys = {func, jac, 2, &mu}; double t = 0.0, t1 = 100.0; double h = 1e-6; double y[2] = { 1.0, 0.0 }; gsl_ieee_env_setup(); while (t < t1) { int status = gsl_odeiv_evolve_apply (e, c, s, &sys, &t, t1, &h, y); if (status != GSL_SUCCESS) break; printf("%.5e %.5e %.5e\n", t, y[0], y[1]); } gsl_odeiv_evolve_free(e); gsl_odeiv_control_free(c); gsl_odeiv_step_free(s); return 0; }
The main loop of the program evolves the solution from (y, y') = (1, 0) at t=0 to t=100. The step-size h is automatically adjusted by the controller to maintain an absolute accuracy of @c{$10^{-6}$} 10^{-6} in the function values y.
Numerical solution of the Van der Pol oscillator equation using Prince-Dormand 8th order Runge-Kutta.
To obtain the values at regular intervals, rather than the variable spacings chosen by the control function, the main loop can be modified to advance the solution from one point to the next. For example, the following main loop prints the solution at the fixed points t = 0, 1, 2, \dots, 100,
for (i = 1; i <= 100; i++) { double ti = i * t1 / 100.0; while (t < ti) { gsl_odeiv_evolve_apply (e, c, s, &sys, &t, ti, &h, y); } printf("%.5e %.5e %.5e\n", t, y[0], y[1]); }
Many of the the basic Runge-Kutta formulas can be found in the Handbook of Mathematical Functions,
The implicit Bulirsch-Stoer algorithm bsimp
is described in the
following paper,
This chapter describes functions for performing interpolation. The library provides a variety of interpolation methods, including Cubic splines and Akima splines. The interpolation types are interchangeable, allowing different methods to be used without recompiling. Interpolations can be defined for both normal and periodic boundary conditions. Additional functions are available for computing derivatives and integrals of interpolating functions.
The functions described in this section are declared in the header files `gsl_interp.h' and `gsl_spline.h'.
Given a set of data points (x_1, y_1) \dots (x_n, y_n) the routines described in this section compute a continuous interpolating function y(x) such that y_i = y(x_i). The interpolation is piecewise smooth, and its behavior at the points is determined by the type of interpolation used.
The interpolation function for a given dataset is stored in a
gsl_interp
object. These are created by the following functions.
gsl_interp
) does not save
the data arrays xa and ya and only stores the static state
computed from the data. The xa data array is always assumed to be
strictly ordered; the behavior for other arrangements is not defined.
The interpolation library provides five interpolation types:
The following related functions are available,
printf("interp uses '%s' interpolation\n", gsl_interp_name (interp));
would print something like,
interp uses 'cspline' interpolation.
The state of searches can be stored in a gsl_interp_accel
object,
which is a kind of iterator for interpolation lookups. It caches the
previous value of an index lookup. When the subsequent interpolation
point falls in the same interval its index value can be returned
immediately.
x_array[i] <= x < x_array[i+1]
. The index is searched for
in the range [index_lo,index_hi].
xarray[i] <= x <
xarray[i+1]
.
The functions described in the previous sections required the user to
supply pointers to the x and y arrays on each call. The
following functions are equivalent to the corresponding
gsl_interp
functions but maintain a copy of this data in the
gsl_spline
object. This removes the need to pass both xa
and ya as arguments on each evaluation. These functions are
defined in the header file `gsl_spline.h'.
The following program demonstrates the use of the interpolation and spline functions. It computes a cubic spline interpolation of the 10-point dataset (x_i, y_i) where x_i = i + \sin(i)/2 and y_i = i + \cos(i^2) for i = 0 \dots 9.
#include <config.h> #include <stdlib.h> #include <stdio.h> #include <math.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_spline.h> int main (void) { int i; double xi, yi, x[10], y[10]; printf ("#m=0,S=2\n"); for (i = 0; i < 10; i++) { x[i] = i + 0.5 * sin (i); y[i] = i + cos (i * i); printf ("%g %g\n", x[i], y[i]); } printf ("#m=1,S=0\n"); { gsl_interp_accel *acc = gsl_interp_accel_alloc (); gsl_spline *spline = gsl_spline_alloc (gsl_interp_cspline, 10); gsl_spline_init (spline, x, y, 10); for (xi = x[0]; xi < x[9]; xi += 0.01) { double yi = gsl_spline_eval (spline, xi, acc); printf ("%g %g\n", xi, yi); } gsl_spline_free (spline); gsl_interp_accel_free(acc); } return 0; }
The output is designed to be used with the GNU plotutils
graph
program,
$ ./a.out > interp.dat $ graph -T ps < interp.dat > interp.ps
The result shows a smooth interpolation of the original points. The
interpolation method can changed simply by varying the first argument of
gsl_spline_alloc
.
Descriptions of the interpolation algorithms and further references can be found in the following book,
The functions described in this chapter compute numerical derivatives by finite differencing. An adaptive algorithm is used to find the best choice of finite difference and to estimate the error in the derivative. These functions are declared in the header file `gsl_diff.h'
The following code estimates the derivative of the function
f(x) = x^{3/2}
at x=2 and at x=0. The function f(x) is
undefined for x<0 so the derivative at x=0 is computed
using gsl_diff_forward
.
#include <stdio.h> #include <gsl/gsl_math.h> #include <gsl/gsl_diff.h> double f (double x, void * params) { return pow (x, 1.5); } int main (void) { gsl_function F; double result, abserr; F.function = &f; F.params = 0; printf("f(x) = x^(3/2)\n"); gsl_diff_central (&F, 2.0, &result, &abserr); printf("x = 2.0\n"); printf("f'(x) = %.10f +/- %.5f\n", result, abserr); printf("exact = %.10f\n\n", 1.5 * sqrt(2.0)); gsl_diff_forward (&F, 0.0, &result, &abserr); printf("x = 0.0\n"); printf("f'(x) = %.10f +/- %.5f\n", result, abserr); printf("exact = %.10f\n", 0.0); return 0; }
Here is the output of the program,
$ ./demo f(x) = x^(3/2) x = 2.0 f'(x) = 2.1213203435 +/- 0.01490 exact = 2.1213203436 x = 0.0 f'(x) = 0.0012172897 +/- 0.05028 exact = 0.0000000000
The algorithms used by these functions are described in the following book,
This chapter describes routines for computing Chebyshev approximations to univariate functions. A Chebyshev approximation is a truncation of the series f(x) = \sum c_n T_n(x), where the Chebyshev polynomials T_n(x) = \cos(n \arccos x) provide an orthogonal basis of polynomials on the interval [-1,1] with the weight function @c{$1 / \sqrt{1-x^2}$} 1 / \sqrt{1-x^2}. The first few Chebyshev polynomials are, T_0(x) = 1, T_1(x) = x, T_2(x) = 2 x^2 - 1.
The functions described in this chapter are declared in the header file `gsl_chebyshev.h'.
A Chebyshev series is stored using the following structure,
typedef struct { double * c; /* coefficients c[0] .. c[order] */ int order; /* order of expansion */ double a; /* lower interval point */ double b; /* upper interval point */ } gsl_cheb_struct
The approximation is made over the range [a,b] using order+1 terms, including the coefficient c[0].
gsl_cheb_series
struct.
The following functions allow a Chebyshev series to be differentiated or integrated, producing a new Chebyshev series. Note that the error estimate produced by evaluating the derivative series will be underestimated due to the contribution of higher order terms being neglected.
The following example program computes Chebyshev approximations to a step function. This is an extremely difficult approximation to make, due to the discontinuity, and was chosen as an example where approximation error is visible. For smooth functions the Chebyshev approximation converges extremely rapidly and errors would not be visible.
#include <stdio.h> #include <gsl/gsl_math.h> #include <gsl/gsl_chebyshev.h> double f (double x, void *p) { if (x < 0.5) return 0.25; else return 0.75; } int main (void) { int i, n = 10000; gsl_cheb_series *cs = gsl_cheb_alloc (40); gsl_function F; F.function = f; F.params = 0; gsl_cheb_init (cs, &F, 0.0, 1.0); for (i = 0; i < n; i++) { double x = i / (double)n; double r10 = gsl_cheb_eval_n (cs, 10, x); double r40 = gsl_cheb_eval (cs, x); printf ("%g %g %g %g\n", x, GSL_FN_EVAL (&F, x), r10, r40); } gsl_cheb_free (cs); return 0; }
The output from the program gives the original function, 10-th order approximation and 40-th order approximation, all sampled at intervals of 0.001 in x.
The following paper describes the use of Chebyshev series,
The functions described in this chapter accelerate the convergence of a series using the Levin u-transform. This method takes a small number of terms from the start of a series and uses a systematic approximation to compute an extrapolated value and an estimate of its error. The u-transform works for both convergent and divergent series, including asymptotic series.
These functions are declared in the header file `gsl_sum.h'.
The following functions compute the full Levin u-transform of a series with its error estimate. The error estimate is computed by propagating rounding errors from each term through to the final extrapolation.
These functions are intended for summing analytic series where each term
is known to high accuracy, and the rounding errors are assumed to
originate from finite precision. They are taken to be relative errors of
order GSL_DBL_EPSILON
for each term.
The calculation of the error in the extrapolated value is an O(N^2) process, which is expensive in time and memory. A faster but less reliable method which estimates the error from the convergence of the extrapolated value is described in the next section For the method described here a full table of intermediate values and derivatives through to O(N) must be computed and stored, but this does give a reliable error estimate. .
w->sum_plain
. The algorithm
calculates the truncation error (the difference between two successive
extrapolations) and round-off error (propagated from the individual
terms) to choose an optimal number of terms for the extrapolation.
The functions described in this section compute the Levin u-transform of series and attempt to estimate the error from the "truncation error" in the extrapolation, the difference between the final two approximations. Using this method avoids the need to compute an intermediate table of derivatives because the error is estimated from the behavior of the extrapolated value itself. Consequently this algorithm is an O(N) process and only requires O(N) terms of storage. If the series converges sufficiently fast then this procedure can be acceptable. It is appropriate to use this method when there is a need to compute many extrapolations of series with similar converge properties at high-speed. For example, when numerically integrating a function defined by a parameterized series where the parameter varies only slightly. A reliable error estimate should be computed first using the full algorithm described above in order to verify the consistency of the results.
w->sum_plain
. The algorithm
terminates when the difference between two successive extrapolations
reaches a minimum or is sufficiently small. The difference between these
two values is used as estimate of the error and is stored in
abserr_trunc. To improve the reliability of the algorithm the
extrapolated values are replaced by moving averages when calculating the
truncation error, smoothing out any fluctuations.
The following code calculates an estimate of \zeta(2) = \pi^2 / 6 using the series,
After N terms the error in the sum is O(1/N), making direct summation of the series converge slowly.
#include <stdio.h> #include <gsl/gsl_math.h> #include <gsl/gsl_sum.h> #define N 20 int main (void) { double t[N]; double sum_accel, err; double sum = 0; int n; gsl_sum_levin_u_workspace * w = gsl_sum_levin_u_alloc (N); const double zeta_2 = M_PI * M_PI / 6.0; /* terms for zeta(2) = \sum_{n=0}^{\infty} 1/n^2 */ for (n = 0; n < N; n++) { double np1 = n + 1.0; t[n] = 1.0 / (np1 * np1); sum += t[n]; } gsl_sum_levin_u_accel (t, N, w, &sum_accel, &err); printf("term-by-term sum = % .16f using %d terms\n", sum, N); printf("term-by-term sum = % .16f using %d terms\n", w->sum_plain, w->terms_used); printf("exact value = % .16f\n", zeta_2); printf("accelerated sum = % .16f using %d terms\n", sum_accel, w->terms_used); printf("estimated error = % .16f\n", err); printf("actual error = % .16f\n", sum_accel - zeta_2); gsl_sum_levin_u_free (w); return 0; }
The output below shows that the Levin u-transform is able to obtain an estimate of the sum to 1 part in 10^10 using the first eleven terms of the series. The error estimate returned by the function is also accurate, giving the correct number of significant digits.
bash$ ./a.out term-by-term sum = 1.5961632439130233 using 20 terms term-by-term sum = 1.5759958390005426 using 13 terms exact value = 1.6449340668482264 accelerated sum = 1.6449340668166479 using 13 terms estimated error = 0.0000000000508580 actual error = -0.0000000000315785
Note that a direct summation of this series would require 10^10 terms to achieve the same precision as the accelerated sum does in 13 terms.
The algorithms used by these functions are described in the following papers,
The theory of the u-transform was presented by Levin,
A review paper on the Levin Transform is available online,
This chapter describes functions for performing Discrete Hankel Transforms (DHTs). The functions are declared in the header file `gsl_dht.h'.
The discrete Hankel transform acts on a vector of sampled data, where the samples are assumed to have been taken at points related to the zeroes of a Bessel function of fixed order; compare this to the case of the discrete Fourier transform, where samples are taken at points related to the zeroes of the sine or cosine function.
Specifically, let f(t) be a function on the unit interval. Then the finite \nu-Hankel transform of f(t) is defined to be the set of numbers g_m given by
so that
Suppose that f is band-limited in the sense that g_m=0 for m > M. Then we have the following fundamental sampling theorem.
It is this discrete expression which defines the discrete Hankel
transform. The kernel in the summation above defines the matrix of the
\nu-Hankel transform of size M-1. The coefficients of
this matrix, being dependent on \nu and M, must be
precomputed and stored; the gsl_dht
object encapsulates this
data. The allocation function gsl_dht_alloc
returns a
gsl_dht
object which must be properly initialized with
gsl_dht_init
before it can be used to perform transforms on data
sample vectors, for fixed \nu and M, using the
gsl_dht_apply
function. The implementation allows a scaling of
the fundamental interval, for convenience, so that one take assume the
function is defined on the interval [0,X], rather than the unit
interval.
Notice that by assumption f(t) vanishes at the endpoints of the interval, consistent with the inversion formula and the sampling formula given above. Therefore, this transform corresponds to an orthogonal expansion in eigenfunctions of the Dirichlet problem for the Bessel differential equation.
The algorithms used by these functions are described in the following papers,
¤³¤Î¾Ï¤Ç¤ÏÇ¤°Õ¤Î1¼¡¸µ´Ø¿ô¤Î²ò¤òÃµº÷¤¹¤ë¥ë¡¼¥Á¥ó¤Ë¤Ä¤¤¤Æ½Ò¤Ù¤ë. ¤³¤Î¥é¥¤ ¥Ö¥é¥ê¤ÏÍÍ¡¹¤ÊÈ¿ÉüÅª²òÃµºº¤È¼ýÂ«¥Æ¥¹¥È¤ÎÄã¥ì¥Ù¥ë¤Î¥³¥ó¥Ý¡¼¥Í¥ó¥È¤ò¼ÂÁõ¤· ¤Æ¤¤¤ë. ¤³¤ì¤é¤ò¥æ¡¼¥¶¡¼¤¬ÁÈ¤ß¤¢¤ï¤»¤Æ, È¿Éü¤ÎÅÓÃæÃÊ³¬¤¹¤Ù¤Æ¤ò¸«¤ÆË¾¤Þ¤· ¤¤²ò¤òÆÀ¤ë¤³¤È¤¬¤Ç¤¤ë. ¥á¥½¥Ã¥É¤Î³Æ¥¯¥é¥¹¤ÇÆ±¤¸ÏÈÁÈ¤¬»È¤ï¤ì¤Æ¤¤¤ë¤Î¤Ç, ¥×¥í¥°¥é¥à¤òºÆ¥³¥ó¥Ñ¥¤¥ë¤»¤º¤Ë¼Â¹Ô»þ¤Ë²òË¡¤òÀÚ¤êÂØ¤¨¤ë¤³¤È¤¬¤Ç¤¤ë. ²òË¡ ¤Î³Æ¥¤¥ó¥¹¥¿¥ó¥¹¤Ç¾õÂÖ¤òÊÝ»ý¤·¤Æ¤¤¤ë¤Î¤Ç, ¥Þ¥ë¥Á¥¹¥ì¥Ã¥É¥×¥í¥°¥é¥à¤Ç¤â»È ¤¦¤³¤È¤¬¤Ç¤¤ë.
¥Ø¥Ã¥À¥Õ¥¡¥¤¥ë`gsl_roots.h'¤Ë²òÃµºº´Ø¿ô¤Î¥×¥í¥È¥¿¥¤¥×¤È´ØÏ¢¤¹¤ëÄêµÁ ¤¬¤¢¤ë.
1¼¡¸µ²òÃµºº¥¢¥ë¥´¥ê¥º¥à¤Ï2¤Ä¤Î¥¯¥é¥¹¤ËÊ¬¤±¤ë¤³¤È¤¬¤Ç¤¤ë. ²ò¤Î°Ï¤¤ ¹þ¤ß¤È²ò¤ÎÀöÎý¤Ç¤¢¤ë. ²ò¤ò°Ï¤¤¹þ¤à¥¢¥ë¥´¥ê¥º¥à¤Ï¼ýÂ«¤òÊÝ¾Ú¤Ç¤¤ë. °Ï¤¤¹þ¤ß¥¢¥ë¥´¥ê¥º¥à¤Ï²ò¤Î¤¢¤ë¤³¤È¤òÃÎ¤Ã¤Æ¤¤¤ë¶è´Ö¤¬½ÐÈ¯ÅÀ¤È¤Ê¤ë. ¶è´Ö¤Î Éý¤ÏÈ¿ÉüÅª¤Ë½Ì¾®¤µ¤ì, Ë¾¤àµöÍÆ¸íº¹°Ê²¼¤Ë¶è´ÖÉý¤¬½Ì¾®¤¹¤ë¤Þ¤ÇÃµºº¤¬Â³¤¯. ¤³¤ì¤Ï²ò¤Î°ÌÃÖ¤Î¸íº¹É¾²Á¤¬¸·Ì©¤Ë¹Ô¤¨¤ëÍøÅÀ¤¬¤¢¤ë.
²ò¤ÎÀöÎý¤Îµ»Ë¡¤Ï, ²ò¤Î½é´ü¿äÂ¬ÃÍ¤ò²þÎÉ¤·¤è¤¦¤È¤¹¤ë. ¤³¤Î¥¢¥ë¥´¥ê¥º ¥à¤Ï²ò¤Ë¡Ö½½Ê¬¶á¤¤¡×½ÐÈ¯ÅÀ¤òÍ¿¤¨¤¿¤È¤¤Î¤ß¼ýÂ«¤¹¤ë. ¤Þ¤¿¥¹¥Ô¡¼¥É¤Î¤¿¤á¸· Ì©¤Ê¸íº¹É¾²Á¤òµ¾À·¤Ë¤¹¤ë. ²ò¶áËµ¤Ç´Ø¿ô¤ÎµóÆ°¤ò¶á»÷¤¹¤ë¤³¤È¤Ç, ½é´üÃÍ¤Î¹â ¼¡¤Î²þÎÉ¤ò»î¤ß¤ë. ´Ø¿ô¤ÎµóÆ°¤¬¥¢¥ë¥´¥ê¥º¥à¤Î¤â¤Î¤Ë¶á¤¯, ½é´üÃÍ¤¬¤è¤¯¿äÄê ¤Ç¤¤ë»þ¤Ë¤ÏÀöÎý¥¢¥ë¥´¥ê¥º¥à¤ÏÁÇÁá¤¯¼ýÂ«¤¹¤ë.
GSL ¤Ç¤Ï¤É¤Á¤é¤Î¥¿¥¤¥×¤Î¥¢¥ë¥´¥ê¥º¥à¤âÆ±¤¸ÏÈÁÈ¤ß¤Ë¼ý¤á¤¿. ¥æ¡¼¥¶¡¼¤Ï¥¢¥ë ¥´¥ê¥º¥à¤Î¹â¥ì¥Ù¥ë¥É¥é¥¤¥Ð¤ò¼ÂÁõ¤·, ¥é¥¤¥Ö¥é¥ê¤Ï³Æ¥¹¥Æ¥Ã¥×¤ËÉ¬Í×¤Ê¸Ä¡¹¤Î ´Ø¿ô¤ò¼ÂÁõ¤¹¤ë. È¿Éü¤Ë¤Ï3¤Ä¤Î¥á¥¤¥ó¥Õ¥§¡¼¥º¤¬Â¸ºß¤¹¤ë:
°Ï¤¤¹þ¤ßË¡¤Î¾õÂÖ¤Ïgsl_root_fsolver
¹½Â¤ÂÎ¤ËÊÝ»ý¤µ¤ì¤ë. ¹¹¿·¥×¥í¥·¡¼
¥¸¥ã¤Ï´Ø¿ôÃÍ¤Î¤ßÍÑ¤¤¤ë(Æ³´Ø¿ôÃÍ¤ÏÍÑ¤¤¤Ê¤¤). ÀöÎýË¡¤Î¾õÂÖ¤Ï
gsl_root_fdfsolver
¹½Â¤ÂÎ¤ËÊÝ»ý¤µ¤ì¤ë. ¹¹¿·¤Ë¤Ï´Ø¿ô¤È¤½¤ÎÆ³´Ø¿ô¤¬
É¬Í×¤È¤Ê¤ë(¤³¤Î¤¿¤áfdf
¤È¤¤¤¦Ì¾Á°¤Ë¤Ê¤Ã¤Æ¤¤¤ë).
²òÃµºº´Ø¿ô¤Ï1ÅÙ¤Ë1¤Ä¤Î²ò¤·¤«Ãµºº¤·¤Ê¤¤. ÃµººÎÎ°èÆâ¤Ë¤¤¤¯¤Ä¤«¤Î²ò¤¬¤¢¤ë¤È ¤, »Ï¤á¤Ë¸«ÉÕ¤«¤Ã¤¿²ò¤òÊÖ¤¹. ¤·¤«¤·¤É¤Î²ò¤¬¸«ÉÕ¤«¤Ã¤¿¤Î¤«¤òÄ´¤Ù¤ë¤Î¤ÏÆñ ¤·¤¤. Â¿¤¯¤Î¾ì¹ç, Ê£¿ô¤Î²ò¤¬¤¢¤ëÎÎ°è¤Ç²ò¤òÃµ¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤â²¿¤â·Ù ¹ð¤µ¤ì¤Ê¤¤.
½Å²ò¤ò»ý¤Ä´Ø¿ô¤Î¼è¤ê°·¤¤¤Ë¤ÏÃí°Õ¤òÍ×¤¹¤ë(¤¿¤È¤¨¤Ð ¤ä ¤Î¤è¤¦¤Ê). ¶ö¿ô¼¡¤Î½Å²ò¤ËÂÐ¤·¤Æ¤Ï°Ï¤¤¹þ¤ßË¡¤Ï»È¤¨¤Ê¤¤. ¤³¤ì¤é¤ÎÊýË¡¤Ç¤Ï, ½é´üÈÏ°Ï¤Ï0¤ò¤è¤®¤é¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤. ¤Ä¤Þ¤êÎ¾Ã¼ÅÀ¤ÇÊÒÊý¤ÏÀµ, ¤â¤¦ÊÒÊý¤Ï Éé¤ÎÃÍ¤ò¤È¤é¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤. ¶ö¿ô¼¡¤Î½Å²ò¤Ï0¤ò¤è¤®¤é¤º, ÀÜ¤¹¤ë¤À¤±¤Ç¤¢ ¤ë. °Ï¤¤¹þ¤ßË¡¤Ï´ñ¿ô¼¡¤Î½Å²ò(3¼¡, 5¼¡...)¤Ç¤Ïµ¡Ç½¤¹¤ë. ²ò¤ÎÀöÎýË¡¤Ï °ìÈÌÅª¤Ë¹â¼¡¤Î½Å²ò¤òÃµºº¤¹¤ë¤³¤È¤¬¤Ç¤¤ë¤¬, ¼ýÂ«¤¬°¤¯¤Ê¤ë. ¤³¤Î¾ì¹ç¼ýÂ« ¤Î²ÃÂ®¤ËSteffenson¥¢¥ë¥´¥ê¥º¥à¤òÍÑ¤¤¤ë¤È¤è¤¤.
ÃµººÎÎ°è¤Ë ¤¬²ò¤ò»ý¤Ä¤³¤È¤ÏÀäÂÐÉ¬Í×¤È¤¤¤¦¤ï¤±¤Ç¤Ï¤Ê¤¤¤¬, ²ò¤ÎÂ¸ºß¤òÄ´¤Ù¤ë¤Î ¤Ë¿ôÃÍ²òÃµºº´Ø¿ô¤ò»È¤¦¤Ù¤¤Ç¤Ï¤Ê¤¤. ¤â¤Ã¤È¤è¤¤ÊýË¡¤¬¤¢¤ë. ¿ôÃÍ²òÃµºº¤¬´Ö °ã¤¦²ÄÇ½À¤¬¤¢¤ë¤Î¤À¤«¤é, ¤è¤¯ÃÎ¤é¤Ê¤¤´Ø¿ô¤ò´ÝÅê¤²¤¹¤ë¤Î¤Ï¤è¤¯¤Ê¤¤. °ìÈÌ Åª¤Ë²ò¤òÃµ¤¹Á°¤Ë¥°¥é¥Õ¤òÉÁ¤¤¤Æ¥Á¥§¥Ã¥¯¤¹¤ë¤Î¤¬¤è¤¤¤À¤í¤¦.
¤³¤Î´Ø¿ô¤Ï¥¿¥¤¥×T¤Î¥½¥ë¥Ð¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤ò¿·¤·¤¯³ä¤ê¤¢¤Æ¤Æ¥Ý¥¤¥ó¥¿ ¤òÊÖ¤¹. Îã¤¨¤Ð, ¼¡¤Î¥³¡¼¥É¤Ïbisection¥½¥ë¥Ð¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤òºîÀ®¤¹¤ë:
const gsl_root_fsolver_type * T = gsl_root_fsolver_bisection; gsl_root_fsolver * s = gsl_root_fsolver_alloc (T);
¥½¥ë¥Ð¤òºî¤ë¤Î¤Ë½½Ê¬¤Ê¥á¥â¥ê¤¬¤Ê¤¤¾ì¹ç, ´Ø¿ô¤Ï¥Ì¥ë¥Ý¥¤¥ó¥¿¤òÊÖ¤·, ¥¨¥é¡¼
¥³¡¼¥ÉGSL_ENOMEM
¤Ë¤è¤ê¥¨¥é¡¼¥Ï¥ó¥É¥é¤¬¸Æ¤Ó¤À¤µ¤ì¤ë.
¤³¤Î´Ø¿ô¤Ï¥¿¥¤¥×T¤ÎÆ³´Ø¿ô¥Ù¡¼¥¹¤Î¥½¥ë¥Ð¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤ò¿·¤·¤¯³ä¤ê ¤¢¤Æ¤Æ¥Ý¥¤¥ó¥¿¤òÊÖ¤¹. Îã¤¨¤Ð, ¼¡¤Î¥³¡¼¥É¤ÏNewton-Raphson¥½¥ë¥Ð¤Î¥¤¥ó¥¹¥¿ ¥ó¥¹¤òºîÀ®¤¹¤ë:
const gsl_root_fdfsolver_type * T = gsl_root_fdfsolver_newton; gsl_root_fdfsolver * s = gsl_root_fdfsolver_alloc (T);
¥½¥ë¥Ð¤òºî¤ë¤Î¤Ë½½Ê¬¤Ê¥á¥â¥ê¤¬¤Ê¤¤¾ì¹ç, ´Ø¿ô¤Ï¥Ì¥ë¥Ý¥¤¥ó¥¿¤òÊÖ¤·, ¥¨¥é¡¼
¥³¡¼¥ÉGSL_ENOMEM
¤Ë¤è¤ê¥¨¥é¡¼¥Ï¥ó¥É¥é¤¬¸Æ¤Ó¤À¤µ¤ì¤ë.
¤³¤Î´Ø¿ô¤ÏÂ¸ºß¤¹¤ë¥½¥ë¥Ðs¤¬´Ø¿ôf¤Î²ò¤ò½é´üÃµººÈÏ°Ï [x_lower, x_upper]¤ÇÃµºº¤¹¤ë¤è¤¦(ºÆ)½é´ü²½¤¹¤ë.
¤³¤Î´Ø¿ô¤ÏÂ¸ºß¤¹¤ë¥½¥ë¥Ðs¤¬´Ø¿ô¤È¤½¤ÎÈùÊ¬fdf¤ª¤è¤Ó½é´üÃÍ root¤ÇÃµºº¤¹¤ë¤è¤¦¤Ë(ºÆ)½é´ü²½¤¹¤ë.
¤³¤Î´Ø¿ô¤Ï¥½¥ë¥Ðs¤Ë³ä¤ê¤¢¤Æ¤é¤ì¤¿¥á¥â¥ê¤ò²òÊü¤¹¤ë.
¤³¤ì¤é¤Î´Ø¿ô¤Ï¥½¥ë¥Ð¤ÎÌ¾Á°¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹. Îã¤¨¤Ð
printf("s is a '%s' solver\n", gsl_root_fsolver_name (s)) ;
¤Ï, s is a 'bisection' solver
¤Î¤è¤¦¤Ê½ÐÎÏ¤òÊÖ¤¹.
²òÃµºº¤ò¤µ¤»¤ë¤¿¤á, 1ÊÑ¿ô¤ÎÏ¢Â³´Ø¿ô, ¤ª¤è¤Ó¾ì¹ç¤Ë¤è¤ê¤½¤ÎÆ³´Ø¿ô¤òÍ¿¤¨¤ë É¬Í×¤¬¤¢¤ë. °ìÈÌ¤Î¥Ñ¥é¥á¡¼¥¿¤òµö¤¹¤¿¤á, ´Ø¿ô¤Ï¼¡¤Î¥Ç¡¼¥¿·¿¤ÇÄêµÁ¤µ¤ì¤Æ¤¤ ¤ëÉ¬Í×¤¬¤¢¤ë:
double (* function) (double x, void * params)
void * params
°ìÈÌ¤Î2¼¡´Ø¿ô¤òÎã¤Ë¤¢¤²¤ë:
¤³¤³¤Ç
¤À¤È¤·¤è¤¦. ¼¡¤Î¥³¡¼¥É¤Ï²òÃµºº¤¬²ÄÇ½¤Ê´Ø¿ôgsl_function
F
¤ò
ÄêµÁ¤¹¤ë:
struct my_f_params { double a; double b; double c; } ; double my_f (double x, void * p) { struct my_f_params * params = (struct my_f_params *)p; double a = (params->a); double b = (params->b); double c = (params->c); return (a * x + b) * x + c; } gsl_function F; struct my_f_params params = { 3.0, 2.0, 1.0 }; F.function = &my_f; F.params = ¶ms;
´Ø¿ô ¤Ï¼¡¤Î¥Þ¥¯¥í¤Ë¤è¤êÉ¾²Á¤Ç¤¤ë¡§
#define GSL_FN_EVAL(F,x) (*((F)->function))(x,(F)->params)
double (* f) (double x, void * params)
double (* df) (double x, void * params)
void (* fdf) (double x, void * params, double * f, double * df)
void * params
¼¡¤Î´Ø¿ô¤òÎã¤Ëµó¤²¤ë:
double my_f (double x, void * params) { return exp (2 * x); } double my_df (double x, void * params) { return 2 * exp (2 * x); } void my_fdf (double x, void * params, double * f, double * df) { double t = exp (2 * x); *f = t; *df = 2 * t; /* computing using existing values */ } gsl_function_fdf FDF; FDF.f = &my_f; FDF.df = &my_df; FDF.fdf = &my_fdf; FDF.params = 0;
´Ø¿ô ¤Ï¼¡¤Î¥Þ¥¯¥í¤ÇÉ¾²Á¤Ç¤¤ë:
#define GSL_FN_FDF_EVAL_F(FDF,x) (*((FDF)->f))(x,(FDF)->params)
Æ³´Ø¿ô ¤Ï¼¡¤Î¥Þ¥¯¥í¤ÇÉ¾²Á¤Ç¤¤ë:
#define GSL_FN_FDF_EVAL_DF(FDF,x) (*((FDF)->df))(x,(FDF)->params)
¤½¤·¤Æ´Ø¿ôÃÍ ¤ª¤è¤ÓÆ³´Ø¿ôÃÍ ¤òÆ±»þ¤Ëµá¤á¤ë¤Ë¤Ï¼¡¤Î¥Þ¥¯¥í¤ò»È¤¦:
#define GSL_FN_FDF_EVAL_F_DF(FDF,x,y,dy) (*((FDF)->fdf))(x,(FDF)->params,(y),(dy))
¤³¤Î¥Þ¥¯¥í¤Ï
¤òy¤Ë, ¤½¤·¤Æ
¤òdy¤Ë³ÊÇ¼¤¹¤ë---¤³¤ì¤é¤Ï¤È¤â¤Ëdouble
·¿¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë.
¥æ¡¼¥¶¡¼¤Ï½é´üÈÏ°Ï¤«½é´üÃÍ¤òÍ¿¤¨¤ë¤³¤È¤Ë¤Ê¤ë. ¤³¤Î¾Ï¤Ç¤ÏÃµººÈÏ°Ï¤ä¿äÄêÃÍ ¤¬¤É¤Î¤è¤¦¤Ëµ¡Ç½¤·, ´Ø¿ô¤Î°ú¿ô¤¬¤É¤Î¤è¤¦¤Ë¤½¤ì¤é¤òÀ©¸æ¤¹¤ë¤Î¤«¤ò²òÀâ¤¹¤ë.
¿äÄêÃÍ¤ÏÃ±¤Ëx¤ÎÃÍ¤Ç¤¢¤ë. ²ò¤ËÂÐ¤·¤ÆµöÍÆÈÏ°ÏÆâ¤Ë¶á¤Å¤¯¤Þ¤ÇÈ¿Éü¤µ¤ì
¤ë. double
·¿¤ò¤â¤Ä.
ÃµººÈÏ°Ï¤Ï¶è´Ö¤ÎÎ¾Ã¼ÅÀ¤Ç¤¢¤ë. ¶è´ÖÉý¤¬Í×µáÀºÅÙÆâ¤Ë¤Ê¤ë¤Þ¤ÇÈ¿Éü¤µ¤ì¤ë. ¶è ´Ö¤Ï¾å¸Â¤È²¼¸Â¤ò¼¨¤¹2¤Ä¤ÎÃÍ¤Ç»ØÄê¤µ¤ì¤ë. Ã¼ÅÀ¤ò¶è´Ö¤Ë´Þ¤à¤«¤É¤¦¤«¤Ï¶è´Ö ¤Î»È¤ï¤ì¤ë¾õ¶·¤Ë¤è¤ë.
°Ê²¼¤Î´Ø¿ô¤Ï³Æ¥¢¥ë¥´¥ê¥º¥à¤Ç¤ÎÈ¿Éü¤òÀ©¸æ¤¹¤ë. ³Æ´Ø¿ô¤ÏÁêÅö¤¹¤ë·¿¤Î¥½¥ë¥Ð ¤Î¾õÂÖ¤ò¹¹¿·¤¹¤ëÈ¿Éü¤ò1²ó¹Ô¤¦. Æ±¤¸´Ø¿ô¤Ç¤¢¤é¤æ¤ë¥½¥ë¥Ð¤ËÂÐ±þ¤¹¤ë¤¿¤á, ¥³¡¼¥É¤òÊÑ¹¹¤¹¤ë¤³¤È¤Ê¤¯¼Â¹Ô»þ¤Ë¥á¥½¥Ã¥É¤òÊÑ¹¹¤¹¤ë¤³¤È¤¬¤Ç¤¤ë.
GSL_EBADFUNC
Inf
¤Þ¤¿¤ÏNaN
¤È¤Ê¤ëÆÃ°ÛÅÀ¤¬È¯À¸¤·¤¿.
GSL_EZERODIV
¥½¥ë¥Ð¤Ï¤½¤Î»þÅÀ¤Ç¤Î²ò¤ÎºÇ¤âÎÉ¤¤¿äÄêÃÍ¤òÊÝ»ý¤·¤Æ¤¤¤ë. °Ï¤¤¹þ¤ßË¡¤Ç¤Ï¤½¤Î »þÅÀ¤ÇºÇ¤âÎÉ¤¤²ò¶è´Ö¤òÊÝ»ý¤·¤Æ¤¤¤ë. ¤³¤Î¾ðÊó¤Ï¼¡¤Î´Ø¿ô¤Ç»²¾È¤¹¤ë¤³¤È¤¬¤Ç ¤¤ë:
²òÃµºº¥×¥í¥·¡¼¥¸¥ã¤Ï°Ê²¼¤Î¾ò·ï¤¬¿¿¤Ë¤Ê¤Ã¤¿¤È¤¤ËÄä»ß¤¹¤ë:
¤³¤ì¤é¤Î¾ò·ï¤Î½èÍý¤Ï¥æ¡¼¥¶¡¼¤ËÇ¤¤»¤é¤ì¤Æ¤¤¤ë. °Ê²¼¤Î´Ø¿ô¤Ï¥æ¡¼¥¶¡¼¤¬É¸½à Åª¤ÊÊýË¡¤Ë¤è¤ê¸½ºß¤Î·ë²Ì¤ÎÀºÅÙ¤òÄ´¤Ù¤ë¤¿¤á¤ËÍÑ°Õ¤µ¤ì¤Æ¤¤¤ë.
GSL_SUCCESS
¤òÊÖ¤¹.
¤¿¤À¤·, ¶è´Ö ¤¬¸¶ÅÀ¤ò´Þ¤ó¤Ç¤¤¤Ê¤¤¤³¤È¤¬É¬Í×¤Ç¤¢¤ë. ¸¶ÅÀ¤ò´Þ¤ó¤Ç¤¤¤ë¾ì¹ç¤Ë¤Ï ¤Ï(¶è´Ö¤ÎºÇ¾®ÃÍ¤Ç¤¢¤ë)0¤ËÃÖ¤¤«¤¨¤é¤ì¤ë. ¤³¤ì¤Ï¸¶ÅÀ¶á¤¯¤Î²ò¤Ç¤ÎÁêÂÐ¸íº¹ ¤ÎÀµ³Î¤ÊÉ¾²Á¤ËÉ¬Í×¤ÊÁàºî¤Ç¤¢¤ë.
¶è´Ö¤ËÂÐ¤¹¤ë¤³¤Î¾ò·ï¤Ë¤è¤ê, ¶è´ÖÆâ¤Î¿äÄêÃÍr¤¬¿¿¤Î²ò r^{*} ¤ËÂÐ¤·¤ÆÆ±ÍÍ¤Î¾ò·ï¤òËþ¤¿¤¹¤³¤È¤òÍ×ÀÁ¤¹¤ë:
¤¿¤À¤·¶è´ÖÆâ¤Ë¿¿¤Î²ò¤¬Â¸ºß¤¹¤ë¤È²¾Äê¤·¤Æ¤¤¤ë.
GSL_SUCCESS
¤òÊÖ¤¹:
Ëþ¤¿¤µ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¤ÏGSL_CONTINUE
¤òÊÖ¤¹.
GSL_SUCCESS
¤òÊÖ¤¹:
Ëþ¤¿¤µ¤ì¤Ê¤¤¾ì¹ç¤ÏGSL_CONTINUE
¤òÊÖ¤¹. ¤³¤Î¾ò·ï¤Ï»Äº¹|f(x)|
¤¬½½Ê¬¾®¤µ¤¤¤¿¤á, ¿¿¤Î²òx¤ÎÀµ³Î¤Ê¾ì½ê¤¬½ÅÍ×¤Ç¤Ê¤¯¤Ê¤ë¾ì¹ç¤Ë¤ÏÅ¬¤·
¤Æ¤¤¤ë.
¤³¤Î¾Ï¤ÇÀâÌÀ¤¹¤ë²ò¤Î°Ï¤¤¹þ¤ßË¡¤Ï²ò¤¬´Þ¤Þ¤ì¤Æ¤¤¤ë¤³¤È¤òÃÎ¤Ã¤Æ¤¤¤ë½é´üÈÏ°Ï ¤¬É¬Í×¤È¤Ê¤ë--a¤Èb¤¬ÈÏ°Ï¤ÎÃ¼ÅÀ¤Ç¤¢¤ì¤Ð, f(a)¤È f(b)¤ÏÉä¹æ¤¬°Û¤Ê¤ëÉ¬Í×¤¬¤¢¤ë. ¤Ä¤Þ¤êÍ¿¤¨¤é¤ì¤¿¶è´Ö¤Ç¾¯¤Ê¤¯¤È¤â1²ó ¤Ï0¤ò¤è¤®¤ëÉ¬Í×¤¬¤¢¤ë. ÂÅÅö¤Ê½é´üÈÏ°Ï¤¬Í¿¤¨¤é¤ì¤ì¤Ð, ´Ø¿ô¤Î¿¶Éñ¤¤¤¬¤è¤± ¤ì¤Ð¤³¤ì¤é¤Î¥¢¥ë¥´¥ê¥º¥à¤Ï¼ºÇÔ¤¹¤ë¤³¤È¤Ï¤Ê¤¤.
°Ï¤¤¹þ¤ßË¡¤Ç¤Ï¶ö¿ô¼¡¤Î½Å²ò¤Ï¸«¤Ä¤±¤ë¤³¤È¤¬¤Ç¤¤Ê¤¤. x¼´¤ËÀÜ¤¹¤ë¤« ¤é¤Ç¤¢¤ë.
2Ê¬Ë¡¤Ï°Ï¤¤¹þ¤ßË¡¤Î¤¦¤ÁºÇ¤â´ÊÃ±¤ÊÊýË¡¤Ç¤¢¤ë. Àþ·¿¼ýÂ«¤ò¼¨¤·, ¥é¥¤ ¥Ö¥é¥êÃæ¤ÇºÇ¤âÃÙ¤¯¼ýÂ«¤¹¤ë¥¢¥ë¥´¥ê¥º¥à¤Ç¤¢¤ë.
³ÆÃÊ¤Ç, ¶è´Ö¤Ï2ÅùÊ¬¤µ¤ì, ÃæÅÀ¤Ç¤Î´Ø¿ôÃÍ¤¬µá¤á¤é¤ì¤ë. ¤³¤ÎÃÍ¤ÎÉä¹æ¤Ë¤è¤ê ¤É¤Á¤é¤Î¶è´Ö¤Ë²ò¤¬´Þ¤Þ¤ì¤ë¤«¤ò·èÄê¤¹¤ë. ¶è´Ö¤ÎÈ¾Ê¬¤¬¼Î¤Æ¤é¤ì»Ä¤ëÈ¾Ê¬¤¬¿· ¤·¤¤¿äÄê¶è´Ö¤È¤Ê¤ë. ¤³¤Î¥×¥í¥·¡¼¥¸¥ã¤¬, ¶è´ÖÉý¤¬½½Ê¬¾®¤µ¤¯¤Ê¤ë¤Þ¤Ç·«¤êÊÖ ¤µ¤ì¤ë.
¤É¤Î»þÅÀ¤Ç¤â, ²ò¤Î¿äÄêÃÍ¤ÏÃæÅÀ¤ÎÃÍ¤Ç¤¢¤ë.
2Ê¬Ë¡¤ò4²ó·«¤êÊÖ¤·¤¿. a_n¤Ï¶è´Ö»ÏÅÀ¤ÎnÈÖÌÜ¤Î°ÌÃÖ, b_n¤Ï¶è´Ö½ªÅÀ¤ÎnÈÖÌÜ¤Î°ÌÃÖ. ³ÆÃÊ¤Ç¤ÎÃæÅÀ¤Î°ÌÃÖ¤â¼¨¤·¤Æ¤¢¤ë.
false position ¥¢¥ë¥´¥ê¥º¥à¤ÏÀþ·¿Êä´°¤Ë¤è¤ë²òÃµººË¡¤Ç¤¢¤ë. ¼ýÂ«¤Ï Àþ·¿¤À¤¬, °ìÈÌ¤Ë2Ê¬Ë¡¤è¤êÁá¤¤.
³ÆÃÊ¤Ç, Ã¼ÅÀ(a,f(a)), (b,f(b))¤ò·ë¤ÖÄ¾Àþ¤¬°ú¤«¤ì, x ¼´¤È¤Î¸òÅÀ¤ò¡ÖÃæÅÀ¡×¤È¤¹¤ë. ¤³¤ÎÅÀ¤Ç¤Î´Ø¿ôÃÍ¤¬·×»»¤µ¤ì, ¤½¤ÎÉä¹æ¤Ç¤É¤Á¤é ¤ÎÎÎ°è¤òºÎÍÑ¤¹¤ë¤«¤ò·èÄê¤·, ÊÒÊý¤ò¼Î¤Æ»Ä¤ê¤ò¼¡¤ÎÎÎ°è¤È¤¹¤ë. ¤³¤Î¥×¥í¥·¡¼ ¥¸¥ã¤¬¶è´Ö¤¬½½Ê¬¤Ë¾®¤µ¤¯¤Ê¤ë¤Þ¤Ç·«¤êÊÖ¤µ¤ì¤ë.
¸½ºß¤Î¿äÄêÃÍ¤ÏÀþ·¿Êä´°¤Ë¤è¤êµá¤á¤é¤ì¤ë.
Brent-DekkerË¡(BrentË¡¤ÈÎ¬¤¹¤³¤È¤Ë¤¹¤ë)¤Ï2Ê¬Ë¡¥¢¥ë¥´¥ê¥º¥à¤È ÆâÁÞ¤òÁÈ¤ß¤¢¤ï¤»¤¿¤â¤Î¤Ç¤¢¤ë. ¤³¤ÎÊýË¡¤Ï¹Ó¤Ã¤Ý¤¤¤¬Áá¤¤¥¢¥ë¥´¥ê¥º¥à¤Ç¤¢¤ë.
³ÆÃÊ¤ÇBrentË¡¤ÏÆâÁÞ¶ÊÀþ¤Ç´Ø¿ô¤ò¶á»÷¤¹¤ë. ½éÃÊ¤Ç¤Ï2Ã¼ÅÀ¤ÎÄ¾Àþ¶á»÷¤À¤¬, ÃÊ ¤¬¿Ê¤à¤Ë¤Ä¤ì ºÇ¿·¤Î3ÅÀ¤Ë¤Ä¤¤¤ÆµÕ2¾è¥Õ¥£¥Ã¥È¤ò¹Ô¤¦. ÆâÁÞ¶ÊÀþ¤Èx¼´ ¤Î¸òÅÀ¤ò²ò¤Î¿äÄêÃÍ¤È¤¹¤ë. ¿äÄêÃÍ¤¬¸½ºß¤Î¿äÄê¶è´Ö¤Ë¤¢¤ë¾ì¹ç¤ÏÆâÁÞÅÀ¤òÍÑ¤¤, ¶è´Ö¤ò½Ì¾®¤¹¤ë. ÆâÁÞÅÀ¤¬µá¤á¤é¤ì¤Ê¤¤»þ¤ÏÄÌ¾ï¤Î2Ê¬Ë¡¤ËÀÚ¤ê¤«¤¨¤ë.
ºÇ¿·¤Î¿äÄêÃÍ¤ÏºÇ¿·¤ÎÆâÁÞ¤â¤·¤¯¤Ï2Ê¬Ë¡¤ÎÃÍ¤Ç¤¢¤ë.
¤³¤Î¾Ï¤ÇÀâÌÀ¤¹¤ë²ò¤ÎÀöÎýË¡¤Ï²ò¤Î°ÌÃÖ¤ò¤¢¤é¤«¤¸¤á¿äÄê¤·¤Æ¤ª¤¯É¬Í×¤¬¤¢¤ë. ¼ýÂ«¤¹¤ë¤È¤¤¤¦ÀäÂÐÅª¤ÊÊÝ¾Ú¤Ï¤Ê¤¤--´Ø¿ô¤Ï¤³¤Î¼êË¡¤ËÅ¬¤·¤¿·Á¤ò¤·¤Æ¤¤¤Ê¤¯¤Æ ¤Ï¤Ê¤é¤º, ½é´üÃÍ¤Ï¿¿¤Î²ò¤Ë½½Ê¬¶á¤¯¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤. ¾ò·ï¤¬Ëþ¤¿¤µ¤ì¤¿¤È¤ ¤Ë¤Ï¼ýÂ«¤Ï2¼¡¤Ç¤¢¤ë.
¤³¤ì¤é¤Î¥¢¥ë¥´¥ê¥º¥à¤Ï´Ø¿ô¤È¤½¤ÎÆ³´Ø¿ô¤¬É¬Í×¤È¤Ê¤ë.
NewtonË¡¤ÏÉ¸½àÅª¤Ê²òÀöÎý¥¢¥ë¥´¥ê¥º¥à¤Ç¤¢¤ë. ¤³¤Î¥¢¥ë¥´¥ê¥º¥à¤Ï²ò¤Î°ÌÃÖ¤Î ½é´ü¿äÄêÃÍ¤«¤é»Ï¤Þ¤ë. ³ÆÃÊ¤Ç, ´Ø¿ôf¤ÎÀÜÀþ¤¬¤Ò¤«¤ì¤ë. ÀÜÀþ¤È x¼´¤Î¸òÅÀ¤¬¿·¤·¤¤¿äÄêÃÍ¤È¤Ê¤ë. ·«¤êÊÖ¤·¤Ï¼¡¤Î¿ôÎó¤ÇÄêµÁ¤µ¤ì¤ë:
NewtonË¡¤ÏÃ±°ì¤Î²ò¤ËÂÐ¤·¤Æ¤Ï2¼¡¤Ç, Ê£¿ô¤Î²ò¤Ë¤ÏÀþ·¿¤Ç¼ýÂ«¤¹¤ë.
NewtonË¡¤Ç¤Î¿ô²ó¤Î·«¤êÊÖ¤·. g_n¤ÏnÈÖÌÜ¤Î¿äÄêÃÍ¤Ç¤¢¤ë.
³äÀþË¡¤ÏNewtonË¡¤òÃ±½ã²½¤·¤¿¤â¤Î¤Ç, Æ³´Ø¿ô¤òËè²óµá¤á¤ëÉ¬Í×¤¬¤Ê¤¤.
½éÃÊ¤Ç¤Ï¥¢¥ë¥´¥ê¥º¥à¤ÏNewtonË¡¤«¤é»Ï¤á, Æ³´Ø¿ô¤òÍÑ¤¤¤ë.
Â³¤¯·«¤êÊÖ¤·¤Ç¤Ï, ÈùÊ¬ÃÍ¤òµá¤á¤ëÂå¤ï¤ê¤Ë¿ôÃÍÅª¤Ê¿äÄêÃÍ¤ÇÂåÍÑ¤¹¤ë. Á°¤Î2 ÅÀ¤Î·¹¤¤ò»È¤¦¤³¤È¤Ë¤Ê¤ë:
Æ³´Ø¿ô¤¬²ò¤Î¶áËµ¤Ç¤µ¤Û¤ÉÊÑ²½¤·¤Ê¤¤¤È¤¤Ë¤Ï, ³äÀþË¡¤Ï¤«¤Ê¤ê¤Î¾ÊÎÏ²½¤ò¿Þ¤ë ¤³¤È¤¬¤Ç¤¤ë. Æ³´Ø¿ô¤ÎÉ¾²Á¤¬´Ø¿ô¼«¿È¤ÎÉ¾²Á¤è¤ê¥³¥¹¥È¤¬0.44ÇÜ°Ê¾å¤«¤«¤ë¤Î ¤Ç¤¢¤ì¤Ð, ³äÀþË¡¤Ï¥Ë¥å¡¼¥È¥óË¡¤è¤êÁá¤¤. ¿ôÃÍÈùÊ¬¤Î·×»»¤ÈÆ±ÍÍ, 2ÅÀ¤Î´Ö³Ö ¤¬¾®¤µ¤¯¤Ê¤ê¤¹¤®¤ë¤È·åÍî¤Á¤¬À¸¤¸¤ë.
Ã±°ì¤Î²ò¤Ç¤¢¤ì¤Ð, ¼ýÂ«¤Ï (Ìó1.62)¼¡¤Ç¤¢¤ë. Ê£¿ô¤Î²ò¤Ç¤ÏÀþ·¿¤Ç¼ýÂ«¤¹¤ë.
SteffensonË¡¤Ï¤³¤ì¤é¥ë¡¼¥Á¥ó¤ÎÃæ¤ÇºÇ¤âÁá¤¤¼ýÂ«¤ò¼¨¤¹. ¤³¤ì¤Ï´ðËÜÅª ¤ÊNewtonË¡¤ËAitken¤Î¡Ö¥Ç¥ë¥¿2¾è¡×²ÃÂ®¤òÁÈ¤ß¤¢¤ï¤»¤¿¤â¤Î¤Ç¤¢¤ë. NewtonË¡ ¤Î³ÆÃÊ¤òx_i¤È¤·¤¿¤È¤, ²ÃÂ®¤Ë¤è¤ê¿·¤·¤¤¿ôÎóR_i¤òÀ¸À®¤¹¤ë:
¤³¤ì¤Ï¹çÍýÅª¤Ê¾ò·ï¤Î¤â¤È¤Ç¸µ¤Î¿ôÎó¤è¤ê¤âÁá¤¯¼ýÂ«¤¹¤ë. ²ÃÂ®¿ôÎó¤òÀ¸À®¤¹¤ë ¤Ë¤ÏºÇÄã3¹àÉ¬Í×¤Ç¤¢¤ë. ½éÃÊ¤Ç¤Ï¸µ¤ÎNewtonË¡¤Î·ë²Ì¤òÊÖ¤¹. ²ÃÂ®¹à¤ÎÊ¬Êì¤¬0 ¤Ë¤Ê¤ë¾ì¹ç¤âNewtonË¡¤Î·ë²Ì¤òÊÖ¤¹.
²ÃÂ®¥×¥í¥·¡¼¥¸¥ã¤òÍÑ¤¤¤Æ¤¤¤ë¤¿¤á, ´Ø¿ô¤Î¿¶Éñ¤¤¤¬°¤¤¾ì¹ç¤Ë¤Ï¤³¤ÎÊýË¡¤ÏÉÔ °ÂÄê¤Ë¤Ê¤ë.
¤É¤Î²òÃµººË¡¤Ç¤¢¤Ã¤Æ¤â, ²ò¤¯¤Ù¤ÊýÄø¼°¤òÍÑ°Õ¤¹¤ëÉ¬Í×¤¬¤¢¤ë. ¤³¤ÎÎã¤Ç¤Ï, Á°¤Ë¼¨¤·¤¿°ìÈÌ¤Î2¼¡ÊýÄø¼°¤ò²ò¤¯¤³¤È¤Ë¤·¤è¤¦. ¤Þ¤º´Ø¿ô¤Î¥Ñ¥é¥á¡¼¥¿¤òÄêµÁ ¤¹¤ë¤¿¤á¤Ë¥Ø¥Ã¥À¥Õ¥¡¥¤¥ë(`demo_fn.h')¤¬É¬Í×¤Ç¤¢¤ë.
struct quadratic_params { double a, b, c; }; double quadratic (double x, void *params); double quadratic_deriv (double x, void *params); void quadratic_fdf (double x, void *params, double *y, double *dy);
´Ø¿ô¤ÎÄêµÁ¤ÏÊÌ¤Î¥Õ¥¡¥¤¥ë(`demo_fn.c')¤Ç¹Ô¤¦¤³¤È¤Ë¤·¤è¤¦.
double quadratic (double x, void *params) { struct quadratic_params *p = (struct quadratic_params *) params; double a = p->a; double b = p->b; double c = p->c; return (a * x + b) * x + c; } double quadratic_deriv (double x, void *params) { struct quadratic_params *p = (struct quadratic_params *) params; double a = p->a; double b = p->b; double c = p->c; return 2.0 * a * x + b; } void quadratic_fdf (double x, void *params, double *y, double *dy) { struct quadratic_params *p = (struct quadratic_params *) params; double a = p->a; double b = p->b; double c = p->c; *y = (a * x + b) * x + c; *dy = 2.0 * a * x + b; }
ºÇ½é¤Î¥×¥í¥°¥é¥à¤ÏBrentË¡¤Î¥½¥ë¥Ðgsl_root_fsolver_brent
¤òÍÑ¤¤, ¼¡
¤ÎÊýÄø¼°¤ò²ò¤¯¤â¤Î¤Ç¤¢¤ë.
¤³¤Î²ò¤Ï ¤Ç¤¢¤ë.
#include <stdio.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_math.h> #include <gsl/gsl_roots.h> #include "demo_fn.h" #include "demo_fn.c" int main (void) { int status; int iter = 0, max_iter = 100; const gsl_root_fsolver_type *T; gsl_root_fsolver *s; double r = 0, r_expected = sqrt (5.0); double x_lo = 0.0, x_hi = 5.0; gsl_function F; struct quadratic_params params = {1.0, 0.0, -5.0}; F.function = &quadratic; F.params = ¶ms; T = gsl_root_fsolver_brent; s = gsl_root_fsolver_alloc (T); gsl_root_fsolver_set (s, &F, x_lo, x_hi); printf ("using %s method\n", gsl_root_fsolver_name (s)); printf ("%5s [%9s, %9s] %9s %10s %9s\n", "iter", "lower", "upper", "root", "err", "err(est)"); do { iter++; status = gsl_root_fsolver_iterate (s); r = gsl_root_fsolver_root (s); x_lo = gsl_root_fsolver_x_lower (s); x_hi = gsl_root_fsolver_x_upper (s); status = gsl_root_test_interval (x_lo, x_hi, 0, 0.001); if (status == GSL_SUCCESS) printf ("Converged:\n"); printf ("%5d [%.7f, %.7f] %.7f %+.7f %.7f\n", iter, x_lo, x_hi, r, r - r_expected, x_hi - x_lo); } while (status == GSL_CONTINUE && iter < max_iter); return status; }
°Ê²¼¤Ë·ë²Ì¤òµó¤²¤ë:
bash$ ./a.out using brent method iter [ lower, upper] root err err(est) 1 [1.0000000, 5.0000000] 1.0000000 -1.2360680 4.0000000 2 [1.0000000, 3.0000000] 3.0000000 +0.7639320 2.0000000 3 [2.0000000, 3.0000000] 2.0000000 -0.2360680 1.0000000 4 [2.2000000, 3.0000000] 2.2000000 -0.0360680 0.8000000 5 [2.2000000, 2.2366300] 2.2366300 +0.0005621 0.0366300 Converged: 6 [2.2360634, 2.2366300] 2.2360634 -0.0000046 0.0005666
BrentË¡¤Ç¤Ï¤Ê¤¯2Ê¬Ë¡¤ò»È¤¦¤è¤¦¤Ë¤¹¤ë¾ì¹ç¤Ï,
gsl_root_fsolver_brent
¤ògsl_root_fsolver_bisection
¤ËÊÑ¹¹¤¹
¤ì¤Ð¤è¤¤. ¤³¤Î¾ì¹ç, ¼ýÂ«¤¬ÃÙ¤¯¤Ê¤ë:
bash$ ./a.out using bisection method iter [ lower, upper] root err err(est) 1 [0.0000000, 2.5000000] 1.2500000 -0.9860680 2.5000000 2 [1.2500000, 2.5000000] 1.8750000 -0.3610680 1.2500000 3 [1.8750000, 2.5000000] 2.1875000 -0.0485680 0.6250000 4 [2.1875000, 2.5000000] 2.3437500 +0.1076820 0.3125000 5 [2.1875000, 2.3437500] 2.2656250 +0.0295570 0.1562500 6 [2.1875000, 2.2656250] 2.2265625 -0.0095055 0.0781250 7 [2.2265625, 2.2656250] 2.2460938 +0.0100258 0.0390625 8 [2.2265625, 2.2460938] 2.2363281 +0.0002601 0.0195312 9 [2.2265625, 2.2363281] 2.2314453 -0.0046227 0.0097656 10 [2.2314453, 2.2363281] 2.2338867 -0.0021813 0.0048828 11 [2.2338867, 2.2363281] 2.2351074 -0.0009606 0.0024414 Converged: 12 [2.2351074, 2.2363281] 2.2357178 -0.0003502 0.0012207
¼¡¤Î¥×¥í¥°¥é¥à¤ÏÆ±¤¸´Ø¿ô¤òÆ³´Ø¿ô¤âÍÑ¤¤¤Æ²ò¤òÃµºº¤¹¤ë¤â¤Î¤Ç¤¢¤ë.
#include <stdio.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_math.h> #include <gsl/gsl_roots.h> #include "demo_fn.h" #include "demo_fn.c" int main (void) { int status; int iter = 0, max_iter = 100; const gsl_root_fdfsolver_type *T; gsl_root_fdfsolver *s; double x0, x = 5.0, r_expected = sqrt (5.0); gsl_function_fdf FDF; struct quadratic_params params = {1.0, 0.0, -5.0}; FDF.f = &quadratic; FDF.df = &quadratic_deriv; FDF.fdf = &quadratic_fdf; FDF.params = ¶ms; T = gsl_root_fdfsolver_newton; s = gsl_root_fdfsolver_alloc (T); gsl_root_fdfsolver_set (s, &FDF, x); printf ("using %s method\n", gsl_root_fdfsolver_name (s)); printf ("%-5s %10s %10s %10s\n", "iter", "root", "err", "err(est)"); do { iter++; status = gsl_root_fdfsolver_iterate (s); x0 = x; x = gsl_root_fdfsolver_root (s); status = gsl_root_test_delta (x, x0, 0, 1e-3); if (status == GSL_SUCCESS) printf ("Converged:\n"); printf ("%5d %10.7f %+10.7f %10.7f\n", iter, x, x - r_expected, x - x0); } while (status == GSL_CONTINUE && iter < max_iter); return status; }
NewtonË¡¤Î·ë²Ì¤ò°Ê²¼¤Ëµó¤²¤ë:
bash$ ./a.out using newton method iter root err err(est) 1 3.0000000 +0.7639320 -2.0000000 2 2.3333333 +0.0972654 -0.6666667 3 2.2380952 +0.0020273 -0.0952381 Converged: 4 2.2360689 +0.0000009 -0.0020263
¸íº¹¤ÏÁ°¤ÎÃÊ¤È¤Îº¹¤ò¼è¤ë¤è¤ê¼¡¤ÎÃÊ¤È¤Îº¹¤ò¤È¤ëÊý¤¬¤è¤êÀµ³Î¤È¤Ê¤ë.
gsl_root_fdfsolver_newton
¤ògsl_root_fdfsolver_secant
¤ä
gsl_root_fdfsolver_steffenson
¤ËÊÑ¤¨¤ë¤³¤È¤ÇÂ¾¤ÎÊýË¡¤ËÀÚ¤ê¤«¤¨¤ë¤³
¤È¤¬¤Ç¤¤ë.
Brent-Dekker¥¢¥ë¥´¥ê¥º¥à¤Ë¤Ä¤¤¤Æ¤Ï°Ê²¼¤Î2¤Ä¤ÎÏÀÊ¸¤ò»²¾È¤¹¤ë¤³¤È.
This chapter describes routines for finding minima of arbitrary one-dimensional functions. The library provides low level components for a variety of iterative minimizers and convergence tests. These can be combined by the user to achieve the desired solution, with full access to the intermediate steps of the algorithms. Each class of methods uses the same framework, so that you can switch between minimizers at runtime without needing to recompile your program. Each instance of a minimizer keeps track of its own state, allowing the minimizers to be used in multi-threaded programs.
The header file `gsl_min.h' contains prototypes for the minimization functions and related declarations. To use the minimization algorithms to find the maximum of a function simply invert its sign.
The minimization algorithms begin with a bounded region known to contain a minimum. The region is described by an lower bound a and an upper bound b, with an estimate of the minimum x.
The value of the function at x must be less than the value of the function at the ends of the interval,
This condition guarantees that a minimum is contained somewhere within the interval. On each iteration a new point x' is selected using one of the available algorithms. If the new point is a better estimate of the minimum, f(x') < f(x), then the current estimate of the minimum x is updated. The new point also allows the size of the bounded interval to be reduced, by choosing the most compact set of points which satisfies the constraint f(a) > f(x) < f(b). The interval is reduced until it encloses the true minimum to a desired tolerance. This provides a best estimate of the location of the minimum and a rigorous error estimate.
Several bracketing algorithms are available within a single framework. The user provides a high-level driver for the algorithm, and the library provides the individual functions necessary for each of the steps. There are three main phases of the iteration. The steps are,
The state for the minimizers is held in a gsl_min_fminimizer
struct. The updating procedure uses only function evaluations (not
derivatives).
Note that minimization functions can only search for one minimum at a time. When there are several minima in the search area, the first minimum to be found will be returned; however it is difficult to predict which of the minima this will be. In most cases, no error will be reported if you try to find a minimum in an area where there is more than one.
With all minimization algorithms it can be difficult to determine the location of the minimum to full numerical precision. The behavior of the function in the region of the minimum x^* can be approximated by a Taylor expansion,
and the second term of this expansion can be lost when added to the first term at finite precision. This magnifies the error in locating x^*, making it proportional to \sqrt \epsilon (where \epsilon is the relative accuracy of the floating point numbers). For functions with higher order minima, such as x^4, the magnification of the error is correspondingly worse. The best that can be achieved is to converge to the limit of numerical accuracy in the function values, rather than the location of the minimum itself.
const gsl_min_fminimizer_type * T = gsl_min_fminimizer_goldensection; gsl_min_fminimizer * s = gsl_min_fminimizer_alloc (T);
If there is insufficient memory to create the minimizer then the function
returns a null pointer and the error handler is invoked with an error
code of GSL_ENOMEM
.
If the interval given does not contain a minimum, then the function
returns an error code of GSL_FAILURE
.
gsl_min_fminimizer_set
but uses
the values f_minimum, f_lower and f_upper instead of
computing f(minimum)
, f(x_lower)
and f(x_upper)
.
printf("s is a '%s' minimizer\n", gsl_min_fminimizer_name (s));
would print something like s is a 'brent' minimizer
.
You must provide a continuous function of one variable for the
minimizers to operate on. In order to allow for general parameters the
functions are defined by a gsl_function
data type
(see section Providing the function to solve).
The following functions drive the iteration of each algorithm. Each function performs one iteration to update the state of any minimizer of the corresponding type. The same functions work for all minimizers so that different methods can be substituted at runtime without modifications to the code.
GSL_EBADFUNC
Inf
or NaN
.
GSL_FAILURE
The minimizer maintains a current best estimate of the minimum at all times, and the current interval bounding the minimum. This information can be accessed with the following auxiliary functions,
A minimization procedure should stop when one of the following conditions is true:
The handling of these conditions is under user control. The function below allows the user to test the precision of the current result.
GSL_SUCCESS
if the following
condition is achieved,
when the interval x = [a,b] does not include the origin. If the interval includes the origin then \min(|a|,|b|) is replaced by zero (which is the minimum value of |x| over the interval). This ensures that the relative error is accurately estimated for minima close to the origin.
This condition on the interval also implies that any estimate of the minimum x_m in the interval satisfies the same condition with respect to the true minimum x_m^*,
assuming that the true minimum x_m^* is contained within the interval.
The minimization algorithms described in this section require an initial interval which is guaranteed to contain a minimum -- if a and b are the endpoints of the interval and x is an estimate of the minimum then f(a) > f(x) < f(b). This ensures that the function has at least one minimum somewhere in the interval. If a valid initial interval is used then these algorithm cannot fail, provided the function is well-behaved.
The golden section algorithm is the simplest method of bracketing the minimum of a function. It is the slowest algorithm provided by the library, with linear convergence.
On each iteration, the algorithm first compares the subintervals from the endpoints to the current minimum. The larger subinterval is divided in a golden section (using the famous ratio (3-\sqrt 5)/2 = 0.3189660...) and the value of the function at this new point is calculated. The new value is used with the constraint f(a') > f(x') < f(b') to a select new interval containing the minimum, by discarding the least useful point. This procedure can be continued indefinitely until the interval is sufficiently small. Choosing the golden section as the bisection ratio can be shown to provide the fastest convergence for this type of algorithm.
The Brent minimization algorithm combines a parabolic interpolation with the golden section algorithm. This produces a fast algorithm which is still robust.
The outline of the algorithm can be summarized as follows: on each iteration Brent's method approximates the function using an interpolating parabola through three existing points. The minimum of the parabola is taken as a guess for the minimum. If it lies within the bounds of the current interval then the interpolating point is accepted, and used to generate a smaller interval. If the interpolating point is not accepted then the algorithm falls back to an ordinary golden section step. The full details of Brent's method include some additional checks to improve convergence.
The following program uses the Brent algorithm to find the minimum of the function f(x) = \cos(x) + 1, which occurs at x = \pi. The starting interval is (0,6), with an initial guess for the minimum of 2.
#include <stdio.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_math.h> #include <gsl/gsl_min.h> double fn1 (double x, void * params) { return cos(x) + 1.0; } int main (void) { int status; int iter = 0, max_iter = 100; const gsl_min_fminimizer_type *T; gsl_min_fminimizer *s; double m = 2.0, m_expected = M_PI; double a = 0.0, b = 6.0; gsl_function F; F.function = &fn1; F.params = 0; T = gsl_min_fminimizer_brent; s = gsl_min_fminimizer_alloc (T); gsl_min_fminimizer_set (s, &F, m, a, b); printf ("using %s method\n", gsl_min_fminimizer_name (s)); printf ("%5s [%9s, %9s] %9s %10s %9s\n", "iter", "lower", "upper", "min", "err", "err(est)"); printf ("%5d [%.7f, %.7f] %.7f %+.7f %.7f\n", iter, a, b, m, m - m_expected, b - a); do { iter++; status = gsl_min_fminimizer_iterate (s); m = gsl_min_fminimizer_minimum (s); a = gsl_min_fminimizer_x_lower (s); b = gsl_min_fminimizer_x_upper (s); status = gsl_min_test_interval (a, b, 0.001, 0.0); if (status == GSL_SUCCESS) printf ("Converged:\n"); printf ("%5d [%.7f, %.7f] " "%.7f %.7f %+.7f %.7f\n", iter, a, b, m, m_expected, m - m_expected, b - a); } while (status == GSL_CONTINUE && iter < max_iter); return status; }
Here are the results of the minimization procedure.
bash$ ./a.out 0 [0.0000000, 6.0000000] 2.0000000 -1.1415927 6.0000000 1 [2.0000000, 6.0000000] 3.2758640 +0.1342713 4.0000000 2 [2.0000000, 3.2831929] 3.2758640 +0.1342713 1.2831929 3 [2.8689068, 3.2831929] 3.2758640 +0.1342713 0.4142862 4 [2.8689068, 3.2831929] 3.2758640 +0.1342713 0.4142862 5 [2.8689068, 3.2758640] 3.1460585 +0.0044658 0.4069572 6 [3.1346075, 3.2758640] 3.1460585 +0.0044658 0.1412565 7 [3.1346075, 3.1874620] 3.1460585 +0.0044658 0.0528545 8 [3.1346075, 3.1460585] 3.1460585 +0.0044658 0.0114510 9 [3.1346075, 3.1460585] 3.1424060 +0.0008133 0.0114510 10 [3.1346075, 3.1424060] 3.1415885 -0.0000041 0.0077985 Converged: 11 [3.1415885, 3.1424060] 3.1415927 -0.0000000 0.0008175
Further information on Brent's algorithm is available in the following book,
This chapter describes functions for multidimensional root-finding (solving nonlinear systems with n equations in n unknowns). The library provides low level components for a variety of iterative solvers and convergence tests. These can be combined by the user to achieve the desired solution, with full access to the intermediate steps of the iteration. Each class of methods uses the same framework, so that you can switch between solvers at runtime without needing to recompile your program. Each instance of a solver keeps track of its own state, allowing the solvers to be used in multi-threaded programs. The solvers are based on the original Fortran library MINPACK.
The header file `gsl_multiroots.h' contains prototypes for the multidimensional root finding functions and related declarations.
The problem of multidimensional root finding requires the simultaneous solution of n equations, f_i, in n variables, x_i,
In general there are no bracketing methods available for n dimensional systems, and no way of knowing whether any solutions exist. All algorithms proceed from an initial guess using a variant of the Newton iteration,
where x, f are vector quantities and J is the Jacobian matrix @c{$J_{ij} = \partial f_i / \partial x_j$} J_{ij} = d f_i / d x_j. Additional strategies can be used to enlarge the region of convergence. These include requiring a decrease in the norm |f| on each step proposed by Newton's method, or taking steepest-descent steps in the direction of the negative gradient of |f|.
Several root-finding algorithms are available within a single framework. The user provides a high-level driver for the algorithms, and the library provides the individual functions necessary for each of the steps. There are three main phases of the iteration. The steps are,
The evaluation of the Jacobian matrix can be problematic, either because programming the derivatives is intractable or because computation of the n^2 terms of the matrix becomes too expensive. For these reasons the algorithms provided by the library are divided into two classes according to whether the derivatives are available or not.
The state for solvers with an analytic Jacobian matrix is held in a
gsl_multiroot_fdfsolver
struct. The updating procedure requires
both the function and its derivatives to be supplied by the user.
The state for solvers which do not use an analytic Jacobian matrix is
held in a gsl_multiroot_fsolver
struct. The updating procedure
uses only function evaluations (not derivatives). The algorithms
estimate the matrix J or @c{$J^{-1}$}
J^{-1} by approximate methods.
The following functions initialize a multidimensional solver, either with or without derivatives. The solver itself depends only on the dimension of the problem and the algorithm and can be reused for different problems.
const gsl_multiroot_fsolver_type * T = gsl_multiroot_fsolver_hybrid; gsl_multiroot_fsolver * s = gsl_multiroot_fsolver_alloc (T, 3);
If there is insufficient memory to create the solver then the function
returns a null pointer and the error handler is invoked with an error
code of GSL_ENOMEM
.
const gsl_multiroot_fdfsolver_type * T = gsl_multiroot_fdfsolver_newton; gsl_multiroot_fdfsolver * s = gsl_multiroot_fdfsolver_alloc (T, 2);
If there is insufficient memory to create the solver then the function
returns a null pointer and the error handler is invoked with an error
code of GSL_ENOMEM
.
printf("s is a '%s' solver\n", gsl_multiroot_fdfsolver_name (s));
would print something like s is a 'newton' solver
.
You must provide n functions of n variables for the root finders to operate on. In order to allow for general parameters the functions are defined by the following data types:
int (* f) (const gsl_vector * x, void * params, gsl_vector * f)
size_t n
void * params
Here is an example using Powell's test function,
with A = 10^4. The following code defines a
gsl_multiroot_function
system F
which you could pass to a
solver:
struct powell_params { double A; }; int powell (gsl_vector * x, void * p, gsl_vector * f) { struct powell_params * params = *(struct powell_params *)p; double A = (params->A); double x0 = gsl_vector_get(x,0); double x1 = gsl_vector_get(x,1); gsl_vector_set (f, 0, A * x0 * x1 - 1) gsl_vector_set (f, 1, (exp(-x0) + exp(-x1) - (1.0 + 1.0/A))) return GSL_SUCCESS } gsl_multiroot_function F; struct powell_params params = { 10000.0 }; F.function = &powell; F.n = 2; F.params = ¶ms;
int (* f) (const gsl_vector * x, void * params, gsl_vector * f)
int (* df) (const gsl_vector * x, void * params, gsl_matrix * J)
int (* fdf) (const gsl_vector * x, void * params, gsl_vector * f, gsl_matrix * J)
size_t n
void * params
The example of Powell's test function defined above can be extended to include analytic derivatives using the following code,
int powell_df (gsl_vector * x, void * p, gsl_matrix * J) { struct powell_params * params = *(struct powell_params *)p; double A = (params->A); double x0 = gsl_vector_get(x,0); double x1 = gsl_vector_get(x,1); gsl_vector_set (J, 0, 0, A * x1) gsl_vector_set (J, 0, 1, A * x0) gsl_vector_set (J, 1, 0, -exp(-x0)) gsl_vector_set (J, 1, 1, -exp(-x1)) return GSL_SUCCESS } int powell_fdf (gsl_vector * x, void * p, gsl_matrix * f, gsl_matrix * J) { struct powell_params * params = *(struct powell_params *)p; double A = (params->A); double x0 = gsl_vector_get(x,0); double x1 = gsl_vector_get(x,1); double u0 = exp(-x0); double u1 = exp(-x1); gsl_vector_set (f, 0, A * x0 * x1 - 1) gsl_vector_set (f, 1, u0 + u1 - (1 + 1/A)) gsl_vector_set (J, 0, 0, A * x1) gsl_vector_set (J, 0, 1, A * x0) gsl_vector_set (J, 1, 0, -u0) gsl_vector_set (J, 1, 1, -u1) return GSL_SUCCESS } gsl_multiroot_function_fdf FDF; FDF.f = &powell_f; FDF.df = &powell_df; FDF.fdf = &powell_fdf; FDF.n = 2; FDF.params = 0;
Note that the function powell_fdf
is able to reuse existing terms
from the function when calculating the Jacobian, thus saving time.
The following functions drive the iteration of each algorithm. Each function performs one iteration to update the state of any solver of the corresponding type. The same functions work for all solvers so that different methods can be substituted at runtime without modifications to the code.
GSL_EBADFUNC
Inf
or NaN
.
GSL_ENOPROG
The solver maintains a current best estimate of the root at all times. This information can be accessed with the following auxiliary functions,
A root finding procedure should stop when one of the following conditions is true:
The handling of these conditions is under user control. The functions below allow the user to test the precision of the current result in several standard ways.
This function tests for the convergence of the sequence by comparing the
last step dx with the absolute error epsabs and relative
error epsrel to the current position x. The test returns
GSL_SUCCESS
if the following condition is achieved,
for each component of x and returns GSL_CONTINUE
otherwise.
GSL_SUCCESS
if the
following condition is achieved,
and returns GSL_CONTINUE
otherwise. This criterion is suitable
for situations where the the precise location of the root, x, is
unimportant provided a value can be found where the residual is small
enough.
The root finding algorithms described in this section make use of both the function and its derivative. They require an initial guess for the location of the root, but there is no absolute guarantee of convergence -- the function must be suitable for this technique and the initial guess must be sufficiently close to the root for it to work. When the conditions are satisfied then convergence is quadratic.
The algorithm uses a generalized trust region to keep each step under control. In order to be accepted a proposed new position x' must satisfy the condition |D (x' - x)| < \delta, where D is a diagonal scaling matrix and \delta is the size of the trust region. The components of D are computed internally, using the column norms of the Jacobian to estimate the sensitivity of the residual to each component of x. This improves the behavior of the algorithm for badly scaled functions.
On each iteration the algorithm first determines the standard Newton step by solving the system J dx = - f. If this step falls inside the trust region it is used as a trial step in the next stage. If not, the algorithm uses the linear combination of the Newton and gradient directions which is predicted to minimize the norm of the function while staying inside the trust region.
This combination of Newton and gradient directions is referred to as a dogleg step.
The proposed step is now tested by evaluating the function at the resulting point, x'. If the step reduces the norm of the function sufficiently then it is accepted and size of the trust region is increased. If the proposed step fails to improve the solution then the size of the trust region is decreased and another trial step is computed.
The speed of the algorithm is increased by computing the changes to the Jacobian approximately, using a rank-1 update. If two successive attempts fail to reduce the residual then the full Jacobian is recomputed. The algorithm also monitors the progress of the solution and returns an error if several steps fail to make any improvement,
GSL_ENOPROG
GSL_ENOPROGJ
hybridsj
. The steps are
controlled by a spherical trust region |x' - x| < \delta, instead
of a generalized region. This can be useful if the generalized region
estimated by hybridsj
is inappropriate.
Newton's Method is the standard root-polishing algorithm. The algorithm begins with an initial guess for the location of the solution. On each iteration a linear approximation to the function F is used to estimate the step which will zero all the components of the residual. The iteration is defined by the following sequence,
where the Jacobian matrix J is computed from the derivative functions provided by f. The step dx is obtained by solving the linear system,
using LU decomposition.
is proposed, with r being the ratio of norms |f(x')|^2/|f(x)|^2. This procedure is repeated until a suitable step size is found.
The algorithms described in this section do not require any derivative information to be supplied by the user. Any derivatives needed are approximated from by finite difference.
gsl_multiroots_fdjac
with a relative step size of GSL_SQRT_DBL_EPSILON
.
The discrete Newton algorithm is the simplest method of solving a multidimensional system. It uses the Newton iteration
where the Jacobian matrix J is approximated by taking finite differences of the function f. The approximation scheme used by this implementation is,
where \delta_j is a step of size \sqrt\epsilon |x_j| with \epsilon being the machine precision (@c{$\epsilon \approx 2.22 \times 10^{-16}$} \epsilon \approx 2.22 \times 10^-16). The order of convergence of Newton's algorithm is quadratic, but the finite differences require n^2 function evaluations on each iteration. The algorithm may become unstable if the finite differences are not a good approximation to the true derivatives.
The Broyden algorithm is a version of the discrete Newton algorithm which attempts to avoids the expensive update of the Jacobian matrix on each iteration. The changes to the Jacobian are also approximated, using a rank-1 update,
where the vectors dx and df are the changes in x and f. On the first iteration the inverse Jacobian is estimated using finite differences, as in the discrete Newton algorithm. This approximation gives a fast update but is unreliable if the changes are not small, and the estimate of the inverse Jacobian becomes worse as time passes. The algorithm has a tendency to become unstable unless it starts close to the root. The Jacobian is refreshed if this instability is detected (consult the source for details).
This algorithm is not recommended and is included only for demonstration purposes.
The multidimensional solvers are used in a similar way to the
one-dimensional root finding algorithms. This first example
demonstrates the hybrids
scaled-hybrid algorithm, which does not
require derivatives. The program solves the Rosenbrock system of equations,
with a = 1, b = 10. The solution of this system lies at (x,y) = (1,1) in a narrow valley.
The first stage of the program is to define the system of equations,
#include <stdlib.h> #include <stdio.h> #include <gsl/gsl_vector.h> #include <gsl/gsl_multiroots.h> struct rparams { double a; double b; }; int rosenbrock_f (const gsl_vector * x, void *params, gsl_vector * f) { double a = ((struct rparams *) params)->a; double b = ((struct rparams *) params)->b; double x0 = gsl_vector_get (x, 0); double x1 = gsl_vector_get (x, 1); double y0 = a * (1 - x0); double y1 = b * (x1 - x0 * x0); gsl_vector_set (f, 0, y0); gsl_vector_set (f, 1, y1); return GSL_SUCCESS; }
The main program begins by creating the function object f
, with
the arguments (x,y)
and parameters (a,b)
. The solver
s
is initialized to use this function, with the hybrids
method.
int main (void) { const gsl_multiroot_fsolver_type *T; gsl_multiroot_fsolver *s; int status; size_t i, iter = 0; const size_t n = 2; struct rparams p = {1.0, 10.0}; gsl_multiroot_function f = {&rosenbrock_f, n, &p}; double x_init[2] = {-10.0, -5.0}; gsl_vector *x = gsl_vector_alloc (n); gsl_vector_set (x, 0, x_init[0]); gsl_vector_set (x, 1, x_init[1]); T = gsl_multiroot_fsolver_hybrids; s = gsl_multiroot_fsolver_alloc (T, 2); gsl_multiroot_fsolver_set (s, &f, x); print_state (iter, s); do { iter++; status = gsl_multiroot_fsolver_iterate (s); print_state (iter, s); if (status) /* check if solver is stuck */ break; status = gsl_multiroot_test_residual (s->f, 1e-7); } while (status == GSL_CONTINUE && iter < 1000); printf ("status = %s\n", gsl_strerror (status)); gsl_multiroot_fsolver_free (s); gsl_vector_free (x); return 0; }
Note that it is important to check the return status of each solver step, in case the algorithm becomes stuck. If an error condition is detected, indicating that the algorithm cannot proceed, then the error can be reported to the user, a new starting point chosen or a different algorithm used.
The intermediate state of the solution is displayed by the following
function. The solver state contains the vector s->x
which is the
current position, and the vector s->f
with corresponding function
values.
int print_state (size_t iter, gsl_multiroot_fsolver * s) { printf ("iter = %3u x = % .3f % .3f " "f(x) = % .3e % .3e\n", iter, gsl_vector_get (s->x, 0), gsl_vector_get (s->x, 1), gsl_vector_get (s->f, 0), gsl_vector_get (s->f, 1)); }
Here are the results of running the program. The algorithm is started at (-10,-5) far from the solution. Since the solution is hidden in a narrow valley the earliest steps follow the gradient of the function downhill, in an attempt to reduce the large value of the residual. Once the root has been approximately located, on iteration 8, the Newton behavior takes over and convergence is very rapid.
iter = 0 x = -10.000 -5.000 f(x) = 1.100e+01 -1.050e+03 iter = 1 x = -10.000 -5.000 f(x) = 1.100e+01 -1.050e+03 iter = 2 x = -3.976 24.827 f(x) = 4.976e+00 9.020e+01 iter = 3 x = -3.976 24.827 f(x) = 4.976e+00 9.020e+01 iter = 4 x = -3.976 24.827 f(x) = 4.976e+00 9.020e+01 iter = 5 x = -1.274 -5.680 f(x) = 2.274e+00 -7.302e+01 iter = 6 x = -1.274 -5.680 f(x) = 2.274e+00 -7.302e+01 iter = 7 x = 0.249 0.298 f(x) = 7.511e-01 2.359e+00 iter = 8 x = 0.249 0.298 f(x) = 7.511e-01 2.359e+00 iter = 9 x = 1.000 0.878 f(x) = 1.268e-10 -1.218e+00 iter = 10 x = 1.000 0.989 f(x) = 1.124e-11 -1.080e-01 iter = 11 x = 1.000 1.000 f(x) = 0.000e+00 0.000e+00 status = success
Note that the algorithm does not update the location on every iteration. Some iterations are used to adjust the trust-region parameter, after trying a step which was found to be divergent, or to recompute the Jacobian, when poor convergence behavior is detected.
The next example program adds derivative information, in order to
accelerate the solution. There are two derivative functions
rosenbrock_df
and rosenbrock_fdf
. The latter computes both
the function and its derivative simultaneously. This allows the
optimization of any common terms. For simplicity we substitute calls to
the separate f
and df
functions at this point in the code
below.
int rosenbrock_df (const gsl_vector * x, void *params, gsl_matrix * J) { double a = ((struct rparams *) params)->a; double b = ((struct rparams *) params)->b; double x0 = gsl_vector_get (x, 0); double df00 = -a; double df01 = 0; double df10 = -2 * b * x0; double df11 = b; gsl_matrix_set (J, 0, 0, df00); gsl_matrix_set (J, 0, 1, df01); gsl_matrix_set (J, 1, 0, df10); gsl_matrix_set (J, 1, 1, df11); return GSL_SUCCESS; } int rosenbrock_fdf (const gsl_vector * x, void *params, gsl_vector * f, gsl_matrix * J) { rosenbrock_f (x, params, f); rosenbrock_df (x, params, J); return GSL_SUCCESS; }
The main program now makes calls to the corresponding fdfsolver
versions of the functions,
int main (void) { const gsl_multiroot_fdfsolver_type *T; gsl_multiroot_fdfsolver *s; int status; size_t i, iter = 0; const size_t n = 2; struct rparams p = {1.0, 10.0}; gsl_multiroot_function_fdf f = {&rosenbrock_f, &rosenbrock_df, &rosenbrock_fdf, n, &p}; double x_init[2] = {-10.0, -5.0}; gsl_vector *x = gsl_vector_alloc (n); gsl_vector_set (x, 0, x_init[0]); gsl_vector_set (x, 1, x_init[1]); T = gsl_multiroot_fdfsolver_gnewton; s = gsl_multiroot_fdfsolver_alloc (T, n); gsl_multiroot_fdfsolver_set (s, &f, x); print_state (iter, s); do { iter++; status = gsl_multiroot_fdfsolver_iterate (s); print_state (iter, s); if (status) break; status = gsl_multiroot_test_residual (s->f, 1e-7); } while (status == GSL_CONTINUE && iter < 1000); printf ("status = %s\n", gsl_strerror (status)); gsl_multiroot_fdfsolver_free (s); gsl_vector_free (x); return 0; }
The addition of derivative information to the hybrids
solver does
not make any significant difference to its behavior, since it able to
approximate the Jacobian numerically with sufficient accuracy. To
illustrate the behavior of a different derivative solver we switch to
gnewton
. This is a traditional newton solver with the constraint
that it scales back its step if the full step would lead "uphill". Here
is the output for the gnewton
algorithm,
iter = 0 x = -10.000 -5.000 f(x) = 1.100e+01 -1.050e+03 iter = 1 x = -4.231 -65.317 f(x) = 5.231e+00 -8.321e+02 iter = 2 x = 1.000 -26.358 f(x) = -8.882e-16 -2.736e+02 iter = 3 x = 1.000 1.000 f(x) = -2.220e-16 -4.441e-15 status = success
The convergence is much more rapid, but takes a wide excursion out to the point (-4.23,-65.3). This could cause the algorithm to go astray in a realistic application. The hybrid algorithm follows the downhill path to the solution more reliably.
The original version of the Hybrid method is described in the following articles by Powell,
The following papers are also relevant to the algorithms described in this section,
This chapter describes routines for finding minima of arbitrary multidimensional functions. The library provides low level components for a variety of iterative minimizers and convergence tests. These can be combined by the user to achieve the desired solution, while providing full access to the intermediate steps of the algorithms. Each class of methods uses the same framework, so that you can switch between minimizers at runtime without needing to recompile your program. Each instance of a minimizer keeps track of its own state, allowing the minimizers to be used in multi-threaded programs. The minimization algorithms can be used to maximize a function by inverting its sign.
The header file `gsl_multimin.h' contains prototypes for the minimization functions and related declarations.
The problem of multidimensional minimization requires finding a point x such that the scalar function,
takes a value which is lower than at any neighboring point. For smooth functions the gradient g = \nabla f vanishes at the minimum. In general there are no bracketing methods available for the minimization of n-dimensional functions. All algorithms proceed from an initial guess using a search algorithm which attempts to move in a downhill direction. A one-dimensional line minimisation is performed along this direction until the lowest point is found to a suitable tolerance. The search direction is then updated with local information from the function and its derivatives, and the whole process repeated until the true n-dimensional minimum is found.
Several minimization algorithms are available within a single framework. The user provides a high-level driver for the algorithms, and the library provides the individual functions necessary for each of the steps. There are three main phases of the iteration. The steps are,
Each iteration step consists either of an improvement to the
line-mimisation in the current direction or an update to the search
direction itself. The state for the minimizers is held in a
gsl_multimin_fdfminimizer
struct.
Note that the minimization algorithms can only search for one local minimum at a time. When there are several local minima in the search area, the first minimum to be found will be returned; however it is difficult to predict which of the minima this will be. In most cases, no error will be reported if you try to find a local minimum in an area where there is more than one.
It is also important to note that the minimization algorithms find local minima; there is no way to determine whether a minimum is a global minimum of the function in question.
The following function initializes a multidimensional minimizer. The minimizer itself depends only on the dimension of the problem and the algorithm and can be reused for different problems.
GSL_ENOMEM
.
printf("s is a '%s' minimizer\n", gsl_multimin_fdfminimizer_name (s));
would print something like s is a 'conjugate_pr' minimizer
.
You must provide a parametric function of n variables for the minimizers to operate on. You also need to provide a routine which calculates the gradient of the function and a third routine which calculates both the function value and the gradient together. In order to allow for general parameters the functions are defined by the following data type:
double (* f) (const gsl_vector * x, void * params)
int (* df) (const gsl_vector * x, void * params, gsl_vector * g)
int (* fdf) (const gsl_vector * x, void * params, double * f, gsl_vector * g)
size_t n
void * params
The following example function defines a simple paraboloid with two parameters,
/* Paraboloid centered on (dp[0],dp[1]) */ double my_f (const gsl_vector *v, void *params) { double x, y; double *dp = (double *)params; x = gsl_vector_get(v, 0); y = gsl_vector_get(v, 1); return 10.0 * (x - dp[0]) * (x - dp[0]) + 20.0 * (y - dp[1]) * (y - dp[1]) + 30.0; } /* The gradient of f, df = (df/dx, df/dy). */ void my_df (const gsl_vector *v, void *params, gsl_vector *df) { double x, y; double *dp = (double *)params; x = gsl_vector_get(v, 0); y = gsl_vector_get(v, 1); gsl_vector_set(df, 0, 20.0 * (x - dp[0])); gsl_vector_set(df, 1, 40.0 * (y - dp[1])); } /* Compute both f and df together. */ void my_fdf (const gsl_vector *x, void *params, double *f, gsl_vector *df) { *f = my_f(x, params); my_df(x, params, df); }
The function can be initialized using the following code,
gsl_multimin_function_fdf my_func; double p[2] = { 1.0, 2.0 }; /* center at (1,2) */ my_func.f = &my_f; my_func.df = &my_df; my_func.fdf = &my_fdf; my_func.n = 2; my_func.params = (void *)p;
The following function drives the iteration of each algorithm. The function performs one iteration to update the state of the minimizer. The same function works for all minimizers so that different methods can be substituted at runtime without modifications to the code.
A minimization procedure should stop when one of the following conditions is true:
The handling of these conditions is under user control. The functions below allow the user to test the precision of the current result.
GSL_SUCCESS
if the following condition is achieved,
and returns GSL_CONTINUE
otherwise. A suitable choice of
epsabs can be made from the desired accuracy in the function for
small variations in x. The relationship between these quantities
is given by @c{$\delta f = g\,\delta x$}
\delta f = g \delta x.
There are several minimization methods available. The best choice of algorithm depends on the problem. Each of the algorithms uses the value of the function and its gradient at each evaluation point.
This example program finds the minimum of the paraboloid function defined earlier. The location of the minimum is offset from the origin in x and y, and the function value at the minimum is non-zero. The main program is given below, it requires the example function given earlier in this chapter.
int main (void) { size_t iter = 0; int status; const gsl_multimin_fdfminimizer_type *T; gsl_multimin_fdfminimizer *s; /* Position of the minimum (1,2). */ double par[2] = { 1.0, 2.0 }; gsl_vector *x; gsl_multimin_function_fdf my_func; my_func.f = &my_f; my_func.df = &my_df; my_func.fdf = &my_fdf; my_func.n = 2; my_func.params = ∥ /* Starting point, x = (5,7) */ x = gsl_vector_alloc (2); gsl_vector_set (x, 0, 5.0); gsl_vector_set (x, 1, 7.0); T = gsl_multimin_fdfminimizer_conjugate_fr; s = gsl_multimin_fdfminimizer_alloc (T, 2); gsl_multimin_fdfminimizer_set (s, &my_func, x, 0.01, 1e-4); do { iter++; status = gsl_multimin_fdfminimizer_iterate (s); if (status) break; status = gsl_multimin_test_gradient (s->gradient, 1e-3); if (status == GSL_SUCCESS) printf ("Minimum found at:\n"); printf ("%5d %.5f %.5f %10.5f\n", iter, gsl_vector_get (s->x, 0), gsl_vector_get (s->x, 1), s->f); } while (status == GSL_CONTINUE && iter < 100); gsl_multimin_fdfminimizer_free (s); gsl_vector_free (x); return 0; }
The initial step-size is chosen as 0.01, a conservative estimate in this case, and the line minimization parameter is set at 0.0001. The program terminates when the norm of the gradient has been reduced below 0.001. The output of the program is shown below,
x y f 1 4.99629 6.99072 687.84780 2 4.98886 6.97215 683.55456 3 4.97400 6.93501 675.01278 4 4.94429 6.86073 658.10798 5 4.88487 6.71217 625.01340 6 4.76602 6.41506 561.68440 7 4.52833 5.82083 446.46694 8 4.05295 4.63238 261.79422 9 3.10219 2.25548 75.49762 10 2.85185 1.62963 67.03704 11 2.19088 1.76182 45.31640 12 0.86892 2.02622 30.18555 Minimum found at: 13 1.00000 2.00000 30.00000
Note that the algorithm gradually increases the step size as it successfully moves downhill, as can be seen by plotting the successive points.
The conjugate gradient algorithm finds the minimum on its second direction because the function is purely quadratic. Additional iterations would be needed for a more complicated function.
A brief description of multidimensional minimization algorithms and further references can be found in the following book,
This chapter describes routines for performing least squares fits to experimental data using linear combinations of functions. The data may be weighted or unweighted. For weighted data the functions compute the best fit parameters and their associated covariance matrix. For unweighted data the covariance matrix is estimated from the scatter of the points, giving a variance-covariance matrix. The functions are divided into separate versions for simple one- or two-parameter regression and multiple-parameter fits. The functions are declared in the header file `gsl_fit.h'
The functions described in this section can be used to perform least-squares fits to a straight line model, Y = c_0 + c_1 X. For weighted data the best-fit is found by minimizing the weighted sum of squared residuals, \chi^2,
for the parameters c_0, c_1. For unweighted data the sum is computed with w_i = 1.
The covariance matrix for the parameters (c0, c1) is estimated from weighted data and returned via the parameters (cov00, cov01, cov11). The weighted sum of squares of the residuals from the best-fit line, \chi^2, is returned in chisq.
The functions described in this section can be used to perform least-squares fits to a straight line model without a constant term, Y = c_1 X. For weighted data the best-fit is found by minimizing the weighted sum of squared residuals, \chi^2,
for the parameter c_1. For unweighted data the sum is computed with w_i = 1.
The variance of the parameter c1 is estimated from the weighted data and returned via the parameters cov11. The weighted sum of squares of the residuals from the best-fit line, \chi^2, is returned in chisq.
The functions described in this section perform least-squares fits to a general linear model, y = X c where y is a vector of n observations, X is an n by p matrix of predictor variables, and c are the p unknown best-fit parameters, which are to be estimated.
The best-fit is found by minimizing the weighted sums of squared residuals, \chi^2,
with respect to the parameters c. The weights are specified by the diagonal elements of the n by n matrix W. For unweighted data W is replaced by the identity matrix.
This formulation can be used for fits to any number of functions and/or variables by preparing the n-by-p matrix X appropriately. For example, to fit to a p-th order polynomial in x, use the following matrix,
where the index i runs over the observations and the index j runs from 0 to p-1.
To fit to a set of p sinusoidal functions with fixed frequencies \omega_1, \omega_2, ..., \omega_p, use,
To fit to p independent variables x_1, x_2, ..., x_p, use,
where x_j(i) is the i-th value of the predictor variable x_j.
The functions described in this section are declared in the header file `gsl_multifit.h'.
The solution of the general linear least-squares system requires an additional working space for intermediate results, such as the singular value decomposition of the matrix X.
The best-fit is found by singular value decomposition of the matrix X using the preallocated workspace provided in work. The modified Golub-Reinsch SVD algorithm is used, with column scaling to improve the accuracy of the singular values. Any components which have zero singular value (to machine precision) are discarded from the fit.
This function computes the best-fit parameters c of the model y = X c for the observations y and the matrix of predictor variables X. The covariance matrix of the model parameters cov is estimated from the weighted data. The weighted sum of squares of the residuals from the best-fit, \chi^2, is returned in chisq.
The best-fit is found by singular value decomposition of the matrix X using the preallocated workspace provided in work. Any components which have zero singular value (to machine precision) are discarded from the fit.
The following program computes a least squares straight-line fit to a simple (fictitious) dataset, and outputs the best-fit line and its associated one standard-deviation error bars.
#include <stdio.h> #include <gsl/gsl_fit.h> int main (void) { int i, n = 4; double x[4] = { 1970, 1980, 1990, 2000 }; double y[4] = { 12, 11, 14, 13 }; double w[4] = { 0.1, 0.2, 0.3, 0.4 }; double c0, c1, cov00, cov01, cov11, chisq; gsl_fit_wlinear (x, 1, w, 1, y, 1, n, &c0, &c1, &cov00, &cov01, &cov11, &chisq); printf("# best fit: Y = %g + %g X\n", c0, c1); printf("# covariance matrix:\n"); printf("# [ %g, %g\n# %g, %g]\n", cov00, cov01, cov01, cov11); printf("# chisq = %g\n", chisq); for (i = 0; i < n; i++) printf("data: %g %g %g\n", x[i], y[i], 1/sqrt(w[i])); printf("\n"); for (i = -30; i < 130; i++) { double xf = x[0] + (i/100.0) * (x[n-1] - x[0]); double yf, yf_err; gsl_fit_linear_est (xf, c0, c1, cov00, cov01, cov11, &yf, &yf_err); printf("fit: %g %g\n", xf, yf); printf("hi : %g %g\n", xf, yf + yf_err); printf("lo : %g %g\n", xf, yf - yf_err); } return 0; }
The following commands extract the data from the output of the program
and display it using the GNU plotutils graph
utility,
$ ./demo > tmp $ more tmp # best fit: Y = -106.6 + 0.06 X # covariance matrix: # [ 39602, -19.9 # -19.9, 0.01] # chisq = 0.8 $ for n in data fit hi lo ; do grep "^$n" tmp | cut -d: -f2 > $n ; done $ graph -T X -X x -Y y -y 0 20 -m 0 -S 2 -Ie data -S 0 -I a -m 1 fit -m 2 hi -m 2 lo
The next program performs a quadratic fit y = c_0 + c_1 x + c_2
x^2 to a weighted dataset using the generalised linear fitting function
gsl_multifit_wlinear
. The model matrix X for a quadratic
fit is given by,
where the column of ones corresponds to the constant term c_0. The two remaining columns corresponds to the terms c_1 x and and c_2 x^2.
The program reads n lines of data in the format (x, y, err) where err is the error (standard deviation) in the value y.
#include <stdio.h> #include <gsl/gsl_multifit.h> int main (int argc, char **argv) { int i, n; double xi, yi, ei, chisq; gsl_matrix *X, *cov; gsl_vector *y, *w, *c; if (argc != 2) { fprintf(stderr,"usage: fit n < data\n"); exit (-1); } n = atoi(argv[1]); X = gsl_matrix_alloc (n, 3); y = gsl_vector_alloc (n); w = gsl_vector_alloc (n); c = gsl_vector_alloc (3); cov = gsl_matrix_alloc (3, 3); for (i = 0; i < n; i++) { int count = fscanf(stdin, "%lg %lg %lg", &xi, &yi, &ei); if (count != 3) { fprintf(stderr, "error reading file\n"); exit(-1); } printf("%g %g +/- %g\n", xi, yi, ei); gsl_matrix_set (X, i, 0, 1.0); gsl_matrix_set (X, i, 1, xi); gsl_matrix_set (X, i, 2, xi*xi); gsl_vector_set (y, i, yi); gsl_vector_set (w, i, 1.0/(ei*ei)); } { gsl_multifit_linear_workspace * work = gsl_multifit_linear_alloc (n, 3); gsl_multifit_wlinear (X, w, y, c, cov, &chisq, work); gsl_multifit_linear_free (work); } #define C(i) (gsl_vector_get(c,(i))) #define COV(i,j) (gsl_matrix_get(cov,(i),(j))) { printf("# best fit: Y = %g + %g X + %g X^2\n", C(0), C(1), C(2)); printf("# covariance matrix:\n"); printf("[ %+.5e, %+.5e, %+.5e \n", COV(0,0), COV(0,1), COV(0,2)); printf(" %+.5e, %+.5e, %+.5e \n", COV(1,0), COV(1,1), COV(1,2)); printf(" %+.5e, %+.5e, %+.5e ]\n", COV(2,0), COV(2,1), COV(2,2)); printf("# chisq = %g\n", chisq); } return 0; }
A suitable set of data for fitting can be generated using the following program. It outputs a set of points with gaussian errors from the curve y = e^x in the region 0 < x < 2.
#include <stdio.h> #include <math.h> #include <gsl/gsl_randist.h> int main (void) { double x; const gsl_rng_type * T; gsl_rng * r; gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc(T); for (x = 0.1; x < 2; x+= 0.1) { double y0 = exp(x); double sigma = 0.1*y0; double dy = gsl_ran_gaussian(r, sigma) printf("%g %g %g\n", x, y0 + dy, sigma); } return 0; }
The data can be prepared by running the resulting executable program,
$ ./generate > exp.dat $ more exp.dat 0.1 0.97935 0.110517 0.2 1.3359 0.12214 0.3 1.52573 0.134986 0.4 1.60318 0.149182 0.5 1.81731 0.164872 0.6 1.92475 0.182212 ....
To fit the data use the previous program, with the number of data points given as the first argument. In this case there are 19 data points.
$ ./fit 19 < exp.dat 0.1 0.97935 +/- 0.110517 0.2 1.3359 +/- 0.12214 ... # best fit: Y = 1.02318 + 0.956201 X + 0.876796 X^2 # covariance matrix: [ +1.25612e-02, -3.64387e-02, +1.94389e-02 -3.64387e-02, +1.42339e-01, -8.48761e-02 +1.94389e-02, -8.48761e-02, +5.60243e-02 ] # chisq = 23.0987
The parameters of the quadratic fit match the coefficients of the expansion of e^x, taking into account the errors on the parameters and the O(x^3) difference between the exponential and quadratic functions for the larger values of x. The errors on the parameters are given by the square-root of the corresponding diagonal elements of the covariance matrix. The chi-squared per degree of freedom is 1.4, indicating a reasonable fit to the data.
A summary of formulas and techniques for least squares fitting can be found in the "Statistics" chapter of the Annual Review of Particle Physics prepared by the Particle Data Group.
The Review of Particle Physics is available online at the website given above.
The tests used to prepare these routines are based on the NIST Statistical Reference Datasets. The datasets and their documentation are available from NIST at the following website,
http://www.nist.gov/itl/div898/strd/index.html.
This chapter describes functions for multidimensional nonlinear least-squares fitting. The library provides low level components for a variety of iterative solvers and convergence tests. These can be combined by the user to achieve the desired solution, with full access to the intermediate steps of the iteration. Each class of methods uses the same framework, so that you can switch between solvers at runtime without needing to recompile your program. Each instance of a solver keeps track of its own state, allowing the solvers to be used in multi-threaded programs.
The header file `gsl_multifit_nlin.h' contains prototypes for the multidimensional nonlinear fitting functions and related declarations.
The problem of multidimensional nonlinear least-squares fitting requires the minimization of the squared residuals of n functions, f_i, in p parameters, x_i,
All algorithms proceed from an initial guess using the linearization,
where x is the initial point, p is the proposed step and J is the Jacobian matrix @c{$J_{ij} = \partial f_i / \partial x_j$} J_{ij} = d f_i / d x_j. Additional strategies are used to enlarge the region of convergence. These include requiring a decrease in the norm ||F|| on each step or using a trust region to avoid steps which fall outside the linear regime.
If there is insufficient memory to create the solver then the function
returns a null pointer and the error handler is invoked with an error
code of GSL_ENOMEM
.
const gsl_multifit_fdfsolver_type * T = gsl_multifit_fdfsolver_lmder; gsl_multifit_fdfsolver * s = gsl_multifit_fdfsolver_alloc (T, 100, 3);
If there is insufficient memory to create the solver then the function
returns a null pointer and the error handler is invoked with an error
code of GSL_ENOMEM
.
printf("s is a '%s' solver\n", gsl_multifit_fdfsolver_name (s));
would print something like s is a 'lmder' solver
.
You must provide n functions of p variables for the minimization algorithms to operate on. In order to allow for general parameters the functions are defined by the following data types:
int (* f) (const gsl_vector * x, void * params, gsl_vector * f)
size_t n
size_t p
void * params
int (* f) (const gsl_vector * x, void * params, gsl_vector * f)
int (* df) (const gsl_vector * x, void * params, gsl_matrix * J)
int (* fdf) (const gsl_vector * x, void * params, gsl_vector * f, gsl_matrix * J)
size_t n
size_t p
void * params
The following functions drive the iteration of each algorithm. Each function performs one iteration to update the state of any solver of the corresponding type. The same functions work for all solvers so that different methods can be substituted at runtime without modifications to the code.
A minimization procedure should stop when one of the following conditions is true:
The handling of these conditions is under user control. The functions below allow the user to test the current estimate of the best-fit parameters in several standard ways.
This function tests for the convergence of the sequence by comparing the
last step dx with the absolute error epsabs and relative
error epsrel to the current position x. The test returns
GSL_SUCCESS
if the following condition is achieved,
for each component of x and returns GSL_CONTINUE
otherwise.
GSL_SUCCESS
if the
following condition is achieved,
and returns GSL_CONTINUE
otherwise. This criterion is suitable
for situations where the the precise location of the minimum, x,
is unimportant provided a value can be found where the gradient is small
enough.
The minimization algorithms described in this section make use of both the function and its derivative. They require an initial guess for the location of the minimum. There is no absolute guarantee of convergence -- the function must be suitable for this technique and the initial guess must be sufficiently close to the minimum for it to work.
The algorithm uses a generalized trust region to keep each step under control. In order to be accepted a proposed new position x' must satisfy the condition |D (x' - x)| < \delta, where D is a diagonal scaling matrix and \delta is the size of the trust region. The components of D are computed internally, using the column norms of the Jacobian to estimate the sensitivity of the residual to each component of x. This improves the behavior of the algorithm for badly scaled functions.
On each iteration the algorithm attempts to minimize the linear system |F + J p| subject to the constraint |D p| < \Delta. The solution to this constrained linear system is found using the Levenberg-Marquardt method.
The proposed step is now tested by evaluating the function at the resulting point, x'. If the step reduces the norm of the function sufficiently, and follows the predicted behavior of the function within the trust region. then it is accepted and size of the trust region is increased. If the proposed step fails to improve the solution, or differs significantly from the expected behavior within the trust region, then the size of the trust region is decreased and another trial step is computed.
The algorithm also monitors the progress of the solution and returns an error if the changes in the solution are smaller than the machine precision. The possible error codes are,
GSL_ETOLF
GSL_ETOLX
GSL_ETOLG
These error codes indicate that further iterations will be unlikely to change the solution from its current value.
There are no algorithms implemented in this section at the moment.
The covariance matrix is given by,
and is computed by QR decomposition of J with column-pivoting. Any columns of R which satisfy
are considered linearly-dependent and are excluded from the covariance matrix (the corresponding rows and columns of the covariance matrix are set to zero).
The following example program fits a weighted exponential model with
background to experimental data, Y = A \exp(-\lambda t) + b. The
first part of the program sets up the functions expb_f
and
expb_df
to calculate the model and its Jacobian. The appropriate
fitting function is given by,
where we have chosen t_i = i. The Jacobian matrix J is the derivative of these functions with respect to the three parameters (A, \lambda, b). It is given by,
where x_0 = A, x_1 = \lambda and x_2 = b.
#include <stdlib.h> #include <stdio.h> #include <gsl/gsl_rng.h> #include <gsl/gsl_randist.h> #include <gsl/gsl_vector.h> #include <gsl/gsl_blas.h> #include <gsl/gsl_multifit_nlin.h> struct data { size_t n; double * y; double * sigma; }; int expb_f (const gsl_vector * x, void *params, gsl_vector * f) { size_t n = ((struct data *)params)->n; double *y = ((struct data *)params)->y; double *sigma = ((struct data *) params)->sigma; double A = gsl_vector_get (x, 0); double lambda = gsl_vector_get (x, 1); double b = gsl_vector_get (x, 2); size_t i; for (i = 0; i < n; i++) { /* Model Yi = A * exp(-lambda * i) + b */ double t = i; double Yi = A * exp (-lambda * t) + b; gsl_vector_set (f, i, (Yi - y[i])/sigma[i]); } return GSL_SUCCESS; } int expb_df (const gsl_vector * x, void *params, gsl_matrix * J) { size_t n = ((struct data *)params)->n; double *sigma = ((struct data *) params)->sigma; double A = gsl_vector_get (x, 0); double lambda = gsl_vector_get (x, 1); size_t i; for (i = 0; i < n; i++) { /* Jacobian matrix J(i,j) = dfi / dxj, */ /* where fi = (Yi - yi)/sigma[i], */ /* Yi = A * exp(-lambda * i) + b */ /* and the xj are the parameters (A,lambda,b) */ double t = i; double s = sigma[i]; double e = exp(-lambda * t); gsl_matrix_set (J, i, 0, e/s); gsl_matrix_set (J, i, 1, -t * A * e/s); gsl_matrix_set (J, i, 2, 1/s); } return GSL_SUCCESS; } int expb_fdf (const gsl_vector * x, void *params, gsl_vector * f, gsl_matrix * J) { expb_f (x, params, f); expb_df (x, params, J); return GSL_SUCCESS; }
The main part of the program sets up a Levenberg-Marquardt solver and some simulated random data. The data uses the known parameters (1.0,5.0,0.1) combined with gaussian noise (standard deviation = 0.1) over a range of 40 timesteps. The initial guess for the parameters is chosen as (0.0, 1.0, 0.0).
int main (void) { const gsl_multifit_fdfsolver_type *T; gsl_multifit_fdfsolver *s; int status; size_t i, iter = 0; const size_t n = 40; const size_t p = 3; gsl_matrix *covar = gsl_matrix_alloc (p, p); double y[n], sigma[n]; struct data d = { n, y, sigma}; gsl_multifit_function_fdf f; double x_init[3] = { 1.0, 0.0, 0.0 }; gsl_vector_view x = gsl_vector_view_array (x_init, p); const gsl_rng_type * type; gsl_rng * r; gsl_rng_env_setup(); type = gsl_rng_default; r = gsl_rng_alloc (type); f.f = &expb_f; f.df = &expb_df; f.fdf = &expb_fdf; f.n = n; f.p = p; f.params = &d; /* This is the data to be fitted */ for (i = 0; i < n; i++) { double t = i; y[i] = 1.0 + 5 * exp (-0.1 * t) + gsl_ran_gaussian(r, 0.1); sigma[i] = 0.1; printf("data: %d %g %g\n", i, y[i], sigma[i]); }; T = gsl_multifit_fdfsolver_lmsder; s = gsl_multifit_fdfsolver_alloc (T, n, p); gsl_multifit_fdfsolver_set (s, &f, &x.vector); print_state (iter, s); do { iter++; status = gsl_multifit_fdfsolver_iterate (s); printf ("status = %s\n", gsl_strerror (status)); print_state (iter, s); if (status) break; status = gsl_multifit_test_delta (s->dx, s->x, 1e-4, 1e-4); } while (status == GSL_CONTINUE && iter < 500); gsl_multifit_covar (s->J, 0.0, covar); gsl_matrix_fprintf (stdout, covar, "%g"); #define FIT(i) gsl_vector_get(s->x, i) #define ERR(i) sqrt(gsl_matrix_get(covar,i,i)) printf("A = %.5f +/- %.5f\n", FIT(0), ERR(0)); printf("lambda = %.5f +/- %.5f\n", FIT(1), ERR(1)); printf("b = %.5f +/- %.5f\n", FIT(2), ERR(2)); printf ("status = %s\n", gsl_strerror (status)); gsl_multifit_fdfsolver_free (s); return 0; } int print_state (size_t iter, gsl_multifit_fdfsolver * s) { printf ("iter: %3u x = % 15.8f % 15.8f % 15.8f " "|f(x)| = %g\n", iter, gsl_vector_get (s->x, 0), gsl_vector_get (s->x, 1), gsl_vector_get (s->x, 2), gsl_blas_dnrm2 (s->f)); }
The iteration terminates when the change in x is smaller than 0.0001, as both an absolute and relative change. Here are the results of running the program,
iter: 0 x = 1.00000000 0.00000000 0.00000000 |f(x)| = 118.574 iter: 1 x = 1.64919392 0.01780040 0.64919392 |f(x)| = 77.2068 iter: 2 x = 2.86269020 0.08032198 1.45913464 |f(x)| = 38.0579 iter: 3 x = 4.97908864 0.11510525 1.06649948 |f(x)| = 10.1548 iter: 4 x = 5.03295496 0.09912462 1.00939075 |f(x)| = 6.4982 iter: 5 x = 5.05811477 0.10055914 0.99819876 |f(x)| = 6.33121 iter: 6 x = 5.05827645 0.10051697 0.99756444 |f(x)| = 6.33119 iter: 7 x = 5.05828006 0.10051819 0.99757710 |f(x)| = 6.33119 A = 5.05828 +/- 0.05983 lambda = 0.10052 +/- 0.00309 b = 0.99758 +/- 0.03944 status = success
The approximate values of the parameters are found correctly. The errors on the parameters are given by the square roots of the diagonal elements of the covariance matrix.
The MINPACK algorithm is described in the following article,
The following paper is also relevant to the algorithms described in this section,
This chapter describes macros for the values of physical constants, such as the speed of light, c, and gravitational constant, G. The values are available in different unit systems, including the standard MKS system (meters, kilograms, seconds) and the CGS system (centimeters, grams, seconds), which is commonly used in Astronomy.
The definitions of constants in the MKS system are available in the file `gsl_const_mks.h'. The constants in the CGS system are defined in `gsl_const_cgs.h'. Dimensionless constants, such as the fine structure constant, which are pure numbers are defined in `gsl_const_num.h'.
The full list of constants is described briefly below. Consult the header files themselves for the values of the constants used in the library.
GSL_CONST_MKS_SPEED_OF_LIGHT
GSL_CONST_NUM_AVOGADRO
GSL_CONST_MKS_FARADAY
GSL_CONST_MKS_BOLTZMANN
GSL_CONST_MKS_MOLAR_GAS
GSL_CONST_MKS_STANDARD_GAS_VOLUME
GSL_CONST_MKS_GAUSS
GSL_CONST_MKS_MICRON
GSL_CONST_MKS_HECTARE
GSL_CONST_MKS_MILES_PER_HOUR
GSL_CONST_MKS_KILOMETERS_PER_HOUR
GSL_CONST_MKS_ASTRONOMICAL_UNIT
GSL_CONST_MKS_GRAVITATIONAL_CONSTANT
GSL_CONST_MKS_LIGHT_YEAR
GSL_CONST_MKS_PARSEC
GSL_CONST_MKS_GRAV_ACCEL
GSL_CONST_MKS_SOLAR_MASS
GSL_CONST_MKS_ELECTRON_CHARGE
GSL_CONST_MKS_ELECTRON_VOLT
GSL_CONST_MKS_UNIFIED_ATOMIC_MASS
GSL_CONST_MKS_MASS_ELECTRON
GSL_CONST_MKS_MASS_MUON
GSL_CONST_MKS_MASS_PROTON
GSL_CONST_MKS_MASS_NEUTRON
GSL_CONST_NUM_FINE_STRUCTURE
GSL_CONST_MKS_RYDBERG
GSL_CONST_MKS_ANGSTROM
GSL_CONST_MKS_BARN
GSL_CONST_MKS_BOHR_MAGNETON
GSL_CONST_MKS_NUCLEAR_MAGNETON
GSL_CONST_MKS_ELECTRON_MAGNETIC_MOMENT
GSL_CONST_MKS_PROTON_MAGNETIC_MOMENT
GSL_CONST_MKS_MINUTE
GSL_CONST_MKS_HOUR
GSL_CONST_MKS_DAY
GSL_CONST_MKS_WEEK
GSL_CONST_MKS_INCH
GSL_CONST_MKS_FOOT
GSL_CONST_MKS_YARD
GSL_CONST_MKS_MILE
GSL_CONST_MKS_MIL
GSL_CONST_MKS_NAUTICAL_MILE
GSL_CONST_MKS_FATHOM
GSL_CONST_MKS_KNOT
GSL_CONST_MKS_POINT
GSL_CONST_MKS_TEXPOINT
GSL_CONST_MKS_ACRE
GSL_CONST_MKS_LITER
GSL_CONST_MKS_US_GALLON
GSL_CONST_MKS_CANADIAN_GALLON
GSL_CONST_MKS_UK_GALLON
GSL_CONST_MKS_QUART
GSL_CONST_MKS_PINT
GSL_CONST_MKS_POUND_MASS
GSL_CONST_MKS_OUNCE_MASS
GSL_CONST_MKS_TON
GSL_CONST_MKS_METRIC_TON
GSL_CONST_MKS_UK_TON
GSL_CONST_MKS_TROY_OUNCE
GSL_CONST_MKS_CARAT
GSL_CONST_MKS_GRAM_FORCE
GSL_CONST_MKS_POUND_FORCE
GSL_CONST_MKS_KILOPOUND_FORCE
GSL_CONST_MKS_POUNDAL
GSL_CONST_MKS_CALORIE
GSL_CONST_MKS_BTU
GSL_CONST_MKS_THERM
GSL_CONST_MKS_HORSEPOWER
GSL_CONST_MKS_BAR
GSL_CONST_MKS_STD_ATMOSPHERE
GSL_CONST_MKS_TORR
GSL_CONST_MKS_METER_OF_MERCURY
GSL_CONST_MKS_INCH_OF_MERCURY
GSL_CONST_MKS_INCH_OF_WATER
GSL_CONST_MKS_PSI
GSL_CONST_MKS_POISE
GSL_CONST_MKS_STOKES
GSL_CONST_MKS_STILB
GSL_CONST_MKS_LUMEN
GSL_CONST_MKS_LUX
GSL_CONST_MKS_PHOT
GSL_CONST_MKS_FOOTCANDLE
GSL_CONST_MKS_LAMBERT
GSL_CONST_MKS_FOOTLAMBERT
GSL_CONST_MKS_CURIE
GSL_CONST_MKS_ROENTGEN
GSL_CONST_MKS_RAD
The following program demonstrates the use of the physical constants in a calculation. In this case, the goal is to calculate the range of light-travel times from Earth to Mars.
The required data is the average distance of each planet from the Sun in astronomical units (the eccentricities of the orbits will be neglected for the purposes of this calculation). The average radius of the orbit of Mars is 1.52 astronomical units, and for the orbit of Earth it is 1 astronomical unit (by definition). These values are combined with the MKS values of the constants for the speed of light and the length of an astronomical unit to produce a result for the shortest and longest light-travel times in seconds. The figures are converted into minutes before being displayed.
#include <stdio.h> #include <gsl/gsl_const_mks.h> int main (void) { double c = GSL_CONST_MKS_SPEED_OF_LIGHT; double au = GSL_CONST_MKS_ASTRONOMICAL_UNIT; double minutes = GSL_CONST_MKS_MINUTE; /* distance stored in meters */ double r_earth = 1.00 * au; double r_mars = 1.52 * au; double t_min, t_max; t_min = (r_mars - r_earth) / c; t_max = (r_mars + r_earth) / c; printf("light travel time from Earth to Mars:\n"); printf("minimum = %.1f minutes\n", t_min / minutes); printf("maximum = %.1f minutes\n", t_max / minutes); return 0; }
Here is the output from the program,
light travel time from Earth to Mars: minimum = 4.3 minutes maximum = 21.0 minutes
Further information on the values of physical constants is available from the NIST website,
This chapter describes functions for examining the representation of floating point numbers and controlling the floating point environment of your program. The functions described in this chapter are declared in the header file `gsl_ieee_utils.h'.
The IEEE Standard for Binary Floating-Point Arithmetic defines binary formats for single and double precision numbers. Each number is composed of three parts: a sign bit (s), an exponent (E) and a fraction (f). The numerical value of the combination (s,E,f) is given by the following formula,
The sign bit is either zero or one. The exponent ranges from a minimum value E_min to a maximum value E_max depending on the precision. The exponent is converted to an unsigned number e, known as the biased exponent, for storage by adding a bias parameter, e = E + bias. The sequence fffff... represents the digits of the binary fraction f. The binary digits are stored in normalized form, by adjusting the exponent to give a leading digit of 1. Since the leading digit is always 1 for normalized numbers it is assumed implicitly and does not have to be stored. Numbers smaller than 2^(E_min) are be stored in denormalized form with a leading zero,
This allows gradual underflow down to 2^(E_min - p) for p bits of precision. A zero is encoded with the special exponent of 2^(E_min - 1) and infinities with the exponent of 2^(E_max + 1).
The format for single precision numbers uses 32 bits divided in the following way,
seeeeeeeefffffffffffffffffffffff s = sign bit, 1 bit e = exponent, 8 bits (E_min=-126, E_max=127, bias=127) f = fraction, 23 bits
The format for double precision numbers uses 64 bits divided in the following way,
seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff s = sign bit, 1 bit e = exponent, 11 bits (E_min=-1022, E_max=1023, bias=1023) f = fraction, 52 bits
It is often useful to be able to investigate the behavior of a calculation at the bit-level and the library provides functions for printing the IEEE representations in a human-readable form.
float
to double
. The output takes one of the
following forms,
NaN
Inf, -Inf
1.fffff...*2^E, -1.fffff...*2^E
0.fffff...*2^E, -0.fffff...*2^E
0, -0
The output can be used directly in GNU Emacs Calc mode by preceding it
with 2#
to indicate binary.
stdout
.
#include <stdio.h> #include <gsl/gsl_ieee_utils.h> int main (void) { float f = 1.0/3.0; double d = 1.0/3.0; double fd = f; /* promote from float to double */ printf(" f="); gsl_ieee_printf_float(&f); printf("\n"); printf("fd="); gsl_ieee_printf_double(&fd); printf("\n"); printf(" d="); gsl_ieee_printf_double(&d); printf("\n"); return 0; }
The binary representation of 1/3 is 0.01010101... . The output below shows that the IEEE format normalizes this fraction to give a leading digit of 1,
f= 1.01010101010101010101011*2^-2 fd= 1.0101010101010101010101100000000000000000000000000000*2^-2 d= 1.0101010101010101010101010101010101010101010101010101*2^-2
The output also shows that a single-precision number is promoted to double-precision by adding zeros in the binary representation.
The IEEE standard defines several modes for controlling the behavior of floating point operations. These modes specify the important properties of computer arithmetic: the direction used for rounding (e.g. whether numbers should be rounded up, down or to the nearest number), the rounding precision and how the program should handle arithmetic exceptions, such as division by zero.
Many of these features can now be controlled via standard functions such
as fpsetround
, which should be used whenever they are available.
Unfortunately in the past there has been no universal API for
controlling their behavior -- each system has had its own way of
accessing them. For example, the Linux kernel provides the function
__setfpucw
(set-fpu-control-word) to set IEEE modes, while
HP-UX and Solaris use the functions fpsetround
and
fpsetmask
. To help you write portable programs GSL allows you to
specify modes in a platform-independent way using the environment
variable GSL_IEEE_MODE
. The library then takes care of all the
necessary machine-specific initializations for you when you call the
function gsl_ieee_env_setup
.
GSL_IEEE_MODE
and
attempts to set up the corresponding specified IEEE modes. The
environment variable should be a list of keywords, separated by
commas, like this,
GSL_IEEE_MODE
= "keyword,keyword,..."
where keyword is one of the following mode-names,
single-precision
double-precision
extended-precision
round-to-nearest
round-down
round-up
round-to-zero
mask-all
mask-invalid
mask-denormalized
mask-division-by-zero
mask-overflow
mask-underflow
trap-inexact
trap-common
If GSL_IEEE_MODE
is empty or undefined then the function returns
immediately and no attempt is made to change the system's IEEE
mode. When the modes from GSL_IEEE_MODE
are turned on the
function prints a short message showing the new settings to remind you
that the results of the program will be affected.
If the requested modes are not supported by the platform being used then
the function calls the error handler and returns an error code of
GSL_EUNSUP
.
The following combination of modes is convenient for many purposes,
GSL_IEEE_MODE="double-precision,"\ "mask-underflow,"\ "mask-denormalized"
This choice ignores any errors relating to small numbers (either denormalized, or underflowing to zero) but traps overflows, division by zero and invalid operations.
#include <math.h> #include <stdio.h> #include <gsl/gsl_ieee_utils.h> int main (void) { double x = 1, oldsum = 0, sum = 0; int i = 0; gsl_ieee_env_setup (); /* read GSL_IEEE_MODE */ do { i++; oldsum = sum; sum += x; x = x / i; printf("i=%2d sum=%.18f error=%g\n", i, sum, sum - M_E); if (i > 30) break; } while (sum != oldsum); return 0; }
Here are the results of running the program in round-to-nearest
mode. This is the IEEE default so it isn't really necessary to specify
it here,
GSL_IEEE_MODE="round-to-nearest" ./a.out i= 1 sum=1.000000000000000000 error=-1.71828 i= 2 sum=2.000000000000000000 error=-0.718282 .... i=18 sum=2.718281828459045535 error=4.44089e-16 i=19 sum=2.718281828459045535 error=4.44089e-16
After nineteen terms the sum converges to within @c{$4 \times 10^{-16}$}
4 \times 10^-16 of the correct value.
If we now change the rounding mode to
round-down
the final result is less accurate,
GSL_IEEE_MODE="round-down" ./a.out i= 1 sum=1.000000000000000000 error=-1.71828 .... i=19 sum=2.718281828459041094 error=-3.9968e-15
The result is about
4 \times 10^-15
below the correct value, an order of magnitude worse than the result
obtained in the round-to-nearest
mode.
If we change to rounding mode to round-up
then the series no
longer converges (the reason is that when we add each term to the sum
the final result is always rounded up. This is guaranteed to increase the sum
by at least one tick on each iteration). To avoid this problem we would
need to use a safer converge criterion, such as while (fabs(sum -
oldsum) > epsilon)
, with a suitably chosen value of epsilon.
Finally we can see the effect of computing the sum using
single-precision rounding, in the default round-to-nearest
mode. In this case the program thinks it is still using double precision
numbers but the CPU rounds the result of each floating point operation
to single-precision accuracy. This simulates the effect of writing the
program using single-precision float
variables instead of
double
variables. The iteration stops after about half the number
of iterations and the final result is much less accurate,
GSL_IEEE_MODE="single-precision" ./a.out .... i=12 sum=2.718281984329223633 error=1.5587e-07
with an error of O(10^-7), which corresponds to single precision accuracy (about 1 part in 10^7). Continuing the iterations further does not decrease the error because all the subsequent results are rounded to the same value.
The reference for the IEEE standard is,
A more pedagogical introduction to the standard can be found in the paper "What Every Computer Scientist Should Know About Floating-Point Arithmetic".
This chapter describes some tips and tricks for debugging numerical programs which use GSL.
Any errors reported by the library are passed to the function
gsl_error
. By running your programs under gdb and setting a
breakpoint in this function you can automatically catch any library
errors. You can add a breakpoint for every session by putting
break gsl_error
into your `.gdbinit' file in the directory where your program is started.
If the breakpoint catches an error then you can use a backtrace
(bt
) to see the call-tree, and the arguments which possibly
caused the error. By moving up into the calling function you can
investigate the values of variable at that point. Here is an example
from the program fft/test_trap
, which contains the following
line,
status = gsl_fft_complex_wavetable_alloc (0, &complex_wavetable);
The function gsl_fft_complex_wavetable_alloc
takes the length of
an FFT as its first argument. When this line is executed an error will
be generated because the length of an FFT is not allowed to be zero.
To debug this problem we start gdb
, using the file
`.gdbinit' to define a breakpoint in gsl_error
,
bash$ gdb test_trap GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.16 (i586-debian-linux), Copyright 1996 Free Software Foundation, Inc. Breakpoint 1 at 0x8050b1e: file error.c, line 14.
When we run the program this breakpoint catches the error and shows the reason for it.
(gdb) run Starting program: test_trap Breakpoint 1, gsl_error (reason=0x8052b0d "length n must be positive integer", file=0x8052b04 "c_init.c", line=108, gsl_errno=1) at error.c:14 14 if (gsl_error_handler)
The first argument of gsl_error
is always a string describing the
error. Now we can look at the backtrace to see what caused the problem,
(gdb) bt #0 gsl_error (reason=0x8052b0d "length n must be positive integer", file=0x8052b04 "c_init.c", line=108, gsl_errno=1) at error.c:14 #1 0x8049376 in gsl_fft_complex_wavetable_alloc (n=0, wavetable=0xbffff778) at c_init.c:108 #2 0x8048a00 in main (argc=1, argv=0xbffff9bc) at test_trap.c:94 #3 0x80488be in ___crt_dummy__ ()
We can see that the error was generated in the function
gsl_fft_complex_wavetable_alloc
when it was called with an
argument of n=0. The original call came from line 94 in the
file `test_trap.c'.
By moving up to the level of the original call we can find the line that caused the error,
(gdb) up #1 0x8049376 in gsl_fft_complex_wavetable_alloc (n=0, wavetable=0xbffff778) at c_init.c:108 108 GSL_ERROR ("length n must be positive integer", GSL_EDOM); (gdb) up #2 0x8048a00 in main (argc=1, argv=0xbffff9bc) at test_trap.c:94 94 status = gsl_fft_complex_wavetable_alloc (0, &complex_wavetable);
Thus we have found the line that caused the problem. From this point we
could also print out the values of other variables such as
complex_wavetable
.
The contents of floating point registers can be examined using the
command info float
(not available on all platforms).
(gdb) info float st0: 0xc4018b895aa17a945000 Valid Normal -7.838871e+308 st1: 0x3ff9ea3f50e4d7275000 Valid Normal 0.0285946 st2: 0x3fe790c64ce27dad4800 Valid Normal 6.7415931e-08 st3: 0x3ffaa3ef0df6607d7800 Spec Normal 0.0400229 st4: 0x3c028000000000000000 Valid Normal 4.4501477e-308 st5: 0x3ffef5412c22219d9000 Zero Normal 0.9580257 st6: 0x3fff8000000000000000 Valid Normal 1 st7: 0xc4028b65a1f6d243c800 Valid Normal -1.566206e+309 fctrl: 0x0272 53 bit; NEAR; mask DENOR UNDER LOS; fstat: 0xb9ba flags 0001; top 7; excep DENOR OVERF UNDER LOS ftag: 0x3fff fip: 0x08048b5c fcs: 0x051a0023 fopoff: 0x08086820 fopsel: 0x002b
Individual registers can be examined using the variables $reg, where reg is the register name.
(gdb) p $st1 $1 = 0.02859464454261210347719
It is possible to stop the program whenever a SIGFPE
floating
point exception occurs. This can be useful for finding the cause of an
unexpected infinity or NaN
. The current handler settings can be
shown with the command info signal SIGFPE
.
(gdb) info signal SIGFPE Signal Stop Print Pass to program Description SIGFPE Yes Yes Yes Arithmetic exception
Unless the program uses a signal handler the default setting should be
changed so that SIGFPE is not passed to the program, as this would cause
it to exit. The command handle SIGFPE stop nopass
prevents this.
(gdb) handle SIGFPE stop nopass Signal Stop Print Pass to program Description SIGFPE Yes Yes No Arithmetic exception
Depending on the platform it may be necessary to instruct the kernel to
generate signals for floating point exceptions. For programs using GSL
this can be achieved using the GSL_IEEE_MODE
environment variable
in conjunction with the function gsl_ieee_env_setup()
as described
in see section IEEE floating-point arithmetic.
(gdb) set env GSL_IEEE_MODE=double-precision
Writing reliable numerical programs in C requires great care. The following GCC warning options are recommended when compiling numerical programs:
gcc -ansi -pedantic -Werror -Wall -W -Wmissing-prototypes -Wstrict-prototypes -Wtraditional -Wconversion -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wnested-externs -fshort-enums -fno-common -Dinline= -g -O4
For details of each option consult the manual Using and Porting GCC. The following table gives a brief explanation of what types of errors these options catch.
-ansi -pedantic
-Werror
-Wall
-Wall
, but it is not enough on its own.
-O4
-Wall
rely on the optimizer to analyze the code. If there is no
optimization then the warnings aren't generated.
-W
-Wall
, such as
missing return values and comparisons between signed and unsigned
integers.
-Wmissing-prototypes -Wstrict-prototypes
-Wtraditional
-Wconversion
unsigned int x = -1
. If you need
to perform such a conversion you can use an explicit cast.
-Wshadow
-Wpointer-arith -Wcast-qual -Wcast-align
void
, if you remove a const
cast from a pointer, or if you cast a pointer to a type which has a
different size, causing an invalid alignment.
-Wwrite-strings
const
qualifier so that it
will be a compile-time error to attempt to overwrite them.
-fshort-enums
enum
as short as possible. Normally
this makes an enum
different from an int
. Consequently any
attempts to assign a pointer-to-int to a pointer-to-enum will generate a
cast-alignment warning.
-fno-common
extern
declaration.
-Wnested-externs
extern
declaration is encountered within an
function.
-Dinline=
inline
keyword is not part of ANSI C. Thus if you want to use
-ansi
with a program which uses inline functions you can use this
preprocessor definition to remove the inline
keywords.
-g
gdb
. The only effect of debugging symbols
is to increase the size of the file, and you can use the strip
command to remove them later if necessary.
The following books are essential reading for anyone writing and debugging numerical programs with GCC and GDB.
(See the AUTHORS file in the distribution for up-to-date information.)
The following autoconf test will check for extern inline,
dnl Check for "extern inline", using a modified version dnl of the test for AC_C_INLINE from acspecific.mt dnl AC_CACHE_CHECK([for extern inline], ac_cv_c_extern_inline, [ac_cv_c_extern_inline=no AC_TRY_COMPILE([extern $ac_cv_c_inline double foo(double x); extern $ac_cv_c_inline double foo(double x) { return x+1.0; }; double foo (double x) { return x + 1.0; };], [ foo(1.0) ], [ac_cv_c_extern_inline="yes"]) ]) if test "$ac_cv_c_extern_inline" != no ; then AC_DEFINE(HAVE_INLINE,1) AC_SUBST(HAVE_INLINE) fi
The prototypes for the low-level CBLAS functions are declared in
the file gsl_cblas.h
. For the definition of the functions
consult the documentation available from Netlib (see section References and Further Reading).
The following program computes the product of two matrices using the Level-3 BLAS function SGEMM,
The matrices are stored in row major order but could be stored in column
major order if the first argument of the call to cblas_sgemm
was
changed to CblasColMajor
.
#include <stdio.h> #include <gsl/gsl_cblas.h> int main (void) { int lda = 3; float A[] = { 0.11, 0.12, 0.13, 0.21, 0.22, 0.23 }; int ldb = 2; float B[] = { 1011, 1012, 1021, 1022, 1031, 1032 }; int ldc = 2; float C[] = { 0.00, 0.00, 0.00, 0.00 }; /* Compute C = A B */ cblas_sgemm (CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, 2, 3, 1.0, A, lda, B, ldb, 0.0, C, ldc); printf("[ %g, %g\n", C[0], C[1]); printf(" %g, %g ]\n", C[2], C[3]); return 0; }
To compile the program use the following command line,
gcc demo.c -lgslcblas
There is no need to link with the main library -lgsl
in this
case as the CBLAS library is an independent unit. Here is the output
from the program,
$ ./a.out [ 367.76, 368.12 674.06, 674.72 ]
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
NO WARRANTY
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and a brief idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
Copyright (C) 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
Copyright (C) year your name. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being list their titles, with the Front-Cover Texts being list, and with the Back-Cover Texts being list. A copy of the license is included in the section entitled ``GNU Free Documentation License''.
If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being list"; likewise for Back-Cover Texts.
If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
@normalbottom