/// This is the default population initializer. It simply calls the initializer /// for each member of the population. Then we touch the population to tell it /// that it needs to update stats and/or sort (but we don't actually force /// either one to occur. /// The population object takes care of setting/unsetting the status flags. void GAPopulation::DefaultInitializer(GAPopulation & p) { for(int i = 0; i < p.size(); i++) { p.individual(i).initialize(); } }
/// The default evaluator simply calls the evaluate member of each genome in /// the population. The population object takes care of setting/unsetting the /// status flags for indicating when the population needs to be updated again. void GAPopulation::DefaultEvaluator(GAPopulation & p) { for(int i = 0; i < p.size(); i++) { p.individual(i).evaluate(); } }
// This is an implementation of the sigma truncation scaling method descibed in // goldberg p 124. If the scaled fitness is less than zero, we arbitrarily set // it to zero (thus the truncation part of 'sigma truncation'). void GASigmaTruncationScaling::evaluate(const GAPopulation & p) { for(int i=0; i<p.size(); i++){ double f = (double)(p.individual(i).score()) - (double)(p.ave()); f += (double)c * (double)(p.dev()); if(f < 0) f = 0.0; p.individual(i).fitness((float)f); // might lose information here! } }
// This assumes that the original population contains at least one individual // from which to grow. If it does not, the data in the buffer will be ignored. int RecvPopulation(GAPopulation& pop) { int status = 0; int psize = 0; status = pvm_upkint(&psize, 1, 1); pop.size(psize); for(int i=0; i<pop.size() && status>=0; i++) status = UnpackIndividual(pop.individual(i)); return status; }
// This should eventually use a genome member function rather than an external. // When we pack/unpack a population we also stuff its statistics. int SendPopulation(int toid, const GAPopulation& pop) { int status = 0; int psize = pop.size(); status = pvm_initsend(PvmDataDefault); status = pvm_pkint(&psize, 1, 1); for(int i=0; i<pop.size() && status>=0; i++) status = PackIndividual(pop.individual(i)); status = pvm_send(toid, MSG_INCOMING_POPULATION); return status; }
void Evolver::initGA(float pMutation, int popSize, GABoolean elitist) { GAPopulation pop; for (; popSize > 0; --popSize) { try { Composition *c = new Composition(); SampleBank::getInstance().initComposition(*c); pop.add(c); } catch (std::bad_alloc & e) { throw std::runtime_error("couldn't alloc new composition"); } } initGA(pMutation, elitist, 0, pop); }
// This is an implementation of the most basic form of power scaling, where the // fitness is a function of the objective score raised to some power. Negative // objective scores are not allowed. If we get one, we post an error and set // all of the fitness scores to zero. void GAPowerLawScaling::evaluate(const GAPopulation & p) { for(int i=0; i<p.size(); i++){ double f = p.individual(i).score(); if(f < 0.0){ GAErr(GA_LOC, className(), "evaluate", gaErrPowerNegFitness); for(int ii=0; ii<p.size(); ii++) p.individual(ii).fitness(0.0); return; } f = pow(f,(double)k); p.individual(i).fitness((float)f); // might lose information here! } }
GAGeneticAlgorithm::GAGeneticAlgorithm(const GAPopulation& p) : stats(), params() { pop = new GAPopulation(p); pop->geneticAlgorithm(*this); ud = nullptr; cf = GAGeneticAlgorithm::DEFAULT_TERMINATOR; d_seed = gaDefSeed; params.add(gaNseed, gaSNseed, GAParameter::INT, &d_seed); minmax = gaDefMiniMaxi; params.add(gaNminimaxi, gaSNminimaxi, GAParameter::INT, &minmax); ngen = gaDefNumGen; params.add(gaNnGenerations, gaSNnGenerations, GAParameter::INT, &ngen); nconv = gaDefNConv; stats.nConvergence(nconv); params.add(gaNnConvergence, gaSNnConvergence, GAParameter::INT, &nconv); pconv = gaDefPConv; params.add(gaNpConvergence, gaSNpConvergence, GAParameter::FLOAT, &pconv); pcross = gaDefPCross; params.add(gaNpCrossover, gaSNpCrossover, GAParameter::FLOAT, &pcross); pmut = gaDefPMut; params.add(gaNpMutation, gaSNpMutation, GAParameter::FLOAT, &pmut); int psize = pop->size(); params.add(gaNpopulationSize, gaSNpopulationSize, GAParameter::INT, &psize); stats.scoreFrequency(gaDefScoreFrequency1); params.add(gaNscoreFrequency, gaSNscoreFrequency, GAParameter::INT, &gaDefScoreFrequency1); stats.flushFrequency(gaDefFlushFrequency); params.add(gaNflushFrequency, gaSNflushFrequency, GAParameter::INT, &gaDefFlushFrequency); stats.recordDiversity(gaDefDivFlag); params.add(gaNrecordDiversity, gaSNrecordDiversity, GAParameter::INT, &gaDefDivFlag); stats.scoreFilename(gaDefScoreFilename); params.add(gaNscoreFilename, gaSNscoreFilename, GAParameter::STRING, gaDefScoreFilename); stats.selectScores(gaDefSelectScores); params.add(gaNselectScores, gaSNselectScores, GAParameter::INT, &gaDefSelectScores); stats.nBestGenomes(p.individual(0), gaDefNumBestGenomes); params.add(gaNnBestGenomes, gaSNnBestGenomes, GAParameter::INT, &gaDefNumBestGenomes); scross = p.individual(0).sexual(); across = p.individual(0).asexual(); }
// Set the score info to the appropriate values. Update the score count. void GAStatistics::setScore(const GAPopulation& pop){ aveCur = pop.ave(); maxCur = pop.max(); minCur = pop.min(); devCur = pop.dev(); divCur = ((dodiv == gaTrue) ? pop.div() : (float)-1.0); if(Nscrs == 0) return; gen[nscrs] = curgen; aveScore[nscrs] = aveCur; maxScore[nscrs] = maxCur; minScore[nscrs] = minCur; devScore[nscrs] = devCur; divScore[nscrs] = divCur; nscrs++; }
GADemeGA::GADemeGA(const GAPopulation& p) : GAGeneticAlgorithm(p) { if(p.size() < 1) { GAErr(GA_LOC, className(), "GADemeGA(GAPopulation&)", gaErrNoIndividuals); pop = 0; nrepl = 0; tmppop = 0; pstats = 0; } else { npop = gaDefNPop; params.add(gaNnPopulations, gaSNnPopulations, GAParameter::INT, &npop); nmig = gaDefNMig; params.add(gaNnMigration, gaSNnMigration, GAParameter::INT, &nmig); unsigned int nr = pop->size()/2; nrepl = new int [npop]; deme = new GAPopulation* [npop]; pstats = new GAStatistics [npop]; tmppop = new GAPopulation(p.individual(0), nr); for(unsigned int i=0; i<npop; i++) { nrepl[i] = nr; deme[i] = new GAPopulation(p); } } }
int main(int argc, const char * argv[]) { srand((unsigned)time(NULL)); printf("Running Genetic Algorithm...\n"); printf("Target Genes: %s\n", kTargetGenes.c_str()); printf("Population Size: %d\n", kPopulationSize); printf("Elitism: %s\n", (kElitismEnabled ? "true" : "false")); printf("Elitism Percentage: %d%%\n", kElitismPercentage); printf("Mutation Probability: %d%%\n", kMutationProbability); printf("Crossover Probability: %d%%\n\n", kCrossoverProbability); GAPopulation population = GAPopulation(kPopulationSize, kTargetGenes); population.elitismEnabled = kElitismEnabled; population.tournamentSize = kTournamentSize; population.elitismPercentage = PERCENTAGE(kElitismPercentage); population.mutationProbability = PERCENTAGE(kMutationProbability); population.crossoverProbability = PERCENTAGE(kCrossoverProbability); population.selectionType = GAPopulationSelectionType::GAPopulationSelectionTypeTournament; population.crossoverType = GAPopulationCrossoverType::GAPopulationCrossoverTypeOnePoint; population.evolve(); return 0; }
void GALinearScaling::evaluate(const GAPopulation & p) { // Here we calculate the slope and intercept using the multiplier and objective // score ranges... double pmin = p.min(); double pmax = p.max(); double pave = p.ave(); double delta, a, b; if(pave == pmax){ // no scaling - population is all the same a = 1.0; b = 0.0; } else if(pmin > ((double)c * pave - pmax)/((double)c - 1.0)){ delta = pmax - pave; a = ((double)c - 1.0) * pave / delta; b = pave * (pmax - (double)c * pave) / delta; } else{ // stretch to make min be 0 delta = pave - pmin; a = pave / delta; b = -pmin * pave / delta; } // and now we calculate the scaled scaled values. Negative scores are not // allowed with this kind of scaling, so check for negative values. If we get // a negative value, dump an error message then set all of the scores to 0. for(int i=0; i<p.size(); i++){ double f = p.individual(i).score(); if(f < 0.0){ GAErr(GA_LOC, className(), "evaluate", gaErrNegFitness); for(int ii=0; ii<p.size(); ii++) p.individual(ii).fitness(0.0); return; } f = f * a + b; if(f < 0) f = 0.0; // truncate if necessary (only due to roundoff error) p.individual(i).fitness((float)f); // might lose information here! } }
// This population evaluator is the administrator for the parallelization. // It looks around to see when slaves are available to evaluate a genome. As // soon as a slave is available and a genome needs to be evaluated, this // routine sends it off. When a slave is finished, it posts a message to // say so and this routine gets the message and grabs the results from the // slave that posted the message. // An index of -1 means that the slave has no assignment. The first int in // the stream of stuff is always the ID of the slave (0-nslaves) that is // sending the information. After that it is either nothing (the slave just // reported that it is ready for another genome) or it is a float (the score // of the genome that was assigned to the slave). void PopulationEvaluator(GAPopulation& pop) { PVMDataPtr data = (PVMDataPtr)pop.userData(); int* index = new int [data->nreq]; int done = 0, outstanding = 0, next = 0; int bufid, status, bytes, msgtag, tid, who; while(!done) { // If we have a genome that needs to be evaluated and one of the slaves is // ready to evaluate it, send the genome to the slave. if(next < pop.size() && (bufid=pvm_nrecv(-1, MSG_READY)) != 0) { if(bufid > 0) { pvm_bufinfo(bufid, &bytes, &msgtag, &tid); status = SendGenomeData(pop.individual(next), tid); if(status >= 0) { if((who = id2idx(tid, *data)) >= 0) { index[who] = next; next++; outstanding++; } else { cerr << "PopEval: bogus tid mapping: " << tid << "\n"; } } else { cerr << "PopEval: error sending data to: " << tid; cerr << " error code is: " << status << "\n"; } } else { cerr << "PopEval: error from pvm_nrecv: " << bufid << "\n"; } } // If we have any genomes waiting for their evaluation and any slaves have // posted a message stating that they have a finished score ready for us, get // the score from the slave and stuff it into the appropriate genome. if(outstanding > 0 && (bufid=pvm_nrecv(-1, MSG_GENOME_SCORE)) != 0) { if(bufid > 0) { pvm_bufinfo(bufid, &bytes, &msgtag, &tid); if((who = id2idx(tid, *data)) >= 0) { if(index[who] >= 0) { status = RecvGenomeScore(pop.individual(index[who])); if(status >= 0) { index[who] = -1; outstanding--; } else { cerr << "PopEval: error receiving score from: " << tid; cerr << " error code is: " << status << "\n"; } } else { cerr << "PopEval: index conflict from tid " << tid << "\n"; } } else { cerr << "PopEval: bogus tid mapping: " << tid << "\n"; } } else { cerr << "PopEval: error from pvm_nrecv: " << bufid << "\n"; } } if(next == pop.size() && outstanding == 0) done = 1; if(next > pop.size()) { cerr << "bogus value for next: " << next; cerr << " popsize is: " << pop.size() << "\n"; } } delete [] index; }
// The population initializer invokes the genomes' initializers just like the // standard population initializer, but here we farm out the genomes to the // slaves before invoking the initialization. Farm out the genomes and give // the slaves the initialize command rather than the evaluate command. void PopulationInitializer(GAPopulation& pop) { PVMDataPtr data = (PVMDataPtr)pop.userData(); int* index = new int [data->nreq]; int done = 0, outstanding = 0, next = 0; int bufid, status, bytes, msgtag, tid, who; while(!done) { // If we have a genome that needs to be initialized and one of the slaves is // available, then ask the slave to configure a genome and send us back the // configured, initialized genome. if(next < pop.size() && (bufid=pvm_nrecv(-1, MSG_READY)) != 0) { if(bufid > 0) { status = pvm_bufinfo(bufid, &bytes, &msgtag, &tid); status = SendGenomeInitialize(pop.individual(next), tid); if(status >= 0) { if((who = id2idx(tid, *data)) >= 0) { index[who] = next; next++; outstanding++; } else { cerr << "PopInit: bogus tid mapping: " << tid << "\n"; } } else { cerr << "PopInit: error sending initialize command to: " << tid; cerr << " genome " << next << " will be inited by next slave\n"; cerr << " error code is: " << status << "\n"; } } else { cerr << "PopInit: error from pvm_nrecv: " << bufid << "\n"; } } // If we have requests for initialization outstanding and a slave has posted // a message stating that it will provide genome data, then get the data from // the slave and stuff it into the appropriate genome in the population. if(outstanding > 0 && (bufid=pvm_nrecv(-1, MSG_GENOME_DATA)) != 0) { if(bufid > 0) { status = pvm_bufinfo(bufid, &bytes, &msgtag, &tid); if((who = id2idx(tid, *data)) >= 0) { if(index[who] >= 0) { status = RecvGenomeData(pop.individual(index[who])); if(status >= 0) { index[who] = -1; outstanding--; } else { cerr << "PopInit: error receiving data from: " << tid; cerr << " error code is: " << status << "\n"; } } else { cerr << "PopInit: index conflict from tid " << tid << "\n"; } } else { cerr << "PopInit: bogus tid mapping: " << tid << "\n"; } } else { cerr << "PopInit: error from pvm_nrecv: " << bufid << "\n"; } } if(next == pop.size() && outstanding == 0) done = 1; if(next > pop.size()) { cerr << "bogus value for next: " << next; cerr << " popsize is: " << pop.size() << "\n"; } } delete [] index; }
// Reset the GA's statistics based on the population. To do this right you // should initialize the population before you pass it to this routine. If you // don't, the stats will be based on a non-initialized population. void GAStatistics::reset(const GAPopulation & pop){ curgen = 0; numsel = numcro = nummut = numrep = numeval = numpeval = 0; memset(gen, 0, Nscrs*sizeof(int)); memset(aveScore, 0, Nscrs*sizeof(float)); memset(maxScore, 0, Nscrs*sizeof(float)); memset(minScore, 0, Nscrs*sizeof(float)); memset(devScore, 0, Nscrs*sizeof(float)); memset(divScore, 0, Nscrs*sizeof(float)); nscrs = 0; setScore(pop); if(Nscrs > 0) flushScores(); memset(cscore, 0, Nconv*sizeof(float)); nconv = 0; // should set to -1 then call setConv cscore[0] = ((pop.order() == GAPopulation::HIGH_IS_BEST) ? pop.max() : pop.min()); // cscore[0] = pop.max(); // setConvergence(maxScore[0]); updateBestIndividual(pop, gaTrue); aveCur = aveInit = pop.ave(); maxCur = maxInit = maxever = pop.max(); minCur = minInit = minever = pop.min(); devCur = devInit = pop.dev(); divCur = divInit = ((dodiv == gaTrue) ? pop.div() : (float)-1.0); on = pop.ave(); offmax = pop.max(); offmin = pop.min(); numpeval = pop.nevals(); for(int i=0; i<pop.size(); i++) numeval += pop.individual(i).nevals(); }
// Use this method to update the statistics to account for the current // population. This routine increments the generation counter and assumes that // the population that gets passed is the current population. // If we are supposed to flush the scores, then we dump them to the specified // file. If no flushing frequency has been specified then we don't record. void GAStatistics::update(const GAPopulation & pop){ ++curgen; // must do this first so no divide-by-zero if(scoreFreq > 0 && (curgen % scoreFreq == 0)) setScore(pop); if(Nscrs > 0 && nscrs >= Nscrs) flushScores(); maxever = (pop.max() > maxever) ? pop.max() : maxever; minever = (pop.min() < minever) ? pop.min() : minever; float tmpval; tmpval = (on*(curgen-1) + pop.ave()) / curgen; on = tmpval; tmpval = (offmax*(curgen-1) + pop.max()) / curgen; offmax = tmpval; tmpval = (offmin*(curgen-1) + pop.min()) / curgen; offmin = tmpval; setConvergence((pop.order() == GAPopulation::HIGH_IS_BEST) ? pop.max() : pop.min()); updateBestIndividual(pop); numpeval = pop.nevals(); }
// Update the genomes in the 'best of all' population to reflect any // changes made to the current population. We just grab the genomes with // the highest scores from the current population, and if they are higher than // those of the genomes in the boa population, they get copied. Note that // the bigger the boa array, the bigger your running performance hit because // we have to look through all of the boa to figure out which are better than // those in the population. The fastest way to use the boa is to keep only // one genome in the boa population. A flag of 'True' will reset the boa // population so that it is filled with the best of the current population. // Unfortunately it could take a long time to update the boa array using the // copy method. We'd like to simply keep pointers to the best genomes, but // the genomes change from generation to generation, so we can't depend on // that. // Notice that keeping boa is useful even for overlapping populations. The // boa keeps individuals that are different from each other - the overlapping // population may not. However, keeping boa is most useful for populations // with little overlap. // When we check to see if a potentially better member is already in our // best-of-all population, we use the operator== comparator not the genome // comparator to do the comparison. void GAStatistics:: updateBestIndividual(const GAPopulation & pop, GABoolean flag){ if(boa == (GAPopulation *)0 || boa->size() == 0) return; // do nothing if(pop.order() != boa->order()) boa->order(pop.order()); if(flag == gaTrue){ // reset the BOA array int j=0; for(int i=0; i<boa->size(); i++){ boa->best(i).copy(pop.best(j)); if(j < pop.size()-1) j++; } return; } if(boa->size() == 1){ // there's only one boa so replace it with bop if(boa->order() == GAPopulation::HIGH_IS_BEST && pop.best().score() > boa->best().score()) boa->best().copy(pop.best()); if(boa->order() == GAPopulation::LOW_IS_BEST && pop.best().score() < boa->best().score()) boa->best().copy(pop.best()); } else{ int i=0, j, k; if(boa->order() == GAPopulation::HIGH_IS_BEST) { while(i < pop.size() && pop.best(i).score() > boa->worst().score()){ for(k=0; pop.best(i).score() < boa->best(k).score() && k < boa->size(); k++); for(j=k; j<boa->size(); j++){ if(pop.best(i) == boa->best(j)) break; if(pop.best(i).score() > boa->best(j).score()){ boa->worst().copy(pop.best(i)); // replace worst individual boa->sort(gaTrue, GAPopulation::RAW); // re-sort the population break; } } i++; } } if(boa->order() == GAPopulation::LOW_IS_BEST) { while(i < pop.size() && pop.best(i).score() < boa->worst().score()){ for(k=0; pop.best(i).score() > boa->best(k).score() && k < boa->size(); k++); for(j=k; j<boa->size(); j++){ if(pop.best(i) == boa->best(j)) break; if(pop.best(i).score() < boa->best(j).score()){ boa->worst().copy(pop.best(i)); // replace worst individual boa->sort(gaTrue, GAPopulation::RAW); // re-sort the population break; } } i++; } } } return; }
// This is an implementation of speciation using the sharing method described // by goldberg in his book. This requires a user-defined distance function in // order to work. The distance function returns a value between // 0 and 1 inclusive to tell us how similar two genomes are to each other. // A value of 0 means that the two genomes are identical to each other, a // value of 1 means they are completely different. // A single genome is identical to itself, so d(i,i) is 0. // If alpha is 1 then we don't use pow(). // If we have a comparator to use, use it. If not, use the comparator of // each genome. // We can cut in half the number of calls to the sharing function by keeping // one half of the ixj matrix. This is because d(i,j) is the same as d(j,i). // We cache the distances in an upper right triangular matrix stored as a // series of floats. // If the population is maximizing then we derate by dividing. If the // population is minimizing then we derate by multiplying. First we check to // see if there is a GA using the population. If there is, we use its min/max // flag to determine whether or not we should be minimizing or maximizing. If // there is not GA with the population, then we use the population's sort order // as the basis for whether to minimize or maximize. // *** This could be done with n*n/2 instead of n*n, to reduce storage, but we // can't reduce computation any more... // *** probably should use the diversity built-in to the population... void GASharing::evaluate(const GAPopulation& p) { if(p.size() > (int)N){ delete [] d; N = p.size(); d = new float[N*N]; } int n = p.size(); int i, j; if(df) { for(i=0; i<n; i++){ // calculate and cache the distances d[i*n+i] = 0.0; // each genome is same as itself for(j=i+1; j<n; j++) d[i*n+j] = d[j*n+i] = (*df)(p.individual(i), p.individual(j)); } } else { for(i=0; i<n; i++){ // calculate and cache the distances d[i*n+i] = 0.0; // each genome is same as itself for(j=i+1; j<n; j++) d[i*n+j] = d[j*n+i] = p.individual(i).compare(p.individual(j)); } } int mm; if(_minmax == 0) { if(p.geneticAlgorithm()) mm = p.geneticAlgorithm()->minimaxi(); else mm = ((p.order() == GAPopulation::HIGH_IS_BEST) ? GAGeneticAlgorithm::MAXIMIZE : GAGeneticAlgorithm::MINIMIZE); } else { mm = _minmax; } for(i=0; i<n; i++){ // now derate the fitness of each genome double sum = 0.0; for(j=0; j<n; j++) { if(d[i*n+j] < _sigma) { if(_alpha == 1) sum += ((d[i*n+j] >= _sigma) ? 0.0 : 1.0 - d[i*n+j]/_sigma); else sum += ((d[i*n+j]>=_sigma) ? 0.0 : 1.0-pow(d[i*n+j]/_sigma,_alpha)); } } double f; if(mm == GAGeneticAlgorithm::MINIMIZE) f = p.individual(i).score() * sum; else f = p.individual(i).score() / sum; p.individual(i).fitness((float)f); // might lose information here! } }
// Assign the fitness scores to be the same as the objective scores for all of // the individuals in the population. void GANoScaling::evaluate(const GAPopulation& p) { for(int i=0; i<p.size(); i++) p.individual(i).fitness(p.individual(i).score()); }
void PetriDish::GAPDEvaluator( GAPopulation & pop ) { assert(pop.size() > 0); // Since this is a static method, get this population's associated PetriDish PetriDish* thisPetriDish = dynamic_cast<PetriDish*>(pop.geneticAlgorithm()); assert(thisPetriDish); #ifdef USING_GASIMPLEGA // A workaround for oldPop not copying our Evaluator to the next set of Genomes (as opposed to hacking GASimpleGA.C) if ( thisPetriDish->_oldPopInitialized == false ) { thisPetriDish->_oldPopInitialized = true; thisPetriDish->oldPop->initialize(); } #endif//USING_GASIMPLEGA // Use all of the available cores (as reported by the processor) for threads unsigned numberOfThreadsToUse; #if USE_BOOST numberOfThreadsToUse = boost::thread::hardware_concurrency(); #else//USE_BOOST numberOfThreadsToUse = (unsigned)sysconf( _SC_NPROCESSORS_ONLN ); int errorCode; #endif//USE_BOOST // Allocate an array of pointers for the threads, as well as the associated background information #if USE_BOOST boost::thread** backgroundThreads = new boost::thread*[numberOfThreadsToUse]; #else//USE_BOOST pthread_t* backgroundThreads = new pthread_t[numberOfThreadsToUse]; #endif//USE_BOOST assert( backgroundThreads ); memset( backgroundThreads, 0, numberOfThreadsToUse * sizeof( void* ) ); BackgroundEvaluator** backgroundEvaluators = new BackgroundEvaluator*[numberOfThreadsToUse]; assert( backgroundEvaluators ); memset( backgroundEvaluators, 0, numberOfThreadsToUse * sizeof( BackgroundEvaluator* ) ); #if DEBUG cout << "Evaluating new GAPopulation ( " << pop.size( ) << " )... ( using " << numberOfThreadsToUse << " threads )" << std::endl; #endif//DEBUG int completed = 0; int indIndex = 0; u_int32_t tIndex = 0U; #if DEBUG float highestScoreSoFar = 0.0f; float total = 0.0f; #endif//DEBUG // Loop until every population member has been evaluated. while ( completed < pop.size( ) && ( thisPetriDish->_interrupt == false ) ) { // If we still have members to evaluate, and there's a free thread open if ( ( indIndex < pop.size( ) ) && ( backgroundThreads[tIndex] == NULL ) ) { // If we haven't allocated space for the evaluator thread information if( backgroundEvaluators[tIndex] == NULL ) { backgroundEvaluators[tIndex] = new BackgroundEvaluator( &pop.individual( indIndex ), indIndex ); assert( backgroundEvaluators[tIndex] != NULL ); } else { assert(backgroundEvaluators[tIndex]->finished() == true); backgroundEvaluators[tIndex]->newIndividual( &pop.individual( indIndex ), indIndex); } #if DEBUG cout << "Starting individual #" << indIndex + 1 << " ( of " << pop.size( ) << " )" << std::endl; #endif//DEBUG // Kick off the thread #if USE_BOOST try { backgroundThreads[tIndex] = new boost::thread( boost::ref(*backgroundEvaluators[tIndex]) ); } catch(const std::exception& e) { cerr << "boost::thread exception: " << e.what() << std::endl; return; } #else//USE_BOOST errorCode = pthread_create(&backgroundThreads[tIndex], NULL, backgroundEvaluate, backgroundEvaluators[tIndex]); if (errorCode!=0) { cerr << "pthread_create: Error #"<< errorCode << " (" << strerror(errorCode) << ")" << std::endl; return; } #endif//USE_BOOST assert(backgroundThreads[tIndex]); indIndex++; } // Our cyclic thread index checks for the completion of a running thread tIndex = ( tIndex+1 ) % numberOfThreadsToUse; // A (possibly still running) thread exists... if ( backgroundThreads[tIndex] != NULL ) { assert( backgroundEvaluators[tIndex] != NULL ); // Check to see if it is finished if ( backgroundEvaluators[tIndex]->finished() ) { // The thread has finished. Gather some stats (if DEBUGging) and free the thread up for the next individual completed++; #if DEBUG GAGenome* genome = backgroundEvaluators[tIndex]->individual( ); float score = genome->score( ); total += score; cout << "Received results from individual #" << backgroundEvaluators[tIndex]->index( ) << " ( " << completed << " of " << pop.size( ) << " finished ): Score: " << genome->score( ) << " (" << total / (float)completed << " is average so far)" << std::endl; if ( score > highestScoreSoFar ) { cout << "Found new high score so far: ( " << score << " > " << highestScoreSoFar <<" )" << std::endl; highestScoreSoFar = score; } #endif//DEBUG #if USE_BOOST delete backgroundThreads[tIndex]; #else//USE_BOOST errorCode = pthread_join(backgroundThreads[tIndex], NULL); assert(errorCode==0); #endif//USE_BOOST backgroundThreads[tIndex] = NULL; } else { // If it hasn't finished yet, give up some main() thread processor time to the background evaluators #if USE_BOOST boost::thread::yield(); #else//USE_BOOST pthread_yield(); #endif//USE_BOOST } } } if ( thisPetriDish->_interrupt == true ) { cerr << "...evaluation interrupted!" << std::endl; thisPetriDish->terminator(InterruptTerminator); } #if DEBUG else { cout << "...finished evaluating this population." << std::endl; } #endif//DEBUG for ( tIndex = 0; tIndex < numberOfThreadsToUse; tIndex++ ) { if ( thisPetriDish->_interrupt == false ) { // Double (sanity) check that all of the threads have actually been processed assert( backgroundThreads[tIndex] == NULL ); if ( backgroundEvaluators[tIndex] != NULL ) // This can happen if all cores aren't used { assert( backgroundEvaluators[tIndex]->finished() == true ); } } else { // If the GA has been interrupted, try and clean up the background threads if ( backgroundThreads[tIndex] != NULL ) { #if USE_BOOST backgroundThreads[tIndex]->interrupt(); delete backgroundThreads[tIndex]; #else//USE_BOOST pthread_cancel(backgroundThreads[tIndex]); errorCode = pthread_join(backgroundThreads[tIndex], NULL); assert(errorCode==0); #endif//USE_BOOST backgroundThreads[tIndex] = NULL; } } if ( backgroundEvaluators[tIndex] != NULL ) // This can happen if all cores aren't used { delete backgroundEvaluators[tIndex]; backgroundEvaluators[tIndex] = NULL; } } // Free up the thread and background information arrays if (backgroundThreads) { delete[] backgroundThreads; backgroundThreads = NULL; } if (backgroundEvaluators) { delete[] backgroundEvaluators; backgroundEvaluators = NULL; } }
// The default evaluator simply calls the evaluate member of each genome in // the population. The population object takes care of setting/unsetting the // status flags for indicating when the population needs to be updated again. void GAPopulation::DefaultEvaluator(GAPopulation & p){ // MPI aux vars int mpi_rc; MPI_Status mpi_Stat; // Array to store the scores of the individuals float *mpi_score; mpi_score = (float*)malloc( p.size()*sizeof(float) ); // Each thread computes individuals from is to (ie-1) int is, ie; is = (int)((float)p.size()/(float)p.vmpi_tasks*((float)p.vmpi_rank)); ie = (int)((float)p.size()/(float)p.vmpi_tasks*((float)p.vmpi_rank+1.0)); if(ie>p.size() || p.vmpi_rank==(p.vmpi_tasks-1)){ ie = p.size(); } // Individual loop for(int i=is; i<ie; i++) { p.individual(i).evaluate(); mpi_score[i] = p.individual(i).score(); } // The master process: if(p.vmpi_rank == 0) { // recives the partial scores for(int i=1; i<p.vmpi_tasks; i++) { is = (int)((float)p.size()/(float)p.vmpi_tasks*((float)i)); ie = (int)((float)p.size()/(float)p.vmpi_tasks*((float)i+1.0)); mpi_rc = MPI_Recv(mpi_score+is, ie-is, MPI_FLOAT, i, 1, MPI_COMM_WORLD, &mpi_Stat); } // and sends the whole array for(int i=1; i<p.vmpi_tasks; i++) mpi_rc = MPI_Send(mpi_score, p.size(), MPI_FLOAT, i, 1, MPI_COMM_WORLD); } // The rest: else { // sends the partial computed scores mpi_rc = MPI_Send(mpi_score+is, ie-is, MPI_FLOAT, 0, 1, MPI_COMM_WORLD); // and recieves the whole array mpi_rc = MPI_Recv(mpi_score, p.size(), MPI_FLOAT, 0, 1, MPI_COMM_WORLD, &mpi_Stat); } // Update the scores of the individuals for(int i=0; i<p.size(); i++) p.individual(i).score(mpi_score[i]); }
void MyEvaluator(GAPopulation &pop) { #pragma omp parallel for for(int i=0; i<pop.size(); i++) pop.individual(i).evaluate(); }