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.math3.genetics; 018 019 import org.apache.commons.math3.exception.OutOfRangeException; 020 import org.apache.commons.math3.exception.util.LocalizedFormats; 021 import org.apache.commons.math3.random.RandomGenerator; 022 import org.apache.commons.math3.random.JDKRandomGenerator; 023 024 /** 025 * Implementation of a genetic algorithm. All factors that govern the operation 026 * of the algorithm can be configured for a specific problem. 027 * 028 * @since 2.0 029 * @version $Id: GeneticAlgorithm.java 1416643 2012-12-03 19:37:14Z tn $ 030 */ 031 public class GeneticAlgorithm { 032 033 /** 034 * Static random number generator shared by GA implementation classes. Set the randomGenerator seed to get 035 * reproducible results. Use {@link #setRandomGenerator(RandomGenerator)} to supply an alternative to the default 036 * JDK-provided PRNG. 037 */ 038 //@GuardedBy("this") 039 private static RandomGenerator randomGenerator = new JDKRandomGenerator(); 040 041 /** the crossover policy used by the algorithm. */ 042 private final CrossoverPolicy crossoverPolicy; 043 044 /** the rate of crossover for the algorithm. */ 045 private final double crossoverRate; 046 047 /** the mutation policy used by the algorithm. */ 048 private final MutationPolicy mutationPolicy; 049 050 /** the rate of mutation for the algorithm. */ 051 private final double mutationRate; 052 053 /** the selection policy used by the algorithm. */ 054 private final SelectionPolicy selectionPolicy; 055 056 /** the number of generations evolved to reach {@link StoppingCondition} in the last run. */ 057 private int generationsEvolved = 0; 058 059 /** 060 * Create a new genetic algorithm. 061 * @param crossoverPolicy The {@link CrossoverPolicy} 062 * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) 063 * @param mutationPolicy The {@link MutationPolicy} 064 * @param mutationRate The mutation rate as a percentage (0-1 inclusive) 065 * @param selectionPolicy The {@link SelectionPolicy} 066 * @throws OutOfRangeException if the crossover or mutation rate is outside the [0, 1] range 067 */ 068 public GeneticAlgorithm(final CrossoverPolicy crossoverPolicy, 069 final double crossoverRate, 070 final MutationPolicy mutationPolicy, 071 final double mutationRate, 072 final SelectionPolicy selectionPolicy) throws OutOfRangeException { 073 074 if (crossoverRate < 0 || crossoverRate > 1) { 075 throw new OutOfRangeException(LocalizedFormats.CROSSOVER_RATE, 076 crossoverRate, 0, 1); 077 } 078 if (mutationRate < 0 || mutationRate > 1) { 079 throw new OutOfRangeException(LocalizedFormats.MUTATION_RATE, 080 mutationRate, 0, 1); 081 } 082 this.crossoverPolicy = crossoverPolicy; 083 this.crossoverRate = crossoverRate; 084 this.mutationPolicy = mutationPolicy; 085 this.mutationRate = mutationRate; 086 this.selectionPolicy = selectionPolicy; 087 } 088 089 /** 090 * Set the (static) random generator. 091 * 092 * @param random random generator 093 */ 094 public static synchronized void setRandomGenerator(final RandomGenerator random) { 095 randomGenerator = random; 096 } 097 098 /** 099 * Returns the (static) random generator. 100 * 101 * @return the static random generator shared by GA implementation classes 102 */ 103 public static synchronized RandomGenerator getRandomGenerator() { 104 return randomGenerator; 105 } 106 107 /** 108 * Evolve the given population. Evolution stops when the stopping condition 109 * is satisfied. Updates the {@link #getGenerationsEvolved() generationsEvolved} 110 * property with the number of generations evolved before the StoppingCondition 111 * is satisfied. 112 * 113 * @param initial the initial, seed population. 114 * @param condition the stopping condition used to stop evolution. 115 * @return the population that satisfies the stopping condition. 116 */ 117 public Population evolve(final Population initial, final StoppingCondition condition) { 118 Population current = initial; 119 generationsEvolved = 0; 120 while (!condition.isSatisfied(current)) { 121 current = nextGeneration(current); 122 generationsEvolved++; 123 } 124 return current; 125 } 126 127 /** 128 * Evolve the given population into the next generation. 129 * <p> 130 * <ol> 131 * <li>Get nextGeneration population to fill from <code>current</code> 132 * generation, using its nextGeneration method</li> 133 * <li>Loop until new generation is filled:</li> 134 * <ul><li>Apply configured SelectionPolicy to select a pair of parents 135 * from <code>current</code></li> 136 * <li>With probability = {@link #getCrossoverRate()}, apply 137 * configured {@link CrossoverPolicy} to parents</li> 138 * <li>With probability = {@link #getMutationRate()}, apply 139 * configured {@link MutationPolicy} to each of the offspring</li> 140 * <li>Add offspring individually to nextGeneration, 141 * space permitting</li> 142 * </ul> 143 * <li>Return nextGeneration</li> 144 * </ol> 145 * 146 * @param current the current population. 147 * @return the population for the next generation. 148 */ 149 public Population nextGeneration(final Population current) { 150 Population nextGeneration = current.nextGeneration(); 151 152 RandomGenerator randGen = getRandomGenerator(); 153 154 while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) { 155 // select parent chromosomes 156 ChromosomePair pair = getSelectionPolicy().select(current); 157 158 // crossover? 159 if (randGen.nextDouble() < getCrossoverRate()) { 160 // apply crossover policy to create two offspring 161 pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond()); 162 } 163 164 // mutation? 165 if (randGen.nextDouble() < getMutationRate()) { 166 // apply mutation policy to the chromosomes 167 pair = new ChromosomePair( 168 getMutationPolicy().mutate(pair.getFirst()), 169 getMutationPolicy().mutate(pair.getSecond())); 170 } 171 172 // add the first chromosome to the population 173 nextGeneration.addChromosome(pair.getFirst()); 174 // is there still a place for the second chromosome? 175 if (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) { 176 // add the second chromosome to the population 177 nextGeneration.addChromosome(pair.getSecond()); 178 } 179 } 180 181 return nextGeneration; 182 } 183 184 /** 185 * Returns the crossover policy. 186 * @return crossover policy 187 */ 188 public CrossoverPolicy getCrossoverPolicy() { 189 return crossoverPolicy; 190 } 191 192 /** 193 * Returns the crossover rate. 194 * @return crossover rate 195 */ 196 public double getCrossoverRate() { 197 return crossoverRate; 198 } 199 200 /** 201 * Returns the mutation policy. 202 * @return mutation policy 203 */ 204 public MutationPolicy getMutationPolicy() { 205 return mutationPolicy; 206 } 207 208 /** 209 * Returns the mutation rate. 210 * @return mutation rate 211 */ 212 public double getMutationRate() { 213 return mutationRate; 214 } 215 216 /** 217 * Returns the selection policy. 218 * @return selection policy 219 */ 220 public SelectionPolicy getSelectionPolicy() { 221 return selectionPolicy; 222 } 223 224 /** 225 * Returns the number of generations evolved to reach {@link StoppingCondition} in the last run. 226 * 227 * @return number of generations evolved 228 * @since 2.1 229 */ 230 public int getGenerationsEvolved() { 231 return generationsEvolved; 232 } 233 234 }