Commit d1cc4788ae5e95ebcf46c1d8480dc64a0fd055c3

Authored by Jürgen Knödlseder
1 parent 13f15a9c

Document optimization.

doc/source/user_manual/modules/linalg.rst
1   -.. _sec_linalg: Linear algebra -------------- Overview ~~~~~~~~ The linear algebra module provides classes for vector and matrix manipulation. The ``GVector`` class implements a double precision floating point vector, the classes ``GMatrix``, ``GMatrixSymmetric`` and ``GMatrixSparse`` implement a matrix of double precision floating point values for a generic matrix, a symmetric matrix and a sparse matrix, respectively. All matrix classes derive from the abstract ``GMatrixBase`` base class that provides common functionalities to all matrix classes. :ref:`fig_uml_linalg` present an overview over the C++ classes of the linear algebra module and their relations. .. _fig_uml_linalg: .. figure:: uml_linalg.png :width: 100% Linear algebra module The classes ``GSparseNumeric`` and ``GSparseSymbolic`` are classes that are used by ``GMatrixSparse`` but these classes are not exposed to the outside world (i.e. the class definitions are not part of the GammaLib interface). Matrix Storage classes ~~~~~~~~~~~~~~~~~~~~~~ Three matrix storage classes are implemented: * ``GMatrix`` implements a plain matrix storage class which stores all elements of the matrix in memory; * ``GMatrixSymmetric`` implements a symmetric matrix storage class which stores only a triangle of matrix elements, imposing thus strict matrix symmetry; * ``GMatrixSparse`` implements a sparse matrix storage class which stores only non-zero matrix elements in a column-wise organisation. Storage class conversion constructors exist for all three classes to transform one storage class into another:: 1 GMatrix plain(10,10); 2 GMatrixSymmetric symmetric(plain); 3 GMatrixSparse sparse(symmetric); In the above example, a plain matrix is instantiated in line 1, the plain matrix is converted into a symmetric matrix in line 2, and the symmetric matrix is converted into a sparse matrix in line 3. Additional complementary storage class conversion constructors exist, but conversion to a symmetric matrix is of course only possible if the matrix is indeed symmetric. Matrix elements are accessed using the ``operator()``. Filling of sparse matrix elements using this operator is possible, although this can be time consuming due to internal memory management. In general, each fill of a new non-zero element needs to shift all elements that are located after that element in memory. To reduce the memory management overhead in the filling of a sparse matrix, methods have been implemented that allow to fill a matrix column wise:: 1 GMatrixSparse sparse(10,5); 2 GVector column(10); 3 column[0] = 1.0; 4 column[1] = 2.0; 5 column[5] = 8.0; 6 sparse.column(0, column); 7 sparse.add_to_column(0, column); Line 1 allocates a sparse matrix with 10 rows and 5 columns, line 2 instantiates a vector with 10 elements. In lines 3-5, 3 elements of the vector are set to specific values, all other elements will default to 0. In line 6, the elements of the vector are set as the elements of the first matrix column (column 0). Line 7 differs from line 6 in that the elements are now not set but added to the existing matrix elements. To further reduce the memory management overhead for the column-wise fill of a sparse matrix, a "fill-stack" has been implemented. The "fill-stack" is a buffer that implements a queue for columns that are to be set or added to the matrix. The columns will be stored in this "fill-stack" in the order they are provided, and only once the "fill-stack" is full, or upon request, the "fill-stack" will be flushed into memory. The "fill-stack" is used as follows:: sparse.stack_init(size, entries); ... sparse.column(0, column); ... sparse.stack_flush(); ... sparse.stack_destroy(); The ``stack_init(size, entries)`` method initialises the "fill-stack", where ``size`` is the size of the allocated memory buffer and ``entries`` is the maximum number of columns that will be held by the buffer. If ``size`` is set to 0 (the default value), a default ``size`` value of 512 is used. If ``entries`` is set to 0 (the default value), the number of matrix columns is taken as default ``entries`` value. Note that a too large number of elements will produce some overhead due to "fill-stack" management, hence ``entries`` should not exceeed a value of the order of 10-100. The ``stack_flush()`` method flushes the stack, which is mandatory before any usage of the matrix. Note that the "fill-stack" **is not inserted automatically** before any matrix operation, hence manual stack flushing is needed to make all filled matrix elements available for usage. The ``stack_destroy()`` method will flush the stack and free all stack elements. This method should be called once no filling is required anymore. If ``stack_destroy()`` is called immediately after filling, no call to ``stack_flush()`` is needed as the ``stack_destroy()`` method flushes the stack before destroying it. The matrix stack is also destroyed by the sparse matrix destructor, hence manual stack destruction is not mandatory.
2 1 \ No newline at end of file
  2 +.. _sec_linalg: Linear algebra -------------- Overview ~~~~~~~~ The linear algebra module provides classes for vector and matrix manipulation. The ``GVector`` class implements a double precision floating point vector, the classes ``GMatrix``, ``GMatrixSymmetric`` and ``GMatrixSparse`` implement a matrix of double precision floating point values for a generic matrix, a symmetric matrix and a sparse matrix, respectively. All matrix classes derive from the abstract ``GMatrixBase`` base class that provides common functionalities to all matrix classes. :ref:`fig_uml_linalg` presents an overview over the C++ classes of the linear algebra module and their relations. .. _fig_uml_linalg: .. figure:: uml_linalg.png :width: 100% Linear algebra module The classes ``GSparseNumeric`` and ``GSparseSymbolic`` are classes that are used by ``GMatrixSparse`` but these classes are not exposed to the outside world (i.e. the class definitions are not part of the GammaLib interface). Matrix Storage classes ~~~~~~~~~~~~~~~~~~~~~~ Three matrix storage classes are implemented: * ``GMatrix`` implements a plain matrix storage class which stores all elements of the matrix in memory; * ``GMatrixSymmetric`` implements a symmetric matrix storage class which stores only a triangle of matrix elements, imposing thus strict matrix symmetry; * ``GMatrixSparse`` implements a sparse matrix storage class which stores only non-zero matrix elements in a column-wise organisation. Storage class conversion constructors exist for all three classes to transform one storage class into another:: 1 GMatrix plain(10,10); 2 GMatrixSymmetric symmetric(plain); 3 GMatrixSparse sparse(symmetric); In the above example, a plain matrix is instantiated in line 1, the plain matrix is converted into a symmetric matrix in line 2, and the symmetric matrix is converted into a sparse matrix in line 3. Additional complementary storage class conversion constructors exist, but conversion to a symmetric matrix is of course only possible if the matrix is indeed symmetric. Matrix elements are accessed using the ``operator()``. Filling of sparse matrix elements using this operator is possible, although this can be time consuming due to internal memory management. In general, each fill of a new non-zero element needs to shift all elements that are located after that element in memory. To reduce the memory management overhead in the filling of a sparse matrix, methods have been implemented that allow to fill a matrix column wise:: 1 GMatrixSparse sparse(10,5); 2 GVector column(10); 3 column[0] = 1.0; 4 column[1] = 2.0; 5 column[5] = 8.0; 6 sparse.column(0, column); 7 sparse.add_to_column(0, column); Line 1 allocates a sparse matrix with 10 rows and 5 columns, line 2 instantiates a vector with 10 elements. In lines 3-5, 3 elements of the vector are set to specific values, all other elements will default to 0. In line 6, the elements of the vector are set as the elements of the first matrix column (column 0). Line 7 differs from line 6 in that the elements are now not set but added to the existing matrix elements. To further reduce the memory management overhead for the column-wise fill of a sparse matrix, a "fill-stack" has been implemented. The "fill-stack" is a buffer that implements a queue for columns that are to be set or added to the matrix. The columns will be stored in this "fill-stack" in the order they are provided, and only once the "fill-stack" is full, or upon request, the "fill-stack" will be flushed into memory. The "fill-stack" is used as follows:: sparse.stack_init(size, entries); ... sparse.column(0, column); ... sparse.stack_flush(); ... sparse.stack_destroy(); The ``stack_init(size, entries)`` method initialises the "fill-stack", where ``size`` is the size of the allocated memory buffer and ``entries`` is the maximum number of columns that will be held by the buffer. If ``size`` is set to 0 (the default value), a default ``size`` value of 512 is used. If ``entries`` is set to 0 (the default value), the number of matrix columns is taken as default ``entries`` value. Note that a too large number of elements will produce some overhead due to "fill-stack" management, hence ``entries`` should not exceeed a value of the order of 10-100. The ``stack_flush()`` method flushes the stack, which is mandatory before any usage of the matrix. Note that the "fill-stack" **is not inserted automatically** before any matrix operation, hence manual stack flushing is needed to make all filled matrix elements available for usage. The ``stack_destroy()`` method will flush the stack and free all stack elements. This method should be called once no filling is required anymore. If ``stack_destroy()`` is called immediately after filling, no call to ``stack_flush()`` is needed as the ``stack_destroy()`` method flushes the stack before destroying it. The matrix stack is also destroyed by the sparse matrix destructor, hence manual stack destruction is not mandatory.
3 3 \ No newline at end of file
... ...
doc/source/user_manual/modules/opt.rst
1   -.. _sec_opt: Optimizers ----------
2 1 \ No newline at end of file
  2 +.. _sec_opt: Optimizers ---------- Overview ~~~~~~~~ The optimizer module provides classes for function optimization. The abstract ``GOptimizerFunction`` base class defines the interface for the function that should be optimized. The ``GOptimizerPar`` class defines a parameter of the function, and the ``GOptimizerPars`` container class collects all function parameters. The optimizer is represented by the abstract ``GOptimizer`` base class. So far, the only optimizer algorithm implemented in GammaLib is the Levenberg-Marquardt algorithm, implemented by the class ``GOptimizerLM``. :ref:`fig_uml_opt` presents an overview over the C++ classes of the optimizer module and their relations. .. _fig_uml_opt: .. figure:: uml_opt.png :width: 100% Optimizer module The model parameter class ``GModelPar``, as a specific implementation of the optimizer parameter class, derives from ``GOptimizerPar``. One implementation of an optimizer function is the ``GObservations::likelihood`` class that is used for maximum likelihood fitting within GammaLib. Optimizing a function ~~~~~~~~~~~~~~~~~~~~~ This example illustrates how the minimum of a quadratic function of the form :math:`f(x)=2ax^2+bx+c` can be determined using the optimizer. Please see ``examples/cpp/optimize`` for the source code. First, the function is implemented as a class derived from ``GOptimizerFunction``:: 1 class function : public GOptimizerFunction { 2 public: 3 function(void) : m_value(0), m_gradient(1), m_covar(1,1) {} 4 void eval(const GOptimizerPars& pars); 5 double value(void) { return m_value; } 6 GVector* gradient(void) { return &m_gradient; } 7 GMatrixSparse* covar(void) { return &m_covar; } 8 protected: 9 double m_value; //!< Function value 10 GVector m_gradient; //!< Function gradient vector 11 GMatrixSparse m_covar; //!< Covariance matrix 12 }; 13 void function::eval(const GOptimizerPars& pars) 14 { 15 const double a = 2.0; 16 const double b = -4.0; 17 const double c = 2.0; 18 double x = pars[0]->value(); 19 m_value = a*x*x + b*x + c; 20 m_gradient[0] = 2.0*a*x + b; 21 m_covar(0,0) = m_gradient[0] * m_gradient[0]; 22 }; Lines 1-12 define the ``function`` class that requires implementing the ``eval``, ``value``, ``gradient`` and ``covar`` methods. The ``eval`` method, implement in lines 13-22, performs the computation of the function value, the gradient and the products of the gradients. The optimization is then done using the following code:: 1 GOptimizerLM opt; 2 function fct; 3 GOptimizerPars pars(1); 4 pars[0]->value(1.5); 5 opt.optimize(fct, pars); 6 std::cout << "Function value .....: " << fct.value() << std::endl; 7 std::cout << "Parameter value ....: " << pars[0]->value() << std::endl; Line 1 allocates an Levenberg-Marquardt optimizer, line 2 creates an instance of the function to optimize. In line 3, a parameter container with a single parameter is allocated, and the value of the single parameter is set to 1.5 in line 4. In line 5, the optimizer is called, and the resulting function value and best fitted parameter is logged in to console in lines 6-7.
3 3 \ No newline at end of file
... ...
doc/source/user_manual/modules/uml_opt.png 0 → 100644

34.5 KB

examples/cpp/optimize/optimize.cpp
... ... @@ -47,12 +47,13 @@ protected:
47 47 };
48 48 void function::eval(const GOptimizerPars& pars)
49 49 {
50   - const double a = 2.0;
51   - const double b = 5.0;
52   - const double c = 1.0;
53   - double x = pars.par(0).value();
  50 + const double a = 2.0;
  51 + const double b = -4.0;
  52 + const double c = 2.0;
  53 + double x = pars[0]->value();
54 54 m_value = a*x*x + b*x + c;
55   - m_gradient[0] = 2*a*x + b;
  55 + m_gradient[0] = 2.0*a*x + b;
  56 + m_covar(0,0) = m_gradient[0] * m_gradient[0];
56 57 };
57 58  
58 59  
... ... @@ -63,22 +64,27 @@ void function::eval(const GOptimizerPars&amp; pars)
63 64 ***************************************************************************/
64 65 int main(void) {
65 66  
  67 + // Allocate logger
  68 + GLog log;
  69 + log.cout(true);
  70 +
66 71 // Allocate optimizer
  72 + //GOptimizerLM opt(log);
67 73 GOptimizerLM opt;
68 74  
69 75 // Allocate function
70   - function fct();
  76 + function fct;
71 77  
72 78 // Allocate parameters and set initial value
73 79 GOptimizerPars pars(1);
74   - pars[0].value(3.0);
  80 + pars[0]->value(1.5);
75 81  
76 82 // Optimize parameters
77 83 opt.optimize(fct, pars);
78 84  
79 85 // Print derivatives
80   - std::cout << "Function value: " << fct.value() << std::endl;
81   - std::cout << "Parameter ....: " << pars[0].value() << std::endl;
  86 + std::cout << "Function value .....: " << fct.value() << std::endl;
  87 + std::cout << "Parameter value ....: " << pars[0]->value() << std::endl;
82 88  
83 89 // Exit
84 90 return 0;
... ...
include/GOptimizerPars.hpp
... ... @@ -59,6 +59,7 @@ class GOptimizerPars : public GContainer {
59 59 public:
60 60 // Constructors and destructors
61 61 GOptimizerPars(void);
  62 + explicit GOptimizerPars(const int& number);
62 63 GOptimizerPars(const GOptimizerPars& pars);
63 64 virtual ~GOptimizerPars(void);
64 65  
... ...
pyext/GOptimizerPars.i
... ... @@ -38,6 +38,7 @@ class GOptimizerPars : public GContainer {
38 38 public:
39 39 // Constructors and destructors
40 40 GOptimizerPars(void);
  41 + explicit GOptimizerPars(const int& number);
41 42 GOptimizerPars(const GOptimizerPars& pars);
42 43 virtual ~GOptimizerPars(void);
43 44  
... ...
src/opt/GOptimizerPars.cpp
... ... @@ -64,6 +64,29 @@ GOptimizerPars::GOptimizerPars(void)
64 64  
65 65  
66 66 /***********************************************************************//**
  67 + * @brief Parameter constructor
  68 + *
  69 + * @param[in] number Number of parameters to allocate.
  70 + *
  71 + * Allocates @p number parameters in the parameter container.
  72 + ***************************************************************************/
  73 +GOptimizerPars::GOptimizerPars(const int& number)
  74 +{
  75 + // Initialise members
  76 + init_members();
  77 +
  78 + // Allocate parameters
  79 + for (int i = 0; i < number; ++i) {
  80 + m_alloc.push_back(true);
  81 + m_pars.push_back(new GOptimizerPar);
  82 + }
  83 +
  84 + // Return
  85 + return;
  86 +}
  87 +
  88 +
  89 +/***********************************************************************//**
67 90 * @brief Copy constructor
68 91 *
69 92 * @param[in] pars Optimizer parameters.
... ...