GeneticAlgorithm.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.math4.legacy.genetics;

  18. import org.apache.commons.math4.legacy.exception.OutOfRangeException;
  19. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  20. import org.apache.commons.rng.simple.RandomSource;
  21. import org.apache.commons.rng.UniformRandomProvider;

  22. /**
  23.  * Implementation of a genetic algorithm. All factors that govern the operation
  24.  * of the algorithm can be configured for a specific problem.
  25.  *
  26.  * @since 2.0
  27.  */
  28. public class GeneticAlgorithm {

  29.     /**
  30.      * Static random number generator shared by GA implementation classes.
  31.      * Use {@link #setRandomGenerator(UniformRandomProvider)} to supply an
  32.      * alternative to the default PRNG, and/or select a specific seed.
  33.      */
  34.     //@GuardedBy("this")
  35.     private static UniformRandomProvider randomGenerator = RandomSource.WELL_19937_C.create();

  36.     /** the crossover policy used by the algorithm. */
  37.     private final CrossoverPolicy crossoverPolicy;

  38.     /** the rate of crossover for the algorithm. */
  39.     private final double crossoverRate;

  40.     /** the mutation policy used by the algorithm. */
  41.     private final MutationPolicy mutationPolicy;

  42.     /** the rate of mutation for the algorithm. */
  43.     private final double mutationRate;

  44.     /** the selection policy used by the algorithm. */
  45.     private final SelectionPolicy selectionPolicy;

  46.     /** the number of generations evolved to reach {@link StoppingCondition} in the last run. */
  47.     private int generationsEvolved;

  48.     /**
  49.      * Create a new genetic algorithm.
  50.      * @param crossoverPolicy The {@link CrossoverPolicy}
  51.      * @param crossoverRate The crossover rate as a percentage (0-1 inclusive)
  52.      * @param mutationPolicy The {@link MutationPolicy}
  53.      * @param mutationRate The mutation rate as a percentage (0-1 inclusive)
  54.      * @param selectionPolicy The {@link SelectionPolicy}
  55.      * @throws OutOfRangeException if the crossover or mutation rate is outside the [0, 1] range
  56.      */
  57.     public GeneticAlgorithm(final CrossoverPolicy crossoverPolicy,
  58.                             final double crossoverRate,
  59.                             final MutationPolicy mutationPolicy,
  60.                             final double mutationRate,
  61.                             final SelectionPolicy selectionPolicy) throws OutOfRangeException {

  62.         if (crossoverRate < 0 || crossoverRate > 1) {
  63.             throw new OutOfRangeException(LocalizedFormats.CROSSOVER_RATE,
  64.                                           crossoverRate, 0, 1);
  65.         }
  66.         if (mutationRate < 0 || mutationRate > 1) {
  67.             throw new OutOfRangeException(LocalizedFormats.MUTATION_RATE,
  68.                                           mutationRate, 0, 1);
  69.         }
  70.         this.crossoverPolicy = crossoverPolicy;
  71.         this.crossoverRate = crossoverRate;
  72.         this.mutationPolicy = mutationPolicy;
  73.         this.mutationRate = mutationRate;
  74.         this.selectionPolicy = selectionPolicy;
  75.     }

  76.     /**
  77.      * Set the (static) random generator.
  78.      *
  79.      * @param random random generator
  80.      */
  81.     public static synchronized void setRandomGenerator(final UniformRandomProvider random) {
  82.         randomGenerator = random;
  83.     }

  84.     /**
  85.      * Returns the (static) random generator.
  86.      *
  87.      * @return the static random generator shared by GA implementation classes
  88.      */
  89.     public static synchronized UniformRandomProvider getRandomGenerator() {
  90.         return randomGenerator;
  91.     }

  92.     /**
  93.      * Evolve the given population. Evolution stops when the stopping condition
  94.      * is satisfied. Updates the {@link #getGenerationsEvolved() generationsEvolved}
  95.      * property with the number of generations evolved before the StoppingCondition
  96.      * is satisfied.
  97.      *
  98.      * @param initial the initial, seed population.
  99.      * @param condition the stopping condition used to stop evolution.
  100.      * @return the population that satisfies the stopping condition.
  101.      */
  102.     public Population evolve(final Population initial, final StoppingCondition condition) {
  103.         Population current = initial;
  104.         generationsEvolved = 0;
  105.         while (!condition.isSatisfied(current)) {
  106.             current = nextGeneration(current);
  107.             generationsEvolved++;
  108.         }
  109.         return current;
  110.     }

  111.     /**
  112.      * Evolve the given population into the next generation.
  113.      * <ol>
  114.      *  <li>Get nextGeneration population to fill from <code>current</code>
  115.      *      generation, using its nextGeneration method</li>
  116.      *  <li>Loop until new generation is filled:
  117.      *  <ul><li>Apply configured SelectionPolicy to select a pair of parents
  118.      *          from <code>current</code></li>
  119.      *      <li>With probability = {@link #getCrossoverRate()}, apply
  120.      *          configured {@link CrossoverPolicy} to parents</li>
  121.      *      <li>With probability = {@link #getMutationRate()}, apply
  122.      *          configured {@link MutationPolicy} to each of the offspring</li>
  123.      *      <li>Add offspring individually to nextGeneration,
  124.      *          space permitting</li>
  125.      *  </ul></li>
  126.      *  <li>Return nextGeneration</li>
  127.      * </ol>
  128.      *
  129.      * @param current the current population.
  130.      * @return the population for the next generation.
  131.      */
  132.     public Population nextGeneration(final Population current) {
  133.         Population nextGeneration = current.nextGeneration();

  134.         UniformRandomProvider randGen = getRandomGenerator();

  135.         while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) {
  136.             // select parent chromosomes
  137.             ChromosomePair pair = getSelectionPolicy().select(current);

  138.             // crossover?
  139.             if (randGen.nextDouble() < getCrossoverRate()) {
  140.                 // apply crossover policy to create two offspring
  141.                 pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond());
  142.             }

  143.             // mutation?
  144.             if (randGen.nextDouble() < getMutationRate()) {
  145.                 // apply mutation policy to the chromosomes
  146.                 pair = new ChromosomePair(
  147.                     getMutationPolicy().mutate(pair.getFirst()),
  148.                     getMutationPolicy().mutate(pair.getSecond()));
  149.             }

  150.             // add the first chromosome to the population
  151.             nextGeneration.addChromosome(pair.getFirst());
  152.             // is there still a place for the second chromosome?
  153.             if (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) {
  154.                 // add the second chromosome to the population
  155.                 nextGeneration.addChromosome(pair.getSecond());
  156.             }
  157.         }

  158.         return nextGeneration;
  159.     }

  160.     /**
  161.      * Returns the crossover policy.
  162.      * @return crossover policy
  163.      */
  164.     public CrossoverPolicy getCrossoverPolicy() {
  165.         return crossoverPolicy;
  166.     }

  167.     /**
  168.      * Returns the crossover rate.
  169.      * @return crossover rate
  170.      */
  171.     public double getCrossoverRate() {
  172.         return crossoverRate;
  173.     }

  174.     /**
  175.      * Returns the mutation policy.
  176.      * @return mutation policy
  177.      */
  178.     public MutationPolicy getMutationPolicy() {
  179.         return mutationPolicy;
  180.     }

  181.     /**
  182.      * Returns the mutation rate.
  183.      * @return mutation rate
  184.      */
  185.     public double getMutationRate() {
  186.         return mutationRate;
  187.     }

  188.     /**
  189.      * Returns the selection policy.
  190.      * @return selection policy
  191.      */
  192.     public SelectionPolicy getSelectionPolicy() {
  193.         return selectionPolicy;
  194.     }

  195.     /**
  196.      * Returns the number of generations evolved to reach {@link StoppingCondition} in the last run.
  197.      *
  198.      * @return number of generations evolved
  199.      * @since 2.1
  200.      */
  201.     public int getGenerationsEvolved() {
  202.         return generationsEvolved;
  203.     }
  204. }