From d25538f66e443dcde511a3626170c9d75b9793f5 Mon Sep 17 00:00:00 2001 From: Fabrizio Colonna Date: Tue, 11 Oct 2016 00:30:50 +0100 Subject: [PATCH] Added competition evolution engine --- framework/pom.xml | 14 +- .../framework/AbstractEvolutionEngine.java | 20 +-- .../framework/CompetitionEngine.java | 140 ++++++++++++++++++ .../GenerationalEvolutionEngine.java | 8 +- pom.xml | 10 +- 5 files changed, 171 insertions(+), 21 deletions(-) create mode 100644 framework/src/java/main/org/uncommons/watchmaker/framework/CompetitionEngine.java diff --git a/framework/pom.xml b/framework/pom.xml index 51ecd7bd..a41db17f 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -26,6 +26,16 @@ watchmaker-framework + + + + jfree + jfreechart + 1.0.0 + + + + org.uncommons.maths @@ -43,8 +53,8 @@ 6.2.1 test - - + + src/java/main src/java/test diff --git a/framework/src/java/main/org/uncommons/watchmaker/framework/AbstractEvolutionEngine.java b/framework/src/java/main/org/uncommons/watchmaker/framework/AbstractEvolutionEngine.java index 486f10dd..6d5131e8 100644 --- a/framework/src/java/main/org/uncommons/watchmaker/framework/AbstractEvolutionEngine.java +++ b/framework/src/java/main/org/uncommons/watchmaker/framework/AbstractEvolutionEngine.java @@ -35,17 +35,17 @@ public abstract class AbstractEvolutionEngine implements EvolutionEngine { // A single multi-threaded worker is shared among multiple evolution engine instances. - private static FitnessEvaluationWorker concurrentWorker = null; + protected static FitnessEvaluationWorker concurrentWorker = null; - private final Set> observers = new CopyOnWriteArraySet>(); + protected final Set> observers = new CopyOnWriteArraySet>(); - private final Random rng; - private final CandidateFactory candidateFactory; - private final FitnessEvaluator fitnessEvaluator; + protected final Random rng; + protected final CandidateFactory candidateFactory; + protected final FitnessEvaluator fitnessEvaluator; - private volatile boolean singleThreaded = false; + protected volatile boolean singleThreaded = false; - private List satisfiedTerminationConditions; + protected List satisfiedTerminationConditions; /** @@ -166,7 +166,7 @@ public List> evolvePopulation(int populationSize, return evaluatedPopulation; } - + /** * This method performs a single step/iteration of the evolutionary process. * @param evaluatedPopulation The population at the beginning of the process. @@ -303,7 +303,7 @@ public void removeEvolutionObserver(EvolutionObserver observer) * Send the population data to all registered observers. * @param data Information about the current state of the population. */ - private void notifyPopulationChange(PopulationData data) + protected void notifyPopulationChange(PopulationData data) { for (EvolutionObserver observer : observers) { @@ -330,7 +330,7 @@ public void setSingleThreaded(boolean singleThreaded) /** * Lazily create the multi-threaded worker for fitness evaluations. */ - private static synchronized FitnessEvaluationWorker getSharedWorker() + protected static synchronized FitnessEvaluationWorker getSharedWorker() { if (concurrentWorker == null) { diff --git a/framework/src/java/main/org/uncommons/watchmaker/framework/CompetitionEngine.java b/framework/src/java/main/org/uncommons/watchmaker/framework/CompetitionEngine.java new file mode 100644 index 00000000..495c0337 --- /dev/null +++ b/framework/src/java/main/org/uncommons/watchmaker/framework/CompetitionEngine.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 Fabrizio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +package org.uncommons.watchmaker.framework; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; + +/** + * Subclassing of one Watchmaker's class to add the capability to run a game + * before applying evolution rules and population change. + * + * @param Type of object to evolve + */ +public abstract class CompetitionEngine extends GenerationalEvolutionEngine { + + public CompetitionEngine(CandidateFactory candidateFactory, + EvolutionaryOperator evolutionScheme, + FitnessEvaluator fitnessEvaluator, + SelectionStrategy selectionStrategy, Random rng) { + super(candidateFactory, evolutionScheme, fitnessEvaluator, selectionStrategy, rng); + } + + @Override + public List> evolvePopulation(int populationSize, + int eliteCount, + Collection seedCandidates, + TerminationCondition... conditions) { + if( eliteCount < 0 || eliteCount >= populationSize ) { + throw new IllegalArgumentException("Elite count must be non-negative and less than population size."); + } + + if( conditions.length == 0 ) { + throw new IllegalArgumentException("At least one TerminationCondition must be specified."); + } + + satisfiedTerminationConditions = null; + + int currentGenerationIndex = 0; + long startTime = System.currentTimeMillis(); + + List population = candidateFactory.generateInitialPopulation( populationSize, seedCandidates, rng ); + + // The competition is first run here + population = runCompetition(population); + // Check that there are still individuals after the population + if( population.size() <= 1 ) { + throw new IllegalArgumentException("The population after the competition is too small"); + } + + // Calculate the fitness scores for each member of the initial population. + List> evaluatedPopulation = evaluatePopulation( population ); + + EvolutionUtils.sortEvaluatedPopulation( + evaluatedPopulation, + fitnessEvaluator.isNatural() + ); + + PopulationData data = EvolutionUtils.getPopulationData( + evaluatedPopulation, + fitnessEvaluator.isNatural(), + eliteCount, + currentGenerationIndex, + startTime + ); + + // Notify observers of the state of the population. + notifyPopulationChange( data ); + + List satisfiedConditions = EvolutionUtils.shouldContinue( data, conditions ); + while( satisfiedConditions == null ) { + ++currentGenerationIndex; + + evaluatedPopulation = nextEvolutionStep( evaluatedPopulation, eliteCount, rng ); + + // Competition is run again + evaluatedPopulation = runCompetitionInternal( evaluatedPopulation ); + if( population.size() <= 1 ) { + throw new IllegalArgumentException("The population after the competition is too small"); + } + + EvolutionUtils.sortEvaluatedPopulation( + evaluatedPopulation, + fitnessEvaluator.isNatural() + ); + + data = EvolutionUtils.getPopulationData( + evaluatedPopulation, + fitnessEvaluator.isNatural(), + eliteCount, + currentGenerationIndex, + startTime + ); + + // Notify observers of the state of the population. + notifyPopulationChange(data); + + satisfiedConditions = EvolutionUtils.shouldContinue(data, conditions); + } + + this.satisfiedTerminationConditions = satisfiedConditions; + + return evaluatedPopulation; + } + + private List> runCompetitionInternal(List> population) { + List work = new ArrayList<>(population.size()); + + for( EvaluatedCandidate candidate : population ) { + work.add(candidate.getCandidate()); + } + + work = runCompetition( work ); + + return this.evaluatePopulation( work ); + } + + /** + * This method runs the competition between the individuals of the population + * + * @param population The population of individuals + * @return The population of individuals after the competition + */ + protected abstract List runCompetition( List population ); +} diff --git a/framework/src/java/main/org/uncommons/watchmaker/framework/GenerationalEvolutionEngine.java b/framework/src/java/main/org/uncommons/watchmaker/framework/GenerationalEvolutionEngine.java index 3e43915f..fe43179f 100644 --- a/framework/src/java/main/org/uncommons/watchmaker/framework/GenerationalEvolutionEngine.java +++ b/framework/src/java/main/org/uncommons/watchmaker/framework/GenerationalEvolutionEngine.java @@ -44,9 +44,9 @@ */ public class GenerationalEvolutionEngine extends AbstractEvolutionEngine { - private final EvolutionaryOperator evolutionScheme; - private final FitnessEvaluator fitnessEvaluator; - private final SelectionStrategy selectionStrategy; + protected final EvolutionaryOperator evolutionScheme; + protected final FitnessEvaluator fitnessEvaluator; + protected final SelectionStrategy selectionStrategy; /** * Creates a new evolution engine by specifying the various components required by @@ -101,7 +101,7 @@ public GenerationalEvolutionEngine(CandidateFactory candidateFactory, /** - * {@inheritDoc} + * {@inheritDoc} */ @Override protected List> nextEvolutionStep(List> evaluatedPopulation, diff --git a/pom.xml b/pom.xml index 6698f38b..1f4ee105 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ watchmaker Watchmaker Framework - Root Project http://watchmaker.uncommons.org - + Apache License, Version 2.0 @@ -21,13 +21,13 @@ repo - + https://github.com/dwdyer/watchmaker scm:git://github.com/dwdyer/watchmaker.git scm:git:ssh://git@github.com:dwdyer/watchmaker.git - + dwdyer @@ -57,8 +57,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.7 + 1.7 ${project.build.sourceEncoding}