1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  *
37  * \brief Declare function optimization routines.
38  *
39  * \author Christian Blau <blau@kth.se>
40  * \ingroup module_math
41  */
42 
43 #ifndef GMX_MATH_OPTIMIZATION_H
44 #define GMX_MATH_OPTIMIZATION_H
45 
46 #include <functional>
47 #include <vector>
48 
49 #include "gromacs/utility/arrayref.h"
50 #include "gromacs/utility/real.h"
51 
52 namespace gmx
53 {
54 
55 /*! \internal
56  *  \brief Compiles results of an a function optimisation.
57  */
58 struct OptimisationResult
59 {
60     //! The coordinates at which the optimal function value has been found
61     std::vector<real> coordinates_;
62     //! The value of the function at the optimal coordinates
63     real functionValue_;
64 };
65 
66 /*! \brief Derivative-free downhill simplex optimisation.
67  *
68  * Find a local minimum of an N-dimensional mapping
69  * \f$\mathbb{R}^N\to\mathbb{R}\f$ using the downhill simplex algorithm by
70  * Nelder and Mead as described in
71  *
72  *       Saša Singer and John Nelder (2009), Scholarpedia, 4(7):2928.
73  *       doi:10.4249/scholarpedia.2928
74  *
75  * Stops when the oriented simplex length is less than a constant factor times the
76  * initial lengths or when a maximum step size is reached.
77  *
78  * For best performance pre-condition problem magnitudes to 1.
79  *
80  * The following algorithm is implemented in this function
81  *  1 Define the N+1 vertices of the initial simplex
82  *      The inital simplex is constructed from the initial guess and N
83  *      additional vertices by adding 0.05 to the initial guess (or 0.0025 if
84  *      the initial guess is the null vector) from the initial vertex (in line
85  *      with usual implementations).
86  *
87  *  1a Sort vertices according to function value with the lowest function value
88  *    first in order to minimize the function.
89  *
90  *  2 Calculate the centroid of the simplex as arithmetic mean of all vertices
91  *    except the worst, \f$ x_c = \frac1n \sum_{i=1}{N} x_i\f$.
92  *
93  *  3 Reflect the worst simplex vertex (the one with the highest function value)
94  *    at the centroid to obtain a reflection point
95  *    \f$ x_r = x_c + \alpha (x_c - x_{N+1}) \f$ which lies outside the vertex.
96  *
97  *  3a Replace worst point with reflection point if reflection point function
98  *    value is better than second worst point, but not better than best and go
99  *    to 1a.
100  *
101  *  4 If the reflection point is better than all other points so far, attempt
102  *    an expansion by calculating the expansion point at
103  *    \f$ x_e = x_c + \gamma (x_r - x_c) \f$. Swap out the worst point in the
104  *    vertex with the expansion point if better than reflection point, otherwise
105  *    use the reflection point and go to 1a.
106  *
107  *  5 Attempt contraction, because reflection was not successful;
108  *    \f$ x_t = x_c + \rho (x_{N+1} - x_c) \f$. If the contraction point is
109  *    better than the worst point, swap out worst point with contracted point
110  *    and go to 1a.
111  *
112  *  6 Shrink the vertex. Replace all points except the best one with
113  *    \f$ x_i = x_1 + \sigma (x_i - x_1) \f$ and go to 1a.
114  *
115  * \param[in] functionToMinimize function to be minimized
116  * \param[in] initialGuess of coordinates
117  * \param[in] minimumRelativeSimplexLength minimal oriented simplex length with
118  *                                         respect to initial simplex
119  * \param[in] maxSteps to run algorithm for
120  *
121  * \returns the lowest found function value and corresponding coordinates.
122  */
123 OptimisationResult nelderMead(const std::function<real(ArrayRef<const real>)>& functionToMinimize,
124                               ArrayRef<const real>                             initialGuess,
125                               real minimumRelativeSimplexLength = 1e-8,
126                               int  maxSteps                     = 10'000);
127 
128 } // namespace gmx
129 #endif // GMX_MATH_OPTIMIZATION_H
130