genotype.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/genotype.h"
21 #include "randomgenerator.h"
22 #include <cmath>
23 #include <cstring>
24 #include "configurationparameters.h"
25 
26 namespace farsa {
27 
28 Genotype::Genotype( unsigned int size ) {
29  sizev = size;
30  //--- allocate a bit more that necessary for safety
31  allocated = (unsigned int)( (size+1)/8 ) + 1;
32  data = new unsigned char[allocated];
33  fitnessv.resize(1);
34  fitnessv[0] = 0.0;
35  rankv = 0.0;
36  notesv = "";
37 }
38 
40  delete []data;
41 }
42 
43 Genotype::Genotype( QString str, bool compressed ) {
44  if ( !compressed ) {
45  sizev = str.size();
46  //--- allocate a bit more that necessary for safety
47  allocated = (unsigned int)( (sizev+1)/8 ) + 1;
48  data = new unsigned char[allocated];
49  for( unsigned int i=0; i<sizev; i++ ) {
50  if ( str[i] == '1' ) {
51  set( i );
52  } else {
53  unset( i );
54  }
55  }
56  } else {
57  QByteArray temp2 = QByteArray::fromBase64( str.toAscii() );
58  QByteArray temp = qUncompress( temp2 );
59  sizev = temp.size();
60  //--- allocate a bit more that necessary for safety
61  allocated = (unsigned int)( (sizev+1)/8 ) + 1;
62  data = new unsigned char[allocated];
63  for( unsigned int i=0; i<sizev; i++ ) {
64  if ( temp[i] == '1' ) {
65  set( i );
66  } else {
67  unset( i );
68  }
69  }
70  }
71  fitnessv.resize(1);
72  fitnessv[0] = 0.0;
73  rankv = 0.0;
74  notesv = "";
75 }
76 
78  sizev = genotype.sizev;
79  allocated = genotype.allocated;
80  data = new unsigned char[allocated];
81  memcpy( data, genotype.data, allocated );
82  fitnessv = genotype.fitnessv;
83  rankv = genotype.rankv;
84  notesv = genotype.notesv;
85 }
86 
87 Genotype& Genotype::operator=( const Genotype& genotype ) {
88  //--- even if constructed with default constructor at least 1 char is allocated
89  delete []data;
90  sizev = genotype.sizev;
91  allocated = genotype.allocated;
92  data = new unsigned char[allocated];
93  memcpy( data, genotype.data, allocated );
94  fitnessv = genotype.fitnessv;
95  rankv = genotype.rankv;
96  notesv = genotype.notesv;
97  return (*this);
98 }
99 
100 void Genotype::configure( ConfigurationParameters& params, QString prefix ) {
101  int newsize = params.getValue( prefix + QString( "bitsize" ) ).toInt();
102  Q_ASSERT_X( newsize > 0,
103  "Genotype::configure",
104  "The bitsize must be present in the config file and its value must be greater than zero" );
105  resize( newsize );
106  QString zipdata = params.getValue( prefix + QString( "data" ) );
107  if ( !zipdata.isNull() ) {
108  fromCompressedString( zipdata );
109  }
110  QStringList valuesList = params.getValue( prefix + QString( "fitness" ) )
111  .split( QRegExp("\\s+"), QString::SkipEmptyParts );
112  if ( valuesList.size() > 0 ) {
113  // read the values of fitness
114  fitnessv.resize(0);
115  foreach( QString avalue, valuesList ) {
116  fitnessv << avalue.toDouble();
117  }
118  }
119  rankv = params.getValue( prefix + QString( "rank" ) ).toDouble();
120  notesv = params.getValue( prefix + QString( "notes" ) );
121 }
122 
123 void Genotype::save( ConfigurationParameters& params, QString prefix ) {
124  params.createParameter( prefix, QString("type"), "Genotype" );
125  params.createParameter( prefix, QString("bitsize"), QString("%1").arg(sizev) );
126  QString fitstring;
127  foreach( double avalue, fitnessv ) {
128  fitstring.append( QString("%1 ").arg(avalue) );
129  }
130  params.createParameter( prefix, QString("fitness"), fitstring );
131  params.createParameter( prefix, QString("data"), toCompressedString() );
132  params.createParameter( prefix, QString("rank"), QString("%1").arg(rankv) );
133  params.createParameter( prefix, QString("notes"), notesv );
134 }
135 
136 void Genotype::describe( QString type ) {
137  Descriptor d = addTypeDescription( type, "Genotype is a binary string, each bit correspond to a gene" );
138  d.describeInt( "bitsize" ).limits( 1, INT_MAX ).def( 8 ).props( IsMandatory ).help( "The number of bits of the Genotype", "The Genotype is represented by a binary string of bitsize length" );
139  d.describeReal( "fitness" ).props( IsList ).help( "The fitness of the Genotype", "The fitness of a Genotype support multi objective fitness; if you specify a vector of values they are considered different objectives of the fitness" );
140  d.describeString( "data" ).help( "The bits composing the Genotype stored in a compressed string" );
141  d.describeReal( "rank" ).help( "The rank indicate who is more fitted that others and how much; the values are dependent on the kind of GeneticAlgo used" );
142 }
143 
144 void Genotype::assign( const Genotype* genotype ) {
145  Genotype& self = *this;
146  self = *genotype;
147 }
148 
150  return new Genotype( *this );
151 }
152 
153 unsigned int Genotype::size() const {
154  return sizev;
155 }
156 
157 void Genotype::resize( unsigned int newsize ) {
158  //--- even if constructed with default constructor at least 1 char is allocated
159  unsigned int old_allocated = allocated;
160  sizev = newsize;
161  allocated = (unsigned int)( (sizev+1)/8 ) + 1;
162  unsigned char* newdata = new unsigned char[allocated];
163  memcpy( newdata, data, ( allocated < old_allocated ) ? allocated : old_allocated );
164  delete []data;
165  data = newdata;
166  return;
167 }
168 
169 void Genotype::setFitness( double value ) {
170  setObjective( 0, value );
171 }
172 
173 double Genotype::fitness() const {
174  return objective(0);
175 }
176 
177 void Genotype::setObjective( int i, double value ) {
178  fitnessv.resize( qMax( fitnessv.size(), i+1 ) );
179  fitnessv[i] = value;
180 }
181 
182 double Genotype::objective( int i ) const {
183  return fitnessv[i];
184 }
185 
187  return fitnessv.size();
188 }
189 
190 bool Genotype::dominatedBy( const Genotype* g ) {
191  int numObjToCompare = qMin( fitnessv.size(), g->fitnessv.size() );
192  bool allLessEq = true;
193  bool oneLessStrictly = false;
194  for( int i=0; i<numObjToCompare; i++ ) {
195  if ( fitnessv[i] > g->fitnessv[i] ) {
196  allLessEq = false;
197  }
198  if ( fitnessv[i] < g->fitnessv[i] ) {
199  oneLessStrictly = true;
200  }
201  }
202  return (allLessEq && oneLessStrictly);
203 }
204 
205 void Genotype::setRank( double r ) {
206  rankv = r;
207 }
208 
209 double Genotype::rank() const {
210  return rankv;
211 }
212 
213 bool Genotype::bit( unsigned int i ) const {
214  //--- find the index into data array that contains bit request
215  int id = i >> 3; //i/8;
216  //--- create the mask for extract the bit requested
217  //--- 0x80 is binary 1000.0000
218  unsigned char mask = 0x80 >> (i&7); //(i%8);
219  return ( data[id] & mask );
220 }
221 
222 void Genotype::set( unsigned int i ) {
223  //--- find the index into data array that contains bit request
224  int id = i >> 3; //i/8;
225  //--- create the mask
226  //--- 0x80 is binary 1000.0000
227  unsigned char mask = 0x80 >> (i&7); //(i%8);
228  //--- setting bit to 1
229  data[id] = data[id] | mask;
230 }
231 
232 void Genotype::unset( unsigned int i ) {
233  //--- find the index into data array that contains bit request
234  int id = i >> 3; //i/8;
235  //--- create the mask
236  //--- 0x80 is binary 1000.0000
237  unsigned char mask = 0x80 >> (i&7); //(i%8);
238  //--- setting bit to 0, require the inverted mask
239  data[id] = data[id] & (~mask);
240 }
241 
242 void Genotype::toggle( unsigned int i ) {
243  //--- find the index into data array that contains bit request
244  int id = i >> 3; //i/8;
245  //--- create the mask
246  //--- 0x80 is binary 1000.0000
247  unsigned char mask = 0x80 >> (i&7); //(i%8);
248  //--- toggling a bit using XOR
249  data[id] = data[id] ^ mask;
250 }
251 
252 QString Genotype::notes() const {
253  return notesv;
254 }
255 
256 void Genotype::setNotes( QString notes ) {
257  notesv = notes;
258 }
259 
260 int Genotype::hammingDistance( const Genotype* other ) {
261  int ret = 0;
262  for( unsigned int i=0; i<sizev; i++ ) {
263  ret += ( bit(i) == other->bit(i) );
264  }
265  return ret;
266 }
267 
269  for( unsigned int i=0; i<sizev; i++ ) {
270  if ( globalRNG->getBool( 0.5 ) ) {
271  set( i );
272  } else {
273  unset( i );
274  }
275  }
276 }
277 
278 QString Genotype::toString() const {
279  QString ret;
280  ret.resize( sizev );
281  for( unsigned int i=0; i<sizev; i++ ) {
282  ret[i] = ( bit(i) ) ? '1' : '0';
283  }
284  return ret;
285 }
286 
287 void Genotype::fromString( QString str ) {
288  int dim = qMin( (int)sizev, str.size() );
289  for( int i=0; i<dim; i++ ) {
290  if ( str[i] == '1' ) {
291  set( i );
292  } else {
293  unset( i );
294  }
295  }
296 }
297 
299  QByteArray temp;
300  temp.resize( sizev );
301  for( unsigned int i=0; i<sizev; i++ ) {
302  temp[i] = ( bit(i) ) ? '1' : '0';
303  }
304  QByteArray temp2 = qCompress( temp, 9 );
305  return temp2.toBase64();
306 }
307 
308 bool Genotype::fromCompressedString( QString str ) {
309  QByteArray temp2 = QByteArray::fromBase64( str.toAscii() );
310  QByteArray temp = qUncompress( temp2 );
311  if ( temp.isEmpty() ) {
312  return false;
313  }
314  int dim = qMin( (int)sizev, temp.size() );
315  for( int i=0; i<dim; i++ ) {
316  if ( temp[i] == '1' ) {
317  set( i );
318  } else {
319  unset( i );
320  }
321  }
322  return true;
323 }
324 
325 unsigned int Genotype::extractUInt( unsigned int startPos, unsigned int stopPos ) {
326  if ( startPos >= sizev ) return 0;
327 
328  stopPos = qMin( stopPos, sizev );
329  unsigned int len = stopPos - startPos;
330  unsigned int ret = 0;
331  for( ; startPos<stopPos; startPos++ ) {
332  len--;
333  if ( bit(startPos) ) {
334  ret += (unsigned int)( pow( 2.0f, (int)len ) );
335  }
336  }
337  return ret;
338 }
339 
340 void Genotype::insertUInt( unsigned int value, unsigned int startPos, unsigned int stopPos ) {
341  if ( startPos >= sizev ) return ;
342 
343  stopPos = qMin( stopPos, sizev );
344  for( ; stopPos!=startPos; stopPos-- ) {
345  if ( value%2 == 1 ) {
346  set( stopPos-1 );
347  } else {
348  unset( stopPos-1 );
349  }
350  value /= 2;
351  }
352  return;
353 }
354 
355 unsigned int Genotype::geneToBitIndex( unsigned int gene ) const {
356  return gene;
357 }
358 
359 unsigned int Genotype::bitToGeneIndex( unsigned int bit ) const {
360  return bit;
361 }
362 
364  //--- even if constructed with defaul constructor at least 1 char is allocated
365  delete []data;
366  sizev = source->sizev;
367  allocated = source->allocated;
368  data = new unsigned char[allocated];
369  memcpy( data, source->data, allocated );
370  fitnessv = source->fitnessv;
371  rankv = source->rankv;
372  notesv = source->notesv;
373  return;
374 }
375 
376 } // end namespace farsa