/*
* 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.
*/
import java.util.*;
import org.jgap.*;
import org.jgap.gp.*;
import org.jgap.gp.function.*;
import org.jgap.gp.impl.*;
import org.jgap.gp.terminal.*;
/**
* Example demonstrating Genetic Programming (GP) capabilities of JGAP.
* Also demonstrates usage of ADF's.<br>
* The problem is to find a formula for a given truth table (X/Y-pairs).
* <p>
* <ul>
* <li>The setup of the GP is done in method main and specifically in method
* create()</li>
* <li>The problem solving process is started via gp.evolve(800) in the main
* method, with 800 the maximum number of evolutions to take place.
* <li>The evaluation of the evolved formula is done in fitness function
* FormulaFitnessFunction, which is implemented in this class, MathProblem
* </ul>
* <br>
* For details, please see the mentioned methods and the fitness function.
* <p>
* @author Klaus Meffert
* @since 3.0
*/
public class MathProblem
extends GPProblem {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.24 $";
public static Variable vx;
protected static Float[] x = new Float[20];
protected static float[] y = new float[20];
public MathProblem(GPConfiguration a_conf)
throws InvalidConfigurationException {
super(a_conf);
}
/**
* This method is used for setting up the commands and terminals that can be
* used to solve the problem.
* In this example an ADF (an automatically defined function) is used for
* demonstration purpuses. Using an ADF is optional. If you want to use one,
* care about the places marked with "ADF-relevant:" below. If you do not want
* to use an ADF, please remove the below places (and reduce the outer size of
* the arrays "types", "argTypes" and "nodeSets" to one).
* Please notice, that the variables types, argTypes and nodeSets correspond
* to each other: they have the same number of elements and the element at
* the i'th index of each variable corresponds to the i'th index of the other
* variables!
*
* @return GPGenotype
* @throws InvalidConfigurationException
*/
public GPGenotype create()
throws InvalidConfigurationException {
GPConfiguration conf = getGPConfiguration();
// At first, we define the return type of the GP program.
// ------------------------------------------------------
Class[] types = {
// Return type of result-producing chromosome
CommandGene.FloatClass,
// ADF-relevant:
// Return type of ADF 1
CommandGene.FloatClass};
// Then, we define the arguments of the GP parts. Normally, only for ADF's
// there is a specification here, otherwise it is empty as in first case.
// -----------------------------------------------------------------------
Class[][] argTypes = {
// Arguments of result-producing chromosome: none
{},
// ADF-relevant:
// Arguments of ADF1: all 3 are float
{CommandGene.FloatClass, CommandGene.FloatClass, CommandGene.FloatClass}
};
// Next, we define the set of available GP commands and terminals to use.
// Please see package org.jgap.gp.function and org.jgap.gp.terminal
// You can easily add commands and terminals of your own.
// ----------------------------------------------------------------------
CommandGene[][] nodeSets = { {
// We use a variable that can be set in the fitness function.
// ----------------------------------------------------------
vx = Variable.create(conf, "X", CommandGene.FloatClass),
new Multiply(conf, CommandGene.FloatClass),
new Multiply3(conf, CommandGene.FloatClass),
new Divide(conf, CommandGene.FloatClass),
new Sine(conf, CommandGene.FloatClass),
new Exp(conf, CommandGene.FloatClass),
new Pow(conf, CommandGene.FloatClass),
new Terminal(conf, CommandGene.FloatClass, 2.0d, 10.0d, true),
// ADF-relevant:
// Construct a reference to the ADF defined in the second nodeset
// which has index 1 (second parameter of ADF-constructor).
// Furthermore, the ADF expects three input parameters (see argTypes[1])
new ADF(conf, 1 , 3),
},
// ADF-relevant:
// and now the definition of ADF(1)
{
new Add3(conf, CommandGene.FloatClass),
}
};
// Here, we define the expected (optimal) output we want to achieve by the
// function/formula to evolve by the GP.
// -----------------------------------------------------------------------
Random random = new Random();
// Randomly initialize function data (X-Y table) for x^4+x^3+x^2-x
// ---------------------------------------------------------------
for (int i = 0; i < 20; i++) {
float f = 8.0f * (random.nextFloat() - 0.3f);
x[i] = new Float(f);
y[i] = f * f * f * f + f * f * f + f * f - f;
System.out.println(i + ") " + x[i] + " " + y[i]);
}
// Create genotype with initial population. Here, we use the declarations
// made above:
// Use one result-producing chromosome (index 0) with return type float
// (see types[0]), no argument (argTypes[0]) and several valid commands and
// terminals (nodeSets[0]). Contained in the node set is an ADF at index 1
// in the node set (as declared with the second parameter during
// ADF-construction: new ADF(..,1,..)).
// The ADF has return type float (types[1]), three input parameters of type
// float (argTypes[1]) and exactly one function: Add3 (nodeSets[1]).
// ------------------------------------------------------------------------
return GPGenotype.randomInitialGenotype(conf, types, argTypes, nodeSets,
20, true);
}
/**
* Starts the example.
*
* @param args ignored
* @throws Exception
*
* @author Klaus Meffert
* @since 3.0
*/
public static void main(String[] args)
throws Exception {
System.out.println("Formula to discover: X^4 + X^3 + X^2 - X");
// Setup the algorithm's parameters.
// ---------------------------------
GPConfiguration config = new GPConfiguration();
// We use a delta fitness evaluator because we compute a defect rate, not
// a point score!
// ----------------------------------------------------------------------
config.setGPFitnessEvaluator(new DeltaGPFitnessEvaluator());
config.setMaxInitDepth(4);
config.setPopulationSize(1000);
config.setMaxCrossoverDepth(8);
config.setFitnessFunction(new MathProblem.FormulaFitnessFunction());
config.setStrictProgramCreation(true);
GPProblem problem = new MathProblem(config);
// Create the genotype of the problem, i.e., define the GP commands and
// terminals that can be used, and constrain the structure of the GP
// program.
// --------------------------------------------------------------------
GPGenotype gp = problem.create();
gp.setVerboseOutput(true);
// Start the computation with maximum 800 evolutions.
// if a satisfying result is found (fitness value almost 0), JGAP stops
// earlier automatically.
// --------------------------------------------------------------------
gp.evolve(800);
// Print the best solution so far to the console.
// ----------------------------------------------
gp.outputSolution(gp.getAllTimeBest());
// Create a graphical tree of the best solution's program and write it to
// a PNG file.
// ----------------------------------------------------------------------
problem.showTree(gp.getAllTimeBest(), "mathproblem_best.png");
}
/**
* Fitness function for evaluating the produced fomulas, represented as GP
* programs. The fitness is computed by calculating the result (Y) of the
* function/formula for integer inputs 0 to 20 (X). The sum of the differences
* between expected Y and actual Y is the fitness, the lower the better (as
* it is a defect rate here).
*/
public static class FormulaFitnessFunction
extends GPFitnessFunction {
protected double evaluate(final IGPProgram a_subject) {
return computeRawFitness(a_subject);
}
public double computeRawFitness(final IGPProgram ind) {
double error = 0.0f;
Object[] noargs = new Object[0];
// Evaluate function for input numbers 0 to 20.
// --------------------------------------------
for (int i = 0; i < 20; i++) {
// Provide the variable X with the input number.
// See method create(), declaration of "nodeSets" for where X is
// defined.
// -------------------------------------------------------------
vx.set(x[i]);
try {
// Execute the GP program representing the function to be evolved.
// As in method create(), the return type is declared as float (see
// declaration of array "types").
// ----------------------------------------------------------------
double result = ind.execute_float(0, noargs);
// Sum up the error between actual and expected result to get a defect
// rate.
// -------------------------------------------------------------------
error += Math.abs(result - y[i]);
// If the error is too high, stop evlauation and return worst error
// possible.
// ----------------------------------------------------------------
if (Double.isInfinite(error)) {
return Double.MAX_VALUE;
}
} catch (ArithmeticException ex) {
// This should not happen, some illegal operation was executed.
// ------------------------------------------------------------
System.out.println("x = " + x[i].floatValue());
System.out.println(ind);
throw ex;
}
}
// In case the error is small enough, consider it perfect.
// -------------------------------------------------------
if (error < 0.001) {
error = 0.0d;
}
return error;
}
}
}