00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
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
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);
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
00161 unsigned long m_nIter;
00162 Model* m_pModel;
00163 Matrix m_mxVars;
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
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
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
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
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
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
00294
00295
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
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
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