ga/src/core/genotype.cpp

00001 /********************************************************************************
00002  *  FARSA Genetic Algorithm Library                                             *
00003  *  Copyright (C) 2007-2008 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #include "core/genotype.h"
00021 #include "randomgenerator.h"
00022 #include <cmath>
00023 #include <cstring>
00024 #include "configurationparameters.h"
00025 
00026 namespace farsa {
00027 
00028 Genotype::Genotype( unsigned int size ) {
00029     sizev = size;
00030     //--- allocate a bit more that necessary for safety
00031     allocated = (unsigned int)( (size+1)/8 ) + 1;
00032     data = new unsigned char[allocated];
00033     fitnessv.resize(1);
00034     fitnessv[0] = 0.0;
00035     rankv = 0.0;
00036     notesv = "";
00037 }
00038 
00039 Genotype::~Genotype() {
00040     delete []data;
00041 }
00042 
00043 Genotype::Genotype( QString str, bool compressed ) {
00044     if ( !compressed ) {
00045         sizev = str.size();
00046         //--- allocate a bit more that necessary for safety
00047         allocated = (unsigned int)( (sizev+1)/8 ) + 1;
00048         data = new unsigned char[allocated];
00049         for( unsigned int i=0; i<sizev; i++ ) {
00050             if ( str[i] == '1' ) {
00051                 set( i );
00052             } else {
00053                 unset( i );
00054             }
00055         }
00056     } else {
00057         QByteArray temp2 = QByteArray::fromBase64( str.toAscii() );
00058         QByteArray temp  = qUncompress( temp2 );
00059         sizev = temp.size();
00060         //--- allocate a bit more that necessary for safety
00061         allocated = (unsigned int)( (sizev+1)/8 ) + 1;
00062         data = new unsigned char[allocated];
00063         for( unsigned int i=0; i<sizev; i++ ) {
00064             if ( temp[i] == '1' ) {
00065                 set( i );
00066             } else {
00067                 unset( i );
00068             }
00069         }
00070     }
00071     fitnessv.resize(1);
00072     fitnessv[0] = 0.0;
00073     rankv = 0.0;
00074     notesv = "";
00075 }
00076 
00077 Genotype::Genotype( const Genotype& genotype ) : ParameterSettableWithConfigureFunction() {
00078     sizev = genotype.sizev;
00079     allocated = genotype.allocated;
00080     data = new unsigned char[allocated];
00081     memcpy( data, genotype.data, allocated );
00082     fitnessv = genotype.fitnessv;
00083     rankv = genotype.rankv;
00084     notesv = genotype.notesv;
00085 }
00086 
00087 Genotype& Genotype::operator=( const Genotype& genotype ) {
00088     //--- even if constructed with default constructor at least 1 char is allocated
00089     delete []data;
00090     sizev = genotype.sizev;
00091     allocated = genotype.allocated;
00092     data = new unsigned char[allocated];
00093     memcpy( data, genotype.data, allocated );
00094     fitnessv = genotype.fitnessv;
00095     rankv = genotype.rankv;
00096     notesv = genotype.notesv;
00097     return (*this);
00098 }
00099 
00100 void Genotype::configure( ConfigurationParameters& params, QString prefix ) {
00101     int newsize = params.getValue( prefix + QString( "bitsize" ) ).toInt();
00102     Q_ASSERT_X( newsize > 0,
00103                 "Genotype::configure",
00104                 "The bitsize must be present in the config file and its value must be greater than zero" );
00105     resize( newsize );
00106     QString zipdata = params.getValue( prefix + QString( "data" ) );
00107     if ( !zipdata.isNull() ) {
00108         fromCompressedString( zipdata );
00109     }
00110     QStringList valuesList = params.getValue( prefix + QString( "fitness" ) )
00111                                     .split( QRegExp("\\s+"), QString::SkipEmptyParts );
00112     fitnessv.resize(0);
00113     foreach( QString avalue, valuesList ) {
00114         fitnessv << avalue.toDouble();
00115     }
00116     rankv = params.getValue( prefix + QString( "rank" ) ).toDouble();
00117     notesv = params.getValue( prefix + QString( "notes" ) );
00118 }
00119 
00120 void Genotype::save( ConfigurationParameters& params, QString prefix ) {
00121     params.createParameter( prefix, QString("type"), "Genotype" );
00122     params.createParameter( prefix, QString("bitsize"), QString("%1").arg(sizev) );
00123     QString fitstring;
00124     foreach( double avalue, fitnessv ) {
00125         fitstring.append( QString("%1 ").arg(avalue) );
00126     }
00127     params.createParameter( prefix, QString("fitness"), fitstring );
00128     params.createParameter( prefix, QString("data"), toCompressedString() );
00129     params.createParameter( prefix, QString("rank"), QString("%1").arg(rankv) );
00130     params.createParameter( prefix, QString("notes"), notesv );
00131 }
00132 
00133 void Genotype::describe( QString type ) {
00134     Descriptor d = addTypeDescription( type, "Genotype is a binary string, each bit correspond to a gene" );
00135     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" );
00136     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" );
00137     d.describeString( "data" ).help( "The bits composing the Genotype stored in a compressed string" );
00138     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" );
00139 }
00140 
00141 void Genotype::assign( const Genotype* genotype ) {
00142     Genotype& self = *this;
00143     self = *genotype;
00144 }
00145 
00146 Genotype* Genotype::clone() const {
00147     return new Genotype( *this );
00148 }
00149 
00150 unsigned int Genotype::size() const {
00151     return sizev;
00152 }
00153 
00154 void Genotype::resize( unsigned int newsize ) {
00155     //--- even if constructed with default constructor at least 1 char is allocated
00156     unsigned int old_allocated = allocated;
00157     sizev = newsize;
00158     allocated = (unsigned int)( (sizev+1)/8 ) + 1;
00159     unsigned char* newdata = new unsigned char[allocated];
00160     memcpy( newdata, data, ( allocated < old_allocated ) ? allocated : old_allocated );
00161     delete []data;
00162     data = newdata;
00163     return;
00164 }
00165 
00166 void Genotype::setFitness( double value ) {
00167     setObjective( 0, value );
00168 }
00169 
00170 double Genotype::fitness() const {
00171     return objective(0);
00172 }
00173 
00174 void Genotype::setObjective( int i, double value ) {
00175     fitnessv.resize( qMax( fitnessv.size(), i+1 ) );
00176     fitnessv[i] = value;
00177 }
00178 
00179 double Genotype::objective( int i ) const {
00180     return fitnessv[i];
00181 }
00182 
00183 int Genotype::numOfObjectives() const {
00184     return fitnessv.size();
00185 }
00186 
00187 bool Genotype::dominatedBy( const Genotype* g ) {
00188     int numObjToCompare = qMin( fitnessv.size(), g->fitnessv.size() );
00189     bool allLessEq = true;
00190     bool oneLessStrictly = false;
00191     for( int i=0; i<numObjToCompare; i++ ) {
00192         if ( fitnessv[i] > g->fitnessv[i] ) {
00193             allLessEq = false;
00194         }
00195         if ( fitnessv[i] < g->fitnessv[i] ) {
00196             oneLessStrictly = true;
00197         }
00198     }
00199     return (allLessEq && oneLessStrictly);
00200 }
00201 
00202 void Genotype::setRank( double r ) {
00203     rankv = r;
00204 }
00205 
00206 double Genotype::rank() const {
00207     return rankv;
00208 }
00209 
00210 bool Genotype::bit( unsigned int i ) const {
00211     //--- find the index into data array that contains bit request
00212     int id = i >> 3; //i/8;
00213     //--- create the mask for extract the bit requested
00214     //--- 0x80 is binary 1000.0000
00215     unsigned char mask = 0x80 >> (i&7); //(i%8);
00216     return ( data[id] & mask );
00217 }
00218 
00219 void Genotype::set( unsigned int i ) {
00220     //--- find the index into data array that contains bit request
00221     int id = i >> 3; //i/8;
00222     //--- create the mask
00223     //--- 0x80 is binary 1000.0000
00224     unsigned char mask = 0x80 >> (i&7); //(i%8);
00225     //--- setting bit to 1
00226     data[id] = data[id] | mask;
00227 }
00228 
00229 void Genotype::unset( unsigned int i ) {
00230     //--- find the index into data array that contains bit request
00231     int id = i >> 3; //i/8;
00232     //--- create the mask
00233     //--- 0x80 is binary 1000.0000
00234     unsigned char mask = 0x80 >> (i&7); //(i%8);
00235     //--- setting bit to 0, require the inverted mask
00236     data[id] = data[id] & (~mask);
00237 }
00238 
00239 void Genotype::toggle( unsigned int i ) {
00240     //--- find the index into data array that contains bit request
00241     int id = i >> 3; //i/8;
00242     //--- create the mask
00243     //--- 0x80 is binary 1000.0000
00244     unsigned char mask = 0x80 >> (i&7); //(i%8);
00245     //--- toggling a bit using XOR
00246     data[id] = data[id] ^ mask;
00247 }
00248 
00249 QString Genotype::notes() const {
00250     return notesv;
00251 }
00252 
00253 void Genotype::setNotes( QString notes ) {
00254     notesv = notes;
00255 }
00256 
00257 int Genotype::hammingDistance( const Genotype* other ) {
00258     int ret = 0;
00259     for( unsigned int i=0; i<sizev; i++ ) {
00260         ret += ( bit(i) == other->bit(i) );
00261     }
00262     return ret;
00263 }
00264 
00265 void Genotype::randomize() {
00266     for( unsigned int i=0; i<sizev; i++ ) {
00267         if ( globalRNG->getBool( 0.5 ) ) {
00268             set( i );
00269         } else {
00270             unset( i );
00271         }
00272     }
00273 }
00274 
00275 QString Genotype::toString() const {
00276     QString ret;
00277     ret.resize( sizev );
00278     for( unsigned int i=0; i<sizev; i++ ) {
00279         ret[i] = ( bit(i) ) ? '1' : '0';
00280     }
00281     return ret;
00282 }
00283 
00284 void Genotype::fromString( QString str ) {
00285     int dim = qMin( (int)sizev, str.size() );
00286     for( int i=0; i<dim; i++ ) {
00287         if ( str[i] == '1' ) {
00288             set( i );
00289         } else {
00290             unset( i );
00291         }
00292     }
00293 }
00294 
00295 QString Genotype::toCompressedString() const {
00296     QByteArray temp;
00297     temp.resize( sizev );
00298     for( unsigned int i=0; i<sizev; i++ ) {
00299         temp[i] = ( bit(i) ) ? '1' : '0';
00300     }
00301     QByteArray temp2 = qCompress( temp, 9 );
00302     return temp2.toBase64();
00303 }
00304 
00305 bool Genotype::fromCompressedString( QString str ) {
00306     QByteArray temp2 = QByteArray::fromBase64( str.toAscii() );
00307     QByteArray temp  = qUncompress( temp2 );
00308     if ( temp.isEmpty() ) {
00309         return false;
00310     }
00311     int dim = qMin( (int)sizev, temp.size() );
00312     for( int i=0; i<dim; i++ ) {
00313         if ( temp[i] == '1' ) {
00314             set( i );
00315         } else {
00316             unset( i );
00317         }
00318     }
00319     return true;
00320 }
00321 
00322 unsigned int Genotype::extractUInt( unsigned int startPos, unsigned int stopPos ) {
00323     if ( startPos >= sizev ) return 0;
00324 
00325     stopPos = qMin( stopPos, sizev );
00326     unsigned int len = stopPos - startPos;
00327     unsigned int ret = 0;
00328     for( ; startPos<stopPos; startPos++ ) {
00329         len--;
00330         if ( bit(startPos) ) {
00331             ret += (unsigned int)( pow( 2.0f, (int)len ) );
00332         }
00333     }
00334     return ret;
00335 }
00336 
00337 void Genotype::insertUInt( unsigned int value, unsigned int startPos, unsigned int stopPos ) {
00338     if ( startPos >= sizev ) return ;
00339     
00340     stopPos = qMin( stopPos, sizev );
00341     for( ; stopPos!=startPos; stopPos-- ) {
00342         if ( value%2 == 1 ) {
00343             set( stopPos-1 );
00344         } else {
00345             unset( stopPos-1 );
00346         }
00347         value /= 2;
00348     }
00349     return;
00350 }
00351 
00352 unsigned int Genotype::geneToBitIndex( unsigned int gene ) const {
00353     return gene;
00354 }
00355 
00356 unsigned int Genotype::bitToGeneIndex( unsigned int bit ) const {
00357     return bit;
00358 }
00359 
00360 void Genotype::copyDataFrom( Genotype* source ) {
00361     //--- even if constructed with defaul constructor at least 1 char is allocated
00362     delete []data;
00363     sizev = source->sizev;
00364     allocated = source->allocated;
00365     data = new unsigned char[allocated];
00366     memcpy( data, source->data, allocated );
00367     fitnessv = source->fitnessv;
00368     rankv = source->rankv;
00369     notesv = source->notesv;
00370     return;
00371 }
00372 
00373 } // end namespace farsa