EvolvingObjects
|
00001 /* 00002 * C++ification of Nikolaus Hansen's original C-source code for the 00003 * CMA-ES 00004 * 00005 * C++-ificiation performed by Maarten Keijzer (C) 2005. Licensed under 00006 * the LGPL. Original copyright of Nikolaus Hansen can be found below 00007 * 00008 * 00009 * Some changes have been made to the original flow and logic of the 00010 * algorithm: 00011 * 00012 * - Numerical issues are now treated 'before' going into the eigenvector decomposition 00013 * (this was done out of convenience) 00014 * - dMaxSignifiKond (smallest x such that x == x + 1.0) replaced by 00015 * numeric_limits<double>::epsilon() (smallest x such that 1.0 != 1.0 + x) 00016 * 00017 * 00018 */ 00019 00020 /* --------------------------------------------------------- */ 00021 /* --------------------------------------------------------- */ 00022 /* --- File: cmaes.c -------- Author: Nikolaus Hansen --- */ 00023 /* --------------------------------------------------------- */ 00024 /* 00025 * CMA-ES for non-linear function minimization. 00026 * 00027 * Copyright (C) 1996, 2003 Nikolaus Hansen. 00028 * e-mail: hansen@bionik.tu-berlin.de 00029 * 00030 * This library is free software; you can redistribute it and/or 00031 * modify it under the terms of the GNU Lesser General Public 00032 * License as published by the Free Software Foundation; either 00033 * version 2.1 of the License, or (at your option) any later 00034 * version (see http://www.gnu.org/copyleft/lesser.html). 00035 * 00036 * This library is distributed in the hope that it will be useful, 00037 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00038 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00039 * Lesser General Public License for more details. 00040 * 00041 * */ 00042 /* --- Changes : --- 00043 * 03/03/21: argument const double *rgFunVal of 00044 * cmaes_ReestimateDistribution() was treated incorrectly. 00045 * 03/03/29: restart via cmaes_resume_distribution() implemented. 00046 * 03/03/30: Always max std dev / largest axis is printed first. 00047 * 03/08/30: Damping is adjusted for large mueff. 00048 * 03/10/30: Damping is adjusted for large mueff always. 00049 * 04/04/22: Cumulation time and damping for step size adjusted. 00050 * No iniphase but conditional update of pc. 00051 * Version 2.23. 00052 * */ 00053 00054 #include <valarray> 00055 #include <limits> 00056 #include <iostream> 00057 #include <cassert> 00058 00059 #include <utils/eoRNG.h> 00060 00061 #include <es/CMAState.h> 00062 #include <es/CMAParams.h> 00063 #include <es/matrices.h> 00064 #include <es/eig.h> 00065 00066 using namespace std; 00067 00068 namespace eo { 00069 00070 struct CMAStateImpl { 00071 00072 CMAParams p; 00073 00074 lower_triangular_matrix C; // Covariance matrix 00075 square_matrix B; // Eigen vectors (in columns) 00076 valarray<double> d; // eigen values (diagonal matrix) 00077 valarray<double> pc; // Evolution path 00078 valarray<double> ps; // Evolution path for stepsize; 00079 00080 vector<double> mean; // current mean to sample around 00081 double sigma; // global step size 00082 00083 unsigned gen; 00084 vector<double> fitnessHistory; 00085 00086 00087 CMAStateImpl(const CMAParams& params_, const vector<double>& m, double sigma_) : 00088 p(params_), 00089 C(p.n), B(p.n), d(p.n), pc(p.n), ps(p.n), mean(m), sigma(sigma_), 00090 gen(0), fitnessHistory(3) 00091 { 00092 double trace = (p.initialStdevs * p.initialStdevs).sum(); 00093 /* Initialize covariance structure */ 00094 for (unsigned i = 0; i < p.n; ++i) 00095 { 00096 B[i][i] = 1.; 00097 d[i] = p.initialStdevs[i] * sqrt(p.n / trace); 00098 C[i][i] = d[i] * d[i]; 00099 pc[i] = 0.; 00100 ps[i] = 0.; 00101 } 00102 00103 } 00104 00105 void sample(vector<double>& v) { 00106 unsigned n = p.n; 00107 v.resize(n); 00108 00109 vector<double> tmp(n); 00110 for (unsigned i = 0; i < n; ++i) 00111 tmp[i] = d[i] * rng.normal(); 00112 00113 /* add mutation (sigma * B * (D*z)) */ 00114 for (unsigned i = 0; i < n; ++i) { 00115 double sum = 0; 00116 for (unsigned j = 0; j < n; ++j) { 00117 sum += B[i][j] * tmp[j]; 00118 } 00119 v[i] = mean[i] + sigma * sum; 00120 } 00121 } 00122 00123 void reestimate(const vector<const vector<double>* >& pop, double muBest, double muWorst) { 00124 00125 assert(pop.size() == p.mu); 00126 00127 unsigned n = p.n; 00128 00129 fitnessHistory[gen % fitnessHistory.size()] = muBest; // needed for divergence check 00130 00131 vector<double> oldmean = mean; 00132 valarray<double> BDz(n); 00133 00134 /* calculate xmean and rgBDz~N(0,C) */ 00135 for (unsigned i = 0; i < n; ++i) { 00136 mean[i] = 0.; 00137 for (unsigned j = 0; j < pop.size(); ++j) { 00138 mean[i] += p.weights[j] * (*pop[j])[i]; 00139 } 00140 BDz[i] = sqrt(p.mueff)*(mean[i] - oldmean[i])/sigma; 00141 } 00142 00143 vector<double> tmp(n); 00144 /* calculate z := D^(-1) * B^(-1) * rgBDz into rgdTmp */ 00145 for (unsigned i = 0; i < n; ++i) { 00146 double sum = 0.0; 00147 for (unsigned j = 0; j < n; ++j) { 00148 sum += B[j][i] * BDz[j]; 00149 } 00150 tmp[i] = sum / d[i]; 00151 } 00152 00153 /* cumulation for sigma (ps) using B*z */ 00154 for (unsigned i = 0; i < n; ++i) { 00155 double sum = 0.0; 00156 for (unsigned j = 0; j < n; ++j) 00157 sum += B[i][j] * tmp[j]; 00158 00159 ps[i] = (1. - p.ccumsig) * ps[i] + sqrt(p.ccumsig * (2. - p.ccumsig)) * sum; 00160 } 00161 00162 /* calculate norm(ps)^2 */ 00163 double psxps = (ps * ps).sum(); 00164 00165 00166 double chiN = sqrt((double) p.n) * (1. - 1./(4.*p.n) + 1./(21.*p.n*p.n)); 00167 /* cumulation for covariance matrix (pc) using B*D*z~N(0,C) */ 00168 double hsig = sqrt(psxps) / sqrt(1. - pow(1.-p.ccumsig, 2.*gen)) / chiN < 1.5 + 1./(p.n-0.5); 00169 00170 pc = (1. - p.ccumcov) * pc + hsig * sqrt(p.ccumcov * (2. - p.ccumcov)) * BDz; 00171 00172 /* stop initial phase (MK, this was not reachable in the org code, deleted) */ 00173 00174 /* remove momentum in ps, if ps is large and fitness is getting worse */ 00175 00176 if (gen >= fitnessHistory.size()) { 00177 00178 // find direction from muBest and muWorst (muBest == muWorst handled seperately 00179 double direction = muBest < muWorst? -1.0 : 1.0; 00180 00181 unsigned now = gen % fitnessHistory.size(); 00182 unsigned prev = (gen-1) % fitnessHistory.size(); 00183 unsigned prevprev = (gen-2) % fitnessHistory.size(); 00184 00185 bool fitnessWorsens = (muBest == muWorst) || // <- increase norm also when population has converged (this deviates from Hansen's scheme) 00186 ( (direction * fitnessHistory[now] < direction * fitnessHistory[prev]) 00187 && 00188 (direction * fitnessHistory[now] < direction * fitnessHistory[prevprev])); 00189 00190 if(psxps/p.n > 1.5 + 10.*sqrt(2./p.n) && fitnessWorsens) { 00191 double tfac = sqrt((1 + std::max(0., log(psxps/p.n))) * p.n / psxps); 00192 ps *= tfac; 00193 psxps *= tfac*tfac; 00194 } 00195 } 00196 00197 /* update of C */ 00198 /* Adapt_C(t); not used anymore */ 00199 if (p.ccov != 0.) { 00200 //flgEigensysIsUptodate = 0; 00201 00202 /* update covariance matrix */ 00203 for (unsigned i = 0; i < n; ++i) { 00204 vector<double>::iterator c_row = C[i]; 00205 for (unsigned j = 0; j <= i; ++j) { 00206 c_row[j] = 00207 (1 - p.ccov) * c_row[j] 00208 + 00209 p.ccov * (1./p.mucov) * pc[i] * pc[j] 00210 + 00211 (1-hsig) * p.ccumcov * (2. - p.ccumcov) * c_row[j]; 00212 00213 /*C[i][j] = (1 - p.ccov) * C[i][j] 00214 + sp.ccov * (1./sp.mucov) 00215 * (rgpc[i] * rgpc[j] 00216 + (1-hsig)*sp.ccumcov*(2.-sp.ccumcov) * C[i][j]); */ 00217 for (unsigned k = 0; k < p.mu; ++k) { /* additional rank mu update */ 00218 c_row[j] += p.ccov * (1-1./p.mucov) * p.weights[k] 00219 * ( (*pop[k])[i] - oldmean[i]) 00220 * ( (*pop[k])[j] - oldmean[j]) 00221 / sigma / sigma; 00222 00223 // * (rgrgx[index[k]][i] - rgxold[i]) 00224 // * (rgrgx[index[k]][j] - rgxold[j]) 00225 // / sigma / sigma; 00226 } 00227 } 00228 } 00229 } 00230 00231 /* update of sigma */ 00232 sigma *= exp(((sqrt(psxps)/chiN)-1.)/p.damp); 00233 /* calculate eigensystem, must be done by caller */ 00234 //cmaes_UpdateEigensystem(0); 00235 00236 00237 /* treat minimal standard deviations and numeric problems 00238 * Note that in contrast with the original code, some numerical issues are treated *before* we 00239 * go into the eigenvalue calculation */ 00240 00241 treatNumericalIssues(muBest, muWorst); 00242 00243 gen++; // increase generation 00244 } 00245 00246 void treatNumericalIssues(double best, double worst) { 00247 00248 /* treat stdevs */ 00249 for (unsigned i = 0; i < p.n; ++i) { 00250 if (sigma * sqrt(C[i][i]) < p.minStdevs[i]) { 00251 // increase stdev 00252 sigma *= exp(0.05+1./p.damp); 00253 break; 00254 } 00255 } 00256 00257 /* treat convergence */ 00258 if (best == worst) { 00259 sigma *= exp(0.2 + 1./p.damp); 00260 } 00261 00262 /* Jede Hauptachse i testen, ob x == x + 0.1 * sigma * rgD[i] * B[i] */ 00263 /* Test if all the means are not numerically out of whack with our coordinate system*/ 00264 for (unsigned axis = 0; axis < p.n; ++axis) { 00265 double fac = 0.1 * sigma * d[axis]; 00266 unsigned coord; 00267 for (coord = 0; coord < p.n; ++coord) { 00268 if (mean[coord] != mean[coord] + fac * B[coord][axis]) { 00269 break; 00270 } 00271 } 00272 00273 if (coord == p.n) { // means are way too big (little) for numerical accuraccy. Start rocking the craddle a bit more 00274 sigma *= exp(0.2+1./p.damp); 00275 } 00276 00277 } 00278 00279 /* Testen ob eine Komponente des Objektparameters festhaengt */ 00280 /* Correct issues with scale between objective values and covariances */ 00281 bool theresAnIssue = false; 00282 00283 for (unsigned i = 0; i < p.n; ++i) { 00284 if (mean[i] == mean[i] + 0.2 * sigma * sqrt(C[i][i])) { 00285 C[i][i] *= (1. + p.ccov); 00286 theresAnIssue = true; 00287 } 00288 } 00289 00290 if (theresAnIssue) { 00291 sigma *= exp(0.05 + 1./p.damp); 00292 } 00293 } 00294 00295 00296 bool updateEigenSystem(unsigned max_tries, unsigned max_iters) { 00297 00298 if (max_iters==0) max_iters = 30 * p.n; 00299 00300 static double lastGoodMinimumEigenValue = 1.0; 00301 00302 /* Try to get a valid calculation */ 00303 for (unsigned tries = 0; tries < max_tries; ++tries) { 00304 00305 unsigned iters = eig( p.n, C, d, B, max_iters); 00306 if (iters < max_iters) 00307 { // all is well 00308 00309 /* find largest/smallest eigenvalues */ 00310 double minEV = d.min(); 00311 double maxEV = d.max(); 00312 00313 /* (MK Original comment was) :Limit Condition of C to dMaxSignifKond+1 00314 * replaced dMaxSignifKond with 1./numeric_limits<double>::epsilon() 00315 * */ 00316 if (maxEV * numeric_limits<double>::epsilon() > minEV) { 00317 double tmp = maxEV * numeric_limits<double>::epsilon() - minEV; 00318 minEV += tmp; 00319 for (unsigned i=0;i<p.n;++i) { 00320 C[i][i] += tmp; 00321 d[i] += tmp; 00322 } 00323 } /* if */ 00324 lastGoodMinimumEigenValue = minEV; 00325 00326 d = sqrt(d); 00327 00328 //flgEigensysIsUptodate = 1; 00329 //genOfEigensysUpdate = gen; 00330 //clockeigensum += clock() - clockeigenbegin; 00331 return true; 00332 } /* if cIterEig < ... */ 00333 00334 // numerical problems, ignore them and try again 00335 00336 /* Addition des letzten minEW auf die Diagonale von C */ 00337 /* Add the last known good eigen value to the diagonal of C */ 00338 double summand = lastGoodMinimumEigenValue * exp((double) tries); 00339 for (unsigned i = 0; i < p.n; ++i) 00340 C[i][i] += summand; 00341 00342 } /* for iEigenCalcVers */ 00343 00344 return false; 00345 00346 } 00347 00348 00349 }; 00350 00351 CMAState::CMAState(const CMAParams& params, const std::vector<double>& initial_point, const double initial_sigma) 00352 : pimpl(new CMAStateImpl(params, initial_point, initial_sigma)) {} 00353 00354 CMAState::~CMAState() { delete pimpl; } 00355 CMAState::CMAState(const CMAState& that) : pimpl(new CMAStateImpl(*that.pimpl )) {} 00356 CMAState& CMAState::operator=(const CMAState& that) { *pimpl = *that.pimpl; return *this; } 00357 00358 void CMAState::sample(vector<double>& v) const { pimpl->sample(v); } 00359 00360 void CMAState::reestimate(const vector<const vector<double>* >& population, double muBest, double muWorst) { pimpl->reestimate(population, muBest, muWorst); } 00361 bool CMAState::updateEigenSystem(unsigned max_tries, unsigned max_iters) { return pimpl->updateEigenSystem(max_tries, max_iters); } 00362 00363 00364 } // namespace eo