fix(eoStandardBitMutation):

- Fix all operators in eoStandardBitMutation.h
- Bitflip componennt was not bound, use explicit assignement of rates.
- Fix normal and fast operators algorithms.

Co-authored-by: Potalas <potalas@free.fr>
This commit is contained in:
Johann Dreo 2023-02-10 11:51:29 +01:00 committed by Johann Dreo
commit ff744aea7c
2 changed files with 114 additions and 70 deletions

View file

@ -7,6 +7,8 @@
/** Standard bit mutation with mutation rate p: /** Standard bit mutation with mutation rate p:
* choose k from the binomial distribution Bin(n,p) and apply flip_k(x). * choose k from the binomial distribution Bin(n,p) and apply flip_k(x).
* *
* If rate is null (the default), use 1/chrom.size().
*
* @ingroup Bitstrings * @ingroup Bitstrings
* @ingroup Variators * @ingroup Variators
*/ */
@ -14,7 +16,11 @@ template<class EOT>
class eoStandardBitMutation : public eoMonOp<EOT> class eoStandardBitMutation : public eoMonOp<EOT>
{ {
public: public:
eoStandardBitMutation(double rate = 0.5) : /** Constructor.
*
* @param rate mutation rate, 1/chrom.size() if ignored or zero (the default)
*/
eoStandardBitMutation(double rate = 0) :
_rate(rate), _rate(rate),
_nb(1), _nb(1),
_bitflip(_nb) _bitflip(_nb)
@ -22,9 +28,12 @@ class eoStandardBitMutation : public eoMonOp<EOT>
virtual bool operator()(EOT& chrom) virtual bool operator()(EOT& chrom)
{ {
assert(chrom.size()>0);
if(_rate == 0) {
_rate = (double) 1/chrom.size();
}
_nb = eo::rng.binomial(chrom.size(),_rate); _nb = eo::rng.binomial(chrom.size(),_rate);
// BitFlip operator is bound to the _nb reference, _bitflip.number_bits(_nb);
// thus one don't need to re-instantiate.
return _bitflip(chrom); return _bitflip(chrom);
} }
@ -46,8 +55,7 @@ template<class EOT>
class eoUniformBitMutation : public eoMonOp<EOT> class eoUniformBitMutation : public eoMonOp<EOT>
{ {
public: public:
eoUniformBitMutation(double rate = 0.5) : eoUniformBitMutation() :
_rate(rate),
_nb(1), _nb(1),
_bitflip(_nb) _bitflip(_nb)
{} {}
@ -55,15 +63,13 @@ class eoUniformBitMutation : public eoMonOp<EOT>
virtual bool operator()(EOT& chrom) virtual bool operator()(EOT& chrom)
{ {
_nb = eo::rng.random(chrom.size()); _nb = eo::rng.random(chrom.size());
// BitFlip operator is bound to the _nb reference, _bitflip.number_bits(_nb);
// thus one don't need to re-instantiate.
return _bitflip(chrom); return _bitflip(chrom);
} }
virtual std::string className() const {return "eoUniformBitMutation";} virtual std::string className() const {return "eoUniformBitMutation";}
protected: protected:
double _rate;
unsigned _nb; unsigned _nb;
eoDetSingleBitFlip<EOT> _bitflip; eoDetSingleBitFlip<EOT> _bitflip;
}; };
@ -84,17 +90,21 @@ template<class EOT>
class eoConditionalBitMutation : public eoStandardBitMutation<EOT> class eoConditionalBitMutation : public eoStandardBitMutation<EOT>
{ {
public: public:
eoConditionalBitMutation(double rate = 0.5) : eoConditionalBitMutation(double rate = 0) :
eoStandardBitMutation<EOT>(rate) eoStandardBitMutation<EOT>(rate)
{} {}
virtual bool operator()(EOT& chrom) virtual bool operator()(EOT& chrom)
{ {
assert(chrom.size()>0); assert(chrom.size()>0);
this->_nb = eo::rng.binomial(chrom.size()-1,this->_rate); if(this->_rate == 0) {
this->_nb++; this->_rate = (double) 1/chrom.size();
// BitFlip operator is bound to the _nb reference, }
// thus one don't need to re-instantiate. this->_nb = 0;
while(this->_nb < 1) {
this->_nb = eo::rng.binomial(chrom.size(),this->_rate);
}
this->_bitflip.number_bits(this->_nb);
return this->_bitflip(chrom); return this->_bitflip(chrom);
} }
@ -123,12 +133,14 @@ class eoShiftedBitMutation : public eoStandardBitMutation<EOT>
virtual bool operator()(EOT& chrom) virtual bool operator()(EOT& chrom)
{ {
assert(chrom.size()>0); assert(chrom.size()>0);
this->_nb = eo::rng.binomial(chrom.size()-1,this->_rate); if(this->_rate == 0) {
this->_rate = (double) 1/chrom.size();
}
this->_nb = eo::rng.binomial(chrom.size(),this->_rate);
if(this->_nb == 0) { if(this->_nb == 0) {
this->_nb = 1; this->_nb = 1;
} }
// BitFlip operator is bound to the _nb reference, this->_bitflip.number_bits(this->_nb);
// thus one don't need to re-instantiate.
return this->_bitflip(chrom); return this->_bitflip(chrom);
} }
@ -142,7 +154,7 @@ class eoShiftedBitMutation : public eoStandardBitMutation<EOT>
* *
* From: * From:
* Furong Ye, Carola Doerr, and Thomas Back. * Furong Ye, Carola Doerr, and Thomas Back.
* Interpolating local and global search by controllingthe variance of standard bit mutation. * Interpolating local and global search by controlling the variance of standard bit mutation.
* In 2019 IEEE Congress on Evolutionary Computation(CEC), pages 22922299. * In 2019 IEEE Congress on Evolutionary Computation(CEC), pages 22922299.
* *
* In contrast to standard bit mutation, this operators allows to scale * In contrast to standard bit mutation, this operators allows to scale
@ -152,29 +164,40 @@ class eoShiftedBitMutation : public eoStandardBitMutation<EOT>
* @ingroup Variators * @ingroup Variators
*/ */
template<class EOT> template<class EOT>
class eoNormalBitMutation : public eoStandardBitMutation<EOT> class eoNormalBitMutation : public eoMonOp<EOT>
{ {
public: public:
eoNormalBitMutation(double rate = 0.5, double variance = 1) : eoNormalBitMutation(double mean = 0, double variance = 0) :
eoStandardBitMutation<EOT>(rate), _mean(mean),
_variance(variance) _variance(variance),
_nb(1),
_bitflip(_nb)
{} {}
virtual bool operator()(EOT& chrom) virtual bool operator()(EOT& chrom)
{ {
this->_nb = eo::rng.normal(this->_rate * chrom.size(), _variance); assert(chrom.size() > 0);
if(this->_nb >= chrom.size()) { if(_mean == 0) {
this->_nb = eo::rng.random(chrom.size()); _mean = (double) 1/chrom.size();
} }
// BitFlip operator is bound to the _nb reference, if(_variance == 0) {
// thus one don't need to re-instantiate. _variance = std::log(chrom.size());
return this->_bitflip(chrom); }
_nb = eo::rng.normal(_mean, _variance);
if(_nb >= chrom.size()) {
_nb = eo::rng.random(chrom.size());
}
_bitflip.number_bits(_nb);
return _bitflip(chrom);
} }
virtual std::string className() const {return "eoNormalBitMutation";} virtual std::string className() const {return "eoNormalBitMutation";}
protected: protected:
double _mean;
double _variance; double _variance;
unsigned _nb;
eoDetSingleBitFlip<EOT> _bitflip;
}; };
/** Fast mutation which size is sampled from an adaptive power law. /** Fast mutation which size is sampled from an adaptive power law.
@ -188,11 +211,10 @@ class eoNormalBitMutation : public eoStandardBitMutation<EOT>
* @ingroup Variators * @ingroup Variators
*/ */
template<class EOT> template<class EOT>
class eoFastBitMutation : public eoStandardBitMutation<EOT> class eoFastBitMutation : public eoMonOp<EOT>
{ {
public: public:
eoFastBitMutation(double rate = 0.5, double beta = 1.5) : eoFastBitMutation(double beta = 1.5) :
eoStandardBitMutation<EOT>(rate),
_beta(beta) _beta(beta)
{ {
assert(beta > 1); assert(beta > 1);
@ -200,74 +222,96 @@ class eoFastBitMutation : public eoStandardBitMutation<EOT>
virtual bool operator()(EOT& chrom) virtual bool operator()(EOT& chrom)
{ {
this->_nb = powerlaw(chrom.size(),_beta); _nb = powerlaw(chrom.size(),_beta);
// BitFlip operator is bound to the _nb reference, _bitflip.number_bits(_nb);
// thus one don't need to re-instantiate. return _bitflip(chrom);
return this->_bitflip(chrom);
} }
virtual std::string className() const {return "eoFastBitMutation";} virtual std::string className() const {return "eoFastBitMutation";}
protected: protected:
double powerlaw(unsigned int n, double beta)
double powerlaw(unsigned n, double beta)
{ {
double cnb = 0; double cnb = 0;
for(unsigned i=1; i<n; ++i) { for(unsigned int i=1; i<=n/2; ++i) {
cnb += std::pow(i,-beta); cnb += std::pow(i,-beta);
} }
return eo::rng.powerlaw(0,n,beta) / cnb; double trigger = eo::rng.uniform(0,1);
double cursor = 0;
double rate = 1;
for(unsigned int i=1; i<=n/2; ++i) {
cursor += std::pow(i,-beta) / cnb;
if(cursor >= trigger) {
rate = static_cast<double>(i) / static_cast<double>(n);
break;
}
}
return eo::rng.binomial(n,rate);
} }
// double powerlaw(unsigned n, double beta)
// {
// double cnb = 0;
// for(unsigned i=1; i<n; ++i) {
// cnb += std::pow(i,-beta);
// }
// return eo::rng.powerlaw(0,n,beta) / cnb;
// }
double _beta; double _beta;
unsigned _nb;
eoDetSingleBitFlip<EOT> _bitflip;
}; };
/** Bucket mutation which assign probability for each bucket /** Bucket mutation which assign probability for each bucket
*
* @warning Highly untested code, use with caution.
* *
* From: * From:
* Carola Doerr, Johann Dréo, Alexis Robbes * Carola Doerr, Johann Dreo, Alexis Robbes
*/ */
template<class EOT> template<class EOT>
class eoBucketBitMutation : public eoStandardBitMutation<EOT> class eoBucketBitMutation : public eoMonOp<EOT>
{ {
public: public:
eoBucketBitMutation(std::vector<double> bucketsSizes, std::vector<double> bucketValues) : eoBucketBitMutation(std::vector<std::vector<int>> buckets, std::vector<double> bucketsValues) :
_bucketsSizes(bucketsSizes), _buckets(buckets),
_bucketValues(bucketValues) _bucketsValues(bucketsValues)
{ {
assert(bucketsSizes.size() != bucketValues.size()); assert(buckets.size() == bucketsValues.size());
} }
virtual bool operator()(EOT& chrom) virtual bool operator()(EOT& chrom)
{ {
_nb = customlaw(chrom.size(), _buckets, _bucketsValues);
this->_nb = customlaw(chrom.size(), _bucketsSizes, _bucketValues); _bitflip.number_bits(_nb);
// BitFlip operator is bound to the _nb reference, return _bitflip(chrom);
// thus one don't need to re-instantiate.
return this->_bitflip(chrom);
} }
virtual std::string className() const {return "eoBucketBitMutation";} virtual std::string className() const {return "eoBucketBitMutation";}
protected: protected:
double customlaw(unsigned n, std::vector<double> bucketsSizes, std::vector<double> bucketValues) double customlaw(unsigned n, std::vector<std::vector<int>> buckets, std::vector<double> bucketsValues)
{ {
int targetBucketIndex = eo::rng.roulette_wheel(bucketValues); int bucketIndex = eo::rng.roulette_wheel(bucketsValues);
int nb = 0; int startBit = buckets[bucketIndex][0];
int bucketIndex = 0; int endBit = buckets[bucketIndex][1];
while (nb < n && bucketIndex <= targetBucketIndex) int gapBit = endBit - startBit;
{
if (bucketIndex < targetBucketIndex) nb += int(n*bucketsSizes[bucketIndex]); int nbBits;
else nb += int(eo::rng.uniform(1, n*bucketsSizes[targetBucketIndex])); if (gapBit > 0) {
nbBits = rand() % gapBit + startBit;
} else {
nbBits = endBit;
} }
if (nb > n) nb = n; return nbBits;
return nb;
} }
std::vector<double> _bucketsValues;
std::vector<std::vector<int>> _buckets;
std::vector<double> _bucketsSizes; unsigned _nb;
std::vector<double> _bucketValues; eoDetSingleBitFlip<EOT> _bitflip;
}; };
#endif // _eoStandardBitMutation_h_ #endif // _eoStandardBitMutation_h_

View file

@ -30,13 +30,13 @@ eoAlgoFoundryFastGA<Bits>& make_foundry(eoFunctorStore& store, eoInit<Bits>& ini
foundry.crossovers.add< eo1PtBitXover<Bits> >(); foundry.crossovers.add< eo1PtBitXover<Bits> >();
/***** Mutations ****/ /***** Mutations ****/
double p = 1.0; // Probability of flipping eath bit. // Use defaults for all operators (usually falls back to p=1/chrom.size()).
foundry.mutations.add< eoUniformBitMutation<Bits> >(p); // proba of flipping k bits, k drawn in uniform distrib foundry.mutations.add< eoUniformBitMutation<Bits> >(); // proba of flipping k bits, k drawn in uniform distrib
foundry.mutations.add< eoStandardBitMutation<Bits> >(p); // proba of flipping k bits, k drawn in binomial distrib foundry.mutations.add< eoStandardBitMutation<Bits> >(); // proba of flipping k bits, k drawn in binomial distrib
foundry.mutations.add< eoConditionalBitMutation<Bits> >(p); // proba of flipping k bits, k drawn in binomial distrib, minus zero foundry.mutations.add< eoConditionalBitMutation<Bits> >(); // proba of flipping k bits, k drawn in binomial distrib, minus zero
foundry.mutations.add< eoShiftedBitMutation<Bits> >(p); // proba of flipping k bits, k drawn in binomial distrib, changing zeros to one foundry.mutations.add< eoShiftedBitMutation<Bits> >(); // proba of flipping k bits, k drawn in binomial distrib, changing zeros to one
foundry.mutations.add< eoNormalBitMutation<Bits> >(p); // proba of flipping k bits, k drawn in normal distrib foundry.mutations.add< eoNormalBitMutation<Bits> >(); // proba of flipping k bits, k drawn in normal distrib
foundry.mutations.add< eoFastBitMutation<Bits> >(p); // proba of flipping k bits, k drawn in powerlaw distrib foundry.mutations.add< eoFastBitMutation<Bits> >(); // proba of flipping k bits, k drawn in powerlaw distrib
for(size_t i=1; i < 11; i+=1) { for(size_t i=1; i < 11; i+=1) {
foundry.mutations.add< eoDetSingleBitFlip<Bits> >(i); // mutate k bits without duplicates foundry.mutations.add< eoDetSingleBitFlip<Bits> >(i); // mutate k bits without duplicates
} }