/* * 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.anttrail; import java.io.*; import java.util.*; import org.jgap.*; import org.jgap.event.*; import org.jgap.gp.*; import org.jgap.gp.function.*; import org.jgap.gp.impl.*; import org.jgap.util.*; import org.jgap.util.tree.*; /** * The ant trail problem. It demonstrates Genetic Programming (GP) capabilities * of JGAP.<p> * * The ant trail problem searches for a solution to let an ant pick up as many * food as possible on a given parcours with as few steps as possible. The * ant can move, take food and see food in adjacent fields.<p> * * Whenever a new best solution has been found, the ant trail is printed to the * console. Additionally, the tree represented by the GP program is output * in graphical representation.<p> * * @author Klaus Meffert * @since 3.01 */ public class AntTrailProblem extends GPProblem { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.19 $"; protected int[][] m_map; private static int foodAvail; private static int m_maxx; private static int m_maxy; protected static int totalFood; /** * Maximum number of moves allowed. */ public static int m_maxMoves = 400; public AntTrailProblem(GPConfiguration config, String a_filename) throws InvalidConfigurationException, Exception { super(config); GPFitnessFunction func = createFitFunc(); config.setFitnessFunction(func); // Read the trail from file. // ------------------------- m_map = readTrail(a_filename); AntMap antmap = new AntMap(m_map, m_maxMoves); totalFood = countFood(antmap); System.out.println("Food to consume by ant: " + totalFood); GPGenotype gp = create(); gp.setVerboseOutput(true); // Simple implementation of running evolution in a thread. // ------------------------------------------------------- final Thread t = new Thread(gp); 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 % 100 == 0) { double bestFitness = genotype.getFittestProgram(). getFitnessValue(); System.out.println("Evolving generation " + evno + ", best fitness: " + bestFitness + ", memory free: " + freeMem + " MB"); } if (evno > 500000) { 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); } } } }); GeneticEventListener myGeneticEventListener = new EventListener(this, t); config.getEventManager().addEventListener(GeneticEvent. GPGENOTYPE_NEW_BEST_SOLUTION, myGeneticEventListener); t.start(); } /** * 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 { Class[] types = {CommandGene.VoidClass}; Class[][] argTypes = { {} }; int[] minDepths = new int[] {6}; int[] maxDepths = new int[] {9}; GPConfiguration conf = getGPConfiguration(); CommandGene[][] nodeSets = { { new SubProgram(conf, new Class[] {CommandGene.VoidClass, CommandGene.VoidClass, CommandGene.VoidClass}, true), new SubProgram(conf, new Class[] {CommandGene.VoidClass, //nonclassic CommandGene.VoidClass, CommandGene.VoidClass, CommandGene.VoidClass}), new Left(conf), new Right(conf), new Move(conf), new Move(conf, 3), //nonclassic new IfFoodAheadElse(conf), new IfFoodAheadLeft(conf), //nonclassic new IfFoodAheadRight(conf), //nonclassic new Loop(conf, CommandGene.IntegerClass, 3), //nonclassic new TurnToFood(conf), //nonclassic } }; // Create genotype with initial population. // ---------------------------------------- return GPGenotype.randomInitialGenotype(conf, types, argTypes, nodeSets, minDepths, maxDepths, 5000, new boolean[] {true}, true); } private int[][] readTrail(String a_filename) throws Exception { LineNumberReader lnr; try { lnr = new LineNumberReader(new FileReader(a_filename)); } catch (FileNotFoundException fex) { throw new FileNotFoundException("File not found: " + new File(".").getAbsolutePath() + a_filename); } // Read dimensions of trail. // ------------------------- try { StringTokenizer st = new StringTokenizer(lnr.readLine()); m_maxx = Integer.parseInt(st.nextToken()); m_maxy = Integer.parseInt(st.nextToken()); int[][] result = new int[m_maxx][m_maxy]; int y; foodAvail = 0; for (y = 0; y < m_maxy; y++) { String s = lnr.readLine(); if (s == null) { throw new RuntimeException("Ant trail file ended prematurely"); } int x; for (x = 0; x < s.length(); x++) { if (s.charAt(x) == ' ') { result[x][y] = AntMap.EMPTY; } else if (s.charAt(x) == '#') { result[x][y] = AntMap.FOOD; foodAvail++; } else if (s.charAt(x) == '.') { result[x][y] = AntMap.TRAIL; } else { throw new RuntimeException("Bad character '" + s.charAt(x) + "' on line number " + lnr.getLineNumber() + " of the Ant trail file."); } } // fill out rest of X's for (int z = x; z < m_maxx; z++) { result[z][y] = AntMap.EMPTY; } } // fill out rest of Y's for (int z = y; z < m_maxy; z++) { for (int x = 0; x < m_maxx; x++) { result[x][z] = AntMap.EMPTY; } } return result; } catch (NumberFormatException e) { throw new RuntimeException( "The Ant trail file does not begin with x and y integer values."); } catch (IOException e) { throw new RuntimeException( "The Ant trail file could not be read due to an IOException:\n" + e); } } /** * Starts the example. * * @param args ignored * @throws Exception * * @author Klaus Meffert * @since 3.0 */ public static void main(String[] args) { try { System.out.println("Ant trail problem"); GPConfiguration config = new GPConfiguration(); config.setGPFitnessEvaluator(new DeltaGPFitnessEvaluator()); int popSize = 500; String filename; if (args.length == 1) { filename = args[0]; } else { filename = "santafe.trail"; } System.out.println("Using population size of " + popSize); System.out.println("Using map " + filename); config.setMaxInitDepth(7); config.setPopulationSize(popSize); config.setCrossoverProb(0.9f); config.setReproductionProb(0.1f); config.setNewChromsPercent(0.3f); config.setStrictProgramCreation(true); config.setUseProgramCache(true); new AntTrailProblem(config, filename); } catch (Exception ex) { ex.printStackTrace(); System.exit(1); } } /** * Display ant trail as found by GP. * * @param a_antmap the map containing the trail */ protected void displaySolution(int[][] a_antmap) { for (int y = 0; y < m_maxy; y++) { for (int x = 0; x < m_maxx; x++) { char toPrint; int c = a_antmap[x][y]; if (c < 32) { switch (c) { case AntMap.FOOD: toPrint = '#'; break; case AntMap.TRAIL: toPrint = '.'; break; default: toPrint = ' '; } } else { toPrint = (char) c; } System.out.print(toPrint); } System.out.println(); } } private GPFitnessFunction createFitFunc() { return new AntFitnessFunction(); } // static class MyGeneticEventListener implements GeneticEventListener { // // private Thread m_t; // private AntTrailProblem m_problem; // // public MyGeneticEventListener(Thread a_t, AntTrailProblem a_problem) { // m_t = a_t; // m_problem =a_problem; // } class AntFitnessFunction extends GPFitnessFunction { private static final int VALUE1 = 100; 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(); AntMap antmap = new AntMap(m_map, m_maxMoves); a_program.setApplicationData(antmap); try { // Execute the program. // -------------------- a_program.execute_void(0, noargs); // Determine success of individual. // -------------------------------- antmap = (AntMap) a_program.getApplicationData(); // The remaining food is the defect rate here. // ------------------------------------------- int foodTaken = antmap.getFoodTaken(); // countFood(antmap); error = (VALUE1 + totalFood - foodTaken) * 4; 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) { // Add penalty for longer trails. // ------------------------------ int moves = antmap.getMoveCount(); error = error + moves * 1.5; } } catch (IllegalStateException iex) { error = GPFitnessFunction.MAX_FITNESS_VALUE; } return error; } } private static int countFood(AntMap a_map) { int result = 0; for (int x = 0; x < m_maxx; x++) { for (int y = 0; y < m_maxy; y++) { if (a_map.getFromMap(x, y) == AntMap.FOOD) { result++; } } } return result; } } class EventListener implements GeneticEventListener { private AntTrailProblem problem; private Thread m_t; public EventListener(AntTrailProblem a_problem, Thread a_t) { problem = a_problem; m_t = a_t; } /** * 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 = "anttrail_best" + indexString + ".png"; IGPProgram best = genotype.getAllTimeBest(); try { // Create graphical tree of GPProgram. // ----------------------------------- TreeBranchRenderer antBranchRenderer = new AntTreeBranchRenderer(); TreeNodeRenderer antNodeRenderer = new AntTreeNodeRenderer(); problem.showTree(best, filename, antBranchRenderer, antNodeRenderer); // Display solution's trail. // ------------------------- AntMap antmap = (AntMap) best.getApplicationData(); problem.displaySolution(antmap.getMovements()); System.out.println(" Number of moves: " + antmap.getMoveCount()); System.out.println(" Food taken: " + antmap.getFoodTaken()); } catch (InvalidConfigurationException iex) { iex.printStackTrace(); } double bestFitness = genotype.getFittestProgram(). getFitnessValue(); if (bestFitness < 0.001) { genotype.outputSolution(best); m_t.stop(); System.exit(0); } } } /* abcd e f abcdef g Z g h Y h ijklmnopqr TUVWX i s S j t R k u Q l v P m w O n x N o y M p z L q A K xwvutsr B FGHIJ y C E z D D A E C BC F B G A H z I y J x VUTSRQPONMLK w W v X u Y klmnopqrst Z j a i bcdefgh Number of moves: 193 Best solution fitness: 3.0 Best solution: loop(3, (loop(3, (loop(3, (sub[(sub[right --> left --> move --> turn-to-food]) --> (if-food (left) else (move)) --> (loop(3, turn-to-food }) --> (loop(3, (loop(3, turn-to-food }) })]) }) }) } Depth of chromosome: 6 */