toon-members
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Toon-members] TooN optimization/golden_section.h optimization...


From: Edward Rosten
Subject: [Toon-members] TooN optimization/golden_section.h optimization...
Date: Tue, 07 Apr 2009 14:33:06 +0000

CVSROOT:        /cvsroot/toon
Module name:    TooN
Changes by:     Edward Rosten <edrosten>        09/04/07 14:33:06

Modified files:
        optimization   : golden_section.h 
Added files:
        optimization   : conjugate_gradient.h 
        test           : cg_test.cc cg_view.gnuplot 

Log message:
        Added conjugate gradient optimization and test code.
        
        Updated golden_section to make it work better with ConjugateGradient.

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/TooN/optimization/golden_section.h?cvsroot=toon&r1=1.1&r2=1.2
http://cvs.savannah.gnu.org/viewcvs/TooN/optimization/conjugate_gradient.h?cvsroot=toon&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/TooN/test/cg_test.cc?cvsroot=toon&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/TooN/test/cg_view.gnuplot?cvsroot=toon&rev=1.1

Patches:
Index: optimization/golden_section.h
===================================================================
RCS file: /cvsroot/toon/TooN/optimization/golden_section.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -b -r1.1 -r1.2
--- optimization/golden_section.h       3 Apr 2009 17:21:46 -0000       1.1
+++ optimization/golden_section.h       7 Apr 2009 14:33:06 -0000       1.2
@@ -4,27 +4,47 @@
 #include <limits>
 #include <cmath>
 #include <cstdlib>
+#include <iomanip>
 
 namespace TooN
 {
        using std::numeric_limits;
+
+       /// golden_section_search performs a golden section search line 
minimization
+       /// on the functor provided. The inputs a, b, c must bracket the 
minimum, and
+       /// must be in order, so  that \f$ a < b < c \f$ and \f$ f(a) > f(b) < 
f(c) \f$.
+       /// @param a The most negative point along the line.
+       /// @param b The central point.
+       /// @param c The most positive point along the line.
+       /// @param func The functor to minimize
+       /// @param maxiterations  Maximum number of iterations
+       /// @param tolerance Tolerance at which the search should be stopped.
+       /// @return The minima position is returned as the first element of the 
vector,
+       ///         and the minimal value as the second element.
+       template<class Functor, class Precision> Vector<2, Precision> 
golden_section_search(Precision a, Precision b, Precision c, const Functor& 
func, int maxiterations, Precision tol = 
sqrt(numeric_limits<Precision>::epsilon()))
+       {
+               return golden_section_search(a, b, c, func(b), func, 
maxiterations, tol);
+       }
+
        /// golden_section_search performs a golden section search line 
minimization
        /// on the functor provided. The inputs a, b, c must bracket the 
minimum, and
        /// must be in order, so  that \f$ a < b < c \f$ and \f$ f(a) > f(b) < 
f(c) \f$.
        /// @param a The most negative point along the line.
        /// @param b The central point.
+       /// @param fb The value of the function at the central point (\f$b\f$).
        /// @param c The most positive point along the line.
        /// @param func The functor to minimize
-       /// @param tolerance
+       /// @param maxiterations  Maximum number of iterations
+       /// @param tolerance Tolerance at which the search should be stopped.
        /// @return The minima position is returned as the first element of the 
vector,
        ///         and the minimal value as the second element.
-       template<class Functor, class Precision> Vector<2, Precision> 
golden_section_search(Precision a, Precision b, Precision c, const Functor& 
func, Precision tol = sqrt(numeric_limits<Precision>::epsilon()))
+       template<class Functor, class Precision> Vector<2, Precision> 
golden_section_search(Precision a, Precision b, Precision c, Precision fb, 
const Functor& func, int maxiterations, Precision tol = 
sqrt(numeric_limits<Precision>::epsilon()))
        {
                using std::abs;
                //The golden ratio:
                const Precision g = (3.0 - sqrt(5))/2;
 
-               Precision x1, x2;
+               Precision x1, x2, fx1, fx2;
 
                //Perform an initial iteration, to get a 4 point
                //bracketing. This is rather more convenient than
@@ -33,20 +53,24 @@
                {
                        x1 = b - g*(b-a);
                        x2 = b;
+
+                       fx1 = func(x1);
+                       fx2 = fb;
                }
                else
                {
                        x2 = b + g * (c-b);
                        x1 = b;
+
+                       fx1 = fb;
+                       fx2 = func(x2);
                }
 
                //We now have an ordered list of points a x1 x2 c
 
-               Precision fx1 = func(x1);
-               Precision fx2 = func(x2);
-
                //Termination condition from NR in C
-               while(abs(c-a) > tol * (abs(x2)+abs(x1)))
+               int itnum = 1; //We've already done one iteration.
+               while(abs(c-a) > tol * (abs(x2)+abs(x1)) && itnum < 
maxiterations)
                {
                        if(fx1 > fx2)
                        {

Index: optimization/conjugate_gradient.h
===================================================================
RCS file: optimization/conjugate_gradient.h
diff -N optimization/conjugate_gradient.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ optimization/conjugate_gradient.h   7 Apr 2009 14:33:06 -0000       1.1
@@ -0,0 +1,303 @@
+#include <TooN/optimization/golden_section.h>
+#include <utility>
+#include <cmath>
+#include <cassert>
+#include <cstdlib>
+
+namespace TooN{
+
+       namespace Internal{
+       
+
+       ///Turn a multidimensional function in to a 1D function by specifying a
+       ///point and direction.
+       template<int Size, typename Precision, typename Func> struct LineSearch
+       {
+               const Vector<Size, Precision>& start, direction;
+               const Func& f;
+               
+               ///@param s Start point.
+               ///@param d direction
+               ///@param func Function.
+               LineSearch(const Vector<Size, Precision>& s, const Vector<Size, 
Precision>& d, const Func& func)
+               :start(s),direction(d),f(func)
+               {}
+               
+               ///@param x Position to evaluate function
+               ///@return \f$f(\vec{d} + x\vec{s})\f$
+               Precision operator()(Precision x) const
+               {
+                       return f(start + x * direction); 
+               }
+       };
+       
+       ///Bracket a 1D function by searching forward from zero. The assumption
+       ///is that a minima exists in \f$f(x),\ x>0\f$, and this function 
searches
+       ///for a bracket using exponentially growning or shrinking steps.
+       ///@param a_val The value of the function at zero.
+       ///@param func Function to bracket
+       ///@param initial_lambda Initial stepsize
+       ///@return <code>m[i][0]</code> contains the values of \f$x\f$ for the 
bracket, in increasing order, 
+       ///        and <code>m[i][1]</code> contains the corresponding values 
of \f$f(x)\f$.
+       template<typename Precision, typename Func> Matrix<3,2,Precision> 
bracket_minimum_forward(Precision a_val, const Func& func, Precision 
initial_lambda=1)
+       {
+               //Get a, b, c to  bracket a minimum along a line
+               Precision a, b, c, b_val, c_val;
+
+               a=0;
+
+               //Search forward in steps of lambda
+               Precision lambda=initial_lambda;
+               b = lambda;
+               b_val = func(b);
+
+               if(b_val < a_val) //We've gone downhill, so keep searching 
until we go back up
+               {
+                       for(;;)
+                       {
+                               lambda *= 2;
+                               c = lambda;
+                               c_val = func(c);
+
+                               if(c_val >      b_val) // we have a bracket
+                                       break;
+                               else
+                               {
+                                       a = b;
+                                       a_val = b_val;
+                                       b=c;
+                                       b_val=c_val;
+
+                               }
+                       }
+               }
+               else //We've overshot the minimum, so back up
+               {
+                       c = b;
+                       c_val = b_val;
+                       //Here, c_val > a_val
+
+                       for(;;)
+                       {
+                               lambda *= .5;
+                               b = lambda;
+                               b_val = func(b);
+
+                               if(b_val < a_val)// we have a bracket
+                                       break;
+                               else //Contract the bracket
+                               {
+                                       c = b;
+                                       c_val = b_val;
+                               }
+                       }
+               }
+               
+               Matrix<3,2> ret;
+               ret[0] = makeVector(a, a_val);
+               ret[1] = makeVector(b, b_val);
+               ret[2] = makeVector(c, c_val);
+
+               return ret;
+       }
+
+}
+
+
+/** This class provides a nonlinear conjugate-gradient optimizer. The following
+code snippet will perform an optimization on the Rosenbrock Bananna function in
+two dimensions:
+
address@hidden
+double Rosenbrock(const Vector<2>& v)
+{
+               return sq(1 - v[0]) + 100 * sq(v[1] - sq(v[0]));
+}
+
+Vector<2> RosenbrockDerivatives(const Vector<2>& v)
+{
+       double x = v[0];
+       double y = v[1];
+
+       Vector<2> ret;
+       ret[0] = -2+2*x-400*(y-sq(x))*x;
+       ret[1] = 200*y-200*sq(x);
+
+       return ret;
+}
+
+int main()
+{
+       ConjugateGradient<2> cg(makeVector(0,0), Rosenbrock, 
RosenbrockDerivatives);
+
+       while(!cg.iterate(Rosenbrock, RosenbrockDerivatives))
+               cout << "y_" << iteration << " = " << cg.y << endl;
+
+       cout << "Optimal value: " << cg.y << endl;
+}
address@hidden
+
+Linesearch is currently performed using golden-section search and conjugate
+vector updates are performed using the Polak-Ribiere equations.  There many
+tunable parameters, and the internals are readily accessible, so alternative
+termination conditions etc can easily be substituted. However, ususally these
+will not be necessary.
+*/
+template<int Size, class Precision=double> struct ConjugateGradient
+{
+       const int size;      ///< Dimensionality of the space. 
+       Vector<Size> g;      ///< Gradient vector used by the next call to 
iterate()
+       Vector<Size> h;      ///< Conjugate vector to be searched along in the 
next call to iterate()
+       Vector<Size> old_g;  ///< Gradient vector used to compute $h$ in the 
last call to iterate()
+       Vector<Size> old_h;  ///< Conjugate vector searched along in the last 
call to iterate()
+       Vector<Size> x;      ///< Current position (best known point)
+       Vector<Size> old_x;  ///< Previous best known point (not set at 
construction)
+       Precision y;         ///< Function at \f$x\f$
+       Precision old_y;     ///< Function at  old_x
+
+       Precision tolerance; ///< Tolerance used to determine if the 
optimization is complete. Defaults to square root of machine precision.
+       Precision epsilon;   ///< Additive term in tolerance to prevent 
excessive iterations if \f$x_\mathrm{optimal} = 0\f$. Known as \c ZEPS in 
numerical recipies.
+       int       max_iterations; ///< Maximum number of iterations. Defaults 
to \c size\f$*100\f$
+
+       Precision bracket_initial_lambda;///< Initial stepsize used in 
bracketing the minimum for the line search. Defaults to 1.
+       Precision linesearch_tolerance; ///< Tolerance used to determine if the 
linesearch is complete. Defaults to square root of machine precision.
+       int linesearch_max_iterations;  ///< Maximum number of iterations in 
the linesearch. Defaults to 100.
+
+       int iterations; ///< Number of iterations performed
+
+       template<class Func, class Deriv> ConjugateGradient(const Vector<Size>& 
start, const Func& func, const Deriv& deriv)
+       : size(start.size()),
+         g(size),h(size),old_g(size),old_h(size),x(start),old_x(size)
+       {
+               using std::numeric_limits;
+
+               x = start;
+               
+               //Start with the conjugate direction aligned with
+               //the gradient
+               g = deriv(x);
+               h = g;
+
+               y = func(x);
+               old_y = y;
+
+               tolerance = sqrt(numeric_limits<Precision>::epsilon());
+               epsilon = 1e-20;
+               max_iterations = size * 100;
+
+               bracket_initial_lambda = 1;
+
+               linesearch_tolerance =  
sqrt(numeric_limits<Precision>::epsilon());
+               linesearch_max_iterations=100;
+
+               iterations=0;
+       }
+       
+
+       ///Perform a linesearch from the current point (x) along the current
+       ///conjugate vector (h).  /The linesearch does not make use of 
derivatives.
+       ///You probably do not want to use /this function. See iterate() 
instead.
+       ///This function updates:
+       /// - x
+       /// - old_c
+       /// - y
+       /// - old_y
+       /// - iterations
+       /// Note that the conjugate direction and gradient are not updated.
+       /// @param func Functor returning the function value at a given point. 
+       template<class Func> void find_next_point(const Func& func)
+       {
+               Internal::LineSearch<Size, Precision, Func> line(x, -h, func);
+
+               //Always search in the conjugate direction (h)
+
+               //First bracket a minimum. 
+               Matrix<3,2,Precision> bracket = 
Internal::bracket_minimum_forward(y, line, bracket_initial_lambda);
+
+               double a = bracket[0][0];
+               double b = bracket[1][0];
+               double c = bracket[2][0];
+
+               double a_val = bracket[0][1];
+               double b_val = bracket[1][1];
+               double c_val = bracket[2][1];
+
+               //We should have a bracket here
+               assert(a < b && b < c);
+               assert(a_val > b_val && b_val < c_val);
+               
+               //Find the real minimum
+               Vector<2, Precision>  m = golden_section_search(a, b, c, b_val, 
line, linesearch_max_iterations); 
+       
+               assert(m[0] >= a && m[0] <= c);
+               assert(m[1] <= b_val);
+               
+               //Update the current position and value
+               old_y = y;
+               old_x = x;
+
+               x -= m[0] * h;
+               y = m[1];
+               
+               iterations++;
+       }
+       
+       ///Check to see it iteration should stop. You probably do not want to 
use
+       ///this function. See iterate() instead. This function updates nothing.
+       bool finished()
+       {
+               using std::abs;
+               return iterations > max_iterations || 2*abs(y - old_y) <= 
tolerance * (abs(y) + abs(old_y) + epsilon);
+       }
+       
+       ///After an iteration, update the gradient and conjugate using the
+       ///Polak-Ribiere equations.  /@param deriv Functor to compute 
derivatives at
+       ///the specified point. This function updates:
+       ///- g
+       ///- old_g
+       ///- h
+       ///- old_h
+       template<class Deriv> void update_vectors_PR(const Deriv& deriv)
+       {
+               //Update the position, gradient and conjugate directions
+               old_g = g;
+               old_h = h;
+
+               g = deriv(x);
+               //Precision gamma = (g * g - oldg*g)/(oldg * oldg);
+               Precision gamma = (g * g - old_g*g)/(old_g * old_g);
+               h = g + gamma * old_h;
+       }
+       
+       ///Use this function to iterate over the optimization. Note that after
+       ///iterate returns false, g, h, old_g and old_h will not have been
+       ///updated.
+       ///This function updates:
+       /// - x
+       /// - old_c
+       /// - y
+       /// - old_y
+       /// - iterations
+       /// - g*
+       /// - old_g*
+       /// - h*
+       /// - old_h*
+       /// *'d variables not updated on the last iteration.
+       ///@param func Functor returning the function value at a given point. 
+       ///@param deriv Functor to compute derivatives at the specified point.
+       ///@return Whether to continue.
+       template<class Func, class Deriv> bool iterate(const Func& func, const 
Deriv& deriv)
+       {
+               find_next_point(func);
+
+               if(!finished())
+               {
+                       update_vectors_PR(deriv);
+                       return 1;
+               }
+               else
+                       return 0;
+       }
+};
+
+}

Index: test/cg_test.cc
===================================================================
RCS file: test/cg_test.cc
diff -N test/cg_test.cc
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ test/cg_test.cc     7 Apr 2009 14:33:06 -0000       1.1
@@ -0,0 +1,64 @@
+#include <TooN/optimization/golden_section.h>
+#include <TooN/optimization/conjugate_gradient.h>
+#include <iostream>
+#include <cmath>
+#include <cassert>
+#include <cstdlib>
+
+using namespace TooN;
+using namespace std;
+
+double sq(double d)
+{
+       return d*d;
+}
+
+double Rosenbrock(const Vector<2>& v)
+{
+               return sq(1 - v[0]) + 100 * sq(v[1] - sq(v[0]));
+}
+
+Vector<2> RosenbrockDerivatives(const Vector<2>& v)
+{
+       double x = v[0];
+       double y = v[1];
+
+       Vector<2> ret;
+       ret[0] = -2+2*x-400*(y-sq(x))*x;
+       ret[1] = 200*y-200*sq(x);
+
+       return ret;
+}
+
+
+double Spiral(const Vector<2>& v)
+{
+       double x = v[0];
+       double y = v[1];
+       
+       return sin(20.0*sqrt(x*x+y*y)+2.0*atan(y/x))+2.0*x*x+2.0*y*y;
+}
+
+
+Vector<2> SpiralDerivatives(const Vector<2>& v)
+{
+       double x = v[0];
+       double y = v[1];
+
+       double dx 
=2.0*(10.0*cos(20.0*sqrt(x*x+y*y)+2.0*atan(y/x))*x*x*x+10.0*cos(20.0*sqrt(x*x+y*y)+2.0*atan(y/x))*x*y*y-cos(20.0*sqrt(x*x+y*y)+2.0*atan(y/x))*y*sqrt(x*x+y*y)+2.0*x*x*x*sqrt(x*x+y*y)+2.0*x*sqrt(x*x+y*y)*y*y)/sqrt(pow(x*x+y*y,3.0));
+       double dy =  
2.0*(10.0*cos(20.0*sqrt(x*x+y*y)+2.0*atan(y/x))*y*x*x+10.0*cos(20.0*sqrt(x*x+y*y)+2.0*atan(y/x))*y*y*y+cos(20.0*sqrt(x*x+y*y)+2.0*atan(y/x))*x*sqrt(x*x+y*y)+2.0*y*sqrt(x*x+y*y)*x*x+2.0*y*y*y*sqrt(x*x+y*y))/sqrt(pow(x*x+y*y,3.0));
+
+       return makeVector(dx, dy);
+}
+
+int main()
+{
+       ConjugateGradient<2> cg(makeVector(1.5, 1.5), Spiral, 
SpiralDerivatives);
+       cg.bracket_initial_lambda=1e-7; //Prevent the bracket from jumping over 
spiral arms for a prettier display
+
+       cout << cg.x << " " << cg.y << endl;
+       while(cg.iterate(Spiral, SpiralDerivatives))
+               cout << cg.x << " " << cg.y << endl;
+       cout << cg.x << " " << cg.y << endl;
+}
+

Index: test/cg_view.gnuplot
===================================================================
RCS file: test/cg_view.gnuplot
diff -N test/cg_view.gnuplot
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ test/cg_view.gnuplot        7 Apr 2009 14:33:06 -0000       1.1
@@ -0,0 +1,19 @@
+####
+#### Compile cg_test.cc to cg_test
+####
+
+set multiplot
+set nocontour
+set pm3d
+unset surface
+set view 0,0
+set isosample 100
+
+splot [-2:2] [-2:2] sin(20*sqrt(x**2 + y**2) + atan(y/x)*2) + 1*(x**2 + y**2)
+unset pm3d
+set surface
+splot [-2:2] [-2:2] '< ./cg_test' using 1:2:3 with linespoints title 
'Conjugate Gradient path'
+unset multiplot
+
+
+




reply via email to

[Prev in Thread] Current Thread [Next in Thread]