Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 09:06:52

0001 #ifndef RIVET_MendelMin_H
0002 #define RIVET_MendelMin_H
0003 
0004 #include "Rivet/Tools/Random.hh"
0005 #include <valarray>
0006 #include <random>
0007 #include <functional>
0008 #include <map>
0009 #include <vector>
0010 #include <cmath>
0011 #include <iostream>
0012 #include <iomanip>
0013 
0014 namespace Rivet {
0015 
0016   using std::valarray;
0017 
0018 
0019   /// @brief A genetic algorithm functional minimizer
0020   ///
0021   /// MendelMin implements a home brewed genetic algorithm for finding
0022   /// the minimum of a function defined on a unit hypercube returning a
0023   /// non-negative real number (eg. a Chi-squared value).
0024   class MendelMin {
0025   public:
0026 
0027     /// Typedef for a valaray of parameters to the function to be minimised.
0028     using Params = std::valarray<double>;
0029     /// Typedef for the function to be minimised
0030     using FuncT = std::function<double(const Params&, const Params&)>;
0031     /// Typedef for the function to be minimised
0032     using FuncNoFixedT = std::function<double(const Params&)>;
0033     // /// Typedef for the [0,1] random number generator
0034     // using RndT = std::function<double()>;
0035 
0036 
0037     /// Constructor with fixed parameters
0038     ///
0039     /// Mandatory arguments: the function, @a fin, to be minimised;
0040     /// the dimension, @a ndim, of the unit hypercube for which @a fin is
0041     /// defined; a set of fixed parameters not to be optimised.
0042     ///
0043     /// Optional arguments are: the number, @a npop, of individuals in the
0044     /// population; and @a margin which determines how much randomness is
0045     /// involved when an individual is evolved twowards the fittest individual.
0046     MendelMin(const FuncT& fin, unsigned int ndim,
0047               const Params& fixpar, //const RndT & rndin,
0048               unsigned int npop=20, //unsigned int ngen=20, //< unused
0049               double margin=0.1)
0050       : _f(fin), _q(fixpar), //_rnd(rndin),
0051         _NDim(ndim), _margin(margin),
0052         _pop(npop), _fit(npop, -1.0), showTrace(false) {}
0053 
0054 
0055     /// Constructor without fixed parameters
0056     ///
0057     /// Mandatory arguments: the function, @a fin, to be minimised; the
0058     /// dimension, @a ndim, of the unit hypercube for which @a fin is defined.
0059     ///
0060     /// Optional arguments are: the number, @a npop, of individuals in the
0061     /// population; and @a margin which determines how much randomness is
0062     /// involved when an individual is evolved twowards the fittest individual.
0063     MendelMin(const FuncNoFixedT& fin, unsigned int ndim,
0064               //const RndT & rndin,
0065               unsigned int npop=20, unsigned int ngen=20,
0066               double margin=0.1)
0067       : MendelMin([&](const Params& ps, const Params&) -> double { return fin(ps); },
0068                   ndim, {}, npop, /* ngen, */ margin)
0069     {   }
0070 
0071 
0072     /// Supply a best guess for the fittest parameter point to help
0073     /// things along.
0074     void guess(const Params & p) {
0075       _pop.push_back(p);
0076       limit01(_pop.back());
0077       _fit.push_back(f(_pop.back()));
0078     }
0079 
0080 
0081     /// Evolve the population a given number of generations and return
0082     /// the best fit value.
0083     double evolve(unsigned int nGen) {
0084       for ( unsigned n = 0; n < nGen; ++n ) {
0085         // Calculate the fitness.
0086         auto mm = minmax();
0087         // Always kill the fittest individual.
0088         if ( showTrace ) _debug();
0089         for ( unsigned int i = 1; i < _pop.size(); ++i ) {
0090           if ( _fit[i] > rnd()*(mm.second - mm.first) )
0091             // Kill all individuals that have low fitness or are just unlucky.
0092             _fit[i] = -1.0;
0093           else
0094             // Improve This individual to be more like the fittest.
0095             move(_pop[i],_pop[0]);
0096         }
0097       }
0098       return _fit[0];
0099     }
0100 
0101     /// Return the fittest parameter point found.
0102     Params fittest() const {
0103       return _pop[0];
0104     }
0105 
0106     /// Return the fittest value found.
0107     double fit() const {
0108       return _fit[0];
0109     }
0110 
0111     /// Simple wrapper around the random number generator.
0112     double rnd() const {
0113       return rand01(); //_rnd();
0114     }
0115 
0116     /// Return a random parameter point in the unit hypercube.
0117     Params rndParams() const {
0118       Params ret(_NDim);
0119       for ( unsigned int i = 0; i < _NDim; ++i ) ret[i] = rnd();
0120       return ret;
0121     }
0122 
0123     /// Limit a parameter point to inside the unit hypercube.
0124     void limit01(Params & p) const {
0125       for ( unsigned int i = 0; i < _NDim; ++i )
0126         p[i] = std::max(0.0, std::min(p[i], 1.0));
0127     }
0128 
0129     /// Move a @a bad parameter point towards a @a better one. The new
0130     /// point is picked randomly within the generalized hypercube where
0131     /// @a bad and @a better are at diagonally opposite corners, enlarged by
0132     /// a fraction _margin.
0133     void move(Params & bad, const Params & better) const {
0134       bad += (better - bad)*(rndParams()*(1.0 + 2.0*_margin) - _margin);
0135       limit01(bad);
0136     }
0137 
0138     /// Simple wrapper around the function to be minimised.
0139     double f(const Params & p) const {
0140       return _f(p, _q);
0141     }
0142 
0143 
0144     /// Calculate the fitness values of all individuals and put the
0145     /// fittest one first. @return the best and worst fitness values.
0146     std::pair<double, double> minmax() {
0147       std::pair<double,double> mm(std::numeric_limits<double>::max(), 0.0);
0148       unsigned int iwin = 0;
0149       for ( unsigned int i = 0; i < _pop.size(); ++i ) {
0150         double & v = _fit[i];
0151         // negative fitness value means the individual is dead, so we
0152         // welocme a new immigrant.
0153         if ( v < 0.0 ) _pop[i] = rndParams();
0154 
0155         // The calculated fitness value cannot be negative.
0156         v = std::max(0.0, f(_pop[i]));
0157 
0158         // Compare to the best and worst fitness so far.
0159         if ( v < mm.first ) iwin = i;
0160         mm.first = std::min(v, mm.first);
0161         mm.second = std::max(mm.second, v);
0162       }
0163 
0164       // Move the winner to the top.
0165       if ( iwin != 0 ) {
0166         std::swap(_pop[0], _pop[iwin]);
0167         std::swap(_fit[0], _fit[iwin]);
0168       }
0169       return mm;
0170     }
0171 
0172     /// Inspect a population and the fitness of its individuals.
0173     void _debug() {
0174       std::cout << "GenAlgMax population status:" << std::endl;
0175       for ( unsigned int i = 0; i < _pop.size(); ++i ) {
0176         std::cout << std::setw(10) << _fit[i] << " (" << _pop[i][0];
0177         for ( unsigned int ip = 1; ip < _NDim; ++ip )
0178           std::cout << "," << _pop[i][ip];
0179         std::cout << ")" << std::endl;
0180       }
0181     }
0182 
0183 
0184   private:
0185 
0186     /// The function to be minimised.
0187     const FuncT _f;
0188 
0189     /// The fixed parameter points.
0190     Params _q;
0191 
0192     /// The fixed parameters of the function.
0193     //  const double _q;
0194 
0195     /// The random number generator.
0196     //const RndT _rnd;
0197 
0198     /// The dimension of the unit hypercube of allowed parameter points.
0199     unsigned int _NDim;
0200 
0201     /// WHen evolving less fit parameter point towards the fittest one
0202     /// we choose randomly in a generalised hypercube where these two
0203     /// parameter points are in diagonally opposite corners, expanded in
0204     /// each direction with This fraction.
0205     double _margin;
0206 
0207     /// The population of parameter points.
0208     std::vector<Params> _pop;
0209 
0210 
0211     /// The fitness value of each individual in the population.
0212     std::vector<double> _fit;
0213 
0214   public:
0215 
0216     /// Set true to get a verbose record of the evolution.
0217     bool showTrace;
0218 
0219   };
0220 
0221 
0222   /// Construct a MendelMin object taking as arguments: the function, @a
0223   /// fin, to be minimised; a random number generator, @rndin, returning
0224   /// double random numbers between 0 and 1; the dimension, @a ndim, of
0225   /// the unit hypercube for which @a fin is defined; the number, @a
0226   /// npop, of individuals in the population; and a @a margin which
0227   /// determines how much ramndomness is involved when an individual is
0228   /// evolved twowards the fittest individual.
0229   // template <typename FuncT, typename RndT>
0230   // MendelMin <FuncT, RndT>
0231   // makeMendelMin(const FuncT & f, const RndT & rnd, unsigned int ndim,
0232   //               unsigned int npop = 20, double margin = 0.1) {
0233   //   return MendelMin<FuncT, RndT>(f, rnd, ndim, npop, margin);
0234   // }
0235 
0236 }
0237 
0238 #endif // RIVET_MendelMin_H