SimulatedAnnealing.cxx

Go to the documentation of this file.
00001 // @(#)root/tmva $Id: SimulatedAnnealing.cxx 33928 2010-06-15 16:19:31Z stelzer $   
00002 // Author: Andreas Hoecker, Joerg Stelzer, Helge Voss, Krzysztof Danielowski, Kamil Kraszewski, Maciej Kruk
00003 
00004 /**********************************************************************************
00005  * Project: TMVA - a Root-integrated toolkit for multivariate data analysis       *
00006  * Package: TMVA                                                                  *
00007  * Class  : SimulatedAnnealing                                                    *
00008  * Web    : http://tmva.sourceforge.net                                           *
00009  *                                                                                *
00010  * Description:                                                                   *
00011  *      Implementation (see header for description)                               *
00012  *                                                                                *
00013  * Authors (alphabetical):                                                        *
00014  *      Krzysztof Danielowski <danielow@cern.ch>       - IFJ & AGH, Poland        *
00015  *      Kamil Kraszewski      <kalq@cern.ch>           - IFJ & UJ, Poland         *
00016  *      Maciej Kruk           <mkruk@cern.ch>          - IFJ & AGH, Poland        *
00017  *                                                                                *
00018  * Copyright (c) 2008:                                                            *
00019  *      IFJ-Krakow, Poland                                                        *
00020  *      CERN, Switzerland                                                         * 
00021  *      MPI-K Heidelberg, Germany                                                 * 
00022  *                                                                                *
00023  * Redistribution and use in source and binary forms, with or without             *
00024  * modification, are permitted according to the terms listed in LICENSE           *
00025  * (http://tmva.sourceforge.net/LICENSE)                                          *
00026  **********************************************************************************/
00027 
00028 //_______________________________________________________________________
00029 //
00030 // Implementation of Simulated Annealing fitter
00031 //_______________________________________________________________________
00032 #include "TMVA/SimulatedAnnealing.h"
00033 
00034 #include "TRandom3.h"
00035 #include "TMath.h"
00036 
00037 #include "TMVA/Interval.h"
00038 #include "TMVA/IFitterTarget.h"
00039 #include "TMVA/GeneticRange.h"
00040 #include "TMVA/Timer.h"
00041 #include "TMVA/MsgLogger.h"
00042 
00043 ClassImp(TMVA::SimulatedAnnealing)
00044 
00045 //_______________________________________________________________________
00046 TMVA::SimulatedAnnealing::SimulatedAnnealing( IFitterTarget& target, const std::vector<Interval*>& ranges )
00047    : fKernelTemperature     (kIncreasingAdaptive),
00048      fFitterTarget          ( target ),
00049      fRandom                ( new TRandom3(100) ),
00050      fRanges                ( ranges ),
00051      fMaxCalls              ( 500000 ),
00052      fInitialTemperature    ( 1000 ),
00053      fMinTemperature        ( 0 ),
00054      fEps                   ( 1e-10 ),
00055      fTemperatureScale      ( 0.06 ),
00056      fAdaptiveSpeed         ( 1.0 ),
00057      fTemperatureAdaptiveStep( 0.0 ),
00058      fUseDefaultScale       ( kFALSE ),
00059      fUseDefaultTemperature ( kFALSE ),
00060      fLogger( new MsgLogger("SimulatedAnnealing") ),
00061      fProgress(0.0)
00062 {
00063    // constructor
00064    fKernelTemperature = kIncreasingAdaptive;
00065 }
00066 
00067 //_______________________________________________________________________
00068 void TMVA::SimulatedAnnealing::SetOptions( Int_t    maxCalls,
00069                                            Double_t initialTemperature,
00070                                            Double_t minTemperature,
00071                                            Double_t eps,
00072                                            TString  kernelTemperatureS,
00073                                            Double_t temperatureScale,
00074                                            Double_t adaptiveSpeed,
00075                                            Double_t temperatureAdaptiveStep,
00076                                            Bool_t   useDefaultScale,
00077                                            Bool_t   useDefaultTemperature)   
00078 {
00079    // option setter
00080 
00081    fMaxCalls = maxCalls;
00082    fInitialTemperature = initialTemperature;
00083    fMinTemperature = minTemperature;
00084    fEps = eps;
00085 
00086    if      (kernelTemperatureS == "IncreasingAdaptive") {
00087       fKernelTemperature = kIncreasingAdaptive; 
00088       Log() << kINFO << "Using increasing adaptive algorithm" << Endl;
00089    }
00090    else if (kernelTemperatureS == "DecreasingAdaptive") {
00091       fKernelTemperature = kDecreasingAdaptive; 
00092       Log() << kINFO << "Using decreasing adaptive algorithm" << Endl;
00093    }
00094    else if (kernelTemperatureS == "Sqrt") {
00095       fKernelTemperature = kSqrt; 
00096       Log() << kINFO << "Using \"Sqrt\" algorithm" << Endl;
00097    }
00098    else if (kernelTemperatureS == "Homo") {
00099       fKernelTemperature = kHomo; 
00100       Log() << kINFO << "Using \"Homo\" algorithm" << Endl;
00101    }
00102    else if (kernelTemperatureS == "Log") {
00103       fKernelTemperature = kLog;  
00104       Log() << kINFO << "Using \"Log\" algorithm" << Endl;
00105    }
00106    else if (kernelTemperatureS == "Sin") {
00107       fKernelTemperature = kSin;
00108       Log() << kINFO << "Using \"Sin\" algorithm" << Endl;
00109    }
00110 
00111    fTemperatureScale        = temperatureScale;
00112    fAdaptiveSpeed           = adaptiveSpeed;
00113    fTemperatureAdaptiveStep = temperatureAdaptiveStep;
00114 
00115    fUseDefaultScale         = useDefaultScale;
00116    fUseDefaultTemperature   = useDefaultTemperature;
00117 }
00118 //_______________________________________________________________________
00119 TMVA::SimulatedAnnealing::~SimulatedAnnealing()
00120 {
00121    // destructor
00122 }
00123 
00124 //_______________________________________________________________________
00125 void TMVA::SimulatedAnnealing::FillWithRandomValues( std::vector<Double_t>& parameters )
00126 {
00127    // random starting parameters
00128    for (UInt_t rIter = 0; rIter < parameters.size(); rIter++) {
00129       parameters[rIter] = fRandom->Uniform(0.0,1.0)*(fRanges[rIter]->GetMax() - fRanges[rIter]->GetMin()) + fRanges[rIter]->GetMin();
00130    }
00131 }
00132 
00133 //_______________________________________________________________________
00134 void TMVA::SimulatedAnnealing::ReWriteParameters( std::vector<Double_t>& from, std::vector<Double_t>& to)
00135 {
00136    // copy parameters
00137    for (UInt_t rIter = 0; rIter < from.size(); rIter++) to[rIter] = from[rIter];
00138 }
00139 
00140 //_______________________________________________________________________
00141 void TMVA::SimulatedAnnealing::GenerateNeighbour( std::vector<Double_t>& parameters, std::vector<Double_t>& oldParameters, 
00142                                                   Double_t currentTemperature )
00143 {
00144    // generate adjacent parameters
00145    ReWriteParameters( parameters, oldParameters );
00146 
00147    for (UInt_t rIter=0;rIter<parameters.size();rIter++) {
00148       Double_t uni,distribution,sign;
00149       do {
00150          uni = fRandom->Uniform(0.0,1.0);
00151          sign = (uni - 0.5 >= 0.0) ? (1.0) : (-1.0);
00152          distribution = currentTemperature * (TMath::Power(1.0 + 1.0/currentTemperature, TMath::Abs(2.0*uni - 1.0)) -1.0)*sign;
00153          parameters[rIter] = oldParameters[rIter] +  (fRanges[rIter]->GetMax()-fRanges[rIter]->GetMin())*0.1*distribution;
00154       }
00155       while (parameters[rIter] < fRanges[rIter]->GetMin() || parameters[rIter] > fRanges[rIter]->GetMax() );
00156    }
00157 }
00158 //_______________________________________________________________________
00159 std::vector<Double_t> TMVA::SimulatedAnnealing::GenerateNeighbour( std::vector<Double_t>& parameters, Double_t currentTemperature )
00160 {
00161    // generate adjacent parameters
00162    std::vector<Double_t> newParameters( fRanges.size() );   
00163 
00164    for (UInt_t rIter=0; rIter<parameters.size(); rIter++) {
00165       Double_t uni,distribution,sign;
00166       do {
00167          uni = fRandom->Uniform(0.0,1.0);
00168          sign = (uni - 0.5 >= 0.0) ? (1.0) : (-1.0);
00169          distribution = currentTemperature * (TMath::Power(1.0 + 1.0/currentTemperature, TMath::Abs(2.0*uni - 1.0)) -1.0)*sign;
00170          newParameters[rIter] = parameters[rIter] +  (fRanges[rIter]->GetMax()-fRanges[rIter]->GetMin())*0.1*distribution;
00171       }
00172       while (newParameters[rIter] < fRanges[rIter]->GetMin() || newParameters[rIter] > fRanges[rIter]->GetMax() );
00173    }
00174 
00175    return newParameters;
00176 }
00177 
00178 //_______________________________________________________________________
00179 void TMVA::SimulatedAnnealing::GenerateNewTemperature( Double_t& currentTemperature, Int_t Iter )
00180 {
00181    // generate new temperature
00182    if      (fKernelTemperature == kSqrt) {
00183          currentTemperature = fInitialTemperature/(Double_t)TMath::Sqrt(Iter+2) * fTemperatureScale;
00184    }
00185    else if (fKernelTemperature == kLog) {
00186       currentTemperature = fInitialTemperature/(Double_t)TMath::Log(Iter+2) * fTemperatureScale;
00187    }
00188    else if (fKernelTemperature == kHomo) {
00189       currentTemperature = fInitialTemperature/(Double_t)(Iter+2) * fTemperatureScale;
00190    }
00191    else if (fKernelTemperature == kSin) {
00192       currentTemperature = (TMath::Sin( (Double_t)Iter / fTemperatureScale ) + 1.0 )/ (Double_t)(Iter+1.0) * fInitialTemperature + fEps;
00193    }
00194    else if (fKernelTemperature == kGeo) {
00195       currentTemperature = currentTemperature*fTemperatureScale;
00196    }
00197    else if (fKernelTemperature == kIncreasingAdaptive) {
00198       currentTemperature = fMinTemperature + fTemperatureScale*TMath::Log(1.0+fProgress*fAdaptiveSpeed);
00199    }
00200    else if (fKernelTemperature == kDecreasingAdaptive) {
00201       currentTemperature = currentTemperature*fTemperatureScale;
00202    }
00203    else Log() << kFATAL << "No such kernel!" << Endl;
00204 }
00205 
00206 //________________________________________________________________________
00207 Bool_t TMVA::SimulatedAnnealing::ShouldGoIn( Double_t currentFit, Double_t localFit, Double_t currentTemperature )
00208 {
00209    // result checker
00210    if (currentTemperature < fEps) return kFALSE;
00211    Double_t lim  = TMath::Exp( -TMath::Abs( currentFit - localFit ) / currentTemperature );
00212    Double_t prob = fRandom->Uniform(0.0, 1.0);
00213    return (prob < lim) ? kTRUE : kFALSE;
00214 }
00215 
00216 //_______________________________________________________________________
00217 void TMVA::SimulatedAnnealing::SetDefaultScale()
00218 {
00219    // setting of default scale
00220    if      (fKernelTemperature == kSqrt) fTemperatureScale = 1.0;
00221    else if (fKernelTemperature == kLog)  fTemperatureScale = 1.0;
00222    else if (fKernelTemperature == kHomo) fTemperatureScale = 1.0;
00223    else if (fKernelTemperature == kSin)  fTemperatureScale = 20.0;
00224    else if (fKernelTemperature == kGeo)  fTemperatureScale = 0.99997;
00225    else if (fKernelTemperature == kDecreasingAdaptive) {
00226       fTemperatureScale = 1.0;
00227       while (TMath::Abs(TMath::Power(fTemperatureScale,fMaxCalls) * fInitialTemperature - fMinTemperature) >
00228              TMath::Abs(TMath::Power(fTemperatureScale-0.000001,fMaxCalls) * fInitialTemperature - fMinTemperature)) {
00229          fTemperatureScale -= 0.000001;
00230       }
00231    }
00232    else if (fKernelTemperature == kIncreasingAdaptive) fTemperatureScale = 0.15*( 1.0 / (Double_t)(fRanges.size() ) );
00233    else Log() << kFATAL << "No such kernel!" << Endl;
00234 }
00235 
00236 //_______________________________________________________________________
00237 Double_t TMVA::SimulatedAnnealing::GenerateMaxTemperature( std::vector<Double_t>& parameters  )
00238 {
00239    // maximum temperature
00240    Int_t equilibrium;
00241    Bool_t stopper = 0; 
00242    Double_t t, dT, cold, delta, deltaY, y, yNew, yBest, yOld;
00243    std::vector<Double_t> x( fRanges.size() ), xNew( fRanges.size() ), xBest( fRanges.size() ), xOld( fRanges.size() );
00244    t = fMinTemperature;
00245    deltaY = cold = 0.0;
00246    dT = fTemperatureAdaptiveStep;
00247    for (UInt_t rIter = 0; rIter < x.size(); rIter++)
00248       x[rIter] = ( fRanges[rIter]->GetMax() + fRanges[rIter]->GetMin() ) / 2.0;
00249    y = yBest = 1E10;
00250    for (Int_t i=0; i<fMaxCalls/50; i++) {
00251       if ((i>0) && (deltaY>0.0)) {
00252          cold = deltaY;
00253          stopper = 1;
00254       }
00255       t += dT*i;
00256       x = xOld = GenerateNeighbour(x,t);
00257       y = yOld = fFitterTarget.EstimatorFunction( xOld );   
00258       equilibrium = 0;
00259       for ( Int_t k=0; (k<30) && (equilibrium<=12); k++ ) {
00260          xNew = GenerateNeighbour(x,t);
00261          //"energy"
00262          yNew = fFitterTarget.EstimatorFunction( xNew );
00263          deltaY = yNew - y;
00264          if (deltaY < 0.0) {     // keep xnew if energy is reduced
00265             std::swap(x,xNew);
00266             std::swap(y,yNew);
00267             if (y < yBest) {
00268                xBest = x;
00269                yBest = y;
00270             }
00271             delta = TMath::Abs( deltaY );
00272             if      (y    != 0.0) delta /= y;
00273             else if (yNew != 0.0) delta /= y;
00274 
00275             // equilibrium is defined as a 10% or smaller change in 10 iterations 
00276             if (delta < 0.1) equilibrium++;
00277             else             equilibrium = 0;
00278          }
00279          else equilibrium++;
00280       }
00281 
00282       // "energy"
00283       yNew = fFitterTarget.EstimatorFunction( xNew ); 
00284       deltaY = yNew - yOld;
00285       if ( (deltaY < 0.0 )&&( yNew < yBest)) {
00286          xBest=x;
00287          yBest = yNew;
00288       }
00289       y = yNew;
00290       if ((stopper) && (deltaY >= (100.0 * cold))) break;  // phase transition with another parameter to change
00291    }
00292    parameters = xBest;
00293    return t;
00294 }
00295 
00296 //_______________________________________________________________________
00297 Double_t TMVA::SimulatedAnnealing::Minimize( std::vector<Double_t>& parameters )
00298 {
00299    // minimisation algorithm
00300    std::vector<Double_t> bestParameters(fRanges.size());
00301    std::vector<Double_t> oldParameters (fRanges.size());
00302 
00303    Double_t currentTemperature, bestFit, currentFit;
00304    Int_t optimizeCalls, generalCalls, equals;
00305 
00306    equals = 0;
00307 
00308    if (fUseDefaultTemperature) {
00309       if (fKernelTemperature == kIncreasingAdaptive) {
00310          fMinTemperature = currentTemperature = 1e-06; 
00311          FillWithRandomValues( parameters );
00312       }
00313       else fInitialTemperature = currentTemperature = GenerateMaxTemperature( parameters );
00314    }
00315    else {
00316       if (fKernelTemperature == kIncreasingAdaptive)
00317          currentTemperature = fMinTemperature; 
00318       else
00319          currentTemperature = fInitialTemperature;
00320       FillWithRandomValues( parameters ); 
00321    }
00322 
00323    if (fUseDefaultScale) SetDefaultScale();
00324 
00325    Log() << kINFO
00326            << "Temperatur scale = "      << fTemperatureScale  
00327            << ", current temperature = " << currentTemperature  << Endl;
00328 
00329    bestParameters = parameters;
00330    bestFit        = currentFit = fFitterTarget.EstimatorFunction( bestParameters );
00331 
00332    optimizeCalls = fMaxCalls/100;             //use 1% calls to optimize best founded minimum
00333    generalCalls  = fMaxCalls - optimizeCalls; //and 99% calls to found that one
00334    fProgress = 0.0;
00335 
00336    Timer timer( fMaxCalls, fLogger->GetSource().c_str() );
00337 
00338    for (Int_t sample = 0; sample < generalCalls; sample++) {
00339       GenerateNeighbour( parameters, oldParameters, currentTemperature );
00340       Double_t localFit = fFitterTarget.EstimatorFunction( parameters );
00341       
00342       if (localFit < currentFit || TMath::Abs(currentFit-localFit) < fEps) { // if not worse than last one
00343          if (TMath::Abs(currentFit-localFit) < fEps) { // if the same as last one
00344             equals++;
00345             if (equals >= 3) //if we still at the same level, we should increase temperature
00346                fProgress+=1.0;
00347          }
00348          else {
00349             fProgress = 0.0;
00350             equals = 0;
00351          }
00352          
00353          currentFit = localFit;
00354          
00355          if (currentFit < bestFit) {
00356             ReWriteParameters( parameters, bestParameters );
00357             bestFit = currentFit;
00358          }
00359       }
00360       else {
00361          if (!ShouldGoIn(localFit, currentFit, currentTemperature))
00362             ReWriteParameters( oldParameters, parameters );
00363          else
00364             currentFit = localFit;
00365          
00366          fProgress+=1.0;
00367          equals = 0;
00368       }
00369       
00370       GenerateNewTemperature( currentTemperature, sample );
00371       
00372       if ((fMaxCalls<100) || sample%Int_t(fMaxCalls/100.0) == 0) timer.DrawProgressBar( sample );
00373    }
00374 
00375    // get elapsed time   
00376    Log() << kINFO << "Elapsed time: " << timer.GetElapsedTime() 
00377            << "                            " << Endl;  
00378    
00379    // supose this minimum is the best one, now just try to improve it
00380 
00381    Double_t startingTemperature = fMinTemperature*(fRanges.size())*2.0; 
00382    currentTemperature = startingTemperature;
00383 
00384    Int_t changes = 0;
00385    for (Int_t sample=0;sample<optimizeCalls;sample++) {
00386       GenerateNeighbour( parameters, oldParameters, currentTemperature );
00387       Double_t localFit = fFitterTarget.EstimatorFunction( parameters );
00388       
00389       if (localFit < currentFit) { //if better than last one
00390          currentFit = localFit;
00391          changes++;
00392          
00393          if (currentFit < bestFit) {
00394             ReWriteParameters( parameters, bestParameters );
00395             bestFit = currentFit;
00396          }
00397       }
00398       else ReWriteParameters( oldParameters, parameters ); //we never try worse parameters
00399 
00400       currentTemperature-=(startingTemperature - fEps)/optimizeCalls;
00401    }
00402 
00403    ReWriteParameters( bestParameters, parameters );
00404 
00405    return bestFit; 
00406 }
00407 

Generated on Tue Jul 5 15:25:36 2011 for ROOT_528-00b_version by  doxygen 1.5.1