Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

PositiveDefiniteLevenbergMarquardtMinimizer.cpp

Go to the documentation of this file.
00001 // PositiveDefiniteLevenbergMarquardtMinimizer.cpp: implementation of the LevenbergMarquardtMinimizer class.
00002 //
00004 
00005 #include "PositiveDefiniteLevenbergMarquardtMinimizer.h"
00006 
00008 // Construction/Destruction
00010 
00011 CPositiveDefiniteLevenbergMarquardtMinimizer::CPositiveDefiniteLevenbergMarquardtMinimizer()
00012 {
00013         m_dMarquardt = 0.00001;
00014         m_dChiSqTol = 0.001;
00015         m_dGradTol = 0.001;
00016         m_iNIterations = 1000;
00017         // need to add filter to default constructor
00018 }
00019 
00020 CPositiveDefiniteLevenbergMarquardtMinimizer::CPositiveDefiniteLevenbergMarquardtMinimizer(CParameterFilter *pFilter, double marquardt, double chiTol, double gradTol, int nIterations)
00021 {
00022         m_dMarquardt = marquardt;
00023         m_dChiSqTol = chiTol;
00024         m_dGradTol = gradTol;
00025         m_iNIterations = nIterations;
00026         m_pFilter = pFilter;
00027 }
00028 
00029 CPositiveDefiniteLevenbergMarquardtMinimizer::~CPositiveDefiniteLevenbergMarquardtMinimizer()
00030 {
00031 
00032 }
00033 
00034 double CPositiveDefiniteLevenbergMarquardtMinimizer::Minimize(double *parameters, Minimizable *minimizable)
00035 {
00036         int i;
00037         // set to false if the derivatives don't need to be recomputed
00038         bool computeFlag = true;
00039         m_bBreakFlag = false;
00040         m_bReconditionFlag = true;
00041         // downcast the minimizable, and throw an exception if it is not an NLLSMinimizable 
00042         // (LevenbergMarquardt should only be used on a sum of squares objective function)
00043         NLLSMinimizable *nlls = dynamic_cast<NLLSMinimizable *>(minimizable);
00044         if(0 == nlls)
00045         {
00046                 throw std::runtime_error("Minimizable is not a sum of squares!");
00047         }
00048         
00049         // get the number of parameters and allocate 
00050         nParameters = nlls->GetNParameters();
00051         m_iNResiduals = nlls->GetNResiduals();
00052         
00053         m_pdAlpha = new double *[nParameters];
00054         m_pdAlpha[0] = new double[nParameters*nParameters];
00055         for(int nPar = 1; nPar < nParameters; nPar++)
00056         {
00057                 m_pdAlpha[nPar] = &(m_pdAlpha[0][nPar*nParameters]);
00058         }
00059         m_pdBeta = new double[nParameters];
00060         m_pdUpperBounds = new double[nParameters];
00061         m_pdLowerBounds = new double[nParameters];
00062 
00063         this->SetBounds(parameters);
00064         
00065         // non-member data
00066         double *deltaP = new double[nParameters];
00067         double *trialP = new double[nParameters];
00068         
00069         // compute the starting cost
00070         double chiSqOld = nlls->ObjectiveFunction(parameters);
00071         cout << "Starting cost of " << chiSqOld << endl;
00072         double chiSqNew = chiSqOld;
00073 
00074         int nIterations = 0;
00075         while( (nIterations < m_iNIterations) && (!m_bBreakFlag))
00076         {
00077                 // compute alpha,beta
00078                 if(computeFlag){ComputeDerivativeInformation(parameters,nlls);}
00079                 
00080                 
00081                 // rescale the diagonal elements of the hessian
00082                 for(i = 0; i < nParameters; i++) 
00083                 { 
00084                         m_pdAlpha[i][i] = (1.0 + m_dMarquardt)*m_pdAlpha[i][i];
00085                 }
00086         
00087                 SVDSolveMarquardtSystem(deltaP);
00088                 
00089                 // undo hessian scaling for next pass (the Marquardt parameter
00090                 // changes regardless)
00091                 for(i = 0; i < nParameters; i++) 
00092                 { 
00093                         m_pdAlpha[i][i] = m_pdAlpha[i][i]/(1.0 + m_dMarquardt);
00094                 }
00095                 
00096                 // now evaluate the cost for the new set of parameters
00097                 m_pFilter->ForwardTransformation(parameters,nParameters);
00098                 for(int j = 0; j < nParameters; j++)
00099                 {
00100                         trialP[j] = parameters[j] + deltaP[j];
00101                 }
00102                 //this->CheckBounds(trialP);
00103                 m_pFilter->BackwardTransformation(parameters,nParameters);
00104                 m_pFilter->BackwardTransformation(trialP,nParameters);
00105 
00106                 
00107                 chiSqNew = nlls->ObjectiveFunction(trialP);
00108                 
00109                 
00110                 // Sometimes the trial parameters are too big and we get -1*INF for 
00111                 // the objective function; for a sum-of-squares objective function,
00112                 // we know it is intrinsically positive so we should not accept 
00113                 // that move and hence those parameters.  The "or" statement in the 
00114                 // next line is an attempt to recover from these errors.
00115                 
00116                 if((chiSqNew >= chiSqOld) || (chiSqNew < 0.0))
00117                 {
00118                         m_dMarquardt = 10*m_dMarquardt;
00119                         computeFlag = false;
00120                 }
00121                 else
00122                 {       
00123                         cout << "Move accepted, new cost is " << chiSqNew << endl;
00124                         double fracTol = (chiSqNew - chiSqOld)/chiSqOld;
00125                         // accept new parameters
00126                         for(i = 0; i < nParameters; i++)
00127                         {
00128                                 parameters[i] = trialP[i];
00129                         }                                               
00130                         chiSqOld = chiSqNew;
00131 
00132                         // write the accepted parameters to a file, because numerically 
00133                         // computing the hessian is extremely expensive
00134                         fstream *pftemp = new fstream("martemp.par",ios::out);
00135                         *pftemp << "##################################################################" << endl;
00136                         *pftemp << "# BEST PARAMETERS" << endl;
00137                         *pftemp << "# COST : " << chiSqNew << endl;
00138                         *pftemp << "##################################################################" << endl;
00139                         for(i = 0; i < nParameters; i++)
00140                         {
00141                                 *pftemp << parameters[i] << endl;
00142                         }
00143                         delete pftemp;
00144                         // END WRITE
00145                         
00146                         // update marquardt parameter
00147                         m_dMarquardt = 0.1*m_dMarquardt;
00148                         computeFlag = true;     
00149                         // set new bounds
00150                         this->SetBounds(parameters);
00151                         
00152                         // quit the minimization if the fractional change in cost is small
00153                         if(fabs(fracTol) < m_dChiSqTol)
00154                         {
00155                                 cout << "Convergence on Chi-Squared criterion." << endl;
00156                                 m_bBreakFlag = true; 
00157                         }
00158                 }
00159 
00160                 nIterations++;
00161         }
00162         
00163 
00164         delete [] deltaP;
00165         delete [] trialP;
00166         delete [] m_pdBeta;
00167         delete [] m_pdAlpha[0];
00168         delete [] m_pdAlpha;
00169 
00170         return chiSqOld;
00171 }
00172 
00174 // Compute approximate Hessian for use in Levenberg-Marquardt alg.
00175 // The hessian is symmetric so we only need compute 1/2 of it then
00176 // copy the rest (might not even need to store the other 1/2?)
00178 
00179 void CPositiveDefiniteLevenbergMarquardtMinimizer::ComputeDerivativeInformation(double *parameters, NLLSMinimizable *nlls)
00180 {
00181         int i;
00182         double transparameter = 0.0;
00183         double saveparameter = 0.0;
00184         double gradNorm = 0.0;
00185         double *fxPlus = new double[m_iNResiduals];
00186         double *fx = new double[m_iNResiduals];
00187         double **jacobian = new double *[m_iNResiduals];
00188         jacobian[0] = new double[m_iNResiduals*nParameters];
00189         for(int nR = 1; nR < m_iNResiduals; nR++)
00190         {
00191                 jacobian[nR] = &(jacobian[0][nR*nParameters]);
00192         }
00193 
00194         double chiSq = nlls->ObjectiveFunction(parameters);
00195         for(int k = 0; k < m_iNResiduals; k++)
00196         {
00197                 fx[k] = (nlls->GetResiduals())[k];
00198         }
00199 
00200         // loop over parameters
00201         for(i = 0; i < nParameters; i++)
00202         {
00203                 // transform the parameter and save its old value
00204                 saveparameter = parameters[i];
00205                 transparameter = m_pFilter->Operator(parameters[i]);
00206 
00207                 // if the parameter happens to equal 0.0, the stepsize will then be 
00208                 // zero and give NaN upon division, so we set it to 1.0e-08
00209                 double h = 1.0e-08;
00210                 if(transparameter > 0.0)
00211                 {
00212                         h = pow(DBL_EPSILON,0.333333)*transparameter;
00213                         //h = DBL_EPSILON*transparameter;
00214                 }
00215                                 
00216                 // forward step
00217                 parameters[i] = m_pFilter->OperatorInverse(transparameter + h);
00218                 double chiSqPlus = nlls->ObjectiveFunction(parameters);
00219                 for(int k = 0; k < m_iNResiduals; k++)
00220                 {
00221                         fxPlus[k] = (nlls->GetResiduals())[k];
00222                 }
00223 
00224                 
00225                 // compute scaled gradient element
00226                 m_pdBeta[i] = -0.5*((chiSqPlus - chiSq)/h);
00227                 gradNorm += m_pdBeta[i]*m_pdBeta[i];
00228                 
00229                 parameters[i] = saveparameter;
00230 
00231                 // fill in jacobian
00232                 for(int j = 0; j < m_iNResiduals; j++)
00233                 {
00234                         double delta = (fxPlus[j] - fx[j])/h;
00235                         jacobian[j][i] = -1*delta;
00236                 }
00237         }
00238         /*
00239         // compute -J^T f
00240         for(i = 0; i < nParameters; i++)
00241         {
00242                 m_pdBeta[i] = 0.0;
00243                 for(int j = 0; j < m_iNResiduals; j++)
00244                 {
00245                         m_pdBeta[i] += -1.0*jacobian[j][i]*fx[j]; 
00246                 }
00247                 gradNorm += m_pdBeta[i]*m_pdBeta[i];
00248         }
00249         */
00250         // compute gradient norm and check it
00251         gradNorm = sqrt(gradNorm);
00252         if(gradNorm < m_dGradTol) 
00253         {
00254                 cout << "Convergence on ||grad|| criterion." << endl;
00255                 m_bBreakFlag = true;
00256         }
00257 
00258         // compute the scaled hessian (it is symmetric)
00259         for(i = 0; i < nParameters; i++)
00260         {
00261                 for(int k = i; k < nParameters; k++)
00262                 {
00263                         m_pdAlpha[i][k] = 0.0;
00264                         for(int j = 0; j < m_iNResiduals; j++)
00265                         {
00266                                 m_pdAlpha[i][k] += jacobian[j][i]*jacobian[j][k];
00267                         }
00268                         m_pdAlpha[k][i] = m_pdAlpha[i][k];
00269                 }
00270         }
00271 
00272         delete [] fxPlus;
00273         delete [] fx;
00274         delete [] jacobian[0];
00275         delete [] jacobian;
00276 
00277         cout << "Hessian calculated . . ." << endl;
00278 
00279         return;
00280 }
00281 
00283 // Solves the Levenberg-Marquardt system alpha.deltaP = beta, using 
00284 // LAPACK functions for Cholesky decomposition and backsubstitution.
00285 // On return, deltaP contains the solution, and alpha and beta are 
00286 // not modified.
00288 
00289 
00290 void CPositiveDefiniteLevenbergMarquardtMinimizer::SVDSolveMarquardtSystem(double *deltaP)
00291 {
00292         int l,i;
00293         // solve the system by SVD decomposition
00294         char JOBU = 'A';
00295         char JOBVT = 'A';
00296         int M = nParameters;
00297         int N = nParameters;
00298         
00299         // matrix A in column-major order
00300         double *A = new double[nParameters*nParameters];
00301         int counter = 0;
00302         for(int j = 0; j < nParameters; j++)
00303         {
00304                 deltaP[j] = 0.0;
00305                 for(int i = 0; i < nParameters; i++)
00306                 {
00307                         A[counter] = m_pdAlpha[i][j];
00308                         counter++;
00309                 }
00310         }
00311         
00312         double *D = new double[nParameters];
00313         double *E = new double[nParameters];
00314         double *TAUQ = new double[nParameters];
00315         double *TAUP = new double[nParameters];
00316 
00317         double *PT = new double[nParameters*nParameters];
00318         double *Q = new double[nParameters*nParameters];
00319         double *ATEMP = new double[nParameters*nParameters];
00320 
00321         int LDA = nParameters;
00322         double *S = new double[nParameters];
00323         int LDU = nParameters;
00324         int LDVT = nParameters;
00325         int LWORK = 5*M;
00326         double *WORK = new double[LWORK];
00327         int INFO = 0;
00328 
00329         // put the matrix into band diagonal form
00330         DGEBRD(&M,&N,A,&LDA,D,E,TAUQ,TAUP,WORK,&LWORK,&INFO);
00331         //cout << "Band diagonal form: INFO = " << INFO << endl;
00332         
00333         // now obtain Q and copy it into our array
00334         for(l = 0; l < nParameters*nParameters; l++)
00335         {
00336                 ATEMP[l] = A[l];
00337         }
00338         char VECT = 'Q';
00339         int K = M;
00340         DORGBR(&VECT,&M,&N,&K,ATEMP,&LDA,TAUQ,WORK,&LWORK,&INFO);
00341         //cout << "Q : INFO = " << INFO << endl;
00342         for(l = 0; l < nParameters*nParameters; l++)
00343         {
00344                 Q[l] = ATEMP[l];
00345         }
00346         // now obtain PT
00347         for(l = 0; l < nParameters*nParameters; l++)
00348         {
00349                 ATEMP[l] = A[l];
00350         }
00351         VECT = 'P';
00352         DORGBR(&VECT,&M,&N,&K,ATEMP,&LDA,TAUP,WORK,&LWORK,&INFO);
00353         //cout << "PT : INFO = " << INFO << endl;
00354         for(l = 0; l < nParameters*nParameters; l++)
00355         {
00356                 PT[l] = ATEMP[l];
00357         }
00358 
00359         // obtain the SVD decomposition of A, using the matrices found above
00360         char UPLO = 'U';
00361         int NCVT = nParameters;
00362         int NRU = nParameters;
00363         int NCC = 0;
00364         double *C = 0;
00365         int LDC = 1;
00366         DBDSQR(&UPLO,&N,&NCVT,&NRU,&NCC,D,E,PT,&LDVT,Q,&LDU,C,&LDC,WORK,&INFO);
00367         cout << "Matrix decomposed. Condition number of Hessian is " << D[0]/D[nParameters-1] << endl;
00368 
00369         // scroll through the singular values and find the ones whose inverses will be 
00370         // huge and set them to zero.
00371         double cutoff = 1.0e-06*D[0];
00372         for(i = 0; i < nParameters; i++)
00373         {
00374                 if(D[i] < cutoff) {D[i] = 0;}
00375                 else {D[i] = 1/D[i];}
00376         }
00377 
00378         // now do the multiplications to find the new parameters
00379         // first UT
00380         for(i = 0; i < nParameters; i++)
00381         {
00382                 for(int j = 0; j < nParameters; j++)
00383                 {
00384                         deltaP[i] += Q[i*nParameters + j]*m_pdBeta[j];
00385                 }
00386         }
00387         // now the inverse singular values
00388         for(i = 0; i < nParameters; i++)
00389         {
00390                 deltaP[i] = deltaP[i]*D[i];
00391         }
00392         // now VT
00393         for(i = 0; i < nParameters; i++)
00394         {
00395                 S[i] = 0.0;
00396                 for(int j = 0; j < nParameters; j++)
00397                 {
00398                         S[i] += PT[i*nParameters + j]*deltaP[j];
00399                 }
00400         }
00401         // copy back into deltaP
00402         for(i = 0; i < nParameters; i++)
00403         {
00404                 deltaP[i] = S[i];
00405         }
00406 
00407         delete [] A;
00408         delete [] ATEMP;
00409         delete [] TAUQ;
00410         delete [] TAUP;
00411         delete [] Q;
00412         delete [] PT;
00413         delete [] D;
00414         delete [] E;
00415         delete [] S;
00416         delete [] WORK;
00417 }
00418 
00419 void CPositiveDefiniteLevenbergMarquardtMinimizer::LUSolveMarquardtSystem(double *deltaP)
00420 {
00421         // we're giving the LAPACK routine the upper triangle (though it doesn't matter)
00422         char UPLO = 'U';
00423         char TRANS = 'N';
00424         // can check for correct factorization
00425         int INFO=0; 
00426         // order of A
00427         int N = nParameters;
00428         int M = nParameters;
00429         
00430         // need to be >= max(1,N);
00431         int LDA = nParameters;
00432         int LDB = nParameters;
00433         // only one right hand side
00434         int NRHS  = 1;
00435 
00436         // allocate temporary storage space
00437         double *B = new double[nParameters];
00438 
00439         // LU DECOMPOSITION & SOLUTION (LAPACK)
00440         // copy alpha and beta appropriately
00441         double *A = new double[nParameters*nParameters];
00442         int *IPIV = new int[nParameters];
00443         int counter = 0;
00444         for(int j = 0; j < nParameters; j++)
00445         {
00446                 B[j] = m_pdBeta[j];
00447                 for(int i = 0; i < nParameters; i++)
00448                 {
00449                         A[counter] = m_pdAlpha[i][j];
00450                         counter++;
00451                 }
00452         }
00453         
00454         // LU decompose
00455         DGETRF(&M,&N,A,&LDA,IPIV,&INFO);
00456         if(INFO > 0)
00457         {
00458                 m_bReconditionFlag = true;
00459                 cout << "Matrix singularity suspected, attempting to recondition . . . " << endl;
00460         }
00461         else
00462         {
00463                 m_bReconditionFlag = false;
00464                 // solve
00465                 DGETRS(&TRANS,&N,&NRHS,A,&LDA,IPIV,B,&LDB,&INFO);
00466                 // copy back the parameters
00467                 for(int i = 0; i < nParameters; i++)
00468                 {
00469                         deltaP[i] = B[i];
00470                 }
00471         }
00472 
00473                 
00474         delete [] A;
00475         delete [] B;
00476         delete [] IPIV;
00477 }
00478 
00479 void CPositiveDefiniteLevenbergMarquardtMinimizer::SetBounds(double *parameters)
00480 {
00481         for(int i = 0; i < nParameters; i++)
00482         {
00483                 m_pdLowerBounds[i] = (1.0/100.0)*parameters[i];
00484                 m_pdUpperBounds[i] = 100.0*parameters[i];
00485         }
00486 }
00487 
00488 void CPositiveDefiniteLevenbergMarquardtMinimizer::CheckBounds(double *parameters)
00489 {
00490         for(int i = 0; i < nParameters; i++)
00491         {
00492                 if(parameters[i] < m_pdLowerBounds[i])
00493                 {
00494                         parameters[i] = m_pdLowerBounds[i];
00495                 }
00496                 else if(parameters[i] > m_pdUpperBounds[i])
00497                 {
00498                         parameters[i] = m_pdUpperBounds[i];
00499                 }
00500         }
00501 }

Generated on Mon Nov 3 09:38:08 2003 by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002