[PD-cvs] externals/grh/GApop/src GApop.cpp,NONE,1.1

Georg Holzmann grholzi at users.sourceforge.net
Tue Jul 12 16:20:41 CEST 2005


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 at 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 at 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 at 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]]);
}





More information about the Pd-cvs mailing list