O2scl is a C++ class library for object-oriented numerical programming. It includes
gsl_vector and gsl_matrix, yet offer indexing with operator[] and other object-oriented featuresSee licensing information at License Information.
INSTALL has some details on this procedure. Generally, you should be able to run ./configure and then type make and make install. More information on the configure command can also be obtained from ./configure --help. O2scl requires the GSL libraries. If the configure script cannot find them, you may have to specify their location in the CPPFLAGS and LDFLAGS environment variables (./configure --help shows some information on this). The documentation is automatically installed by make install.
After make install, you may test the library with make o2scl-test.
This library requires GSL and is designed to work with GSL versions 1.9 or 1.10. Some classes may work with older versions of GSL, but this cannot be guaranteed.
Range-checking for vectors and matrices is performed similar to the GSL approach, and is turned on by default. You can disable range-checking by defining -DO2SCL_NO_RANGE_CHECK
CPPFLAGS="-DO2SCL_NO_RANGE_CHECK" ./configure
The separate libraries o2scl_eos and o2scl_part are installed by default. To disable the installation of these libraries and their associated documentation, run configure with the flags --disable-eoslib or --disable-partlib. Note that o2scl_eos depends on o2scl_part so using --disable-partlib without --disable-eoslib may not work.
There are several warning flags that are useful when configuring and compiling with g++. (See the GSL documentation for an excellent discussion.) For running configure, I use something like
CFLAGS="" CXXFLAGS="" CPPFLAGS="-ansi -pedantic -Wall -W \ -Wconversion -Wno-unused -Wshadow -Wpointer-arith -Wcast-align \ -Wwrite-strings -fshort-enums -ggdb -O3 -DGSL_RANGE_CHECK=1 \ -DHAVE_INLINE -DO2SCL_ARRAY_ABORT \ -I/home/asteiner/install/include" \ LDFLAGS="-L/home/asteiner/install/lib" ./configure -C \ --prefix=/home/asteiner/install
-I/home/asteiner/install/include and -L/home/asteiner/install/lib above ensures that the GSL libraries can be found (this is where they are installed on my machine). The --prefix=/home/asteiner/install argument to ./configure ensures that O2scl is installed there as well.
with the references to the directory /home/asteiner/install in order to install somewhere in my user directory, and because my copy of GSL is installed there, rather than in /usr/local.
The documentation is generated with Doxygen. In principle, the documentation can be regenerated by the end-user, but this is not supported and may require several external applications not included in the distribution.
Un-installation: While there is no explicit "uninstall" procedure, there are only a couple places to check. Installation creates directories named o2scl in the include, doc and shared files directory (which default to /usr/local/include, /usr/local/doc, and /usr/local/share) which can be removed. Finally, all of the libraries are named with the prefix libo2scl and are created by default in /usr/local/lib. As configured with the settings above, the files are in /home/asteiner/install/include/o2scl, /home/asteiner/install/lib, /home/asteiner/install/share/o2scl, and /home/asteiner/install/doc/o2scl.
Most of the classes reside in the namespace o2scl (removed from the documentation). Numerical constants (many of them based on the GSL constants) are placed in separate namespaces (gsl_cgs, gsl_cgsm, gsl_mks, gsl_mksa, gsl_num, o2scl_const, and o2scl_fm). The CBLAS functions are in the o2scl_cblas namespace and vector and complex number arithmetic is in o2scl_arith. There are also two namespaces which hold integration coefficients, o2scl_inte_qag_coeffs and o2scl_inte_qng_coeffs.
Documentation conventions
In the following documentation, function parameters are denoted by parameter, except when used in mathematical formulas as in
.
Nomenclature
Classes directly derived from the GNU Scientific Library are preceeded by the prefix gsl_ and classes derived from CERNLIB are preceeded by the prefix cern_. Some of those classes derived from GSL and CERNLIB operate slightly differently from the original versions. The differences are detailed in the corresponding class documentation.
Error handling
Error handling is GSL-like with functions returning 0 for success and calling a GSL-like error handler. The error handler, err_hnd is a global pointer to an object of type err_class.
The default behaviour for all errors is simply store the error code and information. You can require the default error handler to print out any error (or even exit) using err_class::set_mode(). Also, if O2SCL_ARRAY_ABORT is defined, then exit() will be called any time a gsl_index error is called, e.g. an out-of-bounds array access.
Errors can be set through the macros set_err, set_err_ret, add_err, and add_err_ret, which are defined in the file err_hnd.h.
Functionality similar to assert() is provided with the macro err_assert, which exits if its argument is non-zero, and bool_assert which exits if its argument is false.
Note that the library functions do not typically reset the error handler. For this reason the user may need to reset the error handler before calling functions which do not return an integer error value so that they can easily test to see if an error occured, e.g. using err_hnd::get_errno().
The default error handler can be replaced by simply assigning the address of a descendant of err_class to err_hnd.
Library dependencies
All of the libraries need libo2scl_base, libgsl, and either libgslcblas or some externally-specified libcblas to operate. Other additional dependencies are listed below:
libo2scl_eos needs libo2scl_nuclei, libo2scl_part, libo2scl_other, libo2scl_minimize, libo2scl_inte, and libo2scl_root libo2scl_nuclei needs libo2scl_part, libo2scl_other, libo2scl_inte, and libo2scl_root.libo2scl_part needs libo2scl_other, libo2scl_inte, and libo2scl_root.
examples directory. After installation, they can be compiled and executed by running make o2scl-examples in that directory. This will also test the output of the examples to make sure it is correct and summarize these tests in examples-summary.txt. The output for each example is placed in the corresponding file with a .scr extension.
Alternatively, you can make the executable for each example in the examples directory individually using, e.g. make ex_mroot.
See Example source code for the documented source code of the individual examples
Also, the testing code for each class is sometimes useful for providing examples of their usage. The testing source code for each source file is named with an _ts.cpp prefix in the same directory as the class source.
gsl_ prefix) and O2scl was specifically designed to be used with GSL. GSL is a must-have for most serious numerical work in C or C++ and is required for installation of O2scl .
cern_ prefix). CERNLIB is located at http://cernlib.web.cern.ch/cernlib/mathlib.html
gsl_inte_). It is available at http://www.netlib.org/quadpack .
gsl_complex are defined in cx_arith.h, but no constructor has been written. The object gsl_complex is still a struct, not a class. For example, gsl_complex a={{1,2}}, b={{3,4}};
gsl_complex c=a+b;
cout << GSL_REAL(c) << " " << GSL_IMAG(C) << endl;
gsl_complex and std::complex<double>, two conversion functions gsl_to_complex() and complex_to_gsl() are provided in ovector_cx_tlate.h.
The O2scl library uses a standard nomenclature to distinguish a couple different concepts. The word "array" is always used to refer to C-style arrays, i.e. double[]. If there are two dimensions in the array, it is a "two-dimensional array", i.e. double[][] . The word "vector" is reserved generic objects with array-like semantics, i.e. any type of object or class which can be treated similar to an array in that it has an function operator[] which gives array-like indexing. Thus arrays are vectors, but not all vectors are arrays. There are a couple specific vector types defined in O2scl like ovector and uvector . The STL vector std::vector<double> is also a vector type in this language. The word "matrix" is reserved for the a generic object which has matrix-like semantics and can be accessed using either operator() or two successive applications of operator[] (or sometimes both). The O2scl objects omatrix and umatrix are matrix objects, as is a C-style two-dimensional array, double[][] . The header files are named in this spirit also: functions and classes which operate on generic vector types are in vector.h and functions and classes which work only with C-style arrays are in array.h . The word "tensor" is used for a generic object which has rank n and then has n associated indices. A vector is just a tensor of rank 1 and a matrix is just a tensor of rank 2.
Most of the classes in O2scl which use vectors and/or matrices are designed so that they can be used with any appropriately-defined vector or matrix types. This is a major part of the design goals for O2scl and most of the classes are compatible with matrix and vector objects from GSL, TNT, MV++, uBlas, and Blitz++.
The first index of a matrix type is defined always to be the index associated with "rows" and the second is associated with "columns". The O2scl matrix output functions respect this notation as well, so that all of the elements of the first row is sent to the screen, then all of the elements in the second row, and so on. With this in mind, one can make the distinction between "row-major" and "column-major" matrix storage. C-style two-dimensional arrays are "row-major" in that the elements of the first row occupy the first locations in memory as opposed "column-major" where the first column occupies the first locations in memory. It is important to note that the majority of the classes in O2scl do not care about the details of the underlying memory structure, so long as two successive applications of operator[] (or in some cases operator(,) ) works properly. The storage format used by omatrix and umatrix is row-major, and there are no column-major matrix classes in O2scl (yet).
Matrix indexing with [][] vs. (,)
While vector indexing with operator[] is very compatible with almost any vector type, matrix indexing is a bit more difficult. There are two options: assume matrix objects provide an operator[] method which can be applied twice, i.e. m[i][j], or assume that matrix elements should be referred to with m(i,j). Most of the O2scl classes use the former approach so that they are also compatible with two-dimensional arrays. However, there are sometimes good reasons to want to use operator() for matrix-intensive operations from linear algebra. For this reason, some of the functions given in the linalg directory are specified in two forms: the first default form which assumes [][], and the second form with the same name, but in a namespace which has a suffix _paren and assumes matrix types use (,).
O2scl matrix and vector types
The rest of this section in the User's guide is dedicated to describing the O2scl implementations of vector, matrix, and tensor types.
Vectors and matrices are designed using the templates ovector_tlate and omatrix_tlate, which are compatible with gsl_vector and gsl_matrix. Vectors and matrices with unit stride are provided in uvector_tlate and umatrix_tlate. The most commonly used double-precision versions of these template classes are ovector, omatrix, uvector and umatrix, and their associated "views" (analogous to GSL vector and matrix views) which are named with a _view suffix. The classes ovector_tlate and omatrix_tlate offer the syntactic simplicity of array-like indexing, which is easy to apply to vectors and matrices created with GSL.
The following sections primarily discuss the operation objects of type ovector. The generalizations to objects of type uvector, omatrix, and the complex vector and matrix objects ovector_cx, omatrix_cx, uvector_cx, and umatrix_cx are straightforward.
Views
Vector and matrix views are provided as parents of the vector and matrix classes which do not have methods for memory allocation. As in GSL, it is simple to "view" normal C-style arrays or pointer arrays (see ovector_array), parts of vectors (ovector_subvector), and rows and columns of matrices (omatrix_row and omatrix_col). Several operations are defined, including addition, subtraction, and dot products.
Typedefs
Several typedefs are used to give smaller names to often used templates. Vectors and matrices of double-precision numbers all begin with the prefixes ovector and omatrix. Integer versions begin with the prefixes ovector_int and omatrix_int. Complex versions have an additional "_cx", e.g. ovector_cx. See ovector_tlate.h, ovector_cx_tlate.h, omatrix_tlate.h, and omatrix_cx_tlate.h.
Unit-stride vectors
The uvector_tlate objects are naturally somewhat faster albeit less flexible than their finite-stride counterparts. Conversion to GSL vectors and matrices is not trivial for uvector_tlate objects, but demands copying the entire vector. Vector views, operators, and the nomenclature is similar to that of ovector.
Memory allocation
Memory for vectors can be allocated using ovector::allocate() and ovector::free(). Allocation can also be performed by the constructor, and the destructor automatically calls free() if necessary. In contrast to gsl_vector_alloc(), ovector::allocate() will call ovector::free(), if necessary, to free previously allocated space. Allocating memory does not clear the recently allocated memory to zero. You can use ovector::set_all() with an argument of 0.0 to clear a vector (and similarly for a matrix).
If the memory allocation fails, either in the constructor or in allocate(), then the error handler will be called, partially allocated memory will be freed, and the size will be reset to zero. You can either use the error handler or test to see if the allocation succeeded by examining the size argument, e.g.
const size_t n=10; ovector x(10); if (x.size()==0) cout << "Failed." << endl;
Get and set
Vectors and matrices can be modified using ovector::get() and ovector::set() methods analogous to gsl_vector_get() and gsl_vector_set(), or they can be modified through ovector::operator[] (or ovector::operator() ), e.g.
ovector a(4); a.set(0,2.0); a.set(1,3.0); a[2]=4.0; a[3]=2.0*a[1];
If you want to set all of the values in an ovector or an omatrix at the same time, then use ovector::set_all() or omatrix::set_all().
Range checking
Range checking is performed depending on whether or not O2SCL_NO_RANGE_CHECK is defined. It can be defined in the arguments to ./configure upon installation to turn off range checking. Note that this is completely separate from the GSL range checking mechanism, so range checking may be on in O2scl even if it has been turned off in GSL. Range checking is used primarily in the vector, matrix, and tensor get() and set() methods.
To see if range checking was turned on during installation (without calling the error handler), use lib_settings_class::range_check().
Note that range checking in O2scl code is most often present in header files, rather than in source code. This means that range checking can be turned on or off in user-defined functions separately from whether or not it was used in the library classes and functions.
Shallow and deep copy
Copying O2scl vectors using constructors or the = operator is performed according to what kind of object is on the left-hand side (LHS) of the equals sign. If the LHS is a view, then a shallow copy is performed, and if the LHS is a ovector, then a deep copy is performed. If an attempt is made to perform a deep copy onto a vector that has already been allocated, then that previously allocated memory is automatically freed. The user must be careful to ensure that information is not lost this way, even though no memory leak will occur.
For generic deep vector and matrix copying, you can use the template functions vector_copy(), matrix_copy(). These would allow you, for example, to copy an ovector to a std::vector<double> object (assuming the memory allocation has already been taken care of).
Vector and matrix arithmetic
Several operators are available as member functions of the corresponding template:
Vector_view unary operators:
Matrix_view unary operators:
Binary operators like addition, subtraction, and matrix multiplication are also defined for ovector, uvector, and related objects in the o2scl_arith namespace. The generic template for a binary operator, e.g.
template<class vec_t> vec_t &operator+(vec_t &v1, vec_t &v2);
O2SCL_OP_VEC_VEC_ADD(o2scl::ovector,std::vector<double>, std::vector<double>)
The GSL BLAS routines can also be used directly with ovector and omatrix objects.
Note that some of these arithmetic operations succeed even with non-matching vector and matrix sizes. For example, adding a 3x3 matrix to a 4x4 matrix will result in a 3x3 matrix and the 7 outer elements of the 4x4 matrix are ignored.
Converting to and from GSL forms
Because of the way ovector is constructed, you may use type conversion to convert to and from objects of type gsl_vector.
ovector a(2); a[0]=1.0; a[1]=2.0; gsl_vector *g=(gsl_vector *)(&a); cout << gsl_vector_get(g,0) << " " << gsl_vector_get(g,1) << endl;
gsl_vector *g=gsl_vector_alloc(2);
gsl_vector_set(0,1.0);
gsl_vector_set(1,2.0);
ovector &a=(ovector &)(*g);
cout << a[0] << " " << a[1] << endl;
ovector a(2); gsl_vector *g=a.get_gsl_vector();
ovector a(2); a[0]=2.0; a[1]=1.0; gsl_vector_sort((gsl_vector *)(&a)); cout << a[0] << " " << a[1] << endl;
Converting from STL form
To "view" a std::vector<double>, you can use ovector_array
std::vector<double> d;
d.push_back(1.0);
d.push_back(3.0);
ovector_array aa(d.size,&(d[0]));
cout << aa[0] << " " << aa[1] << endl;
However, you should note that if the memory for the std::vector is reallocated (for example because of a call to push_back()), then a previously created ovector_view will be incorrect.
push_back() and pop() methods
These two functions give a behavior similar to the corresponding methods for std::vector<>. This will work in O2scl classes, but may not be compatible with all of the GSL functions. This will break if the address of a ovector_tlate is given to a GSL function which accesses the block->size parameter instead of the size parameter of a gsl_vector. Please contact the author of O2scl if you find a GSL function with this behavior.
Views
Views are slightly different than in GSL in that they are now implemented as parent classes. The code
double x[2]={1.0,2.0}; gsl_vector_view_array v(2,x); gsl_vector *g=&(v.vector); gsl_vector_set(g,0,3.0); cout << gsl_vector_get(g,0) << " " << gsl_vector_get(g,1) << endl;
double x[2]={1.0,2.0}; ovector_array a(2,x); a[0]=3.0; cout << a[0] << " " << a[1] << endl;
Passing ovector parameters
It is often best to pass an ovector as a const reference to an ovector_view, i.e.
void function(const ovector_view &a);
const void function(ovector_view &a);
If you intend for a function (rather than the user) to handle the memory allocation, then some care is necessary. The following code
is confusing because the user may have already allocated memory fora. To avoid this, you may want to ensure that the user sends an empty vector. For example, class my_class { int afunction(ovector &a) { if (a.get_size()>0 && a.is_owner()==true) { set_err("Unallocated vector not sent.",1); return 1; } else { a.allocate(1); // do something with a return 0; } } };
get() function, class my_class { protected: ovector a; public: int afunction() { a.allocate(1); // do something with a return 0; } int get_result(const ovector_view &av) { av=a; return 0; } };
template<class vec_t> class my_class { protected: vec_t a; public: int afunction(vec_t &a) { a.allocate(1); // do something with a return 0; } };
Vectors and operator=()
An "operator=(value)" method for setting all vector elements to the same value is not included because it leads to confusion between, ovector_tlate::operator=(const data_t &val) and ovector_tlate::ovector_tlate(size_t val) For example, after implementing operator=() and executing the following
ovector_int o1=2; ovector_int o2; o2=2;
o1 will be a vector of size two, and o2 will be an empty vector!To set all of the vector elements to the same value, use ovector_tlate::set_all(). Because of the existence of constructors like ovector_tlate::ovector_tlate(size_t val), the following code
ovector_int o1=2;
ovector_int o1(2);
ovector_int o1; o1=2;
ovector_int o1(2); is preferable to ovector_int o1=2; to avoid confusion.Matrix structure
The matrices from omatrix_tlate are structured in exactly the same way as in GSL. For a matrix with 2 rows, 4 colums, and a "tda" or "trailing dimension" of 7, the memory for the matrix is structured in the following way:
00 01 02 03 XX XX XX
10 11 12 13 XX XX XX
where XX indicates portions of memory that are unused. The tda can be accessed through, for example, the method omatrix_view_tlate::tda(). The get(size_t,size_t) methods always take the row index as the first argument and the column index as the second argument. The matrices from umatrix_tlate have a trailing dimension which is always equal to the number of columns.
Reversing the order of vectors
You can get a reversed vector view from ovector_reverse_tlate, or uvector_reverse_tlate. For these classes, operator[] and related methods are redefined to perform the reversal. If you want to make many calls to these indexing methods for a reversed vector, then simply copying the vector to a reversed version may be faster.
Const-correctness with vectors
There are several classes named with "_const" to provide different kinds of const views of const vectors. The keyword const still ought to be included to ensure that the object is treated properly. For example,
ovector o(2); o[0]=3.0; o[1]=-1.0; const ovector_const_subvector ocs(o,1,1);
At present, const-correctness in O2scl can be improperly removed, if the const keyword is not properly included. For example, the following code will compile, violated the const-correctness of the ocs variable.
ovector o(2); o[0]=3.0; o[1]=-1.0; ovector_const_subvector ocs(o,1,1); ovector_view ov(ocs); ov[0]=2.0;
Tensors
Some preliminary support is provided for tensors of arbitrary rank and size in the class tensor. Classes tensor1, tensor2, tensor3, and tensor4 are rank-specific versions for 1-, 2-, 3- and 4-rank tensors. For n-dimsional data defined on a grid, tensor_grid provides a space to define a hyper-cubic grid in addition to the the tensor data. This class tensor_grid also provides n-dimensional interpolation of the data defined on the specified grid.
permutation p(4); p.init(); gsl_permutation_swap(&p,2,3);
The functions which apply a permutation to a user-specified vector are member template functions in the permutation class (see permutation::apply() ).
Memory allocation/deallocation between the class and the gsl_struct is compatible in many cases, but mixing these forms is strongly discouraged, i.e. avoid using gsl_permutation_alloc() on a permutation object, but rather use permutation::allocate() instead. The use of permutation::free() is encouraged, but any remaining memory is deallocated in the object destructor.
operator[], the BLAS and linear algebra routines are inside the o2scl_cblas and o2scl_linalg namespaces. For vector and matrix types using operator(), the BLAS and linear algebra routines routines are inside the o2scl_cblas_paren and o2scl_linalg_paren namespaces.The linear algebra classes and functions include:
For fast interpolation of arrays where the independent variable is strictly increasing and without error-checking, you can directly use the children of base_interp.
The two interpolation interfaces
The difference between the two classes, o2scl_interp and o2scl_interp_vec, analogous to the difference between using gsl_interp_eval() and gsl_spline_eval() in GSL. You can create a o2scl_interp object and use it to interpolate among any pair of chosen vectors, i.e.
ovector x(20), y(20); // fill x and y with data o2scl_interp oi; double y_o2sclf=oi.interp(0.5,20,x,y);
ovector x(20), y(20); // fill x and y with data o2scl_interp_vec oi(20,x,y); double y_o2sclf=oi.interp(0.5);
Lookup and binary search
The class search_vec contains a searching functions for objects of type ovector which are monotonic. Note that if you want to find the index of an ovector where a particular value is located without any assumptions with regard to the ordering, you can use ovector::lookup() which performs an exhaustive search.
"Smart" interpolation
The classes smart_interp and smart_interp_vec allow interpolation, lookup, differentiation, and integration of data which is non-monotonic or multiply-valued outside the region of interest. As with o2scl_interp above, the corresponding array versions are given in sma_interp and sma_interp_vec.
Two and higher dimensional interpolation
Preliminary support for two-dimensional interpolation is given in twod_intp, and n-dimensional interpolation in tensor_grid.
const double and placed in namespaces called gsl_cgs, gsl_cgsm, gsl_mks, gsl_mksa, and gsl_num. Some additional constants are given in the namespace o2scl_const. Some of the numerical values have been updated from recently released data from NIST.
n functions of n variablesn fitting parametersn derivatives as a function of n function values and the value of the independent variable
For each of these classes (except funct), there is a version named _vfunct instead of _funct which is designed to be used with C-style arrays instead.
The class name suffixes denote children of a generic function type which are created using different kinds of inputs:
void * and directly returns the function value.There is a small overhead associated with the indirection: a "user class" accesses the function class which then calls function which was specified in the constructor of the function class. In many problems, the overhead associated with the indirection is small. Some of this overhead can always be avoided by inheriting directly from the function class and thus the user class will make a direct virtual function call. To eliminate the overhead entirely, one can specifying a new type for the template parameter in the user class.
Note that virtual functions can be specified through this mechanism as well. For example, if cern_mroot is used to solve a set of equations specified as
class my_type_t { virtual member_func(); }; my_type_t my_instance; class my_derived_type_t : public my_type_t { virtual member_func(); }; my_derived_type_t my_inst2; mm_funct_mfptr<my_type_t> func(&my_inst2,&my_instance::member_func);
std::string objects are given in string_conv.h and includeSee also size_of_exponent(), double_to_latex(), double_to_html, and double_to_ieee_string().
There is a class called columnify, which converts a set of strings into nicely formatted columns by padding with the necessary amount of spaces. This class operates on string objects of type std::string, and also works will for formatting columns of floating-point numbers. This class is used to provide output for matrices in the functions matrix_out(), matrix_out_paren(), and matrix_cx_out_paren(). For output of vectors, see vector_out() in array.h.
A related function, screenify(), reformats a column of strings into many columns stored row-by-row in a new string array. It operates very similar to the way the classic Unix command ls organizes files and directories in multiple columns in order to save screen space.
The function count_words() counts the number of "words" in a string, which are delimited by whitespace.
Warning: For gsl_deriv and cern_deriv, the second and third derivatives are calculated by naive repeated application of the code for the first derivative and can be particularly troublesome if the function is not sufficiently smooth. Error estimation is also incorrect for second and third derivatives.
o2scl_inte.There are several routines for one-dimensional integration.
: gsl_inte_qagiu
to 0: gsl_inte_qagil
to
: gsl_inte_qagicos(x) or sin(x): gsl_inte_qawo_cos and gsl_inte_qawo_sin
is performed by gsl_inte_qaws.
For the GSL-based integration routines, the variables inte::tolx and inte::tolf have the same role as the quantities usually denoted in the GSL integration routines by epsabs and epsrel. In particular, the integration classes attempt to ensure that
and returns an error to attempt to ensure that
where I is the integral to be evaluated. Even when the corresponding descendant of inte::integ() returns success, these inequalities may fail for sufficiently difficult functions. All of the GSL integration routines except for gsl_inte_qng use a workspace given in gsl_inte_table which holds the results of the various subdivisions of the original interval. The size of this workspace (and thus then number of subdivisions) can be controlled with gsl_inte_table::set_wkspace().
The GSL routines were originally based on QUADPACK, which is available at http://www.netlib.org/quadpack .
Multi-dimensional hypercubic integration is performed by composite_inte, the sole descendant of multi_inte. composite_inte allows you to specify a set of one-dimensional integration routines (objects of type inte) and apply them to a multi-dimensional problem.
General multi-dimensional integration is performed by comp_gen_inte, the sole descendant of gen_inte. The user is allowed to specify a upper and lower limits which are functions of the variables for integrations which have not yet been performed, i.e. the n-dimensional integral
Again, one specifies a set of inte objects to apply to each variable to be integrated over.
Monte Carlo integration is also provided (see Monte Carlo Integration).
In the public interfaces to the polynomial solvers, the complex type std::complex<double> is used. These can be converted to and from the GSL complex type using the complex_to_gsl() and gsl_to_complex() functions.
At present, the polynomial routines work with complex numbers as objects of type std::complex<double> and are located in library o2scl_other.
For quadratics, gsl_quadratic_real_coeff is the best if the coefficients are real, while if the coefficients are complex, use quadratic_std_complex. For cubics with real coefficients, cern_cubic_real_coeff is the best, while if the coefficients are complex, use cubic_std_complex.
For a quartic polynomial with real coefficients, cern_quartic_real_coeff is the best, unless the coefficients of odd powers happen to be small, in which case, gsl_quartic_real2 tends to work better. For quartics, generic polynomial solvers such as gsl_poly_real_coeff can provide more accurate (but slower) results. If the coefficients are complex, then you can use simple_quartic_complex.
o2scl_root.One-dimensional solvers
Solution of one equation in one variable is accomplished by children of the class root. This base class provides the structure for three different solving methods:
x x1 and x2. The values of the function at x1 and x2 must have different signs.x and the derivative of the function df.For one-dimensional solving, use cern_root or gsl_root_brent if you have the root bracketed, or gsl_root_stef if you have the derivative available. If you have neither a bracket or a derivative, you can use cern_mroot_root.
If not all of these three functions are overloaded, then the source code in root is designed to try to automatically provide the solution using the remaining functions. Most of the one-dimensional solving routines, in their original form, are written in the second or third form above. For example, gsl_root_brent is originally a bracketing routine of the form root::solve_bkt(), but calls to either root::solve() or root::solve_de() will attempt to automatically bracket the function given the initial guess that is provided. Also, gsl_root_stef is a "root-polishing" routine given derivatives of the form root::solve_de(). If either root::solve() or root::solve_bkt() are called, then root::solve_de() will be called with finite-differencing used to estimate the derivative. Of course, it is frequently most efficient to use the solver in the way it was intended.
Solution of more than one equation is accomplished by descendants of the class mroot. There are two basic functions
n equations given in func with an initial guess x.For multi-dimensional solving, you can use either cern_mroot or gsl_mroot_hybrids. While cern_mroot does not use user-supplied derivatives, gsl_mroot_hybrids can use user-supplied derivative information (as in the GSL hybridsj method).
o2scl_minimize. There are two one-dimensional minimization algorithms, cern_minimize and gsl_min_brent, and they are both bracketing algorithms type where an interval and an initial guess must be provided. If only an initial guess and no bracket is given, these two classes will attempt to find a suitable bracket from the initial guess. While the minimize base class is designed to allow future descendants to optionally use derivative information, this is not yet supported for any one-dimensional minimizers.Multi-dimensional minimization is performed by descendants of multi_min: gsl_mmin_simp, gsl_mmin_conp, gsl_mmin_conf, and gsl_mmin_bfgs2. The class multi_min_fix is a convenient way to perform a minimization while fixing some of the original parameters. The class gsl_mmin_simp does not require or use any derivative information, but the other minimization classes are intended for use when derivatives are available.
Simulated annealing methods are also provided (see Simulated Annealing).
It is important to note that not all of the minimization routines test the second derivative to ensure that it doesn't vanish to ensure that we have found a true minimum.
A naive way of implementing constraints is to add a function to the original which increases the value outside of the allowed region. This can be done with the functions constraint() and lower_bound. There are two analogous functions, cont_constraint() and cont_lower_bound(), which continuous and differentiable versions. Where possible, it is better to use the constrained minimization routines described below.
The constrained minimization classes operate in a similar way to the other multi-dimensional minimization classes (which are derived from multi_min). The constraints are specified with the function
ool_constr_mmin::set_constraints(size_t nc, vec_t &lower, vec_t &upper);
There are five error codes defined in ool_constr_mmin which are specific to the OOL classes.
o2scl_mcarlo (gsl_monte, gsl_miser, and gsl_vegas). These routines are generally superior to the direct methods for integrals over regions with large numbers of spatial dimensions.
Solution of simple initial value problems is performed by ode_iv_solve.
Preliminary support for boundary value problems is given in children of ode_bv_solve.
o2scl_rnga. While the base object rnga is created to allow user-defined random number generators, the only random number generator presently included are from GSL. The GSL random number generator code is reimplemented in the class gsl_rnga to avoid an additional performance penalty. This may not be a truly "object-oriented" interface in that it does not use virtual functions, but it avoids any possible performance penalty. Random number generators are implemented as templates in sim_anneal and mcarlo_inte. In these classes, the random number generator is a template type, rather than a member data pointer, in order to ensure fast execution.
Fourier transforms - see gsl_fft
Series acceleration - see gsl_series
Chebyshev approximations - see gsl_chebapp
Timing execution - see timer_gettod and timer_clock
Polylogarithms - see polylog
There are several data files that are used by various classes in the library. The installation procedure should ensure that these files are automatically found. However, if these data files are moved after installation, then a call to lib_settings_class::set_data_dir() can adjust the library to use the new directory. It is assumed that the directory structure within the data directory has not changed.
Collections of objects can be stored in a collection class, and these collections can be written to or read from text or binary files. User-defined classes may be added to the collections and may be read and written to files as long as a descendant of io_base is provided.
Every type has an associated I/O type which is a descendant of io_base. In order to perform any sort of input/output on any type, an object of the corresponding I/O type must be instantiated by the user. This is not done automatically by the library. (Since it doesn't know which objects are going to be used ahead of time, the library would have to instantiate all of the I/O objects, which is needlessly slow.) This makes the I/O slightly less user-friendly, but much more efficient. For convenience, each subsection of the library has a class (named with an _ioc suffix) which will automatically allocate all I/O types for that subsection.
Level 1 functions: Functions that input/output data from library-defined objects and internal types from files and combine these objects in collections. These are primarily member functions of the class collection.
Level 2 functions: Functions which are designed to allow the user to input or output data for user-generated objects. These are primarily member functions of classes cinput and coutput.
Level 3 functions: Functions which allow low-level modifications on how input and output is performed. Usage of level 3 functions is not immediately recommended for the casual user.
Level 1 usage:
For adding an object to a collection when you have a pointer to the I/O object for the associated type:
int collection::add(std::string name, io_base *tio, void *vec, int sz=0, int sz2=0, bool overwrt=true, bool owner=false);
int collection::add(std::string name, std::string stype, void *vec, int sz=0, int sz2=0, bool overwrt=true, bool owner=false);
To retrieve an object as a
void *from a collection use one of:
int get(std::string tname, void *&vec); int get(std::string tname, void *&vec, int &sz); int get(std::string tname, void *&vec, int &sz, int &sz2); int get(std::string tname, std::string &stype, void *&vec); int get(std::string tname, std::string &stype, void *&vec, int &sz); int get(std::string tname, std::string &stype, void *&vec, int &sz, int &sz2);
void *get(std::string name);
To output one object to a file:
int collection::out_one(out_file_format *outs, std::string stype, std::string name, void *vp, int sz=0, int sz2=0);
To input one object from a file with a given type and name:
int collection::in_one_name(in_file_format *ins, std::string stype, std::string name, void *&vp, int &sz, int &sz2);
int collection::in_one(in_file_format *ins, std::string stype, std::string &name, void *&vp, int &sz, int &sz2);
Level 2 usage (string-based):
If you don't have a pointer to the io_base child object corresponding to the type of subobject that you are manipulating, then you can use the following functions, which take the type name as a string.
To input a sub-object in an io_base template for which memory has already been allocated use one of:
int collection::object_in(std::string type, in_file_format *ins, void *vp, std::string &name); int collection::object_in(std::string type, in_file_format *ins, void *vp, int sz, std::string &name); int collection::object_in(std::string type, in_file_format *ins, void *vp, int sz, int sz2, std::string &name);
To automatically allocate memory and input a sub-object of a io_base template use one of:
int collection::object_in_mem(std::string type, in_file_format *ins, void *vp, std::string &name); int collection::object_in_mem(std::string type, in_file_format *ins, void *vp, int sz, std::string &name); int collection::object_in_mem(std::string type, in_file_format *ins, void *vp, int sz, int sz2, std::string &name);
To output a subobject in an io_base template use:
int collection::object_out(std::string type, out_file_format *outs, void *op, int sz=0, int sz2=0, std::string name="");
Level 2 usage (with io_base pointer):
To input a sub-object in an io_base template for which memory has already been allocated use one of:
virtual int object_in(cinput *cin, in_file_format *ins, object *op, std::string &name); virtual int object_in(cinput *cin, in_file_format *ins, object *op, int sz, std::string &name); virtual int object_in(cinput *cin, in_file_format *ins, object **op, int sz, int sz2, std::string &name); template<size_t N> int object_in(cinput *co, in_file_format *ins, object op[][N], int sz, std::string &name);
To automatically allocate memory and input a sub-object of a io_base template use one of:
virtual int object_in_mem(cinput *cin, in_file_format *ins, object *&op, std::string &name); virtual int object_in_mem(cinput *cin, in_file_format *ins, object *&op, int &sz, std::string &name); virtual int object_in_mem(cinput *cin, in_file_format *ins, object **&op, int &sz, int &sz2, std::string &name); template<size_t N> int object_in_mem(cinput *co, in_file_format *ins, object op[][N], int &sz, std::string &name);
To output a subobject in an io_base template use:
virtual int object_out(coutput *cout, out_file_format *outs, object *op, int sz=0, std::string name=""); virtual int object_out(coutput *cout, out_file_format *outs, object **op, int sz, int sz2, std::string name=""); template<size_t N> int object_out(coutput *cout, out_file_format *outs, object op[][N], int sz, std::string name="");
To automatically allocate/deallocate memory for an object, use:
virtual int mem_alloc(object *&op); virtual int mem_alloc_arr(object *&op, int sz); virtual int mem_alloc_2darr(object **&op, int sz, int sz2); virtual int mem_free(object *op); virtual int mem_free_arr(object *op); virtual int mem_free_2darr(object **op, int sz);
Usage of io_tlate
The functions io_tlate::input() and io_tlate::output() need to be implemented for every class has information for I/O. For subobjects of the class, cinput::object_in() and cinput::object_out() can be called to input or output the information associated with the subobject. For input, cinput::object_in_name(), cinput::object_in_mem(), and cinput::object_in_mem_name() allow the freedom to input an object with a name or with memory allocation. The function coutput::object_out_name() allows one to output an object with a name. If the class contains a pointer to the subobject, then io_base::pointer_in() or io_base::pointer_out() can be used.
/* Example: ex_fptr.cpp ------------------------------------------------------------------- This gives an example of the how member functions and external parameters are supplied to numerical routines. In this case, a member function with two parameters is passed to the gsl_root_brent class, which solves the equation. One of the parameters is member data, and the other is specified using the extra parameter argument to the function. */ #include <o2scl/funct.h> #include <o2scl/gsl_root_brent.h> #include <o2scl/test_mgr.h> using namespace std; using namespace o2scl; class my_class { private: double parameter; public: void set_parameter() { parameter=0.01; } // A function demonstrating the different ways of implementing // function parameters double function_to_solve(double x, double &p) { return atan((x-parameter)*4)*(1.0+sin((x-parameter)*50.0)/p); } }; // A simple function to make the plot int plot(double sol); int main(void) { cout.setf(ios::scientific); test_mgr t; // Only print something out if one of the tests fails t.set_output_level(1); // The solver, specifying the type of the parameter (double) // and the function type (funct<double>) gsl_root_brent<double,funct<double> > solver; my_class c; c.set_parameter(); // This is the "magic" that allows specification of class member // functions as functions to solve. This object-oriented approach // avoids the use of static variables and functions and multiple // inheritance at the expense of a little overhead. We need to // provide the address of an instantiated object and the address of // the member function. funct_mfptr_noerr<my_class,double> function(&c,&my_class::function_to_solve); double x1=-1; double x2=2; double p=1.1; // The value verbose=1 prints out iteration information // and verbose=2 requires a keypress between iterations. // The parameter p=0.1 is used. solver.verbose=1; solver.solve_bkt(x1,x2,p,function); // This is actually a somewhat difficult function to solve because // of the sinusoidal behavior. cout << "Solution: " << x1 << " Function value: " << c.function_to_solve(x1,p) << endl; // Write the function being solved to a file (see source code // in examples directory for details) plot(x1); t.report(); return 0; } // End of example
The image below shows how the solver progresses to the solution of the example function.
ex_fptr.png
/* Example: ex_mroot.cpp ------------------------------------------------------------------- Several ways to use an O2scl solver to solve a simple function */ #include <cmath> #include <o2scl/test_mgr.h> #include <o2scl/mm_funct.h> #include <o2scl/gsl_mroot_hybrids.h> #include <o2scl/cern_mroot.h> using namespace std; using namespace o2scl; int gfn(size_t nv, const ovector_view &x, ovector_view &y, void *&pa) { y[0]=sin(x[1]-0.2); y[1]=sin(x[0]-0.25); return 0; } class cl { public: // Store the number of function and derivative evaluations int nf, nd; int mfn(size_t nv, const ovector_view &x, ovector_view &y, void *&pa) { y[0]=sin(x[1]-0.2); y[1]=sin(x[0]-0.25); nf++; return 0; } int operator()(size_t nv, const ovector_view &x, ovector_view &y, void *&pa) { y[0]=sin(x[1]-0.2); y[1]=sin(x[0]-0.25); nf++; return 0; } int mfnd(size_t nv, ovector_view &x, ovector_view &y, omatrix_view &j, void *&pa) { j[0]