phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractGeneticAlgorithm
{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** the crossover policy used by the algorithm. */ + private final CrossoverPolicy
crossoverPolicy; + + /** the mutation policy used by the algorithm. */ + private final MutationPolicy
mutationPolicy; + + /** the selection policy used by the algorithm. */ + private final SelectionPolicy
selectionPolicy; + + /** + * the number of generations evolved to reach {@link StoppingCondition} in the + * last run. + */ + private int generationsEvolved; + + /** The elitism rate having default value of .25. */ + private double elitismRate = .25; + + /** + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param mutationPolicy The {@link MutationPolicy} + * @param selectionPolicy The {@link SelectionPolicy} + */ + protected AbstractGeneticAlgorithm(final CrossoverPolicy
crossoverPolicy, + final MutationPolicy
mutationPolicy, + final SelectionPolicy
selectionPolicy) { + this.crossoverPolicy = crossoverPolicy; + this.mutationPolicy = mutationPolicy; + this.selectionPolicy = selectionPolicy; + } + + /** + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param mutationPolicy The {@link MutationPolicy} + * @param selectionPolicy The {@link SelectionPolicy} + * @param elitismRate The elitism rate + */ + protected AbstractGeneticAlgorithm(final CrossoverPolicy
crossoverPolicy, + final MutationPolicy
mutationPolicy, + final SelectionPolicy
selectionPolicy, + double elitismRate) { + this.crossoverPolicy = crossoverPolicy; + this.mutationPolicy = mutationPolicy; + this.selectionPolicy = selectionPolicy; + this.elitismRate = elitismRate; + } + + /** + * Returns the crossover policy. + * @return crossover policy + */ + public CrossoverPolicy
getCrossoverPolicy() { + return crossoverPolicy; + } + + /** + * Returns the mutation policy. + * @return mutation policy + */ + public MutationPolicy
getMutationPolicy() { + return mutationPolicy; + } + + /** + * Returns the selection policy. + * @return selection policy + */ + public SelectionPolicy
getSelectionPolicy() { + return selectionPolicy; + } + + /** + * Returns the number of generations evolved to reach {@link StoppingCondition} + * in the last run. + * + * @return number of generations evolved + * @since 2.1 + */ + public int getGenerationsEvolved() { + return generationsEvolved; + } + + /** + * Evolve the given population. Evolution stops when the stopping condition is + * satisfied. Updates the {@link #getGenerationsEvolved() generationsEvolved} + * property with the number of generations evolved before the StoppingCondition + * is satisfied. + * + * @param initial the initial, seed population. + * @param condition the stopping condition used to stop evolution. + * @return the population that satisfies the stopping condition. + */ + public Population
evolve(final Population
initial, final StoppingCondition
condition) { + Population
current = initial; + + LOGGER.info("Starting evolution process."); + // check if stopping condition is satisfied otherwise produce the next + // generation of population. + while (!condition.isSatisfied(current)) { + // notify interested listener + ConvergenceListenerRegistry.
getInstance().notifyAll(generationsEvolved, current); + + current = nextGeneration(current); + this.generationsEvolved++; + } + LOGGER.info("Population convergence achieved after generations: " + generationsEvolved); + + return current; + } + + /** + * Evolve the given population into the next generation. + *
current
+ * generation, using its nextGeneration methodcurrent
,nextGeneration(Population
current); + + /** + * Returns the elitism rate. + * @return elitism rate + */ + public double getElitismRate() { + return elitismRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java new file mode 100644 index 0000000000..07e891316f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.crossover.rategenerator.CrossoverRateGenerator; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.mutation.rategenerator.MutationRateGenerator; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An implementation of Genetic Algorithm. The probability of crossover and + * mutation is generated in an adaptive way. This implementation allows + * configuration of dynamic crossover and mutation rate generator along with + * crossover policy, mutation policy, selection policy and optionally elitism + * rate. + * @param
phenotype of chromosome + */ +public class AdaptiveGeneticAlgorithm
extends AbstractGeneticAlgorithm
{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** The crossover rate generator. **/ + private final CrossoverRateGenerator
crossoverRateGenerator; + + /** The mutation rate generator. **/ + private final MutationRateGenerator
mutationRateGenerator; + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy
crossoverPolicy, + CrossoverRateGenerator
crossoverProbabilityGenerator, + MutationPolicy
mutationPolicy, + MutationRateGenerator
mutationProbabilityGenerator, + SelectionPolicy
selectionPolicy) { + super(crossoverPolicy, mutationPolicy, selectionPolicy); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + * @param elitismRate elitism rate + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy
crossoverPolicy, + CrossoverRateGenerator
crossoverProbabilityGenerator, + MutationPolicy
mutationPolicy, + MutationRateGenerator
mutationProbabilityGenerator, + SelectionPolicy
selectionPolicy, + double elitismRate) { + super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * {@inheritDoc} + */ + @Override + protected Population
nextGeneration(Population
current) { + + LOGGER.debug("Reproducing next generation."); + + // compute statistics of current generation chromosomes. + PopulationStatisticalSummary
populationStats = new PopulationStatisticalSummaryImpl<>(current); + + // Initialize the next generation with elit chromosomes from previous + // generation. + final Population
nextGeneration = current.nextGeneration(getElitismRate()); + + LOGGER.debug( + "No of Elite chromosomes selected from previous generation: " + nextGeneration.getPopulationSize()); + + final int maxOffspringCount = nextGeneration.getPopulationLimit() - nextGeneration.getPopulationSize(); + + // Initialize an empty population for offsprings. + final Population
offspringPopulation = current.nextGeneration(0); + + // perform crossover and generate new offsprings + while (offspringPopulation.getPopulationSize() < maxOffspringCount) { + + // select parent chromosomes + ChromosomePair
pair = getSelectionPolicy().select(current); + LOGGER.debug("Selected Chromosomes: \r\n" + pair.toString()); + + final double crossoverRate = crossoverRateGenerator.generate(pair.getFirst(), pair.getSecond(), + populationStats, getGenerationsEvolved()); + // apply crossover policy to create two offspring + pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond(), crossoverRate); + LOGGER.debug("Offsprings after Crossover: \r\n" + pair.toString()); + + // add the first chromosome to the population + offspringPopulation.addChromosome(pair.getFirst()); + // is there still a place for the second chromosome? + if (offspringPopulation.getPopulationSize() < maxOffspringCount) { + // add the second chromosome to the population + offspringPopulation.addChromosome(pair.getSecond()); + } + } + LOGGER.debug("Performing adaptive mutation of offsprings."); + + // recompute the statistics of the offspring population. + populationStats = new PopulationStatisticalSummaryImpl<>(offspringPopulation); + + // apply mutation policy to the offspring chromosomes and add the mutated + // chromosomes to next generation. + for (Chromosome
chromosome : offspringPopulation) { + nextGeneration.addChromosome(getMutationPolicy().mutate(chromosome, + mutationRateGenerator.generate(chromosome, populationStats, getGenerationsEvolved()))); + } + LOGGER.debug("New Generation: \r\n" + nextGeneration.toString()); + + return nextGeneration; + } + + /** + * Returns crossover probability generator. + * @return crossover probability generator + */ + public CrossoverRateGenerator
getCrossoverProbabilityGenerator() { + return crossoverRateGenerator; + } + + /** + * Returns mutation probability generator. + * @return mutation probability generator + */ + public MutationRateGenerator
getMutationProbabilityGenerator() { + return mutationRateGenerator; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java new file mode 100644 index 0000000000..94e320d42a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implementation of a genetic algorithm. All factors that govern the operation + * of the algorithm can be configured for a specific problem. + * + * @param
phenotype of chromosome + * @since 4.0 + */ +public class GeneticAlgorithm
extends AbstractGeneticAlgorithm
{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** crossover rate string. **/ + private static final String CROSSOVER_RATE = "CROSSOVER_RATE"; + + /** mutation rate string. **/ + private static final String MUTATION_RATE = "MUTATION_RATE"; + + /** the rate of crossover for the algorithm. */ + private final double crossoverRate; + + /** the rate of mutation for the algorithm. */ + private final double mutationRate; + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + */ + public GeneticAlgorithm(final CrossoverPolicy
crossoverPolicy, + final double crossoverRate, + final MutationPolicy
mutationPolicy, + final double mutationRate, + final SelectionPolicy
selectionPolicy) { + super(crossoverPolicy, mutationPolicy, selectionPolicy); + + checkValidity(crossoverRate, mutationRate); + this.crossoverRate = crossoverRate; + this.mutationRate = mutationRate; + } + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + * @param elitismRate The rate of elitism + */ + public GeneticAlgorithm(final CrossoverPolicy
crossoverPolicy, + final double crossoverRate, + final MutationPolicy
mutationPolicy, + final double mutationRate, + final SelectionPolicy
selectionPolicy, + final double elitismRate) { + super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate); + + checkValidity(crossoverRate, mutationRate); + this.crossoverRate = crossoverRate; + this.mutationRate = mutationRate; + } + + private void checkValidity(final double crossoverRateInput, final double inputMutationRate) { + if (crossoverRateInput < 0 || crossoverRateInput > 1) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, crossoverRateInput, CROSSOVER_RATE, 0, 1); + } + if (inputMutationRate < 0 || inputMutationRate > 1) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, inputMutationRate, MUTATION_RATE, 0, 1); + } + } + + /** + * Evolve the given population into the next generation. + *
current
+ * generation, using its nextGeneration methodcurrent
nextGeneration(final Population
current) { + + LOGGER.debug("Reproducing next generation."); + final Population
nextGeneration = current.nextGeneration(getElitismRate()); + + while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit() - 1) { + + // select parent chromosomes + ChromosomePair
pair = getSelectionPolicy().select(current); + LOGGER.debug("Selected Chromosomes: \r\n" + pair.toString()); + + // apply crossover policy to create two offspring + pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond(), crossoverRate); + LOGGER.debug("Offsprings after Crossover: \r\n" + pair.toString()); + + // apply mutation policy to the chromosomes + pair = new ChromosomePair<>(getMutationPolicy().mutate(pair.getFirst(), mutationRate), + getMutationPolicy().mutate(pair.getSecond(), mutationRate)); + LOGGER.debug("Offsprings after Mutation: \r\n" + pair.toString()); + + // add the chromosomes to the population + nextGeneration.addChromosome(pair.getFirst()); + nextGeneration.addChromosome(pair.getSecond()); + } + LOGGER.debug("New Generation : \r\n" + nextGeneration.toString()); + + return nextGeneration; + } + + /** + * Returns the crossover rate. + * @return crossover rate + */ + public double getCrossoverRate() { + return crossoverRate; + } + + /** + * Returns the mutation rate. + * @return mutation rate + */ + public double getMutationRate() { + return mutationRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java new file mode 100644 index 0000000000..a34bc32e84 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.Objects; +import java.util.UUID; + +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * Individual in a population. Chromosomes are compared based on their fitness. + *
+ * The chromosomes are IMMUTABLE, and so their fitness is also immutable and + * therefore it can be cached. + * + * @param
The phenotype of chromosome. The type should override hashCode() + * and equals() methods. + * @since 4.0 + */ +public abstract class AbstractChromosome
implements Chromosome
{ + + /** Value assigned when no fitness has been computed yet. */ + private static final double NO_FITNESS = Double.NEGATIVE_INFINITY; + + /** Cached value of the fitness of this chromosome. */ + private double fitness = NO_FITNESS; + + /** Fitness function to evaluate fitness of chromosome. **/ + private final FitnessFunction
fitnessFunction; + + /** decoder to deode the chromosome's genotype representation. **/ + private final Decoder
decoder; + + /** Id of chromosome. **/ + private final String id; + + /** + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + protected AbstractChromosome(final FitnessFunction
fitnessFunction, final Decoder
decoder) { + this.fitnessFunction = Objects.requireNonNull(fitnessFunction); + this.decoder = Objects.requireNonNull(decoder); + this.id = UUID.randomUUID().toString(); + } + + /** + * returns fitness function. + * @return fitnessFunction + */ + protected FitnessFunction
getFitnessFunction() { + return fitnessFunction; + } + + /** + * Returns the decoder instance. + * @return decoder + */ + protected Decoder
getDecoder() { + return decoder; + } + + /** + * Returns id of chromosome. + * @return id + */ + @Override + public String getId() { + return id; + } + + /** + * Access the fitness of this chromosome. The bigger the fitness, the better the + * chromosome. + *
+ * Computation of fitness is usually very time-consuming task, therefore the + * fitness is cached. + * @return the fitness + */ + @Override + public double evaluate() { + if (this.fitness == NO_FITNESS) { + // no cache - compute the fitness + this.fitness = fitnessFunction.compute(decode()); + } + return this.fitness; + } + + /** + * Decodes the chromosome genotype and returns the phenotype. + * @return phenotype + */ + @Override + public P decode() { + return this.decoder.decode(this); + } + + /** + * Compares two chromosomes based on their fitness. The bigger the fitness, the + * better the chromosome. + * @param another another chromosome to compare + * @return + *
another
is better than this
another
is worse than this
another) {
+ return Double.compare(evaluate(), another.evaluate());
+ }
+
+ /**
+ * Returns true
iff another
has the same
+ * representation and therefore the same fitness. By default, it returns false
+ * -- override it in your implementation if you need it.
+ * @param another chromosome to compare
+ * @return true if another
is equivalent to this chromosome
+ */
+ public boolean isSame(final AbstractChromosome
another) {
+ final P decodedChromosome = decode();
+ final P otherDecodedChromosome = another.decode();
+ return decodedChromosome.equals(otherDecodedChromosome);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return String.format("(f=%s %s)", evaluate(), decode());
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java
new file mode 100644
index 0000000000..ac85e553a2
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.ga.chromosome;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder;
+import org.apache.commons.math4.ga.fitness.FitnessFunction;
+
+/**
+ * This class represents an abstract chromosome containing an immutable list of
+ * allele/genes.
+ * @param phenotype of chromosome
+ * @since 2.0
+ */
+public abstract class AbstractListChromosome {
+
+ /** List of allele/genes. */
+ private final List fitnessFunction,
+ final AbstractListChromosomeDecoder fitnessFunction,
+ AbstractListChromosomeDecoder fitnessFunction,
+ final AbstractListChromosomeDecoder
+ * Usually, this method just calls a constructor of the class.
+ *
+ * @param chromosomeRepresentation the inner array representation of the new
+ * chromosome.
+ * @return new instance extended from FixedLengthChromosome with the given
+ * arrayRepresentation
+ */
+ public abstract AbstractListChromosome phenotype of chromosome
+ * @since 2.0
+ */
+public class BinaryChromosome extends IntegralValuedChromosome {
+
+ /**
+ * @param representation Internal representation of chromosome.
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link AbstractListChromosomeDecoder}
+ */
+ public BinaryChromosome(List fitnessFunction,
+ AbstractListChromosomeDecoder fitnessFunction,
+ AbstractListChromosomeDecoder newChromosome(List phenotype fo chromosome
+ * @param length length of chromosome
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link AbstractListChromosomeDecoder}
+ * @return a binary chromosome
+ */
+ public static BinaryChromosome randomChromosome(int length,
+ FitnessFunction fitnessFunction,
+ AbstractListChromosomeDecoder phenotype of chromosome
+ * @since 4.0
+ */
+public interface Chromosome extends Comparable
+ * Computation of fitness is usually very time-consuming task, therefore the
+ * fitness is cached.
+ * @return the fitness
+ */
+ double evaluate();
+
+ /**
+ * Decodes the chromosome genotype and returns the phenotype.
+ * @return phenotype
+ */
+ P decode();
+
+ /**
+ * Returns unique Id of chromosome.
+ * @return id
+ */
+ String getId();
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java
new file mode 100644
index 0000000000..1157a00822
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.ga.chromosome;
+
+/**
+ * A pair of {@link Chromosome} objects.
+ * @param phenotype of chromosome
+ * @since 2.0
+ */
+public class ChromosomePair {
+
+ /** the first chromosome in the pair. */
+ private final Chromosome first;
+
+ /** the second chromosome in the pair. */
+ private final Chromosome second;
+
+ /**
+ * Create a chromosome pair.
+ * @param c1 the first chromosome.
+ * @param c2 the second chromosome.
+ */
+ public ChromosomePair(final Chromosome c1, final Chromosome c2) {
+ super();
+ first = c1;
+ second = c2;
+ }
+
+ /**
+ * Access the first chromosome.
+ *
+ * @return the first chromosome.
+ */
+ public Chromosome getFirst() {
+ return first;
+ }
+
+ /**
+ * Access the second chromosome.
+ *
+ * @return the second chromosome.
+ */
+ public Chromosome getSecond() {
+ return second;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return String.format("(%s,%s)", getFirst(), getSecond());
+ }
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java
new file mode 100644
index 0000000000..e5669cfb18
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.ga.chromosome;
+
+import java.util.List;
+
+import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder;
+import org.apache.commons.math4.ga.fitness.FitnessFunction;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.internal.utils.ValidationUtils;
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+
+/**
+ * Chromosome represented by a list of integral values. The acceptable integral
+ * values should belong to the range min(inclusive) to max(exclusive).
+ * @param phenotype of chromosome
+ * @since 4.0
+ */
+public class IntegralValuedChromosome extends AbstractListChromosome fitnessFunction,
+ AbstractListChromosomeDecoder fitnessFunction,
+ AbstractListChromosomeDecoder newChromosome(List phenotype fo chromosome
+ * @param length length of chromosome
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link AbstractListChromosomeDecoder}
+ * @param min minimum inclusive value of allele
+ * @param max maximum exclusive value of allele
+ * @return an integral-valued chromosome
+ */
+ public static IntegralValuedChromosome randomChromosome(int length,
+ FitnessFunction fitnessFunction,
+ AbstractListChromosomeDecoder
+ * @param phenotype of chromosome
+ * @since 4.0
+ */
+public class RealValuedChromosome extends AbstractListChromosome fitnessFunction,
+ AbstractListChromosomeDecoder fitnessFunction,
+ AbstractListChromosomeDecoder fitnessFunction,
+ AbstractListChromosomeDecoder fitnessFunction,
+ AbstractListChromosomeDecoder newChromosome(List phenotype of chromosome
+ * @param length length of chromosome genotype
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link AbstractListChromosomeDecoder}
+ * @param min minimum inclusive value generated as allele
+ * @param max maximum exclusive value generated as allele
+ * @return A real-valued chromosome
+ */
+ public static RealValuedChromosome randomChromosome(int length,
+ FitnessFunction fitnessFunction,
+ AbstractListChromosomeDecoder
+ * The first time {@link #isSatisfied(Population)} is invoked, the end time of
+ * the evolution is determined based on the provided phenotype of chromosome
+ * @since 3.1
+ */
+public class FixedElapsedTime implements StoppingCondition {
+
+ /** Maximum allowed time period (in nanoseconds). */
+ private final long maxTimePeriod;
+
+ /** The predetermined termination time (stopping condition). */
+ private long endTime = -1;
+
+ /**
+ * Create a new {@link FixedElapsedTime} instance.
+ *
+ * @param maxTime maximum number of seconds generations are allowed to evolve
+ */
+ public FixedElapsedTime(final long maxTime) {
+ this(maxTime, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Create a new {@link FixedElapsedTime} instance.
+ *
+ * @param maxTime maximum time generations are allowed to evolve
+ * @param unit {@link TimeUnit} of the maxTime argument
+ */
+ public FixedElapsedTime(final long maxTime, final TimeUnit unit) {
+ if (maxTime < 0) {
+ throw new GeneticException(GeneticException.TOO_SMALL, maxTime, 0);
+ }
+ maxTimePeriod = unit.toNanos(maxTime);
+ }
+
+ /**
+ * Determine whether or not the maximum allowed time has passed. The termination
+ * time is determined after the first generation.
+ *
+ * @return population) {
+ if (endTime < 0) {
+ endTime = System.nanoTime() + maxTimePeriod;
+ }
+
+ return System.nanoTime() >= endTime;
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java
new file mode 100644
index 0000000000..37cfff9ef1
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.ga.convergence;
+
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * Stops after a fixed number of generations.
+ *
+ * Each time {@link #isSatisfied(Population)} is invoked, a generation counter
+ * is incremented. Once the counter reaches the configured
+ * {@code maxGenerations} value, {@link #isSatisfied(Population)} returns true.
+ *
+ * @param phenotype of chromosome
+ * @since 2.0
+ */
+public class FixedGenerationCount implements StoppingCondition {
+ /** Number of generations that have passed. */
+ private int numGenerations;
+
+ /** Maximum number of generations (stopping criteria). */
+ private final int maxGenerations;
+
+ /**
+ * Create a new FixedGenerationCount instance.
+ *
+ * @param maxGenerations number of generations to evolve
+ */
+ public FixedGenerationCount(final int maxGenerations) {
+ if (maxGenerations <= 0) {
+ throw new GeneticException(GeneticException.TOO_SMALL, maxGenerations, 1);
+ }
+ this.maxGenerations = maxGenerations;
+ }
+
+ /**
+ * Determine whether or not the given number of generations have passed.
+ * Increments the number of generations counter if the maximum has not been
+ * reached.
+ *
+ * @return population) {
+ if (this.numGenerations < this.maxGenerations) {
+ numGenerations++;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns the number of generations that have already passed.
+ * @return the number of generations that have passed
+ */
+ public int getNumGenerations() {
+ return numGenerations;
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java
new file mode 100644
index 0000000000..601c098711
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.ga.convergence;
+
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * Algorithm used to determine when to stop evolution.
+ *
+ * @param phenotype of chromosome
+ * @since 2.0
+ */
+public interface StoppingCondition {
+
+ /**
+ * Determine whether or not the given population satisfies the stopping
+ * condition.
+ * @param population population of chromosome
+ *
+ * @return population);
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java
new file mode 100644
index 0000000000..2281b2a70b
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.ga.convergence;
+
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * This class represents a stopping condition based on best fitness value.
+ * Convergence will be stopped once best fitness remains unchanged for
+ * predefined number of generations.
+ * @param phenotype of chromosome
+ * @since 4.0
+ */
+public class UnchangedBestFitness implements StoppingCondition {
+
+ /** best fitness of previous generation. **/
+ private double lastBestFitness = Double.MIN_VALUE;
+
+ /**
+ * The configured number of generations for which optimization process will
+ * continue with unchanged best fitness value.
+ **/
+ private final int maxGenerationsWithUnchangedBestFitness;
+
+ /** Number of generations the best fitness value has not been changed. **/
+ private int generationsHavingUnchangedBestFitness;
+
+ /**
+ * @param maxGenerationsWithUnchangedAverageFitness maximum number of
+ * generations with unchanged
+ * best fitness
+ */
+ public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) {
+ this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isSatisfied(Population population) {
+ final double currentBestFitness = population.getFittestChromosome().evaluate();
+
+ if (lastBestFitness == currentBestFitness) {
+ generationsHavingUnchangedBestFitness++;
+ if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) {
+ return true;
+ }
+ } else {
+ this.generationsHavingUnchangedBestFitness = 0;
+ lastBestFitness = currentBestFitness;
+ }
+
+ return false;
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java
new file mode 100644
index 0000000000..a729e0afe7
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.ga.convergence;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * This class represents a stopping condition based on mean fitness value.
+ * Convergence will be stopped once mean fitness remains unchanged for
+ * predefined number of generations.
+ * @param phenotype of chromosome
+ * @since 4.0
+ */
+public class UnchangedMeanFitness implements StoppingCondition {
+
+ /** Mean fitness of previous generation. **/
+ private double lastMeanFitness = Double.MIN_VALUE;
+
+ /**
+ * The configured number of generations for which optimization process will
+ * continue with unchanged best fitness value.
+ **/
+ private final int maxGenerationsWithUnchangedMeanFitness;
+
+ /** Number of generations the mean fitness value has not been changed. **/
+ private int generationsHavingUnchangedMeanFitness;
+
+ /**
+ * @param maxGenerationsWithUnchangedMeanFitness maximum number of generations
+ * with unchanged mean fitness
+ */
+ public UnchangedMeanFitness(final int maxGenerationsWithUnchangedMeanFitness) {
+ this.maxGenerationsWithUnchangedMeanFitness = maxGenerationsWithUnchangedMeanFitness;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isSatisfied(Population population) {
+
+ final double currentMeanFitness = calculateMeanFitness(population);
+
+ if (lastMeanFitness == currentMeanFitness) {
+ generationsHavingUnchangedMeanFitness++;
+ if (generationsHavingUnchangedMeanFitness == maxGenerationsWithUnchangedMeanFitness) {
+ return true;
+ }
+ } else {
+ this.generationsHavingUnchangedMeanFitness = 0;
+ lastMeanFitness = currentMeanFitness;
+ }
+
+ return false;
+ }
+
+ /**
+ * calculates mean fitness of the population.
+ * @param population
+ * @return mean fitness
+ */
+ private double calculateMeanFitness(Population population) {
+ double totalFitness = 0.0;
+ for (Chromosome chromosome : population) {
+ totalFitness += chromosome.evaluate();
+ }
+ return totalFitness / population.getPopulationSize();
+ }
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java
new file mode 100644
index 0000000000..d386944a88
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This package provides Genetic Algorithms components and implementations.
+ */
+package org.apache.commons.math4.ga.convergence;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java
new file mode 100644
index 0000000000..eee06caec4
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.ga.crossover;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.chromosome.ChromosomePair;
+import org.apache.commons.math4.ga.utils.RandomGenerator;
+
+/**
+ * An abstraction to represent the base crossover policy.
+ * @param phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractChromosomeCrossoverPolicy implements CrossoverPolicy {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ChromosomePair crossover(final Chromosome first,
+ final Chromosome second,
+ final double crossoverRate) {
+ if (RandomGenerator.getRandomGenerator().nextDouble() < crossoverRate) {
+ return crossover(first, second);
+ } else {
+ return new ChromosomePair<>(first, second);
+ }
+ }
+
+ /**
+ * Performs crossover of two chromosomes.
+ * @param first The first parent chromosome participating in crossover
+ * @param second The second parent chromosome participating in crossover
+ * @return chromosome pair
+ */
+ protected abstract ChromosomePair crossover(Chromosome first, Chromosome second);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java
new file mode 100644
index 0000000000..2184310c6d
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.ga.crossover;
+
+import org.apache.commons.math4.ga.chromosome.AbstractListChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.chromosome.ChromosomePair;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+
+/**
+ * An abstraction of crossover policy for list chromosomes.
+ * @param phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractListChromosomeCrossoverPolicy {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public ChromosomePair crossover(final Chromosome first, final Chromosome second) {
+ // check for validity.
+ checkValidity(first, second);
+
+ final AbstractListChromosome first, final Chromosome second) {
+ if (!(first instanceof AbstractListChromosome, ?> && second instanceof AbstractListChromosome, ?>)) {
+ throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME);
+ }
+ final AbstractListChromosome mate(AbstractListChromosome phenotype of chromosome
+ * @since 2.0
+ */
+public interface CrossoverPolicy {
+
+ /**
+ * Perform a crossover operation on the given chromosomes.
+ *
+ * @param first the first chromosome.
+ * @param second the second chromosome.
+ * @param crossoverRate the probability of crossover
+ * @return the pair of new chromosomes that resulted from the crossover.
+ */
+ ChromosomePair crossover(Chromosome first, Chromosome second, double crossoverRate);
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java
new file mode 100644
index 0000000000..c0a1678e8b
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.ga.crossover;
+
+import java.util.ArrayList;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.math4.ga.chromosome.AbstractListChromosome;
+import org.apache.commons.math4.ga.chromosome.ChromosomePair;
+import org.apache.commons.math4.ga.utils.RandomGenerator;
+
+/**
+ * Cycle Crossover [CX] builds offspring from ordered chromosomes by
+ * identifying cycles between two parent chromosomes. To form the children, the
+ * cycles are copied from the respective parents.
+ *
+ * To form a cycle the following procedure is applied:
+ * phenotype of chromosome
+ * @since 3.1
+ */
+public class CycleCrossover mate(final AbstractListChromosome phenotype of chromosome
+ * @since 3.1
+ */
+public class NPointCrossover
+ * Note: the number of crossover points must be <
+ * mate(final AbstractListChromosome phenotype of chromosome
+ * @since 2.0
+ *
+ */
+public class OnePointCrossover mate(final AbstractListChromosome
+ * This policy works by applying the following rules:
+ *
+ * Example (random sublist from index 3 to 7, underlined):
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see
+ * Order 1 Crossover Operator
+ *
+ * @param phenotype of chromosome
+ * @since 3.1
+ */
+public class OrderedCrossover mate(final AbstractListChromosome
+ * This crossover policy evaluates each gene of the parent chromosomes by
+ * choosing a uniform random number {@code p} in the range [0, 1]. If {@code p}
+ * < {@code ratio}, the parent genes are swapped. This means with a ratio of
+ * 0.7, 30% of the genes from the first parent and 70% from the second parent
+ * will be selected for the first offspring (and vice versa for the second
+ * offspring).
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see Crossover
+ * techniques (Wikipedia)
+ * @see Crossover
+ * (Obitko.com)
+ * @see Uniform
+ * crossover
+ * @param phenotype of chromosome
+ * @since 3.1
+ */
+public class UniformCrossover mate(final AbstractListChromosomethis
is, with a
+ * given arrayRepresentation
. This is needed in crossover and
+ * mutation operators, where we need a new instance of the same class, but with
+ * different array representation.
+ * representation
can represent a valid chromosome.
+ */
+ private void checkValidity() {
+ ValidationUtils.checkForMinMax(min, max);
+ for (int i : getRepresentation()) {
+ if (i < min || i >= max) {
+ throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IntegralValuedChromosomerepresentation
can represent a valid chromosome.
+ */
+ private void checkValidity() {
+ ValidationUtils.checkForMinMax(min, max);
+ for (double i : getRepresentation()) {
+ if (i < min || i >= max) {
+ throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RealValuedChromosomemaxTime
value.
+ * Once the elapsed time reaches the configured maxTime
value,
+ * {@link #isSatisfied(Population)} returns true.
+ *
+ * @param true
IFF the maximum allowed time period has elapsed
+ */
+ @Override
+ public boolean isSatisfied(Populationtrue
IFF the maximum number of generations has been
+ * exceeded
+ */
+ @Override
+ public boolean isSatisfied(Populationtrue
if this stopping condition is met by the given
+ * population, false
otherwise.
+ */
+ boolean isSatisfied(Population
+ *
+ * The indices that form a cycle are then used to form the children in
+ * alternating order, i.e. in cycle 1, the genes of parent 1 are copied to child
+ * 1, while in cycle 2 the genes of parent 1 are copied to child 2, and so forth
+ * ...
+ *
+ * Example (zero-start cycle):
+ *
+ * p1 = (8 4 7 3 6 2 5 1 9 0) X c1 = (8 1 2 3 4 5 6 7 9 0)
+ * p2 = (0 1 2 3 4 5 6 7 8 9) X c2 = (0 4 7 3 6 2 5 1 8 9)
+ *
+ * cycle 1: 8 0 9
+ * cycle 2: 4 1 7 2 5 6
+ * cycle 3: 3
+ *
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see
+ * Cycle Crossover Operator
+ * @param
+ * -C- denotes a crossover point
+ * -C- -C- -C- -C-
+ * p1 = (1 0 | 1 0 0 1 | 0 1 1) X p2 = (0 1 | 1 0 1 0 | 1 1 1)
+ * \----/ \-------/ \-----/ \----/ \--------/ \-----/
+ * || (*) || || (**) ||
+ * VV (**) VV VV (*) VV
+ * /----\ /--------\ /-----\ /----\ /--------\ /-----\
+ * c1 = (1 0 | 1 0 1 0 | 0 1 1) X c2 = (0 1 | 1 0 0 1 | 0 1 1)
+ *
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @param chromosome length - 1
. This condition can only be checked at
+ * runtime, as the chromosome length is not known in advance.
+ *
+ * @param crossoverPoints the number of crossover points
+ */
+ public NPointCrossover(final int crossoverPoints) {
+ if (crossoverPoints <= 0) {
+ throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, crossoverPoints);
+ }
+ this.crossoverPoints = crossoverPoints;
+ }
+
+ /**
+ * Returns the number of crossover points used by this {@link CrossoverPolicy}.
+ *
+ * @return the number of crossover points
+ */
+ public int getCrossoverPoints() {
+ return crossoverPoints;
+ }
+
+ /**
+ * Performs a N-point crossover. N random crossover points are selected and are
+ * used to divide the parent chromosomes into segments. The segments are copied
+ * in alternate order from the two parents to the corresponding child
+ * chromosomes.
+ *
+ * Example (2-point crossover):
+ *
+ * -C- denotes a crossover point
+ * -C- -C- -C- -C-
+ * p1 = (1 0 | 1 0 0 1 | 0 1 1) X p2 = (0 1 | 1 0 1 0 | 1 1 1)
+ * \----/ \-------/ \-----/ \----/ \--------/ \-----/
+ * || (*) || || (**) ||
+ * VV (**) VV VV (*) VV
+ * /----\ /--------\ /-----\ /----\ /--------\ /-----\
+ * c1 = (1 0 | 1 0 1 0 | 0 1 1) X c2 = (0 1 | 1 0 0 1 | 0 1 1)
+ *
+ *
+ * @param first first parent (p1)
+ * @param second second parent (p2)
+ * @return pair of two children (c1,c2)
+ */
+ @Override
+ protected ChromosomePair
+ * -C- denotes a crossover point
+ * -C- -C-
+ * p1 = (1 0 1 0 0 1 | 0 1 1) X p2 = (0 1 1 0 1 0 | 1 1 1)
+ * \------------/ \-----/ \------------/ \-----/
+ * || (*) || (**)
+ * VV (**) VV (*)
+ * /------------\ /-----\ /------------\ /-----\
+ * c1 = (1 0 1 0 0 1 | 1 1 1) X c2 = (0 1 1 0 1 0 | 0 1 1)
+ *
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @param
+ * -C- denotes a crossover point
+ * -C- -C-
+ * p1 = (1 0 1 0 0 1 | 0 1 1) X p2 = (0 1 1 0 1 0 | 1 1 1)
+ * \------------/ \-----/ \------------/ \-----/
+ * || (*) || (**)
+ * VV (**) VV (*)
+ * /------------\ /-----\ /------------\ /-----\
+ * c1 = (1 0 1 0 0 1 | 1 1 1) X c2 = (0 1 1 0 1 0 | 0 1 1)
+ *
+ *
+ * @param first first parent (p1)
+ * @param second second parent (p2)
+ * @return pair of two children (c1,c2)
+ */
+ @Override
+ protected ChromosomePair
+ *
+ *
+ * p1 = (8 4 7 3 6 2 5 1 9 0) X c1 = (0 4 7 3 6 2 5 1 8 9)
+ * --------- ---------
+ * p2 = (0 1 2 3 4 5 6 7 8 9) X c2 = (8 1 2 3 4 5 6 7 9 0)
+ *
+ *