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..4f61ace342 --- /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 + */ + public 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..9e1676d4f7
--- /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 4.0
+ */
+public class BinaryChromosome extends AbstractChromosome {
+
+ /**
+ * maximum allowed length of binary chromosome.
+ */
+ public static final long MAX_LENGTH = Integer.MAX_VALUE;
+
+ /**
+ * length of binary chromosome.
+ */
+ private final long length;
+
+ /**
+ * binary representation of chromosome.
+ */
+ private final long[] representation;
+
+ /**
+ * @param representation Internal representation of chromosome.
+ * @param length length of chromosome
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link Decoder}
+ */
+ public BinaryChromosome(List fitnessFunction,
+ Decoder decoder) {
+ super(fitnessFunction, decoder);
+ Objects.requireNonNull(representation);
+ checkMaximumLength(length);
+ this.length = length;
+ this.representation = new long[representation.size()];
+ for (int i = 0; i < representation.size(); i++) {
+ this.representation[i] = representation.get(i);
+ }
+ }
+
+ /**
+ * @param representation Internal representation of chromosome.
+ * @param length length of chromosome
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link Decoder}
+ */
+ public BinaryChromosome(Long[] representation,
+ long length,
+ FitnessFunction fitnessFunction,
+ Decoder decoder) {
+ super(fitnessFunction, decoder);
+ Objects.requireNonNull(representation);
+ checkMaximumLength(length);
+ this.length = length;
+ this.representation = new long[representation.length];
+ for (int i = 0; i < representation.length; i++) {
+ this.representation[i] = representation[i];
+ }
+ }
+
+ /**
+ * @param inputRepresentation Internal representation of chromosome.
+ * @param length length of chromosome
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link Decoder}
+ */
+ public BinaryChromosome(long[] inputRepresentation,
+ long length,
+ FitnessFunction fitnessFunction,
+ Decoder decoder) {
+ super(fitnessFunction, decoder);
+ Objects.requireNonNull(inputRepresentation);
+ checkMaximumLength(length);
+ if (length <= (inputRepresentation.length - 1) * Long.SIZE || length > inputRepresentation.length * Long.SIZE) {
+ throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT,
+ "provided length does not match expected representation");
+ }
+ this.length = length;
+ this.representation = new long[inputRepresentation.length];
+ System.arraycopy(inputRepresentation, 0, representation, 0, inputRepresentation.length);
+ }
+
+ /**
+ * @param representation Internal representation of chromosome.
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link Decoder}
+ */
+ public BinaryChromosome(String representation, FitnessFunction fitnessFunction, Decoder decoder) {
+ super(fitnessFunction, decoder);
+ Objects.requireNonNull(representation);
+ this.length = representation.length();
+ this.representation = encode(representation);
+ }
+
+ /**
+ * Checks the input chromosome length against predefined maximum length.
+ * @param chromosomeLength input chromsome length
+ */
+ protected void checkMaximumLength(long chromosomeLength) {
+ if (chromosomeLength > MAX_LENGTH) {
+ throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT,
+ "length exceeded the max length " + MAX_LENGTH);
+ }
+ }
+
+ /**
+ * Validates the string representation.
+ * @param stringRepresentation binary string representation of chromosome
+ */
+ private void validateStringRepresentation(String stringRepresentation) {
+ char allele = '\0';
+ final int chromosomeLength = stringRepresentation.length();
+ for (int i = 0; i < chromosomeLength; i++) {
+ if ((allele = stringRepresentation.charAt(i)) != '0' && allele != '1') {
+ throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT,
+ "Only 0 or 1 are acceptable as characters.");
+ }
+ }
+ }
+
+ /**
+ * encodes the binary string representation as an array of long.
+ * @param stringRepresentation binary string
+ * @return encoded representation
+ */
+ private long[] encode(String stringRepresentation) {
+ validateStringRepresentation(stringRepresentation);
+ final int chromosomeLength = stringRepresentation.length();
+ final int arraySize = (int) Math.ceil(chromosomeLength / (double) Long.SIZE);
+ final long[] encodedRepresentation = new long[arraySize];
+ final int offset = (int) (chromosomeLength % Long.SIZE == 0 ? 0 : Long.SIZE - chromosomeLength % Long.SIZE);
+ encodedRepresentation[0] = Long.parseUnsignedLong(stringRepresentation.substring(0, Long.SIZE - offset), 2);
+ for (int i = Long.SIZE - offset, j = 1; i < chromosomeLength; i += Long.SIZE, j++) {
+ encodedRepresentation[j] = Long.parseUnsignedLong(stringRepresentation.substring(i, i + Long.SIZE), 2);
+ }
+ return encodedRepresentation;
+ }
+
+ /**
+ * Returns the chromosome length.
+ * @return length
+ */
+ public long getLength() {
+ return length;
+ }
+
+ /**
+ * Returns the binary representation.
+ * @return representation
+ */
+ public long[] getRepresentation() {
+ final long[] clonedRepresentation = new long[representation.length];
+ System.arraycopy(representation, 0, clonedRepresentation, 0, representation.length);
+ return clonedRepresentation;
+ }
+
+ /**
+ * Returns the binary string representation of the chromosome.
+ * @return the string representation
+ */
+ public String getStringRepresentation() {
+ if (length > Integer.MAX_VALUE) {
+ throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, length);
+ }
+ return getStringRepresentation(0, length);
+ }
+
+ /**
+ * Returns the binary string representation of the chromosome alleles from
+ * start(inclusive) to end(exclusive) index.
+ * @param start start allele/gene index(inclusive)
+ * @param end end allele/gene index(exclusive)
+ * @return the string representation
+ */
+ public String getStringRepresentation(long start, long end) {
+ if (start >= end) {
+ throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT,
+ "start " + start + " is greater than end " + end);
+ }
+ if (end - start > Integer.MAX_VALUE) {
+ throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, end - start);
+ }
+ final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE);
+ final long offsettedStart = offset + start;
+ final long offsettedEnd = offset + end;
+ final int startAlleleBlockIndex = (int) (offsettedStart / Long.SIZE);
+ final int endAlleleBlockIndex = (int) (offsettedEnd / Long.SIZE);
+ final int startAlleleElementIndex = (int) (offsettedStart % Long.SIZE);
+ final int endAlleleElementIndex = (int) (offsettedEnd % Long.SIZE);
+
+ if (startAlleleBlockIndex == endAlleleBlockIndex) {
+ return getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex,
+ endAlleleElementIndex);
+ } else {
+ final StringBuilder allelesStrRepresentation = new StringBuilder();
+
+ // extract string representation of first allele block.
+ allelesStrRepresentation
+ .append(getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex));
+
+ // extract string representation of all allele blocks except first and last.
+ for (int i = startAlleleBlockIndex + 1; i < endAlleleBlockIndex; i++) {
+ allelesStrRepresentation.append(getAlleleBlockString(i));
+ }
+
+ // extract string representation from last allele block if end allele index !=
+ // 0.
+ if (endAlleleElementIndex != 0) {
+ allelesStrRepresentation
+ .append(getAlleleBlockString(endAlleleBlockIndex).substring(0, endAlleleElementIndex));
+ }
+
+ return allelesStrRepresentation.toString();
+ }
+ }
+
+ /**
+ * Returns the allele block as binary string representation.
+ * @param alleleBlockIndex index of allele block i.e. array element
+ * @return the string representation
+ */
+ private String getAlleleBlockString(final int alleleBlockIndex) {
+ return prepareZeroPrefix(representation[alleleBlockIndex] == 0 ? Long.SIZE - 1 :
+ Long.numberOfLeadingZeros(representation[alleleBlockIndex])) +
+ Long.toUnsignedString(representation[alleleBlockIndex], 2);
+ }
+
+ /**
+ * Prepares zero prefix for binary chromosome.
+ * @param count number of zeros
+ * @return prefix
+ */
+ private String prepareZeroPrefix(int count) {
+ final StringBuilder zeroPrefix = new StringBuilder();
+ for (int i = 0; i < count; i++) {
+ zeroPrefix.append('0');
+ }
+ return zeroPrefix.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FitnessFunction getFitnessFunction() {
+ return super.getFitnessFunction();
+ }
+
+ /**
+ * Creates a new chromosome with provided parameters.
+ * @param chromosomeRepresentation the representation
+ * @param chromosomeLength length of chromosome
+ * @return chromosome
+ */
+ public BinaryChromosome newChromosome(long[] chromosomeRepresentation, long chromosomeLength) {
+ return new BinaryChromosome (chromosomeRepresentation, chromosomeLength, getFitnessFunction(), getDecoder());
+ }
+
+ /**
+ * Creates an instance of Binary Chromosome with random binary representation.
+ * @param phenotype fo chromosome
+ * @param length length of chromosome
+ * @param fitnessFunction The {@link FitnessFunction}
+ * @param decoder The {@link Decoder}
+ * @return a binary chromosome
+ */
+ public static BinaryChromosome randomChromosome(int length,
+ FitnessFunction fitnessFunction,
+ Decoder decoder) {
+ return new BinaryChromosome (ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length,
+ fitnessFunction, decoder);
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java
new file mode 100644
index 0000000000..b29461cba5
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * This abstraction represents a chromosome.
+ * @param 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..91fcfd25c3
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java
@@ -0,0 +1,136 @@
+/*
+ * 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.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..c56434d71b
--- /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.RandomProviderManager;
+
+/**
+ * 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 (RandomProviderManager.getRandomProvider().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..1d0da031e8
--- /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.RandomProviderManager;
+
+/**
+ * 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 the phenotype
+ */
+public class OnePointBinaryCrossover extends AbstractChromosomeCrossoverPolicy {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ChromosomePair crossover(Chromosome first, Chromosome second) {
+
+ if (!(first instanceof BinaryChromosome> && second instanceof BinaryChromosome>)) {
+ throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME);
+ }
+ final BinaryChromosome firstChromosome = (BinaryChromosome ) first;
+ final BinaryChromosome secondChromosome = (BinaryChromosome ) second;
+
+ final long alleleCount = firstChromosome.getLength();
+
+ // array representations of the parents
+ final long[] parent1Rep = firstChromosome.getRepresentation();
+ final long[] parent2Rep = secondChromosome.getRepresentation();
+
+ // and of the children
+ final long[] child1Rep = new long[parent1Rep.length];
+ final long[] child2Rep = new long[parent2Rep.length];
+
+ // select a crossover point at random (0 and length makes no sense)
+ final long crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextLong(alleleCount - 1));
+
+ final int offset = (int) (alleleCount % Long.SIZE == 0 ? 0 : Long.SIZE - alleleCount % Long.SIZE);
+ final long offsettedCrossoverIndex = crossoverIndex + offset;
+
+ final int crossoverBlockIndex = (int) (offsettedCrossoverIndex / Long.SIZE);
+ final int crossoverBlockAlleleIndex = (int) offsettedCrossoverIndex % Long.SIZE;
+
+ if (crossoverBlockAlleleIndex == 0) {
+ // if the offsetted-crossover index is divisible by
+ // Long.SIZE then first copy all
+ // elements of previous array elements.
+ for (int i = 0; i < crossoverBlockIndex; i++) {
+ child1Rep[i] = parent1Rep[i];
+ child2Rep[i] = parent2Rep[i];
+ }
+ // copy all elements from crossover block index.
+ for (int i = crossoverBlockIndex; i < parent1Rep.length; i++) {
+ child1Rep[i] = parent2Rep[i];
+ child2Rep[i] = parent1Rep[i];
+ }
+ } else {
+ // copy all parent array elements to child till crossover block index - 1.
+ for (int i = 0; i < crossoverBlockIndex; i++) {
+ child1Rep[i] = parent1Rep[i];
+ child2Rep[i] = parent2Rep[i];
+ }
+ // do exchange of alleles of the array element indexed at crossover block index.
+ final long parent1CrossoverBlockRep = parent1Rep[crossoverBlockIndex];
+ final long parent2CrossoverBlockRep = parent2Rep[crossoverBlockIndex];
+ final long leftMask = Long.MIN_VALUE >> crossoverBlockAlleleIndex - 1;
+ final long rightMask = crossoverBlockAlleleIndex != 1 ?
+ (long) Math.pow(2, Long.SIZE - crossoverBlockAlleleIndex) - 1 :
+ Long.MAX_VALUE;
+
+ final long child1CrossoverBlockRep = (parent1CrossoverBlockRep & leftMask) |
+ (parent2CrossoverBlockRep & rightMask);
+ final long child2CrossoverBlockRep = (parent2CrossoverBlockRep & leftMask) |
+ (parent1CrossoverBlockRep & rightMask);
+
+ child1Rep[crossoverBlockIndex] = child1CrossoverBlockRep;
+ child2Rep[crossoverBlockIndex] = child2CrossoverBlockRep;
+
+ // Copy all the alleles which belong to array elements having index >
+ // crossover block index.
+ if (crossoverBlockIndex < parent1Rep.length - 1) {
+ for (int i = crossoverBlockIndex + 1; i < parent1Rep.length; i++) {
+ child1Rep[i] = parent2Rep[i];
+ child2Rep[i] = parent1Rep[i];
+ }
+ }
+ }
+
+ final BinaryChromosome childChromosome1 = new BinaryChromosome<>(child1Rep, alleleCount,
+ firstChromosome.getFitnessFunction(), firstChromosome.getDecoder());
+ final BinaryChromosome childChromosome2 = new BinaryChromosome<>(child2Rep, alleleCount,
+ secondChromosome.getFitnessFunction(), secondChromosome.getDecoder());
+
+ return new ChromosomePair<>(childChromosome1, childChromosome2);
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java
new file mode 100644
index 0000000000..2c6da54ccb
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java
@@ -0,0 +1,102 @@
+/*
+ * 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.List;
+
+import org.apache.commons.math4.ga.chromosome.AbstractListChromosome;
+import org.apache.commons.math4.ga.chromosome.ChromosomePair;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+
+/**
+ * One point crossover policy. A random crossover point is selected and the
+ * first part from each parent is copied to the corresponding child, and the
+ * second parts are copied crosswise.
+ *
+ * Example:
+ * 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 AbstractListChromosome phenotype of chromosome
+ */
+public class AdaptiveLinearAverageRankBasedCrossoverRateGenerator implements CrossoverRateGenerator {
+
+ /** minimum crossover rate. **/
+ private final double minimumRate;
+
+ /** maximum crossover rate. **/
+ private final double maximumRate;
+
+ /**
+ * @param minimumRate minimum crossover rate
+ * @param maximumRate maximum crossover rate
+ */
+ public AdaptiveLinearAverageRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) {
+ this.maximumRate = maximumRate;
+ this.minimumRate = minimumRate;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double generate(Chromosome first,
+ Chromosome second,
+ PopulationStatisticalSummary populationStats,
+ int generation) {
+ final int averageRank = (populationStats.findRank(first) + populationStats.findRank(second)) / 2;
+ return minimumRate +
+ (maximumRate - minimumRate) * (1.0 - (double) averageRank / (populationStats.getPopulationSize() - 1));
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java
new file mode 100644
index 0000000000..c03f488f5f
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.rategenerator;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+
+/**
+ * Generates crossover rate based on linear function of relative maximum rank of
+ * input chromosomes in population.
+ * @param phenotype of chromosome
+ */
+public class AdaptiveLinearMaximumRankBasedCrossoverRateGenerator implements CrossoverRateGenerator {
+
+ /** minimum crossover rate. **/
+ private final double minimumRate;
+
+ /** maximum crossover rate. **/
+ private final double maximumRate;
+
+ /**
+ * @param minimumRate minimum crossover rate
+ * @param maximumRate maximum crossover rate
+ */
+ public AdaptiveLinearMaximumRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) {
+ this.maximumRate = maximumRate;
+ this.minimumRate = minimumRate;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double generate(Chromosome first,
+ Chromosome second,
+ PopulationStatisticalSummary populationStats,
+ int generation) {
+ final int maximumRank = Math.max(populationStats.findRank(first), populationStats.findRank(second));
+ return minimumRate +
+ (maximumRate - minimumRate) * (1.0 - (double) maximumRank / (populationStats.getPopulationSize() - 1));
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java
new file mode 100644
index 0000000000..2f77ce12f0
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java
@@ -0,0 +1,50 @@
+/*
+ * 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.rategenerator;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+
+/**
+ * This class represents a constant crossover rate generator.
+ * @param phenotype of chromosome
+ */
+public class ConstantCrossoverRateGenerator implements CrossoverRateGenerator {
+
+ /** the fixed value of crossover rate. **/
+ private final double crossoverRate;
+
+ /**
+ * @param crossoverRate crossover rate
+ */
+ public ConstantCrossoverRateGenerator(double crossoverRate) {
+ this.crossoverRate = crossoverRate;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double generate(Chromosome first,
+ Chromosome second,
+ PopulationStatisticalSummary populationStats,
+ int generation) {
+ return crossoverRate;
+ }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java
new file mode 100644
index 0000000000..438aeeda7c
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.rategenerator;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+
+/**
+ * This abstraction represents crossover rate generator.
+ * @param phenotype of chromosome
+ */
+public interface CrossoverRateGenerator {
+
+ /**
+ * Generates crossover rate.
+ * @param first first chromosome
+ * @param second second chromosome
+ * @param populationStats population statistics summary
+ * @param generation generation number
+ * @return crossover rate rate of crossover
+ */
+ double generate(Chromosome first,
+ Chromosome second,
+ PopulationStatisticalSummary populationStats,
+ int generation);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java
new file mode 100644
index 0000000000..886eab7704
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/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.crossover.rategenerator;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java
new file mode 100644
index 0000000000..96a1920533
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.decoder;
+
+import org.apache.commons.math4.ga.chromosome.AbstractListChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+
+/**
+ * An abstract Decoder of ListChromosome.
+ * @param phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractListChromosomeDecoder {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public P decode(Chromosome chromosome) {
+ checkValidity(chromosome);
+
+ return decode((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() {
+ if (min >= max) {
+ throw new GeneticException(GeneticException.TOO_LARGE, 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() {
+ if (min >= max) {
+ throw new GeneticException(GeneticException.TOO_LARGE, 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)
+ *
+ *