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.toLatin1() );
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  // safe check
119  if ( fitnessv.size() == 0 ) {
120  fitnessv.append( 0 );
121  }
122  }
123  rankv = params.getValue( prefix + QString( "rank" ) ).toDouble();
124  notesv = params.getValue( prefix + QString( "notes" ) );
125 }
126 
127 void Genotype::save( ConfigurationParameters& params, QString prefix ) {
128  params.createParameter( prefix, QString("type"), "Genotype" );
129  params.createParameter( prefix, QString("bitsize"), QString("%1").arg(sizev) );
130  QString fitstring;
131  foreach( double avalue, fitnessv ) {
132  fitstring.append( QString("%1 ").arg(avalue) );
133  }
134  params.createParameter( prefix, QString("fitness"), fitstring );
135  params.createParameter( prefix, QString("data"), toCompressedString() );
136  params.createParameter( prefix, QString("rank"), QString("%1").arg(rankv) );
137  params.createParameter( prefix, QString("notes"), notesv );
138 }
139 
140 void Genotype::describe( QString type ) {
141  Descriptor d = addTypeDescription( type, "Genotype is a binary string, each bit correspond to a gene" );
142  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" );
143  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" );
144  d.describeString( "data" ).help( "The bits composing the Genotype stored in a compressed string" );
145  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" );
146 }
147 
148 void Genotype::assign( const Genotype* genotype ) {
149  Genotype& self = *this;
150  self = *genotype;
151 }
152 
154  return new Genotype( *this );
155 }
156 
157 unsigned int Genotype::size() const {
158  return sizev;
159 }
160 
161 void Genotype::resize( unsigned int newsize ) {
162  //--- even if constructed with default constructor at least 1 char is allocated
163  unsigned int old_allocated = allocated;
164  sizev = newsize;
165  allocated = (unsigned int)( (sizev+1)/8 ) + 1;
166  unsigned char* newdata = new unsigned char[allocated];
167  memcpy( newdata, data, ( allocated < old_allocated ) ? allocated : old_allocated );
168  delete []data;
169  data = newdata;
170  return;
171 }
172 
173 void Genotype::setFitness( double value ) {
174  setObjective( 0, value );
175 }
176 
177 double Genotype::fitness() const {
178  return objective(0);
179 }
180 
181 void Genotype::setObjective( int i, double value ) {
182  fitnessv.resize( qMax( fitnessv.size(), i+1 ) );
183  fitnessv[i] = value;
184 }
185 
186 double Genotype::objective( int i ) const {
187  return fitnessv[i];
188 }
189 
191  return fitnessv.size();
192 }
193 
194 bool Genotype::dominatedBy( const Genotype* g ) {
195  int numObjToCompare = qMin( fitnessv.size(), g->fitnessv.size() );
196  bool allLessEq = true;
197  bool oneLessStrictly = false;
198  for( int i=0; i<numObjToCompare; i++ ) {
199  if ( fitnessv[i] > g->fitnessv[i] ) {
200  allLessEq = false;
201  }
202  if ( fitnessv[i] < g->fitnessv[i] ) {
203  oneLessStrictly = true;
204  }
205  }
206  return (allLessEq && oneLessStrictly);
207 }
208 
209 void Genotype::setRank( double r ) {
210  rankv = r;
211 }
212 
213 double Genotype::rank() const {
214  return rankv;
215 }
216 
217 bool Genotype::bit( unsigned int i ) const {
218  //--- find the index into data array that contains bit request
219  int id = i >> 3; //i/8;
220  //--- create the mask for extract the bit requested
221  //--- 0x80 is binary 1000.0000
222  unsigned char mask = 0x80 >> (i&7); //(i%8);
223  return ( data[id] & mask );
224 }
225 
226 void Genotype::set( unsigned int i ) {
227  //--- find the index into data array that contains bit request
228  int id = i >> 3; //i/8;
229  //--- create the mask
230  //--- 0x80 is binary 1000.0000
231  unsigned char mask = 0x80 >> (i&7); //(i%8);
232  //--- setting bit to 1
233  data[id] = data[id] | mask;
234 }
235 
236 void Genotype::unset( unsigned int i ) {
237  //--- find the index into data array that contains bit request
238  int id = i >> 3; //i/8;
239  //--- create the mask
240  //--- 0x80 is binary 1000.0000
241  unsigned char mask = 0x80 >> (i&7); //(i%8);
242  //--- setting bit to 0, require the inverted mask
243  data[id] = data[id] & (~mask);
244 }
245 
246 void Genotype::toggle( unsigned int i ) {
247  //--- find the index into data array that contains bit request
248  int id = i >> 3; //i/8;
249  //--- create the mask
250  //--- 0x80 is binary 1000.0000
251  unsigned char mask = 0x80 >> (i&7); //(i%8);
252  //--- toggling a bit using XOR
253  data[id] = data[id] ^ mask;
254 }
255 
256 QString Genotype::notes() const {
257  return notesv;
258 }
259 
260 void Genotype::setNotes( QString notes ) {
261  notesv = notes;
262 }
263 
264 int Genotype::hammingDistance( const Genotype* other ) {
265  int ret = 0;
266  for( unsigned int i=0; i<sizev; i++ ) {
267  ret += ( bit(i) == other->bit(i) );
268  }
269  return ret;
270 }
271 
273  for( unsigned int i=0; i<sizev; i++ ) {
274  if ( globalRNG->getBool( 0.5 ) ) {
275  set( i );
276  } else {
277  unset( i );
278  }
279  }
280 }
281 
282 QString Genotype::toString() const {
283  QString ret;
284  ret.resize( sizev );
285  for( unsigned int i=0; i<sizev; i++ ) {
286  ret[i] = ( bit(i) ) ? '1' : '0';
287  }
288  return ret;
289 }
290 
291 void Genotype::fromString( QString str ) {
292  int dim = qMin( (int)sizev, str.size() );
293  for( int i=0; i<dim; i++ ) {
294  if ( str[i] == '1' ) {
295  set( i );
296  } else {
297  unset( i );
298  }
299  }
300 }
301 
303  QByteArray temp;
304  temp.resize( sizev );
305  for( unsigned int i=0; i<sizev; i++ ) {
306  temp[i] = ( bit(i) ) ? '1' : '0';
307  }
308  QByteArray temp2 = qCompress( temp, 9 );
309  return temp2.toBase64();
310 }
311 
312 bool Genotype::fromCompressedString( QString str ) {
313  QByteArray temp2 = QByteArray::fromBase64( str.toLatin1() );
314  QByteArray temp = qUncompress( temp2 );
315  if ( temp.isEmpty() ) {
316  return false;
317  }
318  int dim = qMin( (int)sizev, temp.size() );
319  for( int i=0; i<dim; i++ ) {
320  if ( temp[i] == '1' ) {
321  set( i );
322  } else {
323  unset( i );
324  }
325  }
326  return true;
327 }
328 
329 unsigned int Genotype::extractUInt( unsigned int startPos, unsigned int stopPos ) const {
330  if ( startPos >= sizev ) return 0;
331 
332  stopPos = qMin( stopPos, sizev );
333  unsigned int len = stopPos - startPos;
334  unsigned int ret = 0;
335  for( ; startPos<stopPos; startPos++ ) {
336  len--;
337  if ( bit(startPos) ) {
338  ret += (unsigned int)( pow( 2.0f, (int)len ) );
339  }
340  }
341  return ret;
342 }
343 
344 void Genotype::insertUInt( unsigned int value, unsigned int startPos, unsigned int stopPos ) {
345  if ( startPos >= sizev ) return ;
346 
347  stopPos = qMin( stopPos, sizev );
348  for( ; stopPos!=startPos; stopPos-- ) {
349  if ( value%2 == 1 ) {
350  set( stopPos-1 );
351  } else {
352  unset( stopPos-1 );
353  }
354  value /= 2;
355  }
356  return;
357 }
358 
359 unsigned int Genotype::geneToBitIndex( unsigned int gene ) const {
360  return gene;
361 }
362 
363 unsigned int Genotype::bitToGeneIndex( unsigned int bit ) const {
364  return bit;
365 }
366 
368  //--- even if constructed with defaul constructor at least 1 char is allocated
369  delete []data;
370  sizev = source->sizev;
371  allocated = source->allocated;
372  data = new unsigned char[allocated];
373  memcpy( data, source->data, allocated );
374  fitnessv = source->fitnessv;
375  rankv = source->rankv;
376  notesv = source->notesv;
377  return;
378 }
379 
380 } // end namespace farsa