001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.math.genetics; 018 019 import org.apache.commons.math.random.RandomGenerator; 020 import org.apache.commons.math.random.JDKRandomGenerator; 021 022 /** 023 * Implementation of a genetic algorithm. All factors that govern the operation 024 * of the algorithm can be configured for a specific problem. 025 * 026 * @since 2.0 027 * @version $Revision: 925812 $ $Date: 2010-03-21 16:49:31 +0100 (dim. 21 mars 2010) $ 028 */ 029 public class GeneticAlgorithm { 030 031 /** 032 * Static random number generator shared by GA implementation classes. 033 * Set the randomGenerator seed to get reproducible results. 034 * Use {@link #setRandomGenerator(RandomGenerator)} to supply an alternative 035 * to the default JDK-provided PRNG. 036 */ 037 //@GuardedBy("this") 038 private static RandomGenerator randomGenerator = new JDKRandomGenerator(); 039 040 /** the crossover policy used by the algorithm. */ 041 private final CrossoverPolicy crossoverPolicy; 042 043 /** the rate of crossover for the algorithm. */ 044 private final double crossoverRate; 045 046 /** the mutation policy used by the algorithm. */ 047 private final MutationPolicy mutationPolicy; 048 049 /** the rate of mutation for the algorithm. */ 050 private final double mutationRate; 051 052 /** the selection policy used by the algorithm. */ 053 private final SelectionPolicy selectionPolicy; 054 055 /** the number of generations evolved to reach {@link StoppingCondition} in the last run. */ 056 private int generationsEvolved = 0; 057 058 /** 059 * @param crossoverPolicy The {@link CrossoverPolicy} 060 * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) 061 * @param mutationPolicy The {@link MutationPolicy} 062 * @param mutationRate The mutation rate as a percentage (0-1 inclusive) 063 * @param selectionPolicy The {@link SelectionPolicy} 064 */ 065 public GeneticAlgorithm( 066 CrossoverPolicy crossoverPolicy, double crossoverRate, 067 MutationPolicy mutationPolicy, double mutationRate, 068 SelectionPolicy selectionPolicy) { 069 if (crossoverRate < 0 || crossoverRate > 1) { 070 throw new IllegalArgumentException("crossoverRate must be between 0 and 1"); 071 } 072 if (mutationRate < 0 || mutationRate > 1) { 073 throw new IllegalArgumentException("mutationRate must be between 0 and 1"); 074 } 075 this.crossoverPolicy = crossoverPolicy; 076 this.crossoverRate = crossoverRate; 077 this.mutationPolicy = mutationPolicy; 078 this.mutationRate = mutationRate; 079 this.selectionPolicy = selectionPolicy; 080 } 081 082 /** 083 * Set the (static) random generator. 084 * 085 * @param random random generator 086 */ 087 public static synchronized void setRandomGenerator(RandomGenerator random) { 088 randomGenerator = random; 089 } 090 091 /** 092 * Returns the (static) random generator. 093 * 094 * @return the static random generator shared by GA implementation classes 095 */ 096 public static synchronized RandomGenerator getRandomGenerator() { 097 return randomGenerator; 098 } 099 100 /** 101 * Evolve the given population. Evolution stops when the stopping condition 102 * is satisfied. Updates the {@link #getGenerationsEvolved() generationsEvolved} 103 * property with the number of generations evolved before the StoppingCondition 104 * is satisfied. 105 * 106 * @param initial the initial, seed population. 107 * @param condition the stopping condition used to stop evolution. 108 * @return the population that satisfies the stopping condition. 109 */ 110 public Population evolve(Population initial, StoppingCondition condition) { 111 Population current = initial; 112 generationsEvolved = 0; 113 while (!condition.isSatisfied(current)) { 114 current = nextGeneration(current); 115 generationsEvolved++; 116 } 117 return current; 118 } 119 120 /** 121 * <p>Evolve the given population into the next generation.</p> 122 * <p><ol> 123 * <li>Get nextGeneration population to fill from <code>current</code> 124 * generation, using its nextGeneration method</li> 125 * <li>Loop until new generation is filled:</li> 126 * <ul><li>Apply configured SelectionPolicy to select a pair of parents 127 * from <code>current</code></li> 128 * <li>With probability = {@link #getCrossoverRate()}, apply 129 * configured {@link CrossoverPolicy} to parents</li> 130 * <li>With probability = {@link #getMutationRate()}, apply 131 * configured {@link MutationPolicy} to each of the offspring</li> 132 * <li>Add offspring individually to nextGeneration, 133 * space permitting</li> 134 * </ul> 135 * <li>Return nextGeneration</li> 136 * </ol> 137 * </p> 138 * 139 * @param current the current population. 140 * @return the population for the next generation. 141 */ 142 public Population nextGeneration(Population current) { 143 Population nextGeneration = current.nextGeneration(); 144 145 RandomGenerator randGen = getRandomGenerator(); 146 147 while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) { 148 // select parent chromosomes 149 ChromosomePair pair = getSelectionPolicy().select(current); 150 151 // crossover? 152 if (randGen.nextDouble() < getCrossoverRate()) { 153 // apply crossover policy to create two offspring 154 pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond()); 155 } 156 157 // mutation? 158 if (randGen.nextDouble() < getMutationRate()) { 159 // apply mutation policy to the chromosomes 160 pair = new ChromosomePair( 161 getMutationPolicy().mutate(pair.getFirst()), 162 getMutationPolicy().mutate(pair.getSecond())); 163 } 164 165 // add the first chromosome to the population 166 nextGeneration.addChromosome(pair.getFirst()); 167 // is there still a place for the second chromosome? 168 if (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) { 169 // add the second chromosome to the population 170 nextGeneration.addChromosome(pair.getSecond()); 171 } 172 } 173 174 return nextGeneration; 175 } 176 177 /** 178 * Returns the crossover policy. 179 * @return crossover policy 180 */ 181 public CrossoverPolicy getCrossoverPolicy() { 182 return crossoverPolicy; 183 } 184 185 /** 186 * Returns the crossover rate. 187 * @return crossover rate 188 */ 189 public double getCrossoverRate() { 190 return crossoverRate; 191 } 192 193 /** 194 * Returns the mutation policy. 195 * @return mutation policy 196 */ 197 public MutationPolicy getMutationPolicy() { 198 return mutationPolicy; 199 } 200 201 /** 202 * Returns the mutation rate. 203 * @return mutation rate 204 */ 205 public double getMutationRate() { 206 return mutationRate; 207 } 208 209 /** 210 * Returns the selection policy. 211 * @return selection policy 212 */ 213 public SelectionPolicy getSelectionPolicy() { 214 return selectionPolicy; 215 } 216 217 /** 218 * Returns the number of generations evolved to 219 * reach {@link StoppingCondition} in the last run. 220 * 221 * @return number of generations evolved 222 * @since 2.1 223 */ 224 public int getGenerationsEvolved() { 225 return generationsEvolved; 226 } 227 228 }