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