Update of /cvsroot/pure-data/externals/grh/GApop/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2556/GApop/src
Added Files: GApop.cpp Log Message: initial commit of GApop
--- NEW FILE: GApop.cpp --- //************************************************************************* // GApop - external for PD and MAX/MSP // // This is a genetic algorithm, see the PD help-file // how to use it // // Copyright (c) 2004 Georg Holzmann grh@gmx.at // For information on usage and redistribution, and for a DISCLAIMER OF ALL // WARRANTIES, see the file, "license.txt," in this distribution. // // You'll need flext by Thomas Grill to compile this external //*************************************************************************
// IMPORTANT: enable attribute processing (specify before inclusion of flext headers!) // For clarity, this is done here, but you'd better specify it as a compiler definition // FLEXT_ATTRIBUTES must be 0 or 1, #define FLEXT_ATTRIBUTES 1
// includes #include <flext.h> #include <stdlib.h> #include <ctime>
// check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif
// define the class that stands for a pd/Max object class GApop: // inherit from basic flext class public flext_base { // obligatory flext header (class name,base class name) featuring a setup function FLEXT_HEADER_S(GApop,flext_base,setup)
public: // constructor with a variable argument list GApop(int argc,const t_atom *argv);
protected: // 2 Arrays are saved here: // popbuf: the population itself // fitbuf: the fitness function const t_symbol *fitname, *popname; buffer *fitbuf, *popbuf; // size of the popbuf int buffsize; // the fitness order int *tempfit;
// the other parameters: // pairs = number of pairs at crossover // mutprop = mutation probability // mutrange = mutation range int pairs; float mutprop, mutrange;
// set new buffer for the population void m_set(int argc,const t_atom *argv);
// get population buffer name void mg_pop(AtomList &lst) const; // set population buffer name inline void ms_pop(const AtomList &lst) { m_set(lst.Count(),lst.Atoms()); }
// get fitness function buffer name void mg_fit(AtomList &lst) const; // set fitness function buffer name inline void ms_fit(const AtomList &lst);
// make the fitscaling, crossover and mutation void m_cross();
// takes the incomig ints and gives out the specific individuum: // 0 ... fittest individuum // 1 ... next individuum // ... void m_trigger(int i);
// cuts a number, if it's greater than 1 or smaller than 0 float cutse(float nu);
// get number of pairs for crossover void mg_pairs(int &p) { p = pairs; } // set number of pairs for crossover void ms_pairs(int &p) { pairs = p; }
// get mutation porpability void mg_mutprop(float &mp) { mp = mutprop; } // set mutation porpability void ms_mutprop(float &mp) { mutprop = cutse(mp); } // get mutation range void mg_mutrange(float &mp) { mp = mutrange; } // set mutation range void ms_mutrange(float &mp) { mutrange = cutse(mp); }
// update the array (set the actual frame length) inline void ms_frames() { if(Checkpopbuf()) popbuf->Frames(buffsize); } // check and eventually update fit buffer reference (return true if valid) bool Checkfitbuf(); // check and eventually update pop buffer reference (return true if valid) bool Checkpopbuf(); // gives out a random float in the given boundaries float ZZ(float b1, float b2);
// override default flext help function virtual void m_help();
private: static void setup(t_classid c);
FLEXT_CALLBACK_V(m_set) // wrapper for method m_set (with variable argument list) FLEXT_CALLVAR_V(mg_pop,ms_pop) // wrappers for attribute getter/setter (with variable argument list) FLEXT_CALLVAR_V(mg_fit,ms_fit) // wrappers for attribute getter/setter (with variable argument list)
// callback for method "m_cross" (with no argument): FLEXT_CALLBACK(m_cross) // callback for method "m_trigger" (with one int argument): FLEXT_CALLBACK_I(m_trigger)
// the variables: FLEXT_CALLVAR_I(mg_pairs,ms_pairs) FLEXT_CALLVAR_F(mg_mutprop,ms_mutprop) FLEXT_CALLVAR_F(mg_mutrange,ms_mutrange)
FLEXT_CALLBACK(ms_frames) // callback for attribute setter ms_frames };
// instantiate the class FLEXT_NEW_V("GApop",GApop)
// setup function of the GApop void GApop::setup(t_classid c) { // register methods and attributes FLEXT_CADDMETHOD_(c,0,"set",m_set); // register method "set" for inlet 0 FLEXT_CADDMETHOD_(c,0,"cross",m_cross); // register method "cross" for inlet 0 // register a method to the default inlet (0) FLEXT_CADDMETHOD(c,0,m_trigger);
FLEXT_CADDATTR_VAR(c,"popbuf",mg_pop,ms_pop); // register attribute "popbuf" FLEXT_CADDATTR_VAR(c,"fitbuf",mg_fit,ms_fit); // register attribute "fitbuf"
FLEXT_CADDATTR_VAR(c,"pairs",mg_pairs,ms_pairs); // register attribute for pairs FLEXT_CADDATTR_VAR(c,"mutprop",mg_mutprop,ms_mutprop); // register attribute for mutprop FLEXT_CADDATTR_VAR(c,"mutrange",mg_mutrange,ms_mutrange); // register attribute for mutrange
FLEXT_CADDMETHOD_(c,0,"update",ms_frames); // register method "update" for inlet 0
// write to the console: post("\nGApop - by Georg Holzmann grh@gmx.at, 2004"); post("(send me a help - message !!!)"); }
void GApop::m_help() { // post a help message // thisName() returns a char * for the object name post("\nGApop, Vers.0.0.1 - a genetic algorithm object"); post("compiled with flext on %s",__DATE__); post("1 - set all parameters:"); post("popbuf contains the population (array with numbers"); post(" between 0 and 1, max size is 200)"); post("fitbuf contains the fitness function (numbers between"); post(" 0 and 1, size should be 101: 0 = fitness(0),"); post(" 1 = fitness(0.01), ..., 100 = fitness(1) )"); post("pairs number of pairs for the crossover"); post("mutprop mutation probability (between 0 and 1)"); post("mutrange mutation range in percent (between 0 and 1)"); post("2 - get the data:"); post("cross makes fitscaling, crossover and mutation"); post("numbers in inlet 0 get the values: 0 means the value"); post(" of the fittest, 1 the value of the next..."); post("have fun - Georg Holzmann grh@gmx.at\n"); }
// constructor of GApop GApop::GApop(int argc,const t_atom *argv) { // reset random numbers srand(static_cast<int>(time(NULL))); // set the variables fitbuf=NULL; fitname=NULL; popbuf=NULL; popname=NULL; tempfit=NULL; buffsize=0; pairs = 0; mutprop = 0; mutrange = 0;
// define inlets: // first inlet must always be of type anything (or signal for dsp objects) AddInAnything("message inlet"); // add one inlet for any message // peek outlet AddOutFloat("parameter outlet"); // set buffer according to creation arguments if(argc == 1 && IsSymbol(argv[0])) { m_set(argc,argv); } }
// gives out a random float in the given boundaries float GApop::ZZ(float b1, float b2) { const int faktor = 10000;
int min, max; if(b1<b2) { min = int(b1*faktor); max = int(b2*faktor); } else { max = int(b1*faktor); min = int(b2*faktor); }
return (float(min + rand()%(max-min+1))/faktor); }
// check and eventually update pop buffer reference (return true if valid) bool GApop::Checkpopbuf() { if(!popbuf || !popbuf->Valid()) { post("%s (%s) - no valid population buffer defined",thisName(),GetString(thisTag())); // return zero length return false; } else { if(popbuf->Update()) { // buffer parameters have been updated if(popbuf->Valid()) { post("%s (%s) - updated population buffer reference",thisName(),GetString(thisTag())); return true; } else { post("%s (%s) - population buffer has become invalid",thisName(),GetString(thisTag())); return false; } } else return true; } }
// cuts a number, if it's greater than 1 or smaller than 0 float GApop::cutse(float nu) { if(nu>1) { return 1;} else { if(nu<0) { return 0;} else { return nu; } } }
// and now the same for the fitness buffer bool GApop::Checkfitbuf() { if(!fitbuf || !fitbuf->Valid()) { post("%s (%s) - no valid fitness buffer defined",thisName(),GetString(thisTag())); // return zero length return false; } else { if(fitbuf->Update()) { // buffer parameters have been updated if(fitbuf->Valid()) { post("%s (%s) - updated fitness buffer reference",thisName(),GetString(thisTag())); return true; } else { post("%s (%s) - fitness buffer has become invalid",thisName(),GetString(thisTag())); return false; } } else return true; } }
// set new buffer for the population void GApop::m_set(int argc,const t_atom *argv) { if(argc == 1 && IsSymbol(argv[0])) { // one symbol given as argument // clear existing buffer if(popbuf) delete popbuf; if(tempfit) delete tempfit;
// save buffer name popname = GetSymbol(argv[0]); // make new reference to system buffer object popbuf = new buffer(popname); buffsize = popbuf->Frames();
// make new tempfit buffer tempfit = new int[buffsize];
if(!popbuf->Ok()) { post("%s (%s) - warning: population buffer is currently not valid!",thisName(),GetString(thisTag())); } } else { // invalid argument list, leave buffer as is but issue error message to console post("%s (%s) - message argument of popbuf must be a symbol",thisName(),GetString(thisTag())); } }
// get population buffer name void GApop::mg_pop(AtomList &lst) const { if(popbuf) { // buffer exists: return buffer name lst(1); SetSymbol(lst[0],popname); } else // no buffer: set empty list lst(0); }
// get fitness function buffer name void GApop::mg_fit(AtomList &lst) const { if(fitbuf) { // buffer exists: return buffer name lst(1); SetSymbol(lst[0],fitname); } else // no buffer: set empty list lst(0); }
// set fitness function buffer name void GApop::ms_fit(const AtomList &lst) { if(lst.Count() == 1 && IsSymbol(*lst.Atoms())) { // one symbol given as argument // clear existing buffer delete fitbuf; // save buffer name fitname = GetSymbol(lst[0]); // make new reference to system buffer object fitbuf = new buffer(fitname); if(!fitbuf->Ok()) { post("%s (%s) - warning: fitness buffer is currently not valid!",thisName(),GetString(thisTag())); } } else { // invalid argument list, leave buffer as is but issue error message to console post("%s (%s) - message argument of fitbuf must be a symbol",thisName(),GetString(thisTag())); } }
// make the fitscaling, crossover and mutation void GApop::m_cross() { if(Checkpopbuf() && Checkfitbuf() && pairs<(buffsize/2-1)) {
// 1. step: // every parameter get's a fitness from the // given fitness function // this fitness is saved into the temporary array tempfit1[]
// make temporary array float *tempfit1 = new float[200];
// write the fitness for(int i=0; i < buffsize; i++) { tempfit1[i] = cutse(fitbuf->Data()[int(popbuf->Data()[i]*100+0.5)]); }
// 2. step: // now the fitness order of the parameters are written // into the array tempfit[] for(int j=0; j < buffsize; j++) { int fitti=0; float fittw=0; // get max and set it to 0 for(int k = 0; k < buffsize; k++) { if(fittw<tempfit1[k]) { fittw = tempfit1[k]; fitti = k; } } // write the order to the tempfit array tempfit[j] = fitti; tempfit1[fitti] = 0; }
// 3. step: // the crossover: every pair generates 2 children and replace the // individuums with the lowest fitness // the number of pairs are given (int pairs) for(int ii=pairs; ii>0; ii--) { // the first children popbuf->Data()[tempfit[buffsize-ii*2+1]] = cutse(ZZ(popbuf->Data()[tempfit[ii*2-1]],popbuf->Data()[tempfit[ii*2-2]])); // the second children popbuf->Data()[tempfit[buffsize-ii*2]] = cutse(ZZ(popbuf->Data()[tempfit[ii*2-1]],popbuf->Data()[tempfit[ii*2-2]])); }
// 4. step: // the last step is the mutation: // made with the parameter mutation probability (float mutprop) // and mutation range (float mutrange)
for(int jj=0; jj<buffsize; jj++) { if(rand()%101 < int(mutprop*100)) { popbuf->Data()[jj] = cutse(popbuf->Data()[jj] + (ZZ(0,2*mutrange)-mutrange)); } }
// delete the temporary array delete []tempfit1; }
else { // invalid buffers post("GApop - entered buffers are invalid!"); } }
// takes the incomig ints and gives out the specific individuum: // 0 ... fittest individuum // 1 ... next individuum // ... void GApop::m_trigger(int i) { // if buffer is invalid bail out if(!Checkpopbuf()) return; // make the boundaries for i: if(i<0) {i=0;} if(i>200) {i=200;} // correct syntax, output value ToOutFloat(0,popbuf->Data()[tempfit[i]]); }