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 }