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