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}