package com.ewjordan.evolution.sim; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Random; import org.apache.commons.math.stat.descriptive.DescriptiveStatistics; public class SpeedLimit { static private final Random rand = new Random(); static private final int genomeLength = 500; static private final int populationSize = 5000; // Total population size (after unfit members die off) static private final int generations = 1000; static private final int childrenPerFamily = 8; static private final double crossoverRate = 0.01; static private final double oneToZeroMutationRate = .01; static private final double zeroToOneMutationRate = .00001; static private final double initialFitnessProbability = 0.01; //probability that bits start off as true private List<Genome> population = new ArrayList<Genome>(); static public void main(String[] args) { doEvolution(); } static public void doEvolution() { SpeedLimit test = new SpeedLimit(); for (int i=0; i<populationSize; ++i) { test.population.add(generateInitialGenome(genomeLength,initialFitnessProbability)); } System.out.println("Generation,PopSize,"+getSummaryCSVTitleLine()); for (int i=0; i<generations; ++i) { List<Genome> children = new ArrayList<Genome>(populationSize*childrenPerFamily/2); int count = 0; while (count < populationSize*childrenPerFamily/2) { Genome parentA = test.population.get(rand.nextInt(test.population.size())); Genome parentB = test.population.get(rand.nextInt(test.population.size())); Genome child = Genome.cross(parentA, parentB, crossoverRate); Genome.mutate(child, oneToZeroMutationRate, zeroToOneMutationRate); children.add(child); ++count; } test.population = children; test.sortByFitness(false); List<Genome> accepted = test.population.subList(0, populationSize); test.population = new ArrayList<Genome>(accepted); System.out.println(i + "," + test.population.size() + "," + test.getSummaryCSVLine()); } } static private Genome generateInitialGenome(int len, double probT) { Genome g = new Genome(len); for (int i=0; i<g.bits.length; ++i) { g.bits[i] = (Math.random() < probT); } return g; } private double getAverageFitness() { double sum = 0.0; for (Genome g : population) { sum += computeFitness(g); } return sum / population.size(); } private double getStandardDeviation() { return getSummary().getStandardDeviation(); } private String getSummaryCSVLine() { String line = ""; DescriptiveStatistics summary = getSummary(); line += summary.getMean() + "," + summary.getMin() + "," + summary.getPercentile(25) + "," + summary.getPercentile(50) + "," + summary.getPercentile(75) + "," + summary.getMax() + "," + summary.getStandardDeviation(); return line; } private static String getSummaryCSVTitleLine() { return "Mean,Min,25%,50%,75%,Max,Stdev"; } private DescriptiveStatistics getSummary() { DescriptiveStatistics summary = new DescriptiveStatistics(); for (Genome g : population) { summary.addValue(computeFitness(g)); } return summary; } private double getMin() { return getSummary().getMax(); } private double getMax() { return getSummary().getMax(); } private double computeFitness(Genome g) { if (!g.hasSavedFitness) { // Count number of ones divided by total length int count = 0; for (int i=0; i<g.bits.length; ++i) { if (g.bits[i]) ++count; } g.savedFitness = count / (1.0*g.bits.length); g.hasSavedFitness = true; } return g.savedFitness; } private void sortByFitness(final boolean ascending) { Collections.sort(population, new Comparator<Genome>() { @Override public int compare(Genome arg0, Genome arg1) { double diff = computeFitness(arg1) - computeFitness(arg0); if (ascending) { diff *= -1; } if (diff > 0) { return 1; } else if (diff == 0) { return 0; } else { return -1; } } }); } static private class Genome { static void mutate(Genome g, double oneToZeroRate, double zeroToOneRate) { for (int i=0; i<g.bits.length; ++i) { if (g.bits[i]) { if (Math.random() < oneToZeroRate) g.bits[i] = false; } else { if (Math.random() < zeroToOneRate) g.bits[i] = true; } } } static Genome cross(Genome gA, Genome gB, double crossoverRate) { int len = gA.bits.length; Genome newGenome = new Genome(len); boolean onA = (Math.random() < 0.5); for (int i=0; i<len; ++i) { newGenome.bits[i] = onA ? gA.bits[i] : gB.bits[i]; if (Math.random() < crossoverRate) { onA = !onA; } } return newGenome; } boolean[] bits; boolean hasSavedFitness = false; double savedFitness = 0.0; Genome(int length) { bits = new boolean[length]; } } }