mutation.cpp
1 /********************************************************************************
2  * FARSA Genetic Algorithm Library *
3  * Copyright (C) 2007-2008 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "core/mutation.h"
21 #include "core/geneticalgo.h"
22 #include "configurationparameters.h"
23 #include "configurationhelper.h"
24 
25 namespace farsa {
26 
28  genomev = 0;
29  ga = 0;
30  lastGenMutaRatesChange = 0;
31 
32  // We put at least one element in the map to avoid crashes
33  mutaRates[0] = MutationRate();
34 }
35 
37  //--- nothing to do
38 }
39 
40 void Mutation::setMutationRate( double rate, int start, int length) {
41  // This simply calls the other setMutationRate function with equal initial and final rate
42  setMutationRate(rate, rate, 0.0, start, length);
43 }
44 
45 void Mutation::setMutationRate( double initialRate, double finalRate, double variation, int start, int length) {
46  // Create a mutex locker to make this method thread-safe
47  QMutexLocker locker(&mutaRatesMutex);
48 
49  // First of all we have to change variation sign depending on whether initialMutaRate is
50  // greater than finalMutaRate or not
51  if (((initialRate > finalRate) && (variation > 0.0)) || ((initialRate < finalRate) && (variation < 0.0))) {
52  variation = -variation;
53  }
54 
55  // Iterate over the map to see what we have to do
56  QMap<int, MutationRate>::iterator iter = mutaRates.begin();
57  MutationRate current;
58  while (iter != mutaRates.end()) {
59  if (iter.key() <= start) {
60  // Found a value before the start
61  current = iter.value();
62 
63  iter++;
64  } else if ((length <= 0) || (iter.key() <= (start + length))) {
65  // Overwrite all the values in the range
66  // If length is smaller than one, the range is open-ended and we
67  // have to remove all the values after start
68  current = iter.value();
69 
70  QMap<int, MutationRate>::iterator tmp = iter;
71  iter++;
72  mutaRates.erase(tmp);
73  } else {
74  // Passed the end of the range, can stop iterating
75  break;
76  }
77  }
78 
79  // Set the new rate at the start
80  MutationRate newRate;
81  newRate.initial = initialRate;
82  newRate.final = finalRate;
83  newRate.variation = variation;
84  if (ga != NULL) {
85  newRate.rateForGeneration(ga->generation());
86  } else {
87  newRate.mutaRate = initialRate;
88  }
89  mutaRates[start] = newRate;
90  if (length > 0) {
91  // If the list has a fixed end, set the value that was there before
92  mutaRates[start + length] = current;
93  }
94 }
95 
96 double Mutation::mutationRate( int bit ) {
97  // Create a mutex locker to make this method thread-safe
98  QMutexLocker locker(&mutaRatesMutex);
99 
100  if ((ga != NULL) && (ga->generation() != lastGenMutaRatesChange)) {
101  lastGenMutaRatesChange = ga->generation();
102  updateMutationRates();
103  }
104 
105  return getMutationRateForBit(bit).mutaRate;
106 }
107 
108 double Mutation::initialMutationRate( int bit ) {
109  return getMutationRateForBit(bit).initial;
110 }
111 
112 double Mutation::finalMutationRate( int bit ) {
113  return getMutationRateForBit(bit).final;
114 }
115 
117  return getMutationRateForBit(bit).variation;
118 }
119 
120 #ifdef __GNUC__
121  #warning IN Mutation: WE SHOULD ALLOW SETTING MUTATION RATES FOR VARIOUS BITS FROM CONFIGURATION FILE
122 #endif
123 
124 void Mutation::configure( ConfigurationParameters& params, QString prefix ) {
125  double initial_mutation_rate, final_mutation_rate, variation_mutation_rate;
126 
127  // The mutation_rate parameter simply sets both initial_mutation_rate and final_mutation_rate
128  // to the same value. The value of initial_mutation_rate and final_mutation_rate, however,
129  // overwrites the one of mutation_rate
130  initial_mutation_rate = final_mutation_rate = ConfigurationHelper::getDouble(params, prefix + "mutation_rate", 0.0);
131  initial_mutation_rate = ConfigurationHelper::getDouble(params, prefix + "initial_mutation_rate", initial_mutation_rate);
132  final_mutation_rate = ConfigurationHelper::getDouble(params, prefix + "final_mutation_rate", final_mutation_rate);
133  variation_mutation_rate = ConfigurationHelper::getDouble(params, prefix + "variation_mutation_rate", 0.01);
134  setMutationRate(initial_mutation_rate, final_mutation_rate, variation_mutation_rate);
135  if ( mutationRate(0) == 0 ) {
136  qWarning( "Setting mutation Rate to ZERO!! Check your config file" );
137  }
138 }
139 
140 void Mutation::save( ConfigurationParameters& params, QString prefix ) {
141  // Here we set only mutation_rate if initial_mutation_rate and final_mutation_rate are equal, otherwise we
142  // set both initial_mutation_rate and final_mutation_rate and variation_mutation_rate. Also we only write the
143  // mutation rate for the bit 0
144  params.createParameter( prefix, QString("type"), "Mutation" );
145  if (initialMutationRate(0) == finalMutationRate(0)) {
146  params.createParameter( prefix, QString("mutation_rate"), QString("%1").arg(mutationRate(0)) );
147  } else {
148  params.createParameter( prefix, QString("initial_mutation_rate"), QString("%1").arg(initialMutationRate(0)) );
149  params.createParameter( prefix, QString("final_mutation_rate"), QString("%1").arg(finalMutationRate(0)) );
150  params.createParameter( prefix, QString("variation_mutation_rate"), QString("%1").arg(variationMutationRate(0)) );
151  }
152 }
153 
154 void Mutation::describe( QString type ) {
155  Descriptor d = addTypeDescription( type, "Mutation operator" );
156  d.describeReal( "mutation_rate" ).limits( 0, 1 ).def( 0.05 ).props( IsMandatory ).help( "The probability to apply the mutation operator" );
157  d.describeReal( "initial_mutation_rate" ).limits( 0, 1 ).def( 0.05 ).help( "The initial probability to apply the mutation operator; this has to be used in combination with final_mutation_rate and variation_mutation_rate to implement a probability of mutation that vary over generations" );
158  d.describeReal( "final_mutation_rate" ).limits( 0, 1 ).def( 0.05 ).help( "The final probability to apply the mutation operator; this has to be used in combination with initial_mutation_rate and variation_mutation_rate to implement a probability of mutation that vary over generations" );
159  d.describeReal( "variation_mutation_rate" ).limits( 0, 1 ).def( 0.05 ).help( "The amount of change applied to the current probability to apply the mutation operator in the next generation; this has to be used in combination with initial_mutation_rate and final_mutation_rate to implement a probability of mutation that vary over generations" );
160 }
161 
162 void Mutation::updateMutationRates() {
163  for (QMap<int, MutationRate>::iterator iter = mutaRates.begin(); iter != mutaRates.end(); iter++) {
164  if (ga != NULL) {
165  iter.value().rateForGeneration(ga->generation());
166  } else {
167  iter.value().mutaRate = iter.value().initial;
168  }
169  }
170 }
171 
172 void Mutation::MutationRate::rateForGeneration(unsigned int gen) {
173  mutaRate = initial + variation * double(gen);
174  if (((initial > final) && (mutaRate < final)) || ((initial < final) && (mutaRate > final))) {
175  mutaRate = final;
176  }
177 }
178 
179 #ifdef __GNUC__
180  #warning WE SHOULD TRY TO MAKE Mutation::getMutationRateForBit MORE EFFICIENT
181 #endif
182 
183 const Mutation::MutationRate& Mutation::getMutationRateForBit( int bit ) const {
184  // This is to avoid problems with negative values (which are invalid anyway)
185  if (bit < 0) {
186  bit = 0;
187  }
188 
189  // Find the last entry in the map before the given bit
190  QMap<int, MutationRate>::const_iterator iter = mutaRates.constBegin();
191  for (; (iter != mutaRates.constEnd()) && (iter.key() <= bit); iter++);
192 
193  // There is at least one element in the map, so we can safely move backward one step
194  iter--;
195  return iter.value();
196 }
197 
198 } // end namespace farsa