/*
* This file is part of JGAP.
*
* JGAP offers a dual license model containing the LGPL as well as the MPL.
*
* For licensing information please see the file license.txt included with JGAP
* or have a look at the top of class org.jgap.Chromosome which representatively
* includes the JGAP license policy applicable for any file delivered with JGAP.
*/
package examples.gp;
import org.apache.log4j.*;
import org.jgap.*;
import org.jgap.event.*;
import org.jgap.gp.*;
import org.jgap.gp.function.*;
import org.jgap.gp.impl.*;
import org.jgap.gp.terminal.*;
import org.jgap.util.*;
/**
* Example demonstrating Genetic Programming (GP) capabilities of JGAP.<p>
* Here, the Fibonacci sequence is calculated (only integers are used).<p>
* Please note: We try to find a program that computes Fibonacci iteratively.<p>
* This example utilizes a INodeValidator (see FibonacciNodeValidator).<p>
* Each new best solution found will be displayed as a graphical tree
* representing the GP. The tree is written to a PNG-imagefile onto harddisk.
*
* @author Klaus Meffert
* @since 3.0
*/
public class Fibonacci
extends GPProblem {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.31 $";
private transient static Logger LOGGER = Logger.getLogger(Fibonacci.class);
static Variable vx;
static Variable va;
private final static int NUMFIB = 10;
static Integer[] x = new Integer[NUMFIB];
static int[] y = new int[NUMFIB];
public Fibonacci(GPConfiguration a_conf)
throws InvalidConfigurationException {
super(a_conf);
}
/**
* Sets up the functions to use and other parameters. Then creates the
* initial genotype.
*
* @return the genotype created
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.0
*/
public GPGenotype create()
throws InvalidConfigurationException {
// Define return types of sub programs (see nodeSets).
// The first entry in types corresponds with the first entry in nodeSets,
// etc.
Class[] types = {
CommandGene.VoidClass, CommandGene.VoidClass, CommandGene.IntegerClass};
// The following is only relevant for ADF's and not used here.
Class[][] argTypes = { {}, {}, {}
};
// Configure desired minimum number of nodes per sub program.
// Same as with types: First entry here corresponds with first entry in
// nodeSets.
int[] minDepths = new int[] {2, 3, 0};
// Configure desired maximum number of nodes per sub program.
// First entry here corresponds with first entry in nodeSets.
int[] maxDepths = new int[] {4, 9, 1};
GPConfiguration conf = getGPConfiguration();
/**@todo allow to optionally preset a static program in each chromosome*/
CommandGene[][] nodeSets = { {
new SubProgram(conf, new Class[] {CommandGene.VoidClass,
CommandGene.VoidClass}),
// new Constant(conf, CommandGene.IntegerClass, new Integer(1)),
new StoreTerminal(conf, "mem0", CommandGene.IntegerClass),
new StoreTerminal(conf, "mem1", CommandGene.IntegerClass),
new Increment(conf, CommandGene.IntegerClass),
new NOP(conf),
new Terminal(conf, CommandGene.IntegerClass,0.0, 10.0),
}, {
vx = Variable.create(conf, "X", CommandGene.IntegerClass),
new AddAndStore(conf, CommandGene.IntegerClass, "mem2"),
new ForLoop(conf, CommandGene.IntegerClass, 1, NUMFIB),
new Increment(conf, CommandGene.IntegerClass, -1),
new TransferMemory(conf, "mem2", "mem1"),
new TransferMemory(conf, "mem1", "mem0"),
new ReadTerminal(conf, CommandGene.IntegerClass, "mem0"),
new ReadTerminal(conf, CommandGene.IntegerClass, "mem1"),
new SubProgram(conf, new Class[] {CommandGene.VoidClass,
CommandGene.VoidClass, CommandGene.VoidClass}),
}, {
// Commands will be added programmatically, see below.
// ---------------------------------------------------
}
};
// Add commands working with internal memory.
// ------------------------------------------
nodeSets[2] = CommandFactory.createReadOnlyCommands(nodeSets[2], conf,
CommandGene.IntegerClass, "mem", 1, 2, !true);
// Randomly initialize function data (X-Y table) for Fib(x).
// ---------------------------------------------------------
for (int i = 0; i < NUMFIB; i++) {
int index = i;
x[i] = new Integer(index);
y[i] = fib_iter(index);
System.out.println(i + ") " + x[i] + " " + y[i]);
}
// Create genotype with initial population.
// ----------------------------------------
return GPGenotype.randomInitialGenotype(conf, types, argTypes, nodeSets,
minDepths, maxDepths, 20, new boolean[] {!true, !true, false}, true);
}
//(Sort of) This is what we would like to (and can) find via GP:
private static int fib_iter(int a_index) {
// 1
if (a_index == 0 || a_index == 1) {
return 1;
}
// 2
int a = 1; //Store("mem0", Constant(1))
int b = 1; //Store("mem1", Constant(1))
int x = 0; //Store("mem2", Constant(0))
// 3
for (int i = 2; i <= a_index; i++) { //FORX (Subprogram(A;B;C))
x = a + b; // A: AddAndStore(Read("mem0"),Read("mem1"),"mem2")
a = b; //B: TransferMemory("mem1","mem0")
b = x; //C: TransferMemory("mem2","mem1")
}
return x; //Read("mem2")
}
//(Sort of) This is what we would like to find via GP:
private static int fib_array(int a_index) {
// 1
if (a_index == 0 || a_index == 1) {
return 1;
}
// 2
int[] numbers = new int[a_index + 1];
numbers[0] = numbers[1] = 1;
// 3
for (int i = 2; i <= a_index; i++) {
numbers[i] = numbers[i - 1] + numbers[i - 2];
}
return numbers[a_index];
}
//(Sort of) This is what we would like to (but cannot) find via GP:
private static int fib(int a_index) {
if (a_index == 0 || a_index == 1) {
return 1;
}
return fib(a_index - 1) + fib(a_index - 2);
}
/**
* Starts the example.
*
* @param args ignored
* @throws Exception
*
* @author Klaus Meffert
* @since 3.0
*/
public static void main(String[] args) {
try {
System.out.println("Program to discover: Fibonacci(x)");
GPConfiguration config = new GPConfiguration();
config.setGPFitnessEvaluator(new DeltaGPFitnessEvaluator());
config.setSelectionMethod(new TournamentSelector(4));
int popSize;
if (args.length == 1) {
popSize = Integer.parseInt(args[0]);
}
else {
popSize = 600;
}
System.out.println("Using population size of " + popSize);
config.setMaxInitDepth(6);
config.setPopulationSize(popSize);
config.setFitnessFunction(new Fibonacci.FormulaFitnessFunction());
config.setStrictProgramCreation(false);
config.setProgramCreationMaxTries(3);
config.setMaxCrossoverDepth(5);
// Set a node validator to demonstrate speedup when something is known
// about the solution (see FibonacciNodeValidator).
// -------------------------------------------------------------------
config.setNodeValidator(new FibonacciNodeValidator());
// Activate caching of GP programs --> Fitness values will be cached
// for programs equal to previously evolved ones.
// -----------------------------------------------------------------
config.setUseProgramCache(true);
final GPProblem problem = new Fibonacci(config);
GPGenotype gp = problem.create();
gp.setVerboseOutput(true);
final Thread t = new Thread(gp);
// Simple implementation of running evolution in a thread.
// -------------------------------------------------------
config.getEventManager().addEventListener(GeneticEvent.
GPGENOTYPE_EVOLVED_EVENT, new GeneticEventListener() {
public void geneticEventFired(GeneticEvent a_firedEvent) {
GPGenotype genotype = (GPGenotype) a_firedEvent.getSource();
int evno = genotype.getGPConfiguration().getGenerationNr();
double freeMem = SystemKit.getFreeMemoryMB();
if (evno % 50 == 0) {
double allBestFitness = genotype.getAllTimeBest().getFitnessValue();
LOGGER.info("Evolving generation " + evno
+ ", all-time-best fitness: " + allBestFitness
+ ", memory free: " + freeMem + " MB");
}
if (evno > 3000) {
t.stop();
}
else {
try {
// Collect garbage if memory low.
// ------------------------------
if (freeMem < 50) {
System.gc();
t.sleep(500);
}
else {
// Avoid 100% CPU load.
// --------------------
t.sleep(30);
}
} catch (InterruptedException iex) {
iex.printStackTrace();
System.exit(1);
}
}
}
});
config.getEventManager().addEventListener(GeneticEvent.
GPGENOTYPE_NEW_BEST_SOLUTION, new GeneticEventListener() {
/**
* New best solution found.
*
* @param a_firedEvent GeneticEvent
*/
public void geneticEventFired(GeneticEvent a_firedEvent) {
GPGenotype genotype = (GPGenotype) a_firedEvent.getSource();
int evno = genotype.getGPConfiguration().getGenerationNr();
String indexString = "" + evno;
while (indexString.length() < 5) {
indexString = "0" + indexString;
}
String filename = "fibonacci_best" + indexString + ".png";
IGPProgram best = genotype.getAllTimeBest();
try {
problem.showTree(best, filename);
} catch (InvalidConfigurationException iex) {
iex.printStackTrace();
}
double bestFitness = genotype.getFittestProgram().
getFitnessValue();
if (bestFitness < 0.001) {
genotype.outputSolution(best);
t.stop();
System.exit(0);
}
}
});
t.start();
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public static class FormulaFitnessFunction
extends GPFitnessFunction {
protected double evaluate(final IGPProgram a_subject) {
return computeRawFitness(a_subject);
}
public double computeRawFitness(final IGPProgram a_program) {
double error = 0.0f;
Object[] noargs = new Object[0];
// Initialize local stores.
// ------------------------
a_program.getGPConfiguration().clearStack();
a_program.getGPConfiguration().clearMemory();
// Compute fitness for each program.
// ---------------------------------
/**@todo check if program valid, i.e. worth evaluating*/
for (int i = 2; i < NUMFIB; i++) {
for (int j = 0; j < a_program.size(); j++) {
vx.set(x[i]);
try {
try {
// Init. params (a_program.getTypes()) distinguish program flow.
// This could be coded dynamically but that would slow down
// things a lot.
// -------------------------------------------------------------
if (j == a_program.size() - 1) {
// Only evaluate after whole GP program was run.
// ---------------------------------------------
double result = a_program.execute_int(j, noargs);
error += Math.abs(result - y[i]);
}
else {
// Execute memory manipulating subprograms.
// ----------------------------------------
a_program.execute_void(j, noargs);
}
} catch (IllegalStateException iex) {
error = GPFitnessFunction.MAX_FITNESS_VALUE;
break;
}
} catch (ArithmeticException ex) {
System.out.println("x = " + x[i].intValue());
System.out.println(a_program.getChromosome(j));
throw ex;
}
}
}
if (a_program.getGPConfiguration().stackSize() > 0) {
error = GPFitnessFunction.MAX_FITNESS_VALUE;
}
if (error < 0.000001) {
error = 0.0d;
}
else if (error < GPFitnessFunction.MAX_FITNESS_VALUE) {
/**@todo add penalty for longer solutions*/
}
return error;
}
}
}