source/numeric/quasinewton.cxx

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  The Contents of this file are made available subject to
00004  *  the terms of GNU Lesser General Public License Version 2.1.
00005  *
00006  *
00007  *    GNU Lesser General Public License Version 2.1
00008  *    =============================================
00009  *    Copyright 2005 by Kohei Yoshida.
00010  *    1039 Kingsway Dr., Apex, NC 27502, USA
00011  *
00012  *    This library is free software; you can redistribute it and/or
00013  *    modify it under the terms of the GNU Lesser General Public
00014  *    License version 2.1, as published by the Free Software Foundation.
00015  *
00016  *    This library is distributed in the hope that it will be useful,
00017  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  *    Lesser General Public License for more details.
00020  *
00021  *    You should have received a copy of the GNU Lesser General Public
00022  *    License along with this library; if not, write to the Free Software
00023  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00024  *    MA  02111-1307  USA
00025  *
00026  ************************************************************************/
00027 
00028 #include "numeric/quasinewton.hxx"
00029 #include "numeric/exception.hxx"
00030 #include "numeric/diff.hxx"
00031 #include "numeric/nlpmodel.hxx"
00032 #include "numeric/funcobj.hxx"
00033 #include "numeric/matrix.hxx"
00034 #include "numeric/quadfitlinesearch.hxx"
00035 #include "tool/timer.hxx"
00036 #include "tool/global.hxx"
00037 
00038 #include <boost/shared_ptr.hpp>
00039 #include <memory>
00040 #include <iostream>
00041 #include <iomanip>
00042 #include <cmath>
00043 #include <vector>
00044 
00045 using namespace scsolver::numeric;
00046 using scsolver::numeric::Matrix;
00047 using boost::shared_ptr;
00048 using ::std::vector;
00049 using ::std::string;
00050 using ::std::cout;
00051 using ::std::endl;
00052 using ::std::distance;
00053 using ::std::setprecision;
00054 
00055 namespace scsolver { namespace numeric { namespace nlp {
00056 
00057 class QuasiNewtonImpl
00058 {
00066         static double norm( const Matrix& mxX )
00067         {
00068                 double fNorm = 0.0;
00069                 for ( size_t i = 0; i < mxX.rows(); ++i )
00070                 {
00071                         double f = mxX( i, 0 );
00072                         fNorm += f*f;
00073                 }
00074 #ifdef _MSC_VER
00075                 return ::sqrt( fNorm );
00076 #else
00077                 return std::sqrt( fNorm );
00078 #endif
00079         }
00080 
00081         static double evalF( BaseFuncObj& oF, const Matrix& mxVars, vector<double>& fVars )
00082         {
00083                 size_t nRows = mxVars.rows();
00084                 fVars.reserve( nRows );
00085                 for ( size_t i = 0; i < nRows; ++i )
00086                         fVars.push_back( mxVars( i, 0 ) );
00087 
00088         oF.setVars(fVars);
00089         return oF.eval();
00090         }
00091 
00092         static double evalF( BaseFuncObj& oF, const Matrix& mxVars )
00093         {
00094                 vector<double> fVars;
00095                 return evalF( oF, mxVars, fVars );
00096         }
00097 
00098 public:
00099         QuasiNewtonImpl( QuasiNewton* p ) :
00100                 m_pSelf(p),
00101                 m_nIter(0),
00102                 m_pModel(NULL),
00103                 m_mxVars(0, 0),
00104                 m_mxdVars(0, 0),
00105                 m_mxVarsOld(0, 0),
00106                 m_mxdF(0, 0),
00107                 m_mxdFOld(0, 0),
00108                 m_mxD(0, 0),
00109                 m_mxDOld(0, 0),
00110                 m_fF(0.0),
00111                 m_fFOld(0.0),
00112                 m_fNorm(0.0),
00113                 m_fTolerance(0.0),
00114         m_debug(true)
00115         {
00116         }
00117 
00118         ~QuasiNewtonImpl() throw() {}
00119 
00120         void solve()
00121         {
00122                 // Initialize relevant data members.
00123                 m_pModel = m_pSelf->getModel();
00124                 vector<double> cnVars;
00125         m_pModel->getVars(cnVars);
00126 
00127                 vector<double>::const_iterator it, itBeg = cnVars.begin(), itEnd = cnVars.end();
00128                 m_mxVars.clear();
00129                 for ( it = itBeg; it != itEnd; ++it )
00130                         m_mxVars( distance( itBeg, it ), 0 ) = *it;
00131 
00132                 m_mxdVars.clear();
00133                 m_mxVarsOld.clear();
00134                 m_mxdF.clear();
00135                 m_mxdFOld.clear();
00136                 m_mxD.clear();
00137                 m_mxDOld.clear();
00138                 m_fTolerance = 0.1;
00139                 for ( unsigned long i = 0; i < m_pModel->getPrecision(); ++i )
00140                         m_fTolerance *= 0.1;
00141 
00142                 m_fF = 0.0;
00143                 m_fFOld = m_fTolerance * 100.0;
00144                 m_fNorm = m_fTolerance * 100.0;
00145 
00146         m_pFuncObj = m_pModel->getFuncObject();
00147 
00148                 m_nIter = 0;
00149                 cout << setprecision( m_pModel->getPrecision() );
00150                 ::scsolver::Timer mytimer(5); // set timer to 5 sec
00151                 mytimer.init();
00152                 while ( !runIteration(mytimer) );
00153         
00154                 cout << "f(x) = " << m_fF << endl;
00155         }
00156 
00157 private:
00158         QuasiNewton* m_pSelf;
00159 
00160         // These data members are expected to be initialized at start of 'void solve()' call.
00161         unsigned long m_nIter;
00162         Model* m_pModel;
00163         Matrix m_mxVars;      // variable matrix is single-column.
00164         Matrix m_mxdVars;
00165         Matrix m_mxVarsOld;
00166         Matrix m_mxdF;
00167         Matrix m_mxdFOld;
00168         Matrix m_mxD;
00169         Matrix m_mxDOld;
00170         double m_fF;
00171         double m_fFOld;
00172         double m_fNorm;
00173         double m_fTolerance;
00174 
00175     BaseFuncObj* m_pFuncObj;
00176 
00177     bool m_debug;
00178 
00179         bool evaluateFunc()
00180         {
00181         using ::std::vector;
00182         using ::std::cout;
00183         using ::std::endl;
00184 
00185                 // Solve f(x) given the x vector
00186 
00187                 vector<double> fVars;
00188                 m_fF = QuasiNewtonImpl::evalF( *m_pFuncObj, m_mxVars, fVars );
00189         if (m_debug)
00190             fprintf(stdout, "QuasiNewtonImpl::evaluateFunc:   F = %g\n", m_fF);
00191                 size_t nRows = fVars.size();
00192 
00193                 // Calculate df(x) gradient array
00194         for (size_t i = 0; i < nRows; ++i)
00195         {
00196             m_pFuncObj->setVars(fVars);
00197             SingleVarFuncObj& rSingleFunc = m_pFuncObj->getSingleVarFuncObj(i);
00198             NumericalDiffer diff;
00199             diff.setFuncObject(&rSingleFunc);
00200             diff.setVariable(fVars.at(i));
00201             diff.setPrecision(5);
00202             m_mxdF(i, 0) = diff.run();
00203         }
00204 
00205                 m_fNorm = QuasiNewtonImpl::norm( m_mxdF );
00206         if (m_debug)
00207         {
00208             fprintf(stdout, "QuasiNewtonImpl::evaluateFunc:   df(x) gradient array: ");
00209             m_mxdF.trans().print();
00210             fprintf(stdout, "QuasiNewtonImpl::evaluateFunc:   norm = %g\n", m_fNorm);
00211         }
00212 
00213         double delta = fabs(m_fF - m_fFOld);
00214                 if (delta < m_fTolerance )
00215                 {
00216                         if (m_debug)
00217             {
00218                 fprintf(stdout, "QuasiNewtonImpl::evaluateFunc:   desired precision has been reached (delta = %g)\n", 
00219                         delta);
00220             }
00221                         return true;
00222                 }
00223 
00224                 if ( m_fNorm < m_fTolerance )
00225                 {
00226                         if (m_debug)
00227                 fprintf(stdout, "QuasiNewtonImpl::evaluateFunc:   norm is below specified tolerance limit\n");
00228                         return true;
00229                 }
00230 
00231                 return false;
00232         }
00233 
00240         bool calcDefMatrix()
00241         {
00242                 if ( m_mxD.empty() )
00243                 {
00244                         // Deflection matrix is empty.  Initialize it.
00245                         size_t nX = m_mxVars.rows();
00246                         Matrix mxTemp( nX, nX, true );
00247                         m_mxD.swap( mxTemp );
00248                         if ( m_pModel->getGoal() == GOAL_MAXIMIZE )
00249                                 m_mxD *= -1.0;
00250                 }
00251                 else
00252                 {
00253                         // Both mxD and mxG are columnar matrix.
00254                         Matrix mxD = m_mxVars - m_mxVarsOld;
00255                         Matrix mxG = m_mxdF - m_mxdFOld;
00256                         double fDG = ( mxD.trans()*mxG )( 0, 0 );
00257                         if ( fDG == 0.0 )
00258                         {
00259                                 if (m_debug)
00260                                         cout << "dg is zero!" << endl;
00261                                 return true;
00262                         }
00263 
00264                         // Calculate new deflection matrix via BFGS formula
00265                         Matrix mxA = mxG.trans() * m_mxDOld * mxG;
00266                         double fA = 1.0 + mxA( 0, 0 ) / fDG;
00267                         Matrix mxB = mxD * mxD.trans() / fDG;
00268                         Matrix mxC = ( m_mxDOld * mxG * mxD.trans() + mxD * mxG.trans() * m_mxDOld ) / fDG;
00269                         m_mxD = m_mxDOld + mxB*fA - mxC;
00270                 }
00271 
00272                 m_mxdVars = m_mxD * m_mxdF*(-1.0);
00273 
00274                 if (m_debug && false)
00275                 {
00276                         unsigned long nPrec = m_pModel->getPrecision();
00277                         cout << setprecision( nPrec );
00278                         cout << "f(x) = " << m_fF << endl;
00279                         cout << "df(x) = ";
00280                         m_mxdF.trans().print( nPrec );
00281                         cout << "|| df(x) || = " << m_fNorm << endl;
00282                         cout << "D:";
00283                         m_mxD.print( nPrec );
00284                         cout << "dx = ";
00285                         m_mxdVars.trans().print( nPrec );
00286                 }
00287 
00288                 return false;
00289         }
00290 
00291         void runLinearSearch( const ::scsolver::Timer& timer )
00292         {
00293                 // m_mxVars  : Original X (column matrix)
00294                 // m_mxdVars : dX (column matrix)
00295                 // m_fF      : Original f(x)
00296 
00297         vector<double> vars, dx;
00298         matrixToVector(m_mxVars, vars);
00299         matrixToVector(m_mxdVars, dx);
00300         m_pFuncObj->setVars(vars);
00301         SingleVarFuncObj& singleFunc = m_pFuncObj->getSingleVarFuncObjByRatio(dx);
00302         QuadFitLineSearch qfit;
00303         qfit.setFuncObj(&singleFunc);
00304         qfit.setGoal(m_pModel->getGoal());
00305         qfit.solve();
00306         vars = m_pFuncObj->getVars();
00307 
00308         // Update the stored variables.
00309         m_mxVarsOld = m_mxVars;
00310         vectorToMatrix(vars, m_mxVars, false);
00311         }
00312 
00313         bool runIteration( const ::scsolver::Timer& timer )
00314         {
00315                 if ( timer.isTimedOut() )
00316                         throw IterationTimedOut();
00317 
00318                 if ( m_nIter > 500 )
00319                         throw MaxIterationReached();
00320 
00321                 if (m_debug)
00322                 {
00323                         cout << repeatString( "-", 70 ) << endl;
00324                         cout << "Iteration " << m_nIter << endl;
00325                         cout << repeatString( "-", 70 ) << endl;
00326                         cout << "x = ";
00327                         m_mxVars.trans().print( m_pModel->getPrecision() );
00328                 }
00329 
00330                 if ( evaluateFunc() )
00331         {
00332             if (m_debug)
00333                 fprintf(stdout, "QuasiNewtonImpl::runIteration:   evaluateFunc returned true\n");
00334             return true;
00335         }
00336 
00337                 if ( calcDefMatrix() )
00338         {
00339             if (m_debug)
00340                 fprintf(stdout, "QuasiNewtonImpl::runIteration:   calcDefMatrix returned true\n");
00341             return true;
00342         }
00343 
00344                 runLinearSearch(timer);
00345 
00346                 // Update parameters for next iteration
00347 
00348                 m_mxdFOld = m_mxdF;
00349                 m_mxDOld = m_mxD;
00350                 m_fFOld = m_fF;
00351                 ++m_nIter;
00352 
00353                 return false;
00354         }
00355 };
00356 
00357 //-----------------------------------------------------------------
00358 
00359 QuasiNewton::QuasiNewton() : m_pImpl( new QuasiNewtonImpl( this ) )
00360 {
00361 }
00362 
00363 QuasiNewton::~QuasiNewton() throw()
00364 {
00365 }
00366 
00367 void QuasiNewton::solve()
00368 {
00369         m_pImpl->solve();
00370 }
00371 
00372 }}}
00373 

Generated on Mon Jul 28 09:13:20 2008 for scsolver by  doxygen 1.5.3