EvolvingObjects
CMAState.cpp
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
 All Classes Namespaces Files Functions Variables Typedefs Friends