ROOT  6.06/09
Reference Guide
SimulatedAnnealing.cxx
Go to the documentation of this file.
1 // @(#)root/tmva $Id$
2 // Author: Andreas Hoecker, Joerg Stelzer, Helge Voss, Krzysztof Danielowski, Kamil Kraszewski, Maciej Kruk
3 
4 /**********************************************************************************
5  * Project: TMVA - a Root-integrated toolkit for multivariate data analysis *
6  * Package: TMVA *
7  * Class : SimulatedAnnealing *
8  * Web : http://tmva.sourceforge.net *
9  * *
10  * Description: *
11  * Implementation (see header for description) *
12  * *
13  * Authors (alphabetical): *
14  * Krzysztof Danielowski <danielow@cern.ch> - IFJ & AGH, Poland *
15  * Kamil Kraszewski <kalq@cern.ch> - IFJ & UJ, Poland *
16  * Maciej Kruk <mkruk@cern.ch> - IFJ & AGH, Poland *
17  * *
18  * Copyright (c) 2008: *
19  * IFJ-Krakow, Poland *
20  * CERN, Switzerland *
21  * MPI-K Heidelberg, Germany *
22  * *
23  * Redistribution and use in source and binary forms, with or without *
24  * modification, are permitted according to the terms listed in LICENSE *
25  * (http://tmva.sourceforge.net/LICENSE) *
26  **********************************************************************************/
27 
28 //_______________________________________________________________________
29 //
30 // Implementation of Simulated Annealing fitter
31 ////////////////////////////////////////////////////////////////////////////////
32 
34 
35 #include "TRandom3.h"
36 #include "TMath.h"
37 
38 #include "TMVA/Interval.h"
39 #include "TMVA/IFitterTarget.h"
40 #include "TMVA/GeneticRange.h"
41 #include "TMVA/Timer.h"
42 #include "TMVA/MsgLogger.h"
43 
45 
46 ////////////////////////////////////////////////////////////////////////////////
47 /// constructor
48 
49 TMVA::SimulatedAnnealing::SimulatedAnnealing( IFitterTarget& target, const std::vector<Interval*>& ranges )
50  : fKernelTemperature (kIncreasingAdaptive),
51  fFitterTarget ( target ),
52  fRandom ( new TRandom3(100) ),
53  fRanges ( ranges ),
54  fMaxCalls ( 500000 ),
55  fInitialTemperature ( 1000 ),
56  fMinTemperature ( 0 ),
57  fEps ( 1e-10 ),
58  fTemperatureScale ( 0.06 ),
59  fAdaptiveSpeed ( 1.0 ),
60  fTemperatureAdaptiveStep( 0.0 ),
61  fUseDefaultScale ( kFALSE ),
62  fUseDefaultTemperature ( kFALSE ),
63  fLogger( new MsgLogger("SimulatedAnnealing") ),
64  fProgress(0.0)
65 {
66  fKernelTemperature = kIncreasingAdaptive;
67 }
68 
69 ////////////////////////////////////////////////////////////////////////////////
70 /// option setter
71 
73  Double_t initialTemperature,
74  Double_t minTemperature,
75  Double_t eps,
76  TString kernelTemperatureS,
77  Double_t temperatureScale,
78  Double_t adaptiveSpeed,
79  Double_t temperatureAdaptiveStep,
80  Bool_t useDefaultScale,
81  Bool_t useDefaultTemperature)
82 {
83  fMaxCalls = maxCalls;
84  fInitialTemperature = initialTemperature;
85  fMinTemperature = minTemperature;
86  fEps = eps;
87 
88  if (kernelTemperatureS == "IncreasingAdaptive") {
90  Log() << kINFO << "Using increasing adaptive algorithm" << Endl;
91  }
92  else if (kernelTemperatureS == "DecreasingAdaptive") {
94  Log() << kINFO << "Using decreasing adaptive algorithm" << Endl;
95  }
96  else if (kernelTemperatureS == "Sqrt") {
98  Log() << kINFO << "Using \"Sqrt\" algorithm" << Endl;
99  }
100  else if (kernelTemperatureS == "Homo") {
102  Log() << kINFO << "Using \"Homo\" algorithm" << Endl;
103  }
104  else if (kernelTemperatureS == "Log") {
106  Log() << kINFO << "Using \"Log\" algorithm" << Endl;
107  }
108  else if (kernelTemperatureS == "Sin") {
110  Log() << kINFO << "Using \"Sin\" algorithm" << Endl;
111  }
112 
113  fTemperatureScale = temperatureScale;
114  fAdaptiveSpeed = adaptiveSpeed;
115  fTemperatureAdaptiveStep = temperatureAdaptiveStep;
116 
117  fUseDefaultScale = useDefaultScale;
118  fUseDefaultTemperature = useDefaultTemperature;
119 }
120 ////////////////////////////////////////////////////////////////////////////////
121 /// destructor
122 
124 {
125 }
126 
127 ////////////////////////////////////////////////////////////////////////////////
128 /// random starting parameters
129 
130 void TMVA::SimulatedAnnealing::FillWithRandomValues( std::vector<Double_t>& parameters )
131 {
132  for (UInt_t rIter = 0; rIter < parameters.size(); rIter++) {
133  parameters[rIter] = fRandom->Uniform(0.0,1.0)*(fRanges[rIter]->GetMax() - fRanges[rIter]->GetMin()) + fRanges[rIter]->GetMin();
134  }
135 }
136 
137 ////////////////////////////////////////////////////////////////////////////////
138 /// copy parameters
139 
140 void TMVA::SimulatedAnnealing::ReWriteParameters( std::vector<Double_t>& from, std::vector<Double_t>& to)
141 {
142  for (UInt_t rIter = 0; rIter < from.size(); rIter++) to[rIter] = from[rIter];
143 }
144 
145 ////////////////////////////////////////////////////////////////////////////////
146 /// generate adjacent parameters
147 
148 void TMVA::SimulatedAnnealing::GenerateNeighbour( std::vector<Double_t>& parameters, std::vector<Double_t>& oldParameters,
149  Double_t currentTemperature )
150 {
151  ReWriteParameters( parameters, oldParameters );
152 
153  for (UInt_t rIter=0;rIter<parameters.size();rIter++) {
154  Double_t uni,distribution,sign;
155  do {
156  uni = fRandom->Uniform(0.0,1.0);
157  sign = (uni - 0.5 >= 0.0) ? (1.0) : (-1.0);
158  distribution = currentTemperature * (TMath::Power(1.0 + 1.0/currentTemperature, TMath::Abs(2.0*uni - 1.0)) -1.0)*sign;
159  parameters[rIter] = oldParameters[rIter] + (fRanges[rIter]->GetMax()-fRanges[rIter]->GetMin())*0.1*distribution;
160  }
161  while (parameters[rIter] < fRanges[rIter]->GetMin() || parameters[rIter] > fRanges[rIter]->GetMax() );
162  }
163 }
164 ////////////////////////////////////////////////////////////////////////////////
165 /// generate adjacent parameters
166 
167 std::vector<Double_t> TMVA::SimulatedAnnealing::GenerateNeighbour( std::vector<Double_t>& parameters, Double_t currentTemperature )
168 {
169  std::vector<Double_t> newParameters( fRanges.size() );
170 
171  for (UInt_t rIter=0; rIter<parameters.size(); rIter++) {
172  Double_t uni,distribution,sign;
173  do {
174  uni = fRandom->Uniform(0.0,1.0);
175  sign = (uni - 0.5 >= 0.0) ? (1.0) : (-1.0);
176  distribution = currentTemperature * (TMath::Power(1.0 + 1.0/currentTemperature, TMath::Abs(2.0*uni - 1.0)) -1.0)*sign;
177  newParameters[rIter] = parameters[rIter] + (fRanges[rIter]->GetMax()-fRanges[rIter]->GetMin())*0.1*distribution;
178  }
179  while (newParameters[rIter] < fRanges[rIter]->GetMin() || newParameters[rIter] > fRanges[rIter]->GetMax() );
180  }
181 
182  return newParameters;
183 }
184 
185 ////////////////////////////////////////////////////////////////////////////////
186 /// generate new temperature
187 
189 {
190  if (fKernelTemperature == kSqrt) {
191  currentTemperature = fInitialTemperature/(Double_t)TMath::Sqrt(Iter+2) * fTemperatureScale;
192  }
193  else if (fKernelTemperature == kLog) {
194  currentTemperature = fInitialTemperature/(Double_t)TMath::Log(Iter+2) * fTemperatureScale;
195  }
196  else if (fKernelTemperature == kHomo) {
197  currentTemperature = fInitialTemperature/(Double_t)(Iter+2) * fTemperatureScale;
198  }
199  else if (fKernelTemperature == kSin) {
200  currentTemperature = (TMath::Sin( (Double_t)Iter / fTemperatureScale ) + 1.0 )/ (Double_t)(Iter+1.0) * fInitialTemperature + fEps;
201  }
202  else if (fKernelTemperature == kGeo) {
203  currentTemperature = currentTemperature*fTemperatureScale;
204  }
205  else if (fKernelTemperature == kIncreasingAdaptive) {
206  currentTemperature = fMinTemperature + fTemperatureScale*TMath::Log(1.0+fProgress*fAdaptiveSpeed);
207  }
208  else if (fKernelTemperature == kDecreasingAdaptive) {
209  currentTemperature = currentTemperature*fTemperatureScale;
210  }
211  else Log() << kFATAL << "No such kernel!" << Endl;
212 }
213 
214 ////////////////////////////////////////////////////////////////////////////////
215 /// result checker
216 
217 Bool_t TMVA::SimulatedAnnealing::ShouldGoIn( Double_t currentFit, Double_t localFit, Double_t currentTemperature )
218 {
219  if (currentTemperature < fEps) return kFALSE;
220  Double_t lim = TMath::Exp( -TMath::Abs( currentFit - localFit ) / currentTemperature );
221  Double_t prob = fRandom->Uniform(0.0, 1.0);
222  return (prob < lim) ? kTRUE : kFALSE;
223 }
224 
225 ////////////////////////////////////////////////////////////////////////////////
226 /// setting of default scale
227 
229 {
230  if (fKernelTemperature == kSqrt) fTemperatureScale = 1.0;
231  else if (fKernelTemperature == kLog) fTemperatureScale = 1.0;
232  else if (fKernelTemperature == kHomo) fTemperatureScale = 1.0;
233  else if (fKernelTemperature == kSin) fTemperatureScale = 20.0;
234  else if (fKernelTemperature == kGeo) fTemperatureScale = 0.99997;
235  else if (fKernelTemperature == kDecreasingAdaptive) {
236  fTemperatureScale = 1.0;
237  while (TMath::Abs(TMath::Power(fTemperatureScale,fMaxCalls) * fInitialTemperature - fMinTemperature) >
238  TMath::Abs(TMath::Power(fTemperatureScale-0.000001,fMaxCalls) * fInitialTemperature - fMinTemperature)) {
239  fTemperatureScale -= 0.000001;
240  }
241  }
242  else if (fKernelTemperature == kIncreasingAdaptive) fTemperatureScale = 0.15*( 1.0 / (Double_t)(fRanges.size() ) );
243  else Log() << kFATAL << "No such kernel!" << Endl;
244 }
245 
246 ////////////////////////////////////////////////////////////////////////////////
247 /// maximum temperature
248 
249 Double_t TMVA::SimulatedAnnealing::GenerateMaxTemperature( std::vector<Double_t>& parameters )
250 {
251  Int_t equilibrium;
252  Bool_t stopper = 0;
253  Double_t t, dT, cold, delta, deltaY, y, yNew, yBest, yOld;
254  std::vector<Double_t> x( fRanges.size() ), xNew( fRanges.size() ), xBest( fRanges.size() ), xOld( fRanges.size() );
255  t = fMinTemperature;
256  deltaY = cold = 0.0;
257  dT = fTemperatureAdaptiveStep;
258  for (UInt_t rIter = 0; rIter < x.size(); rIter++)
259  x[rIter] = ( fRanges[rIter]->GetMax() + fRanges[rIter]->GetMin() ) / 2.0;
260  y = yBest = 1E10;
261  for (Int_t i=0; i<fMaxCalls/50; i++) {
262  if ((i>0) && (deltaY>0.0)) {
263  cold = deltaY;
264  stopper = 1;
265  }
266  t += dT*i;
267  x = xOld = GenerateNeighbour(x,t);
268  y = yOld = fFitterTarget.EstimatorFunction( xOld );
269  equilibrium = 0;
270  for ( Int_t k=0; (k<30) && (equilibrium<=12); k++ ) {
271  xNew = GenerateNeighbour(x,t);
272  //"energy"
273  yNew = fFitterTarget.EstimatorFunction( xNew );
274  deltaY = yNew - y;
275  if (deltaY < 0.0) { // keep xnew if energy is reduced
276  std::swap(x,xNew);
277  std::swap(y,yNew);
278  if (y < yBest) {
279  xBest = x;
280  yBest = y;
281  }
282  delta = TMath::Abs( deltaY );
283  if (y != 0.0) delta /= y;
284  else if (yNew != 0.0) delta /= y;
285 
286  // equilibrium is defined as a 10% or smaller change in 10 iterations
287  if (delta < 0.1) equilibrium++;
288  else equilibrium = 0;
289  }
290  else equilibrium++;
291  }
292 
293  // "energy"
294  yNew = fFitterTarget.EstimatorFunction( xNew );
295  deltaY = yNew - yOld;
296  if ( (deltaY < 0.0 )&&( yNew < yBest)) {
297  xBest=x;
298  yBest = yNew;
299  }
300  y = yNew;
301  if ((stopper) && (deltaY >= (100.0 * cold))) break; // phase transition with another parameter to change
302  }
303  parameters = xBest;
304  return t;
305 }
306 
307 ////////////////////////////////////////////////////////////////////////////////
308 /// minimisation algorithm
309 
310 Double_t TMVA::SimulatedAnnealing::Minimize( std::vector<Double_t>& parameters )
311 {
312  std::vector<Double_t> bestParameters(fRanges.size());
313  std::vector<Double_t> oldParameters (fRanges.size());
314 
315  Double_t currentTemperature, bestFit, currentFit;
316  Int_t optimizeCalls, generalCalls, equals;
317 
318  equals = 0;
319 
320  if (fUseDefaultTemperature) {
321  if (fKernelTemperature == kIncreasingAdaptive) {
322  fMinTemperature = currentTemperature = 1e-06;
323  FillWithRandomValues( parameters );
324  }
325  else fInitialTemperature = currentTemperature = GenerateMaxTemperature( parameters );
326  }
327  else {
328  if (fKernelTemperature == kIncreasingAdaptive)
329  currentTemperature = fMinTemperature;
330  else
331  currentTemperature = fInitialTemperature;
332  FillWithRandomValues( parameters );
333  }
334 
335  if (fUseDefaultScale) SetDefaultScale();
336 
337  Log() << kINFO
338  << "Temperatur scale = " << fTemperatureScale
339  << ", current temperature = " << currentTemperature << Endl;
340 
341  bestParameters = parameters;
342  bestFit = currentFit = fFitterTarget.EstimatorFunction( bestParameters );
343 
344  optimizeCalls = fMaxCalls/100; //use 1% calls to optimize best founded minimum
345  generalCalls = fMaxCalls - optimizeCalls; //and 99% calls to found that one
346  fProgress = 0.0;
347 
348  Timer timer( fMaxCalls, fLogger->GetSource().c_str() );
349 
350  for (Int_t sample = 0; sample < generalCalls; sample++) {
351  GenerateNeighbour( parameters, oldParameters, currentTemperature );
352  Double_t localFit = fFitterTarget.EstimatorFunction( parameters );
353 
354  if (localFit < currentFit || TMath::Abs(currentFit-localFit) < fEps) { // if not worse than last one
355  if (TMath::Abs(currentFit-localFit) < fEps) { // if the same as last one
356  equals++;
357  if (equals >= 3) //if we still at the same level, we should increase temperature
358  fProgress+=1.0;
359  }
360  else {
361  fProgress = 0.0;
362  equals = 0;
363  }
364 
365  currentFit = localFit;
366 
367  if (currentFit < bestFit) {
368  ReWriteParameters( parameters, bestParameters );
369  bestFit = currentFit;
370  }
371  }
372  else {
373  if (!ShouldGoIn(localFit, currentFit, currentTemperature))
374  ReWriteParameters( oldParameters, parameters );
375  else
376  currentFit = localFit;
377 
378  fProgress+=1.0;
379  equals = 0;
380  }
381 
382  GenerateNewTemperature( currentTemperature, sample );
383 
384  if ((fMaxCalls<100) || sample%Int_t(fMaxCalls/100.0) == 0) timer.DrawProgressBar( sample );
385  }
386 
387  // get elapsed time
388  Log() << kINFO << "Elapsed time: " << timer.GetElapsedTime()
389  << " " << Endl;
390 
391  // supose this minimum is the best one, now just try to improve it
392 
393  Double_t startingTemperature = fMinTemperature*(fRanges.size())*2.0;
394  currentTemperature = startingTemperature;
395 
396  Int_t changes = 0;
397  for (Int_t sample=0;sample<optimizeCalls;sample++) {
398  GenerateNeighbour( parameters, oldParameters, currentTemperature );
399  Double_t localFit = fFitterTarget.EstimatorFunction( parameters );
400 
401  if (localFit < currentFit) { //if better than last one
402  currentFit = localFit;
403  changes++;
404 
405  if (currentFit < bestFit) {
406  ReWriteParameters( parameters, bestParameters );
407  bestFit = currentFit;
408  }
409  }
410  else ReWriteParameters( oldParameters, parameters ); //we never try worse parameters
411 
412  currentTemperature-=(startingTemperature - fEps)/optimizeCalls;
413  }
414 
415  ReWriteParameters( bestParameters, parameters );
416 
417  return bestFit;
418 }
419 
Double_t GenerateMaxTemperature(std::vector< Double_t > &parameters)
maximum temperature
void FillWithRandomValues(std::vector< Double_t > &parameters)
random starting parameters
void GenerateNewTemperature(Double_t &currentTemperature, Int_t Iter)
generate new temperature
Random number generator class based on M.
Definition: TRandom3.h:29
MsgLogger & Endl(MsgLogger &ml)
Definition: MsgLogger.h:162
Double_t Log(Double_t x)
Definition: TMath.h:526
void swap(ROOT::THist< DIMENSIONS, PRECISION > &a, ROOT::THist< DIMENSIONS, PRECISION > &b) noexcept
Swap two histograms.
Definition: THist.h:196
Basic string class.
Definition: TString.h:137
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kFALSE
Definition: Rtypes.h:92
int equals(Double_t n1, Double_t n2, double ERRORLIMIT=1.E-10)
Definition: UnitTesting.cxx:24
STL namespace.
Short_t Abs(Short_t d)
Definition: TMathBase.h:110
ClassImp(TMVA::SimulatedAnnealing) TMVA
constructor
LongDouble_t Power(LongDouble_t x, LongDouble_t y)
Definition: TMath.h:501
TStopwatch timer
Definition: pirndm.C:37
Double_t x[n]
Definition: legend1.C:17
void GenerateNeighbour(std::vector< Double_t > &parameters, std::vector< Double_t > &oldParameters, Double_t currentTemperature)
generate adjacent parameters
virtual ~SimulatedAnnealing()
destructor
void SetOptions(Int_t maxCalls, Double_t initialTemperature, Double_t minTemperature, Double_t eps, TString kernelTemperatureS, Double_t temperatureScale, Double_t adaptiveSpeed, Double_t temperatureAdaptiveStep, Bool_t useDefaultScale, Bool_t useDefaultTemperature)
option setter
unsigned int UInt_t
Definition: RtypesCore.h:42
Double_t Exp(Double_t x)
Definition: TMath.h:495
void SetDefaultScale()
setting of default scale
double Double_t
Definition: RtypesCore.h:55
Double_t y[n]
Definition: legend1.C:17
Bool_t ShouldGoIn(Double_t currentFit, Double_t localFit, Double_t currentTemperature)
result checker
enum TMVA::SimulatedAnnealing::EKernelTemperature fKernelTemperature
Abstract ClassifierFactory template that handles arbitrary types.
Double_t Minimize(std::vector< Double_t > &parameters)
minimisation algorithm
Double_t Sin(Double_t)
Definition: TMath.h:421
Int_t sign(Double_t x)
Definition: CsgOps.cxx:89
Double_t Sqrt(Double_t x)
Definition: TMath.h:464
const Bool_t kTRUE
Definition: Rtypes.h:91
Definition: math.cpp:60
MsgLogger & Log() const
void ReWriteParameters(std::vector< Double_t > &from, std::vector< Double_t > &to)
copy parameters