Skip to content

Commit

Permalink
Create unit tests from examples
Browse files Browse the repository at this point in the history
Add functions `objective` and `exampleXX` to `examples/ex6.hpp` and `examples/ex8.hpp` to calculate the objective value and run the examples with seed and command line options as input parameters.

Modify `test/GAExamplesTest.cpp` to add unit tests for examples 6 and 8 using the new `example6` and `example8` functions, and update existing unit tests to use the new functions.

Remove main methods from `examples/ex6.C`, `examples/ex8.C`, `examples/ex1.C`, `examples/ex2.C`, `examples/ex3.C`, `examples/ex4.C`, `examples/ex7.C`, `examples/ex9.C`, and move their content to the corresponding `exampleXX` functions in the respective header files.

Add functions `objective` and `exampleXX` to `examples/ex1.hpp`, `examples/ex2.hpp`, `examples/ex3.hpp`, `examples/ex4.hpp`, `examples/ex7.hpp`, `examples/ex9.hpp` to calculate the objective value and run the examples with seed and command line options as input parameters.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/s-martin/galib?shareId=XXXX-XXXX-XXXX-XXXX).
  • Loading branch information
s-martin committed Oct 17, 2024
1 parent bc5f473 commit 13ae1a4
Show file tree
Hide file tree
Showing 52 changed files with 2,026 additions and 1,837 deletions.
8 changes: 4 additions & 4 deletions examples/ex1.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// the members that a GA2DBinaryStringGenome has. And it's ok to cast it
// because we know that we will only get GA2DBinaryStringGenomes and
// nothing else.
float objectiveEx1(GAGenome& g)
float objective(GAGenome& g)
{
auto& genome = (GA2DBinaryStringGenome&)g;
float score = 0.0;
Expand All @@ -33,15 +33,15 @@ float objectiveEx1(GAGenome& g)
return score;
}

GASimpleGA ex1()
GAStatistics example1(unsigned int seed, bool useStatic)
{
int width = 10;
int height = 5;

// Now create the GA and run it. First we create a genome of the type that
// we want to use in the GA. The ga doesn't operate on this genome in the
// optimization - it just uses it to clone a population of genomes.
GA2DBinaryStringGenome genome(width, height, objectiveEx1);
GA2DBinaryStringGenome genome(width, height, objective);

// Now that we have the genome, we create the genetic algorithm and set
// its parameters - number of generations, mutation probability, and crossover
Expand All @@ -56,5 +56,5 @@ GASimpleGA ex1()
// Now we print out the best genome that the GA found.
std::cout << "The GA found:\n" << ga.statistics().bestIndividual() << "\n";

return ga;
return ga.statistics();
}
175 changes: 4 additions & 171 deletions examples/ex10.C
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
/* ----------------------------------------------------------------------------
ex10.C
mbwall 10apr95
Copyright (c) 1995-1996 Massachusetts Institute of Technology
DESCRIPTION:
Sample program that illustrates how to use a distance function to do
speciation. This example does both gene-based and phenotype-based distance
calculations. The differences are quite interesting. Also, the length of the
bit string (i.e. the size of the search space) is also a significant factor in
the performance of the speciation methods.
Notice that Goldberg describes fitness scaling speciation in the context of
a simple genetic algorithm. You can try using it with a steady-state
algorithm, but you'll get bogus results unless you modify the algorithm.
---------------------------------------------------------------------------- */
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <ga.h>
#include "ex10.hpp"

#include <iostream>
#include <fstream>


#define USE_RAW_SINE

#define NBITS 8
Expand All @@ -43,155 +27,11 @@ float Objective(GAGenome &);
float BitDistance(const GAGenome & a, const GAGenome & b);
float PhenotypeDistance(const GAGenome & a, const GAGenome & b);

int
main(int argc, char **argv)
int main(int argc, char **argv)
{
std::cout << "Example 10\n\n";
std::cout << "This program uses sharing to do speciation. The objective\n";
std::cout << "function has more than one optimum, so different genomes\n";
std::cout << "may have equally high scores. Speciation keeps the population\n";
std::cout << "from clustering at one optimum.\n";
std::cout << " Both gene-wise and phenotype-wise distance functions are used.\n";
std::cout << " Populations from all three runs are written to the files \n";
std::cout << "pop.nospec.dat, pop.genespec.dat and pop.phenespec.dat. The\n";
std::cout << "function is written to the file sinusoid.dat\n\n";
std::cout.flush();

// See if we've been given a seed to use (for testing purposes). When you
// specify a random seed, the evolution will be exactly the same each time
// you use that seed number.

for(int ii=1; ii<argc; ii++) {
if(strcmp(argv[ii++],"seed") == 0) {
GARandomSeed((unsigned int)atoi(argv[ii]));
}
}

int i;
char filename[32] = "sinusoid.dat";
char popfilename1[32] = "pop.nospec.dat";
char popfilename2[32] = "pop.genespec.dat";
char popfilename3[32] = "pop.phenespec.dat";
std::ofstream outfile;

// Create a phenotype for two variables. The number of bits you can use to
// represent any number is limited by the type of computer you are using. In
// this case, we use 16 bits to represent a floating point number whose value
// can range from the minimum to maximum value as defined by the macros.

GABin2DecPhenotype map;
map.add(NBITS, MIN_VALUE, MAX_VALUE);

// Create the template genome using the phenotype map we just made.

GABin2DecGenome genome(map, Objective);

// Now create the GA using the genome and set all of the parameters.
// You'll get different results depending on the type of GA that you use. The
// steady-state GA tends to converge faster (depending on the type of replace-
// ment method you specify).

GASimpleGA ga(genome);
ga.set(gaNpopulationSize, 200);
ga.set(gaNnGenerations, 50);
ga.set(gaNpMutation, 0.001);
ga.set(gaNpCrossover, 0.9);
ga.parameters(argc, argv);


// Do the non-speciated and write to file the best-of-generation.

std::cout << "running with no speciation (fitness proportionate scaling)...\n";
std::cout.flush();
GALinearScaling lin;
ga.scaling(lin);
ga.evolve();
genome = ga.statistics().bestIndividual();
std::cout << "the ga found an optimum at the point "<<genome.phenotype(0)<< std::endl;

outfile.open(popfilename1, (std::ios::out | std::ios::trunc));
if(outfile.fail()){
std::cerr << "Cannot open " << popfilename1 << " for output.\n";
exit(1);
}
for(i=0; i<ga.population().size(); i++){
outfile<<((GABin2DecGenome&)(ga.population().individual(i))).phenotype(0);
outfile << "\t";
outfile << ga.population().individual(i).score() << "\n";
}
outfile.close();



// Now do speciation using the gene-wise distance function

std::cout << "running the ga with speciation (sharing using bit-wise)...\n";
std::cout.flush();
GASharing bitSharing(BitDistance);
ga.scaling(bitSharing);
ga.evolve();
genome = ga.statistics().bestIndividual();
std::cout << "the ga found an optimum at the point "<<genome.phenotype(0)<< std::endl;

outfile.open(popfilename2, (std::ios::out | std::ios::trunc));
if(outfile.fail()){
std::cerr << "Cannot open " << popfilename2 << " for output.\n";
exit(1);
}
for(i=0; i<ga.population().size(); i++){
outfile<<((GABin2DecGenome&)(ga.population().individual(i))).phenotype(0);
outfile << "\t";
outfile << ga.population().individual(i).score() << "\n";
}
outfile.close();



// Now do speciation using the phenotype-wise distance function

std::cout << "running the ga with speciation (sharing using phenotype-wise)...\n";
std::cout.flush();
GASharing pheneSharing(PhenotypeDistance);
ga.scaling(pheneSharing);
ga.evolve();
genome = ga.statistics().bestIndividual();
std::cout << "the ga found an optimum at the point "<<genome.phenotype(0)<< std::endl;

outfile.open(popfilename3, (std::ios::out | std::ios::trunc));
if(outfile.fail()){
std::cerr << "Cannot open " << popfilename3 << " for output.\n";
exit(1);
}
for(i=0; i<ga.population().size(); i++){
outfile<<((GABin2DecGenome&)(ga.population().individual(i))).phenotype(0);
outfile << "\t";
outfile << ga.population().individual(i).score() << "\n";
}
outfile.close();


// Now dump the function to file for comparisons

std::cout << "dumping the function to file..." << std::endl;
outfile.open(filename, (std::ios::out | std::ios::trunc));
if(outfile.fail()){
std::cerr << "Cannot open " << filename << " for output.\n";
exit(1);
}
float inc = MAX_VALUE - MIN_VALUE;
inc /= pow(2.0,NBITS);
for(float x=MIN_VALUE; x<=MAX_VALUE; x+=inc){
outfile << x << "\t" << FUNCTION (x) << "\n";
}
outfile << "\n";
outfile.close();

return 0;
example10(0, argc, argv);
return 0;
}





// You can choose between one of two sinusoidal functions. The first one has
// peaks of equal amplitude. The second is modulated.
Expand All @@ -215,10 +55,6 @@ Function2(float v) {
return y+0.00001;
}





// Here are a couple of possible distance functions for this problem. One of
// them uses the genes to determine the same-ness, the other uses the
// phenotypes to determine same-ness. If the genomes are the same, then
Expand All @@ -243,8 +79,6 @@ BitDistance(const GAGenome & c1, const GAGenome & c2){
return x/a.length();
}



// This distance function looks at the phenotypes rather than the genes of the
// genome. This distance function will try to drive them to extremes.

Expand All @@ -255,4 +89,3 @@ PhenotypeDistance(const GAGenome & c1, const GAGenome & c2){

return fabs(a.phenotype(0) - b.phenotype(0)) / (MAX_VALUE-MIN_VALUE);
}

92 changes: 92 additions & 0 deletions examples/ex10.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#pragma once

#include <ga.h>
#include <cmath>

#define USE_RAW_SINE
#define NBITS 8

#ifdef USE_RAW_SINE
#define FUNCTION Function1
#define MIN_VALUE 0
#define MAX_VALUE 5
#else
#define FUNCTION Function2
#define MIN_VALUE -100
#define MAX_VALUE 100
#endif

float Function1(float);
float Function2(float);
float Objective(GAGenome &);
float BitDistance(const GAGenome & a, const GAGenome & b);
float PhenotypeDistance(const GAGenome & a, const GAGenome & b);

float objective(GAGenome & g)
{
return Objective(g);
}

GAStatistics example10(unsigned int seed, int argc, char **argv)
{
GABin2DecPhenotype map;
map.add(NBITS, MIN_VALUE, MAX_VALUE);

GABin2DecGenome genome(map, objective);
genome.crossover(GABin2DecGenome::UniformCrossover);
genome.crossoverProbability(0.6);
genome.mutationProbability(0.01);
genome.comparator(BitDistance);
genome.comparator(PhenotypeDistance);

GASimpleGA ga(genome);
ga.populationSize(30);
ga.nGenerations(100);
ga.pMutation(0.01);
ga.pCrossover(0.6);
ga.evolve(seed);

return ga.statistics();
}

float Function1(float v)
{
return 1 + sin(v * 2 * M_PI);
}

float Function2(float v)
{
float y;
y = 100.0 * exp(-fabs(v) / 50.0) * (1.0 - cos(v * M_PI * 2.0 / 25.0));
if (v < -100 || v > 100)
y = 0;
if (y < 0)
y = 0;
return y + 0.00001;
}

float Objective(GAGenome & c)
{
auto & genome = (GABin2DecGenome &)c;
return FUNCTION(genome.phenotype(0));
}

float BitDistance(const GAGenome & c1, const GAGenome & c2)
{
auto & a = (GABin2DecGenome &)c1;
auto & b = (GABin2DecGenome &)c2;

float x = 0;
for (int i = a.length() - 1; i >= 0; i--)
x += (a[i] != b[i] ? 1 : 0);

return x / a.length();
}

float PhenotypeDistance(const GAGenome & c1, const GAGenome & c2)
{
auto & a = (GABin2DecGenome &)c1;
auto & b = (GABin2DecGenome &)c2;

return fabs(a.phenotype(0) - b.phenotype(0)) / (MAX_VALUE - MIN_VALUE);
}
Loading

0 comments on commit 13ae1a4

Please sign in to comment.