You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2022/01/25 13:29:12 UTC

[commons-math] 02/06: MATH-1563: Introducing new implementation of GA functionality (WIP).

This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch feature__MATH-1563__genetic_algorithm
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 5d71c14523ed9a033abeeca6b00db4759189adf7
Author: avbasak1 <av...@in.ibm.com>
AuthorDate: Sun Jan 2 18:54:37 2022 +0530

    MATH-1563: Introducing new implementation of GA functionality (WIP).
---
 .../examples-ga/examples-ga-math-functions/pom.xml |  47 ++++
 .../mathfunctions/dimension2/Dim2GraphPlotter.java | 145 ++++++++++
 .../dimension2/Dimension2Coordinate.java           |  64 +++++
 .../dimension2/Dimension2Decoder.java              |  49 ++++
 .../dimension2/Dimension2FitnessFunction.java      |  40 +++
 .../Dimension2FunctionAdaptiveOptimizer.java       | 114 ++++++++
 .../dimension2/Dimension2FunctionOptimizer.java    | 114 ++++++++
 .../legacy/Dimension2FunctionOptimizerLegacy.java  | 103 +++++++
 .../dimension2/legacy/LegacyBinaryChromosome.java  |  65 +++++
 .../dimension2/legacy/UnchangedBestFitness.java    |  67 +++++
 .../dimension2/legacy/package-info.java            |  20 ++
 .../ga/mathfunctions/dimension2/package-info.java  |  20 ++
 .../mathfunctions/dimensionN/DimNGraphPlotter.java | 145 ++++++++++
 .../dimensionN/DimensionNCoordinate.java           |  54 ++++
 .../dimensionN/DimensionNDecoder.java              |  52 ++++
 .../dimensionN/DimensionNFitnessFunction.java      |  42 +++
 .../DimensionNFunctionAdaptiveOptimizer.java       | 114 ++++++++
 .../dimensionN/DimensionNFunctionOptimizer.java    | 113 ++++++++
 .../ga/mathfunctions/dimensionN/package-info.java  |  20 ++
 .../examples/ga/mathfunctions/utils/Constants.java |  55 ++++
 .../ga/mathfunctions/utils/package-info.java       |  20 ++
 .../examples-ga/examples-ga-tsp/pom.xml            |  52 ++++
 .../math4/examples/ga/tsp/TSPFitnessFunction.java  |  52 ++++
 .../math4/examples/ga/tsp/TSPOptimizer.java        | 116 ++++++++
 .../math4/examples/ga/tsp/commons/City.java        |  77 ++++++
 .../examples/ga/tsp/commons/DistanceMatrix.java    |  71 +++++
 .../examples/ga/tsp/commons/package-info.java      |  20 ++
 .../examples/ga/tsp/legacy/TSPChromosome.java      |  76 ++++++
 .../examples/ga/tsp/legacy/TSPOptimizerLegacy.java |  99 +++++++
 .../ga/tsp/legacy/UnchangedBestFitness.java        |  67 +++++
 .../math4/examples/ga/tsp/legacy/package-info.java |  20 ++
 .../math4/examples/ga/tsp/package-info.java        |  20 ++
 .../math4/examples/ga/tsp/utils/Constants.java     |  65 +++++
 .../math4/examples/ga/tsp/utils/GraphPlotter.java  | 146 ++++++++++
 .../math4/examples/ga/tsp/utils/package-info.java  |  20 ++
 commons-math-examples/examples-ga/pom.xml          |  56 ++++
 commons-math-examples/pom.xml                      |   1 +
 commons-math-ga/pom.xml                            |  54 ++++
 .../commons/math4/ga/AbstractGeneticAlgorithm.java | 180 ++++++++++++
 .../commons/math4/ga/AdaptiveGeneticAlgorithm.java | 164 +++++++++++
 .../apache/commons/math4/ga/GeneticAlgorithm.java  | 171 ++++++++++++
 .../math4/ga/chromosome/AbstractChromosome.java    | 148 ++++++++++
 .../ga/chromosome/AbstractListChromosome.java      | 125 +++++++++
 .../math4/ga/chromosome/BinaryChromosome.java      | 303 +++++++++++++++++++++
 .../commons/math4/ga/chromosome/Chromosome.java    |  48 ++++
 .../math4/ga/chromosome/ChromosomePair.java        |  66 +++++
 .../ga/chromosome/IntegralValuedChromosome.java    | 136 +++++++++
 .../math4/ga/chromosome/RealValuedChromosome.java  | 160 +++++++++++
 .../commons/math4/ga/chromosome/package-info.java  |  20 ++
 .../math4/ga/convergence/FixedElapsedTime.java     |  80 ++++++
 .../math4/ga/convergence/FixedGenerationCount.java |  76 ++++++
 .../math4/ga/convergence/StoppingCondition.java    |  38 +++
 .../math4/ga/convergence/UnchangedBestFitness.java |  72 +++++
 .../math4/ga/convergence/UnchangedMeanFitness.java |  85 ++++++
 .../commons/math4/ga/convergence/package-info.java |  20 ++
 .../AbstractChromosomeCrossoverPolicy.java         |  53 ++++
 .../AbstractListChromosomeCrossoverPolicy.java     |  76 ++++++
 .../math4/ga/crossover/CrossoverPolicy.java        |  39 +++
 .../commons/math4/ga/crossover/CycleCrossover.java | 168 ++++++++++++
 .../math4/ga/crossover/NPointCrossover.java        | 154 +++++++++++
 .../ga/crossover/OnePointBinaryCrossover.java      | 115 ++++++++
 .../math4/ga/crossover/OnePointCrossover.java      | 102 +++++++
 .../math4/ga/crossover/OrderedCrossover.java       | 131 +++++++++
 .../math4/ga/crossover/UniformCrossover.java       | 123 +++++++++
 .../commons/math4/ga/crossover/package-info.java   |  20 ++
 ...nearAverageRankBasedCrossoverRateGenerator.java |  58 ++++
 ...nearMaximumRankBasedCrossoverRateGenerator.java |  58 ++++
 .../ConstantCrossoverRateGenerator.java            |  50 ++++
 .../rategenerator/CrossoverRateGenerator.java      |  42 +++
 .../ga/crossover/rategenerator/package-info.java   |  20 ++
 .../ga/decoder/AbstractListChromosomeDecoder.java  |  59 ++++
 .../apache/commons/math4/ga/decoder/Decoder.java   |  35 +++
 .../commons/math4/ga/decoder/RandomKeyDecoder.java |  75 +++++
 .../decoder/TransparentListChromosomeDecoder.java  |  39 +++
 .../commons/math4/ga/decoder/package-info.java     |  20 ++
 .../commons/math4/ga/fitness/FitnessFunction.java  |  34 +++
 .../commons/math4/ga/fitness/package-info.java     |  20 ++
 .../ga/internal/exception/GeneticException.java    | 120 ++++++++
 .../math4/ga/internal/exception/package-info.java  |  20 ++
 .../stats/PopulationStatisticalSummaryImpl.java    | 177 ++++++++++++
 .../math4/ga/internal/stats/package-info.java      |  20 ++
 .../math4/ga/listener/ConvergenceListener.java     |  37 +++
 .../ga/listener/ConvergenceListenerRegistry.java   |  91 +++++++
 .../ga/listener/PopulationStatisticsLogger.java    |  49 ++++
 .../commons/math4/ga/listener/package-info.java    |  20 ++
 .../AbstractListChromosomeMutationPolicy.java      | 101 +++++++
 .../commons/math4/ga/mutation/BinaryMutation.java  | 135 +++++++++
 .../math4/ga/mutation/IntegralValuedMutation.java  |  88 ++++++
 .../commons/math4/ga/mutation/MutationPolicy.java  |  36 +++
 .../math4/ga/mutation/RealValuedMutation.java      |  97 +++++++
 .../commons/math4/ga/mutation/package-info.java    |  20 ++
 .../AdaptiveLinearMutationRateGenerator.java       |  54 ++++
 .../ConstantMutationRateGenerator.java             |  47 ++++
 .../rategenerator/MutationRateGenerator.java       |  38 +++
 .../ga/mutation/rategenerator/package-info.java    |  20 ++
 .../org/apache/commons/math4/ga/package-info.java  |  20 ++
 .../math4/ga/population/ListPopulation.java        | 220 +++++++++++++++
 .../commons/math4/ga/population/Population.java    |  59 ++++
 .../commons/math4/ga/population/package-info.java  |  20 ++
 .../math4/ga/selection/SelectionPolicy.java        |  35 +++
 .../math4/ga/selection/TournamentSelection.java    | 108 ++++++++
 .../commons/math4/ga/selection/package-info.java   |  20 ++
 .../ga/stats/PopulationStatisticalSummary.java     |  67 +++++
 .../commons/math4/ga/stats/package-info.java       |  20 ++
 .../ga/utils/ChromosomeRepresentationUtils.java    | 211 ++++++++++++++
 .../math4/ga/utils/RandomProviderManager.java      |  47 ++++
 .../commons/math4/ga/utils/package-info.java       |  20 ++
 .../math4/ga/GeneticAlgorithmTestBinaryOneMax.java | 187 +++++++++++++
 .../math4/ga/GeneticAlgorithmTestPermutations.java | 144 ++++++++++
 .../ga/chromosome/AbstractChromosomeTest.java      |  65 +++++
 .../math4/ga/chromosome/BinaryChromosomeTest.java  |  92 +++++++
 .../math4/ga/chromosome/ChromosomePairTest.java    |  38 +++
 .../chromosome/IntegralValuedChromosomeTest.java   |  93 +++++++
 .../ga/chromosome/RealValuedChromosomeTest.java    |  75 +++++
 .../ga/convergencecond/FixedElapsedTimeTest.java   |  59 ++++
 .../convergencecond/FixedGenerationCountTest.java  |  48 ++++
 .../convergencecond/UnchangedBestFitnessTest.java  |  68 +++++
 .../convergencecond/UnchangedMeanFitnessTest.java  |  68 +++++
 .../AbstractChromosomeCrossoverPolicyTest.java     |  45 +++
 .../AbstractListChromosomeCrossoverPolicyTest.java |  77 ++++++
 .../math4/ga/crossover/CycleCrossoverTest.java     | 115 ++++++++
 .../math4/ga/crossover/NPointCrossoverTest.java    | 127 +++++++++
 .../ga/crossover/OnePointBinaryCrossoverTest.java  |  73 +++++
 .../math4/ga/crossover/OnePointCrossoverTest.java  |  66 +++++
 .../math4/ga/crossover/OrderedCrossoverTest.java   |  63 +++++
 .../math4/ga/crossover/UniformCrossoverTest.java   | 113 ++++++++
 ...MaximumRankBasedCrossoverRateGeneratorTest.java |  68 +++++
 .../decoder/AbstractListChromosomeDecoderTest.java |  44 +++
 .../math4/ga/decoder/RandomKeyDecoderTest.java     |  58 ++++
 .../TransparentListChromosomeDecoderTest.java      |  39 +++
 .../commons/math4/ga/dummy/DummyChromosome.java    |  27 ++
 .../math4/ga/dummy/DummyListChromosome.java        |  47 ++++
 .../math4/ga/dummy/DummyListChromosomeDecoder.java |  41 +++
 .../math4/ga/exception/GeneticExceptionTest.java   |  39 +++
 .../listener/ConvergenceListenerRegistryTest.java  | 107 ++++++++
 .../listener/PopulationStatisticsLoggerTest.java   |  45 +++
 .../AbstractListChromosomeMutationPolicyTest.java  |  44 +++
 .../math4/ga/mutation/BinaryMutationTest.java      |  90 ++++++
 .../ga/mutation/IntegralValuedMutationTest.java    |  85 ++++++
 .../math4/ga/mutation/RealValuedMutationTest.java  |  85 ++++++
 .../AdaptiveLinearMutationRateGeneratorTest.java   |  61 +++++
 .../ConstantMutationRateGeneratorTest.java         |  45 +++
 .../math4/ga/population/ListPopulationTest.java    | 202 ++++++++++++++
 .../ga/selection/TournamentSelectionTest.java      | 109 ++++++++
 .../utils/ChromosomeRepresentationUtilsTest.java   | 163 +++++++++++
 pom.xml                                            |   1 +
 .../resources/spotbugs/spotbugs-exclude-filter.xml |   6 +
 147 files changed, 10897 insertions(+)

diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml
new file mode 100644
index 0000000..d09ca3f
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>examples-ga</artifactId>
+        <version>4.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>examples-ga-math-functions</artifactId>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+
+        <!-- OSGi -->
+        <commons.osgi.symbolicName>org.apache.commons.math4.examples.ga.mathfunctions</commons.osgi.symbolicName>
+        <commons.osgi.export>org.apache.commons.math4.examples.ga.mathfunctions</commons.osgi.export>
+        <!-- Java 9+ -->
+        <commons.automatic.module.name>org.apache.commons.math4.examples.ga.mathfunctions</commons.automatic.module.name>
+        <!-- Workaround to avoid duplicating config files. -->
+        <math.parent.dir>${basedir}/../../..</math.parent.dir>
+
+        <uberjar.name>examples-ga-mathfunctions</uberjar.name>
+        <project.mainClass>org.apache.commons.math4.examples.ga.mathfunctions.Dimension2FunctionOptimizer</project.mainClass>
+        <slf4jVersion>1.7.32</slf4jVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>${slf4jVersion}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java
new file mode 100644
index 0000000..eca37e8
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java
@@ -0,0 +1,145 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2;
+
+import java.awt.BorderLayout;
+import java.util.List;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl;
+import org.apache.commons.math4.ga.listener.ConvergenceListener;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+/**
+ * This class represents the graph plotter during optimization.
+ */
+public class Dim2GraphPlotter extends JFrame implements ConvergenceListener<Dimension2Coordinate> {
+
+    /**
+     * Generated serialversionId.
+     */
+    private static final long serialVersionUID = -5683904006424006584L;
+
+    /** collection of 2-D series. **/
+    private final XYSeriesCollection dataset = new XYSeriesCollection();
+
+    /**
+     * constructor.
+     * @param plotSubject subject of plot
+     * @param xAxisLabel  x axis label
+     * @param yAxisLabel  y axis label
+     */
+    public Dim2GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) {
+        super(plotSubject);
+
+        final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel);
+        add(chartPanel, BorderLayout.CENTER);
+
+        setSize(640, 480);
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        setLocationRelativeTo(null);
+
+        setVisible(true);
+    }
+
+    /**
+     * Adds data point to graph.
+     * @param graphName  name of graph
+     * @param generation generation, to be plotted along x axis
+     * @param value      value, to be plotted along y axis
+     */
+    private void addDataPoint(String graphName, int generation, double value) {
+        XYSeries series = null;
+
+        if (!containsGraph(graphName)) {
+            series = new XYSeries(graphName);
+            dataset.addSeries(series);
+        } else {
+            series = dataset.getSeries(graphName);
+        }
+        series.add(generation, value);
+
+        setVisible(true);
+    }
+
+    /**
+     * Checks if the graph with the given name already exists.
+     * @param graphName name of the graph
+     * @return true/false
+     */
+    @SuppressWarnings("unchecked")
+    private boolean containsGraph(String graphName) {
+        final List<XYSeries> seriesList = dataset.getSeries();
+        if (seriesList == null || seriesList.isEmpty()) {
+            return false;
+        }
+        for (XYSeries series : seriesList) {
+            if (series.getKey().compareTo(graphName) == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Creates chart panel.
+     * @param chartTitle chart title
+     * @param xAxisLabel x axis label
+     * @param yAxisLabel y axis label
+     * @return panel
+     */
+    private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) {
+
+        final boolean showLegend = true;
+        final boolean createURL = false;
+        final boolean createTooltip = false;
+
+        final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset,
+                PlotOrientation.VERTICAL, showLegend, createTooltip, createURL);
+        final XYPlot plot = chart.getXYPlot();
+        final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
+
+        plot.setRenderer(renderer);
+
+        return new ChartPanel(chart);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void notify(int generation, Population<Dimension2Coordinate> population) {
+        PopulationStatisticalSummary<Dimension2Coordinate> populationStatisticalSummary =
+                new PopulationStatisticalSummaryImpl<>(population);
+        this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness());
+        this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness());
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java
new file mode 100644
index 0000000..307f1ef
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java
@@ -0,0 +1,64 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2;
+
+/**
+ * This class represents the coordinate of the problem domain i.e. the phenotype of chromosome.
+ */
+public class Dimension2Coordinate {
+
+    /** coordinate of first dimension. **/
+    private final double x;
+
+    /** coordinate of second dimension. **/
+    private final double y;
+
+    /**
+     * constructor.
+     * @param x coordinate of first dimension
+     * @param y coordinate of second dimension
+     */
+    public Dimension2Coordinate(double x, double y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    /**
+     * returns the coordinate of first dimension.
+     * @return coordinate of first dimension
+     */
+    public double getX() {
+        return x;
+    }
+
+    /**
+     * returns the coordinate of second dimension.
+     * @return coordinate of second dimension
+     */
+    public double getY() {
+        return y;
+    }
+
+    /**
+     * Returns a string representation of coordinate.
+     */
+    @Override
+    public String toString() {
+        return "Coordinate [x=" + x + ", y=" + y + "]";
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java
new file mode 100644
index 0000000..e8394d1
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java
@@ -0,0 +1,49 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2;
+
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.decoder.Decoder;
+
+/**
+ * Decoder to convert chromosome's binary genotype to phenotype
+ * {@link Dimension2Coordinate}.
+ */
+public class Dimension2Decoder implements Decoder<Dimension2Coordinate> {
+
+    /**
+     * Decode the binary representation of chromosome to
+     * {@link Dimension2Coordinate}.
+     * @param chromosome The {@link Chromosome}
+     */
+    @Override
+    public Dimension2Coordinate decode(Chromosome<Dimension2Coordinate> chromosome) {
+        final BinaryChromosome<Dimension2Coordinate> binaryChromosome =
+                (BinaryChromosome<Dimension2Coordinate>) chromosome;
+        final long alleles = binaryChromosome.getRepresentation()[0];
+
+        long mask1 = ~(Long.MAX_VALUE << 12);
+        long mask2 = ~(Long.MAX_VALUE << 24) ^ mask1;
+
+        final double x = (alleles & mask1) / 100d;
+        final double y = ((alleles & mask2) >> 12) / 100d;
+
+        return new Dimension2Coordinate(x, y);
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java
new file mode 100644
index 0000000..33e8f73
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java
@@ -0,0 +1,40 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2;
+
+import org.apache.commons.math4.ga.fitness.FitnessFunction;
+
+/**
+ * This class represents the mathematical fitness function for optimizing a 2
+ * dimension mathematical function.
+ */
+public class Dimension2FitnessFunction implements FitnessFunction<Dimension2Coordinate> {
+
+    /**
+     * Computes the fitness value based on the decoded chromosome.
+     * @param coordinate The {@link Dimension2Coordinate}
+     * @return the fitness value
+     */
+    @Override
+    public double compute(Dimension2Coordinate coordinate) {
+        return -Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .25) *
+                (Math.pow(Math.sin(50 * Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .1)),
+                        2) + 1);
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java
new file mode 100644
index 0000000..6579dda
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java
@@ -0,0 +1,114 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2;
+
+import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants;
+import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm;
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.convergence.UnchangedBestFitness;
+import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover;
+import org.apache.commons.math4.ga.crossover.rategenerator.ConstantCrossoverRateGenerator;
+import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry;
+import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger;
+import org.apache.commons.math4.ga.mutation.BinaryMutation;
+import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.selection.TournamentSelection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class represents an optimizer for a 2-dimensional math function using
+ * genetic algorithm.
+ */
+public class Dimension2FunctionAdaptiveOptimizer {
+
+    /** number of dimension. **/
+    private static final int DIMENSION = 2;
+
+    /** size of tournament. **/
+    private static final int TOURNAMENT_SIZE = 3;
+
+    /** instance of logger. **/
+    private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class);
+
+    /**
+     * Optimizes the 2-dimension fitness function.
+     * @param args arguments
+     */
+    public static void main(String[] args) {
+        final Population<Dimension2Coordinate> initPopulation = getInitialPopulation();
+
+        final Dimension2FunctionAdaptiveOptimizer optimizer = new Dimension2FunctionAdaptiveOptimizer();
+
+        final ConvergenceListenerRegistry<Dimension2Coordinate> convergenceListenerRegistry =
+                ConvergenceListenerRegistry.getInstance();
+        convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<Dimension2Coordinate>());
+        convergenceListenerRegistry
+                .addConvergenceListener(new Dim2GraphPlotter("Adaptive Convergence Stats", "generation", "fitness"));
+
+        optimizer.optimize(initPopulation);
+    }
+
+    private void optimize(Population<Dimension2Coordinate> initial) {
+
+        // initialize a new genetic algorithm
+        final AdaptiveGeneticAlgorithm<Dimension2Coordinate> ga = new AdaptiveGeneticAlgorithm<>(
+                new OnePointBinaryCrossover<Dimension2Coordinate>(), new ConstantCrossoverRateGenerator<>(1),
+                new BinaryMutation<Dimension2Coordinate>(),
+                new AdaptiveLinearMutationRateGenerator<>(Constants.AVERAGE_MUTATION_RATE / 2,
+                        Constants.AVERAGE_MUTATION_RATE * 2),
+                new TournamentSelection<>(TOURNAMENT_SIZE));
+
+        // stopping condition
+        final StoppingCondition<Dimension2Coordinate> stopCond = new UnchangedBestFitness<>(
+                Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS);
+
+        // run the algorithm
+        final Population<Dimension2Coordinate> finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        final Chromosome<Dimension2Coordinate> bestFinal = finalPopulation.getFittestChromosome();
+
+        logger.info("*********************************************");
+        logger.info("***********Optimization Result***************");
+
+        logger.info(bestFinal.toString());
+
+    }
+
+    /**
+     * Generates an initial population.
+     * @return initial population
+     */
+    private static Population<Dimension2Coordinate> getInitialPopulation() {
+        final Population<Dimension2Coordinate> population = new ListPopulation<>(
+                DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION);
+        final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction();
+        final Dimension2Decoder decoder = new Dimension2Decoder();
+        for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) {
+            population.addChromosome(BinaryChromosome.<Dimension2Coordinate>randomChromosome(
+                    DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder));
+        }
+        return population;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java
new file mode 100644
index 0000000..1d6a3fa
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java
@@ -0,0 +1,114 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2;
+
+import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants;
+import org.apache.commons.math4.ga.GeneticAlgorithm;
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.convergence.UnchangedBestFitness;
+import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover;
+import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry;
+import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger;
+import org.apache.commons.math4.ga.mutation.BinaryMutation;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.selection.TournamentSelection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class represents an optimizer for a 2-dimensional math function using
+ * genetic algorithm.
+ */
+public class Dimension2FunctionOptimizer {
+
+    /** number of dimension. **/
+    private static final int DIMENSION = 2;
+
+    /** size of tournament. **/
+    private static final int TOURNAMENT_SIZE = 3;
+
+    /** instance of logger. **/
+    private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class);
+
+    /**
+     * Optimizes the 2-dimension fitness function.
+     * @param args arguments
+     */
+    public static void main(String[] args) {
+        final Population<Dimension2Coordinate> initPopulation = getInitialPopulation();
+
+        final Dimension2FunctionOptimizer optimizer = new Dimension2FunctionOptimizer();
+
+        final ConvergenceListenerRegistry<Dimension2Coordinate> convergenceListenerRegistry =
+                ConvergenceListenerRegistry.getInstance();
+        convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<Dimension2Coordinate>());
+        convergenceListenerRegistry
+                .addConvergenceListener(new Dim2GraphPlotter("Convergence Stats", "generation", "fitness"));
+
+        optimizer.optimize(initPopulation);
+    }
+
+    /**
+     * Optimizes the population.
+     * @param initial The {@link Population}
+     */
+    public void optimize(Population<Dimension2Coordinate> initial) {
+
+        // initialize a new genetic algorithm
+        final GeneticAlgorithm<Dimension2Coordinate> ga = new GeneticAlgorithm<>(
+                new OnePointBinaryCrossover<Dimension2Coordinate>(), Constants.CROSSOVER_RATE,
+                new BinaryMutation<Dimension2Coordinate>(), Constants.AVERAGE_MUTATION_RATE,
+                new TournamentSelection<Dimension2Coordinate>(TOURNAMENT_SIZE), Constants.ELITISM_RATE);
+
+        // stopping condition
+        final StoppingCondition<Dimension2Coordinate> stopCond = new UnchangedBestFitness<>(
+                Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS);
+
+        // run the algorithm
+        final Population<Dimension2Coordinate> finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        final Chromosome<Dimension2Coordinate> bestFinal = finalPopulation.getFittestChromosome();
+
+        logger.info("*********************************************");
+        logger.info("***********Optimization Result***************");
+
+        logger.info(bestFinal.toString());
+
+    }
+
+    /**
+     * Generates an initial population.
+     * @return initial population
+     */
+    private static Population<Dimension2Coordinate> getInitialPopulation() {
+        final Population<Dimension2Coordinate> population = new ListPopulation<>(
+                DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION);
+        final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction();
+        final Dimension2Decoder decoder = new Dimension2Decoder();
+        for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) {
+            population.addChromosome(BinaryChromosome.<Dimension2Coordinate>randomChromosome(
+                    DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder));
+        }
+        return population;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java
new file mode 100644
index 0000000..b523d41
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java
@@ -0,0 +1,103 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2.legacy;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import org.apache.commons.math3.genetics.BinaryChromosome;
+import org.apache.commons.math3.genetics.BinaryMutation;
+import org.apache.commons.math3.genetics.Chromosome;
+import org.apache.commons.math3.genetics.ElitisticListPopulation;
+import org.apache.commons.math3.genetics.GeneticAlgorithm;
+import org.apache.commons.math3.genetics.OnePointCrossover;
+import org.apache.commons.math3.genetics.Population;
+import org.apache.commons.math3.genetics.StoppingCondition;
+import org.apache.commons.math3.genetics.TournamentSelection;
+import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+
+/**
+ * This class represents an optimizer for a 2-dimensional math function using
+ * the legacy genetic algorithm.
+ */
+public class Dimension2FunctionOptimizerLegacy {
+
+    /** number of dimension. **/
+    private static final int DIMENSION = 2;
+
+    /** size of tournament. **/
+    private static final int TOURNAMENT_SIZE = 3;
+
+    /**
+     * Optimizes the 2-dimensional fitness function.
+     * @param args arguments
+     */
+    public static void main(String[] args) {
+        final Population initPopulation = getInitialPopulation();
+        final Dimension2FunctionOptimizerLegacy simulation = new Dimension2FunctionOptimizerLegacy();
+
+        simulation.optimize(initPopulation);
+    }
+
+    /**
+     * Optimizes the initial population using legacy genetic algorithm.
+     * @param initial initial {@link Population}
+     */
+    public void optimize(Population initial) {
+
+        // initialize a new genetic algorithm
+        final GeneticAlgorithm geneticAlgorithm = new GeneticAlgorithm(new OnePointCrossover<>(),
+                Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE,
+                new TournamentSelection(TOURNAMENT_SIZE));
+
+        // stopping condition
+        final StoppingCondition stopCond = new UnchangedBestFitness(
+                Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS);
+
+        // run the algorithm
+        final Population finalPopulation = geneticAlgorithm.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        final Chromosome bestFinal = finalPopulation.getFittestChromosome();
+
+        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) {
+            writer.write("*********************************************");
+            writer.newLine();
+            writer.write("***********Optimization Result***************");
+            writer.write(bestFinal.toString());
+        } catch (IOException e) {
+            throw new GeneticException(e);
+        }
+    }
+
+    /**
+     * Generates the initial population.
+     * @return initial population
+     */
+    private static Population getInitialPopulation() {
+        final Population population = new ElitisticListPopulation(Constants.POPULATION_SIZE_PER_DIMENSION,
+                Constants.ELITISM_RATE);
+        for (int i = 0; i < Constants.POPULATION_SIZE_PER_DIMENSION; i++) {
+            population.addChromosome(new LegacyBinaryChromosome(BinaryChromosome
+                    .randomBinaryRepresentation(DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION)));
+        }
+        return population;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java
new file mode 100644
index 0000000..66c4615
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java
@@ -0,0 +1,65 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2.legacy;
+
+import java.util.List;
+
+import org.apache.commons.math3.genetics.AbstractListChromosome;
+import org.apache.commons.math3.genetics.BinaryChromosome;
+
+/**
+ * A representation of concrete binary chromosome.
+ */
+public class LegacyBinaryChromosome extends BinaryChromosome {
+
+    /**
+     * constructor.
+     * @param representation the internal representation
+     */
+    public LegacyBinaryChromosome(List<Integer> representation) {
+        super(representation);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double fitness() {
+        final List<Integer> alleles = getRepresentation();
+
+        final StringBuilder allelesStr = new StringBuilder();
+        for (Integer allele : alleles) {
+            allelesStr.append(Integer.toBinaryString(allele));
+        }
+
+        final double x = Integer.parseInt(allelesStr.substring(0, 12), 2) / 100.0;
+        final double y = Integer.parseInt(allelesStr.substring(12, 24), 2) / 100.0;
+
+        return -Math.pow(Math.pow(x, 2) + Math.pow(y, 2), .25) *
+                (Math.pow(Math.sin(50 * Math.pow(Math.pow(x, 2) + Math.pow(y, 2), .1)), 2) + 1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AbstractListChromosome<Integer> newFixedLengthChromosome(List<Integer> chromosomeRepresentation) {
+        return new LegacyBinaryChromosome(chromosomeRepresentation);
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java
new file mode 100644
index 0000000..8800f39
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java
@@ -0,0 +1,67 @@
+/*
+ * 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.examples.ga.mathfunctions.dimension2.legacy;
+
+import org.apache.commons.math3.genetics.Population;
+import org.apache.commons.math3.genetics.StoppingCondition;
+
+/**
+ * This class represents the stopping condition based on unchanged best fitness.
+ */
+public class UnchangedBestFitness implements StoppingCondition {
+
+    /** last best fitness. **/
+    private double lastBestFitness = Double.MIN_VALUE;
+
+    /** maximum number of generations evolved with unchanged best fitness. **/
+    private final int maxGenerationsWithUnchangedBestFitness;
+
+    /** generations having unchanged best fitness. **/
+    private int generationsHavingUnchangedBestFitness;
+
+    /**
+     * constructor.
+     * @param maxGenerationsWithUnchangedAverageFitness maximum number of
+     *                                                  generations evolved with
+     *                                                  unchanged best fitness
+     */
+    public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) {
+        this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isSatisfied(Population population) {
+        final double currentBestFitness = population.getFittestChromosome().getFitness();
+
+        if (lastBestFitness == currentBestFitness) {
+            this.generationsHavingUnchangedBestFitness++;
+            if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) {
+                return true;
+            }
+        } else {
+            this.generationsHavingUnchangedBestFitness = 0;
+            lastBestFitness = currentBestFitness;
+        }
+
+        return false;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java
new file mode 100644
index 0000000..7965791
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/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.examples.ga.mathfunctions.dimension2.legacy;
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java
new file mode 100644
index 0000000..432651f
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/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.examples.ga.mathfunctions.dimension2;
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java
new file mode 100644
index 0000000..52571e3
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java
@@ -0,0 +1,145 @@
+/*
+ * 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.examples.ga.mathfunctions.dimensionN;
+
+import java.awt.BorderLayout;
+import java.util.List;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl;
+import org.apache.commons.math4.ga.listener.ConvergenceListener;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+/**
+ * This class represents the graph plotter during optimization.
+ */
+public class DimNGraphPlotter extends JFrame implements ConvergenceListener<DimensionNCoordinate> {
+
+    /**
+     * Generated serialversionId.
+     */
+    private static final long serialVersionUID = -5683904006424006584L;
+
+    /** collection of 2-D series. **/
+    private final XYSeriesCollection dataset = new XYSeriesCollection();
+
+    /**
+     * constructor.
+     * @param plotSubject subject of plot
+     * @param xAxisLabel  x axis label
+     * @param yAxisLabel  y axis label
+     */
+    public DimNGraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) {
+        super(plotSubject);
+
+        final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel);
+        add(chartPanel, BorderLayout.CENTER);
+
+        setSize(640, 480);
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        setLocationRelativeTo(null);
+
+        setVisible(true);
+    }
+
+    /**
+     * Adds data point to graph.
+     * @param graphName  name of graph
+     * @param generation generation, to be plotted along x axis
+     * @param value      value, to be plotted along y axis
+     */
+    private void addDataPoint(String graphName, int generation, double value) {
+        XYSeries series = null;
+
+        if (!containsGraph(graphName)) {
+            series = new XYSeries(graphName);
+            dataset.addSeries(series);
+        } else {
+            series = dataset.getSeries(graphName);
+        }
+        series.add(generation, value);
+
+        setVisible(true);
+    }
+
+    /**
+     * Checks if the graph with the given name already exists.
+     * @param graphName name of the graph
+     * @return true/false
+     */
+    @SuppressWarnings("unchecked")
+    private boolean containsGraph(String graphName) {
+        final List<XYSeries> seriesList = dataset.getSeries();
+        if (seriesList == null || seriesList.isEmpty()) {
+            return false;
+        }
+        for (XYSeries series : seriesList) {
+            if (series.getKey().compareTo(graphName) == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Creates chart panel.
+     * @param chartTitle chart title
+     * @param xAxisLabel x axis label
+     * @param yAxisLabel y axis label
+     * @return panel
+     */
+    private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) {
+
+        final boolean showLegend = true;
+        final boolean createURL = false;
+        final boolean createTooltip = false;
+
+        final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset,
+                PlotOrientation.VERTICAL, showLegend, createTooltip, createURL);
+        final XYPlot plot = chart.getXYPlot();
+        final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
+
+        plot.setRenderer(renderer);
+
+        return new ChartPanel(chart);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void notify(int generation, Population<DimensionNCoordinate> population) {
+        PopulationStatisticalSummary<DimensionNCoordinate> populationStatisticalSummary =
+                new PopulationStatisticalSummaryImpl<>(population);
+        this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness());
+        this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness());
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java
new file mode 100644
index 0000000..98a31bc
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java
@@ -0,0 +1,54 @@
+/*
+ * 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.examples.ga.mathfunctions.dimensionN;
+
+import java.util.List;
+
+/**
+ * This class represents the coordinate of the problem domain i.e. the phenotype
+ * of chromosome.
+ */
+public class DimensionNCoordinate {
+
+    /** coordinate of all dimensions. **/
+    private final List<Double> values;
+
+    /**
+     * constructor.
+     * @param values coordinates of all dimensions.
+     */
+    public DimensionNCoordinate(List<Double> values) {
+        this.values = values;
+    }
+
+    /**
+     * Returns the values of all coordinates.
+     * @return values of coordinates
+     */
+    public List<Double> getValues() {
+        return values;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return "Coordinate [values=" + values + "]";
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java
new file mode 100644
index 0000000..9579815
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java
@@ -0,0 +1,52 @@
+/*
+ * 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.examples.ga.mathfunctions.dimensionN;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.decoder.Decoder;
+
+/**
+ * Decoder to convert chromosome's binary genotype to phenotype
+ * {@link DimensionNCoordinate}.
+ */
+public class DimensionNDecoder implements Decoder<DimensionNCoordinate> {
+
+    /**
+     * decode the binary representation of chromosome to
+     * {@link DimensionNCoordinate}.
+     * @param chromosome The {@link Chromosome}
+     */
+    @Override
+    public DimensionNCoordinate decode(Chromosome<DimensionNCoordinate> chromosome) {
+        final BinaryChromosome<DimensionNCoordinate> binaryChromosome =
+                (BinaryChromosome<DimensionNCoordinate>) chromosome;
+        final long length = binaryChromosome.getLength();
+        final List<Double> coordinates = new ArrayList<>();
+
+        for (int i = 0; i < length; i += 12) {
+            final String dimensionStrValue = binaryChromosome.getStringRepresentation(i, i + 12);
+            coordinates.add(Integer.parseUnsignedInt(dimensionStrValue, 2) / 100d);
+        }
+
+        return new DimensionNCoordinate(coordinates);
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java
new file mode 100644
index 0000000..1c0d7db
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.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.examples.ga.mathfunctions.dimensionN;
+
+import org.apache.commons.math4.ga.fitness.FitnessFunction;
+
+/**
+ * This class represents the mathematical fitness function for optimizing a 2
+ * dimension mathematical function.
+ */
+public class DimensionNFitnessFunction implements FitnessFunction<DimensionNCoordinate> {
+
+    /**
+     * Computes the fitness value based on the decoded chromosome.
+     * @param coordinate The {@link DimensionNCoordinate}
+     * @return the fitness value
+     */
+    @Override
+    public double compute(DimensionNCoordinate coordinate) {
+        double sumOfSquare = 0.0;
+        for (Double value : coordinate.getValues()) {
+            sumOfSquare += Math.pow(value, 2);
+        }
+        return -Math.pow(sumOfSquare, .25) * (Math.pow(Math.sin(50 * Math.pow(sumOfSquare, .1)), 2) + 1);
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java
new file mode 100644
index 0000000..dee317a
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java
@@ -0,0 +1,114 @@
+/*
+ * 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.examples.ga.mathfunctions.dimensionN;
+
+import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants;
+import org.apache.commons.math4.ga.AbstractGeneticAlgorithm;
+import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm;
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.convergence.UnchangedBestFitness;
+import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover;
+import org.apache.commons.math4.ga.crossover.rategenerator.AdaptiveLinearMaximumRankBasedCrossoverRateGenerator;
+import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry;
+import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger;
+import org.apache.commons.math4.ga.mutation.BinaryMutation;
+import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.selection.TournamentSelection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class represents an optimizer for a 2-dimensional math function using
+ * genetic algorithm.
+ */
+public class DimensionNFunctionAdaptiveOptimizer {
+
+    /** number of dimension. **/
+    private static final int DIMENSION = 10;
+
+    /** size of tournament. **/
+    private static final int TOURNAMENT_SIZE = 4;
+
+    /** instance of logger. **/
+    private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class);
+
+    /**
+     * Optimizes the 2-dimension fitness function.
+     * @param args arguments
+     */
+    public static void main(String[] args) {
+        final Population<DimensionNCoordinate> initPopulation = getInitialPopulation();
+
+        final DimensionNFunctionAdaptiveOptimizer optimizer = new DimensionNFunctionAdaptiveOptimizer();
+
+        final ConvergenceListenerRegistry<DimensionNCoordinate> convergenceListenerRegistry =
+                ConvergenceListenerRegistry.getInstance();
+        convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<DimensionNCoordinate>());
+        convergenceListenerRegistry
+                .addConvergenceListener(new DimNGraphPlotter("Adaptive Convergence Stats", "generation", "fitness"));
+
+        optimizer.optimize(initPopulation);
+    }
+
+    private void optimize(Population<DimensionNCoordinate> initial) {
+
+        // initialize a new genetic algorithm
+        final AbstractGeneticAlgorithm<DimensionNCoordinate> ga = new AdaptiveGeneticAlgorithm<DimensionNCoordinate>(
+                new OnePointBinaryCrossover<DimensionNCoordinate>(),
+                new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>(Constants.CROSSOVER_RATE / 2,
+                        Constants.CROSSOVER_RATE),
+                new BinaryMutation<>(), new AdaptiveLinearMutationRateGenerator<>(0, Constants.AVERAGE_MUTATION_RATE),
+                new TournamentSelection<>(TOURNAMENT_SIZE), Constants.ELITISM_RATE);
+
+        // stopping condition
+        final StoppingCondition<DimensionNCoordinate> stopCond = new UnchangedBestFitness<>(
+                Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS);
+
+        // run the algorithm
+        final Population<DimensionNCoordinate> finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        final Chromosome<DimensionNCoordinate> bestFinal = finalPopulation.getFittestChromosome();
+
+        logger.info("*********************************************");
+        logger.info("***********Optimization Result***************");
+        logger.info(bestFinal.toString());
+
+    }
+
+    /**
+     * Generates an initial population.
+     * @return initial population
+     */
+    private static Population<DimensionNCoordinate> getInitialPopulation() {
+        final Population<DimensionNCoordinate> population = new ListPopulation<>(
+                DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION);
+        final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction();
+        final DimensionNDecoder decoder = new DimensionNDecoder();
+        for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) {
+            population.addChromosome(BinaryChromosome.<DimensionNCoordinate>randomChromosome(
+                    DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder));
+        }
+        return population;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java
new file mode 100644
index 0000000..5791b05
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java
@@ -0,0 +1,113 @@
+/*
+ * 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.examples.ga.mathfunctions.dimensionN;
+
+import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants;
+import org.apache.commons.math4.ga.GeneticAlgorithm;
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.convergence.UnchangedBestFitness;
+import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover;
+import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry;
+import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger;
+import org.apache.commons.math4.ga.mutation.BinaryMutation;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.selection.TournamentSelection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class represents an optimizer for a 2-dimensional math function using
+ * genetic algorithm.
+ */
+public class DimensionNFunctionOptimizer {
+
+    /** number of dimension. **/
+    private static final int DIMENSION = 10;
+
+    /** size of tournament. **/
+    private static final int TOURNAMENT_SIZE = 4;
+
+    /** instance of logger. **/
+    private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class);
+
+    /**
+     * Optimizes the 2-dimension fitness function.
+     * @param args arguments
+     */
+    public static void main(String[] args) {
+        final Population<DimensionNCoordinate> initPopulation = getInitialPopulation();
+
+        final DimensionNFunctionOptimizer optimizer = new DimensionNFunctionOptimizer();
+
+        final ConvergenceListenerRegistry<DimensionNCoordinate> convergenceListenerRegistry =
+                ConvergenceListenerRegistry.getInstance();
+        convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<DimensionNCoordinate>());
+        convergenceListenerRegistry
+                .addConvergenceListener(new DimNGraphPlotter("Convergence Stats", "generation", "fitness"));
+
+        optimizer.optimize(initPopulation);
+    }
+
+    /**
+     * Optimizes the population.
+     * @param initial The {@link Population}
+     */
+    public void optimize(Population<DimensionNCoordinate> initial) {
+
+        // initialize a new genetic algorithm
+        final GeneticAlgorithm<DimensionNCoordinate> ga = new GeneticAlgorithm<>(
+                new OnePointBinaryCrossover<DimensionNCoordinate>(), Constants.CROSSOVER_RATE,
+                new BinaryMutation<DimensionNCoordinate>(), Constants.AVERAGE_MUTATION_RATE,
+                new TournamentSelection<>(TOURNAMENT_SIZE), Constants.ELITISM_RATE);
+
+        // stopping condition
+        final StoppingCondition<DimensionNCoordinate> stopCond = new UnchangedBestFitness<>(
+                Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS);
+
+        // run the algorithm
+        final Population<DimensionNCoordinate> finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        final Chromosome<DimensionNCoordinate> bestFinal = finalPopulation.getFittestChromosome();
+
+        logger.info("*********************************************");
+        logger.info("***********Optimization Result***************");
+        logger.info(bestFinal.toString());
+
+    }
+
+    /**
+     * Generates an initial population.
+     * @return initial population
+     */
+    private static Population<DimensionNCoordinate> getInitialPopulation() {
+        final Population<DimensionNCoordinate> population = new ListPopulation<>(
+                DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION);
+        final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction();
+        final DimensionNDecoder decoder = new DimensionNDecoder();
+        for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) {
+            population.addChromosome(BinaryChromosome.<DimensionNCoordinate>randomChromosome(
+                    DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder));
+        }
+        return population;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java
new file mode 100644
index 0000000..6a15fed
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/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.examples.ga.mathfunctions.dimensionN;
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java
new file mode 100644
index 0000000..1389c75
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java
@@ -0,0 +1,55 @@
+/*
+ * 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.examples.ga.mathfunctions.utils;
+
+/**
+ * This abstraction maintains constants used by this module.
+ */
+public final class Constants {
+
+    /** size of population. **/
+    public static final int POPULATION_SIZE_PER_DIMENSION = 10;
+
+    /** size of tournament. **/
+    public static final int TOURNAMENT_SIZE_PER_DIMENSION = 2;
+
+    /** length of chromosome. **/
+    public static final int CHROMOSOME_LENGTH_PER_DIMENSION = 12;
+
+    /** rate of crossover. **/
+    public static final double CROSSOVER_RATE = 1.0;
+
+    /** rate of elitism. **/
+    public static final double ELITISM_RATE = 0.25;
+
+    /** rate of mutation. **/
+    public static final double AVERAGE_MUTATION_RATE = 0.05;
+
+    /** number of generations with unchanged best fitness. **/
+    public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50;
+
+    /** encoding for console logger. **/
+    public static final String ENCODING = "UTF-8";
+
+    /**
+     * constructor.
+     */
+    private Constants() {
+
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java
new file mode 100644
index 0000000..360a1c3
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/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.examples.ga.mathfunctions.utils;
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml
new file mode 100644
index 0000000..b4a7811
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>examples-ga</artifactId>
+        <version>4.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>examples-ga-tsp</artifactId>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+
+        <!-- OSGi -->
+        <commons.osgi.symbolicName>org.apache.commons.math4.examples.ga.tsp</commons.osgi.symbolicName>
+        <commons.osgi.export>org.apache.commons.math4.examples.ga.tsp</commons.osgi.export>
+        <!-- Java 9+ -->
+        <commons.automatic.module.name>org.apache.commons.math4.examples.ga.tsp</commons.automatic.module.name>
+        <!-- Workaround to avoid duplicating config files. -->
+        <math.parent.dir>${basedir}/../../..</math.parent.dir>
+
+        <uberjar.name>examples-ga-mathfunctions</uberjar.name>
+        <project.mainClass>org.apache.commons.math4.examples.ga.tsp.TSPOptimizer</project.mainClass>
+        <slf4jVersion>1.7.32</slf4jVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-csv</artifactId>
+            <version>1.9.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>${slf4jVersion}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java
new file mode 100644
index 0000000..9b06312
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java
@@ -0,0 +1,52 @@
+/*
+ * 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.examples.ga.tsp;
+
+import java.util.List;
+
+
+import org.apache.commons.math4.examples.ga.tsp.commons.City;
+import org.apache.commons.math4.examples.ga.tsp.commons.DistanceMatrix;
+import org.apache.commons.math4.ga.fitness.FitnessFunction;
+
+/**
+ * This class represents the fitness function for tsp.
+ */
+public class TSPFitnessFunction implements FitnessFunction<List<City>> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double compute(List<City> cities) {
+        double totalDistance = 0.0;
+        int index1 = 0;
+        int index2 = 0;
+        for (int i = 0; i < cities.size(); i++) {
+            index1 = i;
+            index2 = (i == cities.size() - 1) ? 0 : i + 1;
+            totalDistance += calculateNodeDistance(cities.get(index1), cities.get(index2));
+        }
+        return -totalDistance;
+    }
+
+    private double calculateNodeDistance(City node1, City node2) {
+        final DistanceMatrix distanceMatrix = DistanceMatrix.getInstance();
+        return distanceMatrix.getDistance(node1, node2);
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java
new file mode 100644
index 0000000..21833ef
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java
@@ -0,0 +1,116 @@
+/*
+ * 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.examples.ga.tsp;
+
+import java.util.List;
+
+import org.apache.commons.math4.examples.ga.tsp.commons.City;
+import org.apache.commons.math4.examples.ga.tsp.utils.Constants;
+import org.apache.commons.math4.examples.ga.tsp.utils.GraphPlotter;
+import org.apache.commons.math4.ga.GeneticAlgorithm;
+import org.apache.commons.math4.ga.chromosome.RealValuedChromosome;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.convergence.UnchangedBestFitness;
+import org.apache.commons.math4.ga.crossover.OnePointCrossover;
+import org.apache.commons.math4.ga.decoder.RandomKeyDecoder;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry;
+import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger;
+import org.apache.commons.math4.ga.mutation.RealValuedMutation;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.selection.TournamentSelection;
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class represents the optimizer for traveling salesman problem.
+ */
+public class TSPOptimizer {
+
+    /** instance of logger. **/
+    private Logger logger = LoggerFactory.getLogger(TSPOptimizer.class);
+
+    /**
+     * Main method to initiate the optimization process.
+     * @param args arguments
+     */
+    public static void main(String[] args) {
+        try {
+            final Population<List<City>> initPopulation = getInitialPopulation(Constants.CITIES);
+
+            final TSPOptimizer optimizer = new TSPOptimizer();
+
+            final ConvergenceListenerRegistry<List<City>> convergenceListenerRegistry = ConvergenceListenerRegistry
+                    .getInstance();
+            convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<>());
+            convergenceListenerRegistry
+                    .addConvergenceListener(new GraphPlotter("Convergence", "generation", "total-distance"));
+
+            optimizer.optimizeSGA(initPopulation, Constants.CITIES);
+
+            Thread.sleep(5000);
+
+        } catch (InterruptedException e) {
+            throw new GeneticException(e);
+        }
+    }
+
+    /**
+     * Optimizes the tsp problem.
+     * @param initial initial population
+     * @param cities  cities
+     */
+    public void optimizeSGA(Population<List<City>> initial, List<City> cities) {
+
+        // initialize a new genetic algorithm
+        final GeneticAlgorithm<List<City>> ga = new GeneticAlgorithm<>(new OnePointCrossover<Integer, List<City>>(),
+                Constants.CROSSOVER_RATE, new RealValuedMutation<List<City>>(), Constants.AVERAGE_MUTATION_RATE,
+                new TournamentSelection<List<City>>(Constants.TOURNAMENT_SIZE));
+
+        // stopping condition
+        final StoppingCondition<List<City>> stopCond = new UnchangedBestFitness<>(
+                Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS);
+
+        // run the algorithm
+        final Population<List<City>> finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        final RealValuedChromosome<List<City>> bestFinal = (RealValuedChromosome<List<City>>) finalPopulation
+                .getFittestChromosome();
+
+        logger.info("*********************************************");
+        logger.info("***********Optimization Result***************");
+
+        logger.info(bestFinal.decode().toString());
+
+    }
+
+    private static Population<List<City>> getInitialPopulation(List<City> cities) {
+        final Population<List<City>> simulationPopulation = new ListPopulation<>(Constants.POPULATION_SIZE);
+
+        for (int i = 0; i < Constants.POPULATION_SIZE; i++) {
+            simulationPopulation.addChromosome(new RealValuedChromosome<>(
+                    ChromosomeRepresentationUtils.randomPermutation(Constants.CHROMOSOME_LENGTH),
+                    new TSPFitnessFunction(), new RandomKeyDecoder<City>(cities)));
+        }
+
+        return simulationPopulation;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java
new file mode 100644
index 0000000..7a6d8d1
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java
@@ -0,0 +1,77 @@
+/*
+ * 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.examples.ga.tsp.commons;
+
+/**
+ * This class represents a city with location coordinate.
+ */
+public final class City {
+
+    /** index of city. **/
+    private final int index;
+
+    /** x coordinate. **/
+    private final double x;
+
+    /** y coordinate. **/
+    private final double y;
+
+    /**
+     * constructor.
+     * @param index index of city
+     * @param x     x coordinate
+     * @param y     y coordinate
+     */
+    public City(int index, double x, double y) {
+        this.index = index;
+        this.x = x;
+        this.y = y;
+    }
+
+    /**
+     * Returns city index.
+     * @return city index
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * Returns x coordinate.
+     * @return x coordinate
+     */
+    public double getX() {
+        return x;
+    }
+
+    /**
+     * Returns y coordinate.
+     * @return y coordinate
+     */
+    public double getY() {
+        return y;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return "Node [index=" + index + ", x=" + x + ", y=" + y + "]";
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java
new file mode 100644
index 0000000..175c7c6
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java
@@ -0,0 +1,71 @@
+/*
+ * 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.examples.ga.tsp.commons;
+
+import java.util.List;
+
+import org.apache.commons.math4.examples.ga.tsp.utils.Constants;
+
+/**
+ * This class represents the distance matrix between cities.
+ */
+public final class DistanceMatrix {
+
+    /** instance of the class. **/
+    private static final DistanceMatrix INSTANCE = new DistanceMatrix();
+
+    /** distances between cities. **/
+    private double[][] distances;
+
+    private DistanceMatrix() {
+        initialize(Constants.CITIES);
+    }
+
+    /**
+     * Returns distances between two cities.
+     * @param city1 first city
+     * @param city2 second city
+     * @return distance
+     */
+    public double getDistance(City city1, City city2) {
+        return distances[city1.getIndex() - 1][city2.getIndex() - 1];
+    }
+
+    /**
+     * Initializes the distance matrix.
+     * @param cities list of cities
+     */
+    private void initialize(List<City> cities) {
+        final int len = cities.size();
+        this.distances = new double[len][len];
+        for (int i = 0; i < len; i++) {
+            for (int j = 0; j < len; j++) {
+                distances[i][j] = Math.pow(Math.pow(cities.get(i).getX() - cities.get(j).getX(), 2) +
+                        Math.pow(cities.get(i).getY() - cities.get(j).getY(), 2), .5);
+            }
+        }
+    }
+
+    /**
+     * Returns the instance of this class.
+     * @return instance
+     */
+    public static DistanceMatrix getInstance() {
+        return INSTANCE;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java
new file mode 100644
index 0000000..e0422fd
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/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.examples.ga.tsp.commons;
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java
new file mode 100644
index 0000000..9a42d39
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.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.examples.ga.tsp.legacy;
+
+import java.util.List;
+
+import org.apache.commons.math3.genetics.RandomKey;
+import org.apache.commons.math4.examples.ga.tsp.commons.City;
+import org.apache.commons.math4.examples.ga.tsp.commons.DistanceMatrix;
+
+/**
+ * This class represents chromosome for tsp problem.
+ */
+public class TSPChromosome extends RandomKey<City> {
+
+    /** list of cities. **/
+    private final List<City> cities;
+
+    /**
+     * constructor.
+     * @param representation internal representation of chromosome
+     * @param cities         list of cities
+     */
+    public TSPChromosome(List<Double> representation, List<City> cities) {
+        super(representation);
+        this.cities = cities;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double fitness() {
+        final List<City> permutatedNodes = decode(cities);
+        return -calculateTotalDistance(permutatedNodes);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public TSPChromosome newFixedLengthChromosome(List<Double> representation) {
+        return new TSPChromosome(representation, cities);
+    }
+
+    private double calculateTotalDistance(List<City> permutedCities) {
+        double totalDistance = 0.0;
+        int index1 = 0;
+        int index2 = 0;
+        for (int i = 0; i < permutedCities.size(); i++) {
+            index1 = i;
+            index2 = (i == permutedCities.size() - 1) ? 0 : i + 1;
+            totalDistance += calculateNodeDistance(permutedCities.get(index1), permutedCities.get(index2));
+        }
+        return totalDistance;
+    }
+
+    private double calculateNodeDistance(City node1, City node2) {
+        return DistanceMatrix.getInstance().getDistance(node1, node2);
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java
new file mode 100644
index 0000000..81ae597
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java
@@ -0,0 +1,99 @@
+/*
+ * 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.examples.ga.tsp.legacy;
+
+import java.io.BufferedWriter;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.List;
+
+import org.apache.commons.math3.genetics.ElitisticListPopulation;
+import org.apache.commons.math3.genetics.GeneticAlgorithm;
+import org.apache.commons.math3.genetics.OnePointCrossover;
+import org.apache.commons.math3.genetics.Population;
+import org.apache.commons.math3.genetics.RandomKey;
+import org.apache.commons.math3.genetics.RandomKeyMutation;
+import org.apache.commons.math3.genetics.StoppingCondition;
+import org.apache.commons.math3.genetics.TournamentSelection;
+import org.apache.commons.math4.examples.ga.tsp.commons.City;
+import org.apache.commons.math4.examples.ga.tsp.utils.Constants;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+
+/**
+ * This class represents the tsp optimizer based on legacy implementation of
+ * Genetic Algorithm.
+ */
+public class TSPOptimizerLegacy {
+
+    /**
+     * MainThread.sleep(5000) method to initiate optimization.
+     * @param args arguments
+     */
+    public static void main(String[] args) {
+
+        final Population initPopulation = getInitialPopulation(Constants.CITIES);
+        final TSPOptimizerLegacy optimizer = new TSPOptimizerLegacy();
+
+        optimizer.optimize(initPopulation, Constants.CITIES);
+
+    }
+
+    /**
+     * Optimizes the tsp problem using legacy GA.
+     * @param initial initial population
+     * @param cities  cities
+     */
+    public void optimize(Population initial, List<City> cities) {
+
+        // initialize a new genetic algorithm
+        final GeneticAlgorithm ga = new GeneticAlgorithm(new OnePointCrossover<Integer>(), Constants.CROSSOVER_RATE,
+                new RandomKeyMutation(), Constants.AVERAGE_MUTATION_RATE,
+                new TournamentSelection(Constants.TOURNAMENT_SIZE));
+
+        // stopping condition
+        final StoppingCondition stopCond = new UnchangedBestFitness(
+                Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS);
+
+        // run the algorithm
+        final Population finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        @SuppressWarnings("unchecked")
+        final RandomKey<City> bestFinal = (RandomKey<City>) finalPopulation.getFittestChromosome();
+
+        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) {
+            writer.write("*********************************************");
+            writer.newLine();
+            writer.write("***********Optimization Result***************");
+            writer.write(bestFinal.toString());
+        } catch (IOException e) {
+            throw new GeneticException(e);
+        }
+    }
+
+    private static Population getInitialPopulation(List<City> cities) {
+        final Population simulationPopulation = new ElitisticListPopulation(Constants.POPULATION_SIZE, .25);
+
+        for (int i = 0; i < Constants.POPULATION_SIZE; i++) {
+            simulationPopulation.addChromosome(new TSPChromosome(RandomKey.randomPermutation(cities.size()), cities));
+        }
+
+        return simulationPopulation;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java
new file mode 100644
index 0000000..44fe93a
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java
@@ -0,0 +1,67 @@
+/*
+ * 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.examples.ga.tsp.legacy;
+
+import org.apache.commons.math3.genetics.Population;
+import org.apache.commons.math3.genetics.StoppingCondition;
+
+/**
+ * This class represents the stopping condition based on unchanged best fitness.
+ */
+public class UnchangedBestFitness implements StoppingCondition {
+
+    /** last best fitness. **/
+    private double lastBestFitness = Double.MIN_VALUE;
+
+    /** maximum number of generations evolved with unchanged best fitness. **/
+    private final int maxGenerationsWithUnchangedBestFitness;
+
+    /** generations having unchanged best fitness. **/
+    private int generationsHavingUnchangedBestFitness;
+
+    /**
+     * constructor.
+     * @param maxGenerationsWithUnchangedAverageFitness maximum number of
+     *                                                  generations evolved with
+     *                                                  unchanged best fitness
+     */
+    public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) {
+        this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isSatisfied(Population population) {
+        final double currentBestFitness = population.getFittestChromosome().getFitness();
+
+        if (lastBestFitness == currentBestFitness) {
+            this.generationsHavingUnchangedBestFitness++;
+            if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) {
+                return true;
+            }
+        } else {
+            this.generationsHavingUnchangedBestFitness = 0;
+            lastBestFitness = currentBestFitness;
+        }
+
+        return false;
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java
new file mode 100644
index 0000000..d46c6fd
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/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.examples.ga.tsp.legacy;
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java
new file mode 100644
index 0000000..938b8f9
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/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.examples.ga.tsp;
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java
new file mode 100644
index 0000000..c41cc31
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java
@@ -0,0 +1,65 @@
+/*
+ * 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.examples.ga.tsp.utils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.math4.examples.ga.tsp.commons.City;
+
+/**
+ * This class contains all required constants for this example.
+ */
+public final class Constants {
+
+    /** size of population. **/
+    public static final int POPULATION_SIZE = 100;
+
+    /** size of tournament. **/
+    public static final int TOURNAMENT_SIZE = 5;
+
+    /** length of chromosome. **/
+    public static final int CHROMOSOME_LENGTH = 14;
+
+    /** rate of crossover. **/
+    public static final double CROSSOVER_RATE = 1.0;
+
+    /** rate of elitism. **/
+    public static final double ELITISM_RATE = 0.25;
+
+    /** rate of mutation. **/
+    public static final double AVERAGE_MUTATION_RATE = 0.05;
+
+    /** maximum number of generations with unchanged best fitness. **/
+    public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50;
+
+    /** list of cities. **/
+    public static final List<City> CITIES = Collections.unmodifiableList(
+            Arrays.asList(new City[] {new City(1, 0, 0), new City(2, 1, 0), new City(3, 2, 0), new City(4, 3, 0),
+                new City(5, 3, 1), new City(6, 3, 2), new City(7, 3, 3), new City(8, 2, 3), new City(9, 1, 3),
+                new City(10, 0, 3), new City(11, 1, 2), new City(12, 2, 2), new City(13, 2, 1), new City(14, 1, 1)}));
+
+    /** encoding for console logger. **/
+    public static final String ENCODING = "UTF-8";
+
+    private Constants() {
+
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java
new file mode 100644
index 0000000..701380b
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java
@@ -0,0 +1,146 @@
+/*
+ * 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.examples.ga.tsp.utils;
+
+import java.awt.BorderLayout;
+
+import java.util.List;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import org.apache.commons.math4.examples.ga.tsp.commons.City;
+import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl;
+import org.apache.commons.math4.ga.listener.ConvergenceListener;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+/**
+ * This class represents the graph plotter during optimization.
+ */
+public class GraphPlotter extends JFrame implements ConvergenceListener<List<City>> {
+
+    /**
+     * Generated serialversionId.
+     */
+    private static final long serialVersionUID = -5683904006424006584L;
+
+    /** collection of 2-D series. **/
+    private final XYSeriesCollection dataset = new XYSeriesCollection();
+
+    /**
+     * constructor.
+     * @param plotSubject subject of plot
+     * @param xAxisLabel  x axis label
+     * @param yAxisLabel  y axis label
+     */
+    public GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) {
+        super(plotSubject);
+
+        final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel);
+        add(chartPanel, BorderLayout.CENTER);
+
+        setSize(640, 480);
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        setLocationRelativeTo(null);
+
+        setVisible(true);
+    }
+
+    /**
+     * Adds data point to graph.
+     * @param graphName  name of graph
+     * @param generation generation, to be plotted along x axis
+     * @param value      value, to be plotted along y axis
+     */
+    private void addDataPoint(String graphName, int generation, double value) {
+        XYSeries series = null;
+
+        if (!containsGraph(graphName)) {
+            series = new XYSeries(graphName);
+            dataset.addSeries(series);
+        } else {
+            series = dataset.getSeries(graphName);
+        }
+        series.add(generation, value);
+
+        setVisible(true);
+    }
+
+    /**
+     * Checks if the graph with the given name already exists.
+     * @param graphName name of the graph
+     * @return true/false
+     */
+    @SuppressWarnings("unchecked")
+    private boolean containsGraph(String graphName) {
+        final List<XYSeries> seriesList = dataset.getSeries();
+        if (seriesList == null || seriesList.isEmpty()) {
+            return false;
+        }
+        for (XYSeries series : seriesList) {
+            if (series.getKey().compareTo(graphName) == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Creates chart panel.
+     * @param chartTitle chart title
+     * @param xAxisLabel x axis label
+     * @param yAxisLabel y axis label
+     * @return panel
+     */
+    private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) {
+
+        final boolean showLegend = true;
+        final boolean createURL = false;
+        final boolean createTooltip = false;
+
+        final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset,
+                PlotOrientation.VERTICAL, showLegend, createTooltip, createURL);
+        final XYPlot plot = chart.getXYPlot();
+        final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
+
+        plot.setRenderer(renderer);
+
+        return new ChartPanel(chart);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void notify(int generation, Population<List<City>> population) {
+        PopulationStatisticalSummary<List<City>> populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>(
+                population);
+        this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness());
+        this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness());
+    }
+
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java
new file mode 100644
index 0000000..5caadde
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/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.examples.ga.tsp.utils;
diff --git a/commons-math-examples/examples-ga/pom.xml b/commons-math-examples/examples-ga/pom.xml
new file mode 100644
index 0000000..8432483
--- /dev/null
+++ b/commons-math-examples/examples-ga/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-math-examples</artifactId>
+        <version>4.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>examples-ga</artifactId>
+    <packaging>pom</packaging>
+    <name>examples-genetic-algorithm</name>
+
+    <properties>
+        <!-- Workaround to avoid duplicating config files. -->
+        <math.parent.dir>${basedir}/../..</math.parent.dir>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-math-ga</artifactId>
+            <version>4.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-math3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jfree</groupId>
+            <artifactId>jfreechart</artifactId>
+            <version>1.5.3</version>
+        </dependency>
+    </dependencies>
+    <modules>
+        <module>examples-ga-math-functions</module>
+        <module>examples-ga-tsp</module>
+    </modules>
+</project>
\ No newline at end of file
diff --git a/commons-math-examples/pom.xml b/commons-math-examples/pom.xml
index ccef4d6..cc1f917 100644
--- a/commons-math-examples/pom.xml
+++ b/commons-math-examples/pom.xml
@@ -168,6 +168,7 @@
   <modules>
     <module>examples-sofm</module>
     <module>examples-kmeans</module>
+    <module>examples-ga</module>
   </modules>
 
 </project>
diff --git a/commons-math-ga/pom.xml b/commons-math-ga/pom.xml
new file mode 100644
index 0000000..a2a3689
--- /dev/null
+++ b/commons-math-ga/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-math-parent</artifactId>
+        <version>4.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>commons-math-ga</artifactId>
+    <name>genetic algorithm</name>
+
+    <description />
+
+    <properties>
+        <!-- The Java Module System Name -->
+        <commons.module.name>org.apache.commons.math4.ga</commons.module.name>
+        <!-- This value must reflect the current name of the base package. -->
+        <commons.osgi.symbolicName>org.apache.commons.math4.ga</commons.osgi.symbolicName>
+        <!-- OSGi -->
+        <commons.osgi.export>org.apache.commons.math4.ga</commons.osgi.export>
+        <!-- Workaround to avoid duplicating config files. -->
+        <math.parent.dir>${basedir}/..</math.parent.dir>
+        <slf4jVersion>1.7.32</slf4jVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-numbers-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-rng-simple</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4jVersion}</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java
new file mode 100644
index 0000000..7f9359f
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java
@@ -0,0 +1,180 @@
+/*
+ * 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.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.crossover.CrossoverPolicy;
+import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry;
+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;
+
+/**
+ * This class represents an abstraction for all Genetic algorithm implementation
+ * comprising the basic properties and operations.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractGeneticAlgorithm<P> {
+
+    /** instance of logger. **/
+    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class);
+
+    /** the crossover policy used by the algorithm. */
+    private final CrossoverPolicy<P> crossoverPolicy;
+
+    /** the mutation policy used by the algorithm. */
+    private final MutationPolicy<P> mutationPolicy;
+
+    /** the selection policy used by the algorithm. */
+    private final SelectionPolicy<P> 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<P> crossoverPolicy,
+            final MutationPolicy<P> mutationPolicy,
+            final SelectionPolicy<P> 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<P> crossoverPolicy,
+            final MutationPolicy<P> mutationPolicy,
+            final SelectionPolicy<P> selectionPolicy,
+            double elitismRate) {
+        this.crossoverPolicy = crossoverPolicy;
+        this.mutationPolicy = mutationPolicy;
+        this.selectionPolicy = selectionPolicy;
+        this.elitismRate = elitismRate;
+    }
+
+    /**
+     * Returns the crossover policy.
+     * @return crossover policy
+     */
+    public CrossoverPolicy<P> getCrossoverPolicy() {
+        return crossoverPolicy;
+    }
+
+    /**
+     * Returns the mutation policy.
+     * @return mutation policy
+     */
+    public MutationPolicy<P> getMutationPolicy() {
+        return mutationPolicy;
+    }
+
+    /**
+     * Returns the selection policy.
+     * @return selection policy
+     */
+    public SelectionPolicy<P> 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<P> evolve(final Population<P> initial, final StoppingCondition<P> condition) {
+        Population<P> 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.<P>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.
+     * <ol>
+     * <li>Get nextGeneration population to fill from <code>current</code>
+     * generation, using its nextGeneration method</li>
+     * <li>Loop until new generation is filled:
+     * <ul>
+     * <li>Apply configured SelectionPolicy to select a pair of parents from
+     * <code>current</code>,</li>
+     * <li>apply configured {@link CrossoverPolicy} to parents,</li>
+     * <li>apply configured {@link MutationPolicy} to each of the offspring</li>
+     * <li>Add offspring individually to nextGeneration, space permitting</li>
+     * </ul>
+     * </li>
+     * <li>Return nextGeneration</li>
+     * </ol>
+     *
+     * @param current the current population
+     * @return the population for the next generation.
+     */
+    protected abstract Population<P> nextGeneration(Population<P> 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 0000000..07e8913
--- /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 <P> phenotype of chromosome
+ */
+public class AdaptiveGeneticAlgorithm<P> extends AbstractGeneticAlgorithm<P> {
+
+    /** instance of logger. **/
+    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class);
+
+    /** The crossover rate generator. **/
+    private final CrossoverRateGenerator<P> crossoverRateGenerator;
+
+    /** The mutation rate generator. **/
+    private final MutationRateGenerator<P> 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<P> crossoverPolicy,
+            CrossoverRateGenerator<P> crossoverProbabilityGenerator,
+            MutationPolicy<P> mutationPolicy,
+            MutationRateGenerator<P> mutationProbabilityGenerator,
+            SelectionPolicy<P> 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<P> crossoverPolicy,
+            CrossoverRateGenerator<P> crossoverProbabilityGenerator,
+            MutationPolicy<P> mutationPolicy,
+            MutationRateGenerator<P> mutationProbabilityGenerator,
+            SelectionPolicy<P> selectionPolicy,
+            double elitismRate) {
+        super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate);
+        this.crossoverRateGenerator = crossoverProbabilityGenerator;
+        this.mutationRateGenerator = mutationProbabilityGenerator;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Population<P> nextGeneration(Population<P> current) {
+
+        LOGGER.debug("Reproducing next generation.");
+
+        // compute statistics of current generation chromosomes.
+        PopulationStatisticalSummary<P> populationStats = new PopulationStatisticalSummaryImpl<>(current);
+
+        // Initialize the next generation with elit chromosomes from previous
+        // generation.
+        final Population<P> 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<P> offspringPopulation = current.nextGeneration(0);
+
+        // perform crossover and generate new offsprings
+        while (offspringPopulation.getPopulationSize() < maxOffspringCount) {
+
+            // select parent chromosomes
+            ChromosomePair<P> 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<P> 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<P> getCrossoverProbabilityGenerator() {
+        return crossoverRateGenerator;
+    }
+
+    /**
+     * Returns mutation probability generator.
+     * @return mutation probability generator
+     */
+    public MutationRateGenerator<P> 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 0000000..94e320d
--- /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 <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class GeneticAlgorithm<P> extends AbstractGeneticAlgorithm<P> {
+
+    /** 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<P> crossoverPolicy,
+            final double crossoverRate,
+            final MutationPolicy<P> mutationPolicy,
+            final double mutationRate,
+            final SelectionPolicy<P> 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<P> crossoverPolicy,
+            final double crossoverRate,
+            final MutationPolicy<P> mutationPolicy,
+            final double mutationRate,
+            final SelectionPolicy<P> 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.
+     * <ol>
+     * <li>Get nextGeneration population to fill from <code>current</code>
+     * generation, using its nextGeneration method</li>
+     * <li>Loop until new generation is filled:
+     * <ul>
+     * <li>Apply configured SelectionPolicy to select a pair of parents from
+     * <code>current</code></li>
+     * <li>With probability = {@link #getCrossoverRate()}, apply configured
+     * {@link CrossoverPolicy} to parents</li>
+     * <li>With probability = {@link #getMutationRate()}, apply configured
+     * {@link MutationPolicy} to each of the offspring</li>
+     * <li>Add offspring individually to nextGeneration, space permitting</li>
+     * </ul>
+     * </li>
+     * <li>Return nextGeneration</li>
+     * </ol>
+     *
+     * @param current the current population.
+     * @return the population for the next generation.
+     */
+    @Override
+    protected Population<P> nextGeneration(final Population<P> current) {
+
+        LOGGER.debug("Reproducing next generation.");
+        final Population<P> nextGeneration = current.nextGeneration(getElitismRate());
+
+        while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit() - 1) {
+
+            // select parent chromosomes
+            ChromosomePair<P> 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 0000000..4f61ace
--- /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.
+ * <p>
+ * The chromosomes are IMMUTABLE, and so their fitness is also immutable and
+ * therefore it can be cached.
+ *
+ * @param <P> The phenotype of chromosome. The type should override hashCode()
+ *            and equals() methods.
+ * @since 4.0
+ */
+public abstract class AbstractChromosome<P> implements Chromosome<P> {
+
+    /** 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<P> fitnessFunction;
+
+    /** decoder to deode the chromosome's genotype representation. **/
+    private final Decoder<P> decoder;
+
+    /** Id of chromosome. **/
+    private final String id;
+
+    /**
+     * @param fitnessFunction The {@link FitnessFunction}
+     * @param decoder         The {@link Decoder}
+     */
+    protected AbstractChromosome(final FitnessFunction<P> fitnessFunction, final Decoder<P> decoder) {
+        this.fitnessFunction = Objects.requireNonNull(fitnessFunction);
+        this.decoder = Objects.requireNonNull(decoder);
+        this.id = UUID.randomUUID().toString();
+    }
+
+    /**
+     * returns fitness function.
+     * @return fitnessFunction
+     */
+    protected FitnessFunction<P> getFitnessFunction() {
+        return fitnessFunction;
+    }
+
+    /**
+     * Returns the decoder instance.
+     * @return decoder
+     */
+    public Decoder<P> 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.
+     * <p>
+     * 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
+     *         <ul>
+     *         <li>-1 if <code>another</code> is better than <code>this</code></li>
+     *         <li>1 if <code>another</code> is worse than <code>this</code></li>
+     *         <li>0 if the two chromosomes have the same fitness</li>
+     *         </ul>
+     */
+    @Override
+    public int compareTo(final Chromosome<P> another) {
+        return Double.compare(evaluate(), another.evaluate());
+    }
+
+    /**
+     * Returns <code>true</code> iff <code>another</code> 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 <code>another</code> is equivalent to this chromosome
+     */
+    public boolean isSame(final AbstractChromosome<P> 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 0000000..9e1676d
--- /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 <T> type of the allele/gene in the representation list. T should be
+ *            immutable.
+ * @param <P> phenotype of chromosome
+ * @since 2.0
+ */
+public abstract class AbstractListChromosome<T, P> extends AbstractChromosome<P> {
+
+    /** List of allele/genes. */
+    private final List<T> representation;
+
+    /**
+     * @param representation  The representation of chromosome genotype as
+     *                        {@link List} of generic T
+     * @param fitnessFunction The {@link FitnessFunction}
+     * @param decoder         An instance of {@link AbstractListChromosomeDecoder},
+     *                        to decode list chromosome.
+     */
+    protected AbstractListChromosome(final List<T> representation,
+            final FitnessFunction<P> fitnessFunction,
+            final AbstractListChromosomeDecoder<T, P> decoder) {
+        this(representation, true, fitnessFunction, decoder);
+    }
+
+    /**
+     * @param representation  The representation of chromosome genotype as an array
+     *                        of generic T
+     * @param fitnessFunction The {@link FitnessFunction}
+     * @param decoder         An instance of {@link AbstractListChromosomeDecoder},
+     *                        to decode list chromosome.
+     */
+    protected AbstractListChromosome(final T[] representation,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<T, P> decoder) {
+        this(Arrays.asList(representation), fitnessFunction, decoder);
+    }
+
+    /**
+     * @param representation  Internal representation of chromosome genotype as an
+     *                        array of generic T
+     * @param copyList        if {@code true}, the representation will be copied,
+     *                        otherwise it will be referenced.
+     * @param fitnessFunction The {@link FitnessFunction}
+     * @param decoder         The instance of {@link AbstractListChromosomeDecoder}
+     */
+    protected AbstractListChromosome(final List<T> representation,
+            final boolean copyList,
+            final FitnessFunction<P> fitnessFunction,
+            final AbstractListChromosomeDecoder<T, P> decoder) {
+        super(fitnessFunction, decoder);
+        Objects.requireNonNull(representation);
+        this.representation = Collections.unmodifiableList(copyList ? new ArrayList<>(representation) : representation);
+    }
+
+    /**
+     * Returns the (immutable) inner representation of the chromosome.
+     * @return the representation of the chromosome
+     */
+    public List<T> getRepresentation() {
+        return representation;
+    }
+
+    /**
+     * Returns the length of the chromosome.
+     * @return the length of the chromosome
+     */
+    public int getLength() {
+        return getRepresentation().size();
+    }
+
+    /**
+     * returns the decoder.
+     * @return decoder
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public AbstractListChromosomeDecoder<T, P> getDecoder() {
+        return (AbstractListChromosomeDecoder<T, P>) super.getDecoder();
+    }
+
+    /**
+     * Creates a new instance of the same class as <code>this</code> is, with a
+     * given <code>arrayRepresentation</code>. This is needed in crossover and
+     * mutation operators, where we need a new instance of the same class, but with
+     * different array representation.
+     * <p>
+     * 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<T, P> newChromosome(List<T> chromosomeRepresentation);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java
new file mode 100644
index 0000000..a9ef839
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java
@@ -0,0 +1,303 @@
+/*
+ * 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 java.util.Objects;
+
+import org.apache.commons.math4.ga.decoder.Decoder;
+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;
+
+/**
+ * BinaryChromosome represented by a vector of 0s and 1s.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class BinaryChromosome<P> extends AbstractChromosome<P> {
+
+    /**
+     * 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<Long> representation,
+            long length,
+            FitnessFunction<P> fitnessFunction,
+            Decoder<P> 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<P> fitnessFunction,
+            Decoder<P> 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<P> fitnessFunction,
+            Decoder<P> 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<P> fitnessFunction, Decoder<P> 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<P> getFitnessFunction() {
+        return super.getFitnessFunction();
+    }
+
+    /**
+     * Creates a new chromosome with provided parameters.
+     * @param chromosomeRepresentation the representation
+     * @param chromosomeLength         length of chromosome
+     * @return chromosome
+     */
+    public BinaryChromosome<P> newChromosome(long[] chromosomeRepresentation, long chromosomeLength) {
+        return new BinaryChromosome<P>(chromosomeRepresentation, chromosomeLength, getFitnessFunction(), getDecoder());
+    }
+
+    /**
+     * Creates an instance of Binary Chromosome with random binary representation.
+     * @param <P>             phenotype fo chromosome
+     * @param length          length of chromosome
+     * @param fitnessFunction The {@link FitnessFunction}
+     * @param decoder         The {@link Decoder}
+     * @return a binary chromosome
+     */
+    public static <P> BinaryChromosome<P> randomChromosome(int length,
+            FitnessFunction<P> fitnessFunction,
+            Decoder<P> decoder) {
+        return new BinaryChromosome<P>(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 0000000..b29461c
--- /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 <P> phenotype of chromosome
+ * @since 4.0
+ */
+public interface Chromosome<P> extends Comparable<Chromosome<P>> {
+
+    /**
+     * Access the fitness of this chromosome. The bigger the fitness, the better the
+     * chromosome.
+     * <p>
+     * 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 0000000..1157a00
--- /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 <P> phenotype of chromosome
+ * @since 2.0
+ */
+public class ChromosomePair<P> {
+
+    /** the first chromosome in the pair. */
+    private final Chromosome<P> first;
+
+    /** the second chromosome in the pair. */
+    private final Chromosome<P> second;
+
+    /**
+     * Create a chromosome pair.
+     * @param c1 the first chromosome.
+     * @param c2 the second chromosome.
+     */
+    public ChromosomePair(final Chromosome<P> c1, final Chromosome<P> c2) {
+        super();
+        first = c1;
+        second = c2;
+    }
+
+    /**
+     * Access the first chromosome.
+     *
+     * @return the first chromosome.
+     */
+    public Chromosome<P> getFirst() {
+        return first;
+    }
+
+    /**
+     * Access the second chromosome.
+     *
+     * @return the second chromosome.
+     */
+    public Chromosome<P> 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 0000000..91fcfd2
--- /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 <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class IntegralValuedChromosome<P> extends AbstractListChromosome<Integer, P> {
+
+    /** minimum acceptable value of allele. **/
+    private final int min;
+
+    /** maximum acceptable value of allele. **/
+    private final int max;
+
+    /**
+     * @param representation  Internal representation 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
+     */
+    public IntegralValuedChromosome(List<Integer> representation,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Integer, P> decoder,
+            int min,
+            int max) {
+        super(representation, fitnessFunction, decoder);
+        this.min = min;
+        this.max = max;
+        checkValidity();
+    }
+
+    /**
+     * @param representation  Internal representation 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
+     */
+    public IntegralValuedChromosome(Integer[] representation,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Integer, P> decoder,
+            int min,
+            int max) {
+        super(representation, fitnessFunction, decoder);
+        this.min = min;
+        this.max = max;
+        checkValidity();
+    }
+
+    /**
+     * Returns the minimum acceptable value of allele.
+     * @return minimum value
+     */
+    public int getMin() {
+        return min;
+    }
+
+    /**
+     * Returns the maximum acceptable value of allele.
+     * @return maximum value
+     */
+    public int getMax() {
+        return max;
+    }
+
+    /**
+     * Asserts that <code>representation</code> 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 IntegralValuedChromosome<P> newChromosome(List<Integer> chromosomeRepresentation) {
+        return new IntegralValuedChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder(), this.min,
+                this.max);
+    }
+
+    /**
+     * Creates an instance of Integral valued Chromosome with random binary
+     * representation.
+     * @param <P>             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 <P> IntegralValuedChromosome<P> randomChromosome(int length,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Integer, P> decoder,
+            int min,
+            int max) {
+        return new IntegralValuedChromosome<>(
+                ChromosomeRepresentationUtils.randomIntegralRepresentation(length, min, max), fitnessFunction, decoder,
+                min, max);
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java
new file mode 100644
index 0000000..603a1e2
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java
@@ -0,0 +1,160 @@
+/*
+ * 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.Arrays;
+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;
+
+/**
+ * DoubleEncodedChromosome is used for representing chromosome encoded as
+ * Double. It is a vector of a fixed length of real numbers.The acceptable real
+ * values should belong to the range min(inclusive) to max(exclusive).
+ * <p>
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class RealValuedChromosome<P> extends AbstractListChromosome<Double, P> {
+
+    /** minimum acceptable value of allele. **/
+    private final double min;
+
+    /** maximum acceptable value of allele. **/
+    private final double max;
+
+    /**
+     * @param representation  an array of real values
+     * @param fitnessFunction the fitness function
+     * @param decoder         the {@link AbstractListChromosomeDecoder}
+     */
+    public RealValuedChromosome(final List<Double> representation,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Double, P> decoder) {
+        super(representation, fitnessFunction, decoder);
+        this.min = 0;
+        this.max = 1d;
+        checkValidity();
+    }
+
+    /**
+     * @param representation  an array of real values
+     * @param fitnessFunction the fitness function
+     * @param decoder         the {@link AbstractListChromosomeDecoder}
+     * @param min             minimum inclusive value of allele
+     * @param max             maximum exclusive value of allele
+     */
+    public RealValuedChromosome(final List<Double> representation,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Double, P> decoder,
+            double min,
+            double max) {
+        super(representation, fitnessFunction, decoder);
+        this.min = min;
+        this.max = max;
+        checkValidity();
+    }
+
+    /**
+     * @param representation  Internal representation of chromosome as genotype
+     * @param fitnessFunction The {@link FitnessFunction}
+     * @param decoder         The {@link AbstractListChromosomeDecoder}
+     */
+    public RealValuedChromosome(final Double[] representation,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Double, P> decoder) {
+        this(Arrays.asList(representation), fitnessFunction, decoder);
+    }
+
+    /**
+     * @param representation  Internal representation of chromosome as genotype
+     * @param fitnessFunction The {@link FitnessFunction}
+     * @param decoder         The {@link AbstractListChromosomeDecoder}
+     * @param min             minimum inclusive value of allele
+     * @param max             maximum exclusive value of allele
+     */
+    public RealValuedChromosome(final Double[] representation,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Double, P> decoder,
+            double min,
+            double max) {
+        this(Arrays.asList(representation), fitnessFunction, decoder, min, max);
+    }
+
+    /**
+     * Return the minimum allele value.
+     * @return minimum
+     */
+    public double getMin() {
+        return min;
+    }
+
+    /**
+     * Returns the maximum allele value.
+     * @return maximum
+     */
+    public double getMax() {
+        return max;
+    }
+
+    /**
+     * Asserts that <code>representation</code> 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 RealValuedChromosome<P> newChromosome(List<Double> chromosomeRepresentation) {
+        return new RealValuedChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder(), this.min,
+                this.max);
+    }
+
+    /**
+     * Creates an instance of RealValued chromosome with randomly generated
+     * representation.
+     * @param <P>             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 <P> RealValuedChromosome<P> randomChromosome(int length,
+            FitnessFunction<P> fitnessFunction,
+            AbstractListChromosomeDecoder<Double, P> decoder,
+            double min,
+            double max) {
+        return new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(length, min, max),
+                fitnessFunction, decoder, min, max);
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java
new file mode 100644
index 0000000..196ddd9
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/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.chromosome;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java
new file mode 100644
index 0000000..8fda02b
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java
@@ -0,0 +1,80 @@
+/*
+ * 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 java.util.concurrent.TimeUnit;
+
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * Stops after a fixed amount of time has elapsed.
+ * <p>
+ * The first time {@link #isSatisfied(Population)} is invoked, the end time of
+ * the evolution is determined based on the provided <code>maxTime</code> value.
+ * Once the elapsed time reaches the configured <code>maxTime</code> value,
+ * {@link #isSatisfied(Population)} returns true.
+ *
+ * @param <P> phenotype of chromosome
+ * @since 3.1
+ */
+public class FixedElapsedTime<P> implements StoppingCondition<P> {
+
+    /** 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 <code>true</code> IFF the maximum allowed time period has elapsed
+     */
+    @Override
+    public boolean isSatisfied(Population<P> 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 0000000..37cfff9
--- /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.
+ * <p>
+ * 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 <P> phenotype of chromosome
+ * @since 2.0
+ */
+public class FixedGenerationCount<P> implements StoppingCondition<P> {
+    /** 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 <code>true</code> IFF the maximum number of generations has been
+     *         exceeded
+     */
+    @Override
+    public boolean isSatisfied(Population<P> 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 0000000..601c098
--- /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 <P> phenotype of chromosome
+ * @since 2.0
+ */
+public interface StoppingCondition<P> {
+
+    /**
+     * Determine whether or not the given population satisfies the stopping
+     * condition.
+     * @param population population of chromosome
+     *
+     * @return <code>true</code> if this stopping condition is met by the given
+     *         population, <code>false</code> otherwise.
+     */
+    boolean isSatisfied(Population<P> 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 0000000..2281b2a
--- /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 <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class UnchangedBestFitness<P> implements StoppingCondition<P> {
+
+    /** 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<P> 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 0000000..a729e0a
--- /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 <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class UnchangedMeanFitness<P> implements StoppingCondition<P> {
+
+    /** 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<P> 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<P> population) {
+        double totalFitness = 0.0;
+        for (Chromosome<P> 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 0000000..d386944
--- /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 0000000..c56434d
--- /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 <P> phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractChromosomeCrossoverPolicy<P> implements CrossoverPolicy<P> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ChromosomePair<P> crossover(final Chromosome<P> first,
+            final Chromosome<P> 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<P> crossover(Chromosome<P> first, Chromosome<P> 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 0000000..2184310
--- /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 <T> genetype of chromosome
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractListChromosomeCrossoverPolicy<T, P> extends AbstractChromosomeCrossoverPolicy<P> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public ChromosomePair<P> crossover(final Chromosome<P> first, final Chromosome<P> second) {
+        // check for validity.
+        checkValidity(first, second);
+
+        final AbstractListChromosome<T, P> firstListChromosome = (AbstractListChromosome<T, P>) first;
+        final AbstractListChromosome<T, P> secondListChromosome = (AbstractListChromosome<T, P>) second;
+
+        return mate(firstListChromosome, secondListChromosome);
+    }
+
+    /**
+     * Validates the chromosome pair.
+     * @param first  first chromosome
+     * @param second second chromosome
+     */
+    @SuppressWarnings("unchecked")
+    protected void checkValidity(final Chromosome<P> first, final Chromosome<P> second) {
+        if (!(first instanceof AbstractListChromosome<?, ?> && second instanceof AbstractListChromosome<?, ?>)) {
+            throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME);
+        }
+        final AbstractListChromosome<T, P> firstListChromosome = (AbstractListChromosome<T, P>) first;
+        final AbstractListChromosome<T, P> secondListChromosome = (AbstractListChromosome<T, P>) second;
+
+        final int length = firstListChromosome.getLength();
+        if (length != secondListChromosome.getLength()) {
+            throw new GeneticException(GeneticException.SIZE_MISMATCH, secondListChromosome.getLength(), length);
+        }
+
+    }
+
+    /**
+     * Performs mating between two chromosomes and returns the offspring pair.
+     * @param first  The first parent chromosome participating in crossover
+     * @param second The second parent chromosome participating in crossover
+     * @return chromosome pair
+     */
+    protected abstract ChromosomePair<P> mate(AbstractListChromosome<T, P> first, AbstractListChromosome<T, P> second);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java
new file mode 100644
index 0000000..65aa191
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * Policy used to create a pair of new chromosomes by performing a crossover
+ * operation on a source pair of chromosomes.
+ * @param <P> phenotype of chromosome
+ * @since 2.0
+ */
+public interface CrossoverPolicy<P> {
+
+    /**
+     * 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<P> crossover(Chromosome<P> first, Chromosome<P> 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 0000000..1d0da03
--- /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 <b>ordered</b> chromosomes by
+ * identifying cycles between two parent chromosomes. To form the children, the
+ * cycles are copied from the respective parents.
+ * <p>
+ * To form a cycle the following procedure is applied:
+ * <ol>
+ * <li>start with the first gene of parent 1</li>
+ * <li>look at the gene at the same position of parent 2</li>
+ * <li>go to the position with the same gene in parent 1</li>
+ * <li>add this gene index to the cycle</li>
+ * <li>repeat the steps 2-5 until we arrive at the starting gene of this
+ * cycle</li>
+ * </ol>
+ * 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):
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see <a href=
+ *      "http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx">
+ *      Cycle Crossover Operator</a>
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @param <P> phenotype of chromosome
+ * @since 3.1
+ */
+public class CycleCrossover<T, P> extends AbstractListChromosomeCrossoverPolicy<T, P> {
+
+    /** If the start index shall be chosen randomly. */
+    private final boolean randomStart;
+
+    /**
+     * Creates a new {@link CycleCrossover} policy.
+     */
+    public CycleCrossover() {
+        this(false);
+    }
+
+    /**
+     * Creates a new {@link CycleCrossover} policy using the given
+     * {@code randomStart} behavior.
+     *
+     * @param randomStart whether the start index shall be chosen randomly or be set
+     *                    to 0
+     */
+    public CycleCrossover(final boolean randomStart) {
+        this.randomStart = randomStart;
+    }
+
+    /**
+     * Returns whether the starting index is chosen randomly or set to zero.
+     *
+     * @return {@code true} if the starting index is chosen randomly, {@code false}
+     *         otherwise
+     */
+    public boolean isRandomStart() {
+        return randomStart;
+    }
+
+    /**
+     * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the
+     * actual crossover.
+     *
+     * @param first  the first chromosome
+     * @param second the second chromosome
+     * @return the pair of new chromosomes that resulted from the crossover
+     */
+    @Override
+    protected ChromosomePair<P> mate(final AbstractListChromosome<T, P> first,
+            final AbstractListChromosome<T, P> second) {
+
+        final int length = first.getLength();
+        // array representations of the parents
+        final List<T> parent1Rep = first.getRepresentation();
+        final List<T> parent2Rep = second.getRepresentation();
+        // and of the children: do a crossover copy to simplify the later processing
+        final List<T> child1Rep = new ArrayList<>(second.getRepresentation());
+        final List<T> child2Rep = new ArrayList<>(first.getRepresentation());
+
+        // the set of all visited indices so far
+        final Set<Integer> visitedIndices = new HashSet<>(length);
+        // the indices of the current cycle
+        final List<Integer> indices = new ArrayList<>(length);
+
+        // determine the starting index
+        int idx = randomStart ? RandomProviderManager.getRandomProvider().nextInt(length) : 0;
+        int cycle = 1;
+
+        while (visitedIndices.size() < length) {
+            indices.add(idx);
+
+            T item = parent2Rep.get(idx);
+            idx = parent1Rep.indexOf(item);
+
+            while (idx != indices.get(0)) {
+                // add that index to the cycle indices
+                indices.add(idx);
+                // get the item in the second parent at that index
+                item = parent2Rep.get(idx);
+                // get the index of that item in the first parent
+                idx = parent1Rep.indexOf(item);
+            }
+
+            // for even cycles: swap the child elements on the indices found in this cycle
+            if (cycle++ % 2 != 0) {
+                for (int i : indices) {
+                    final T tmp = child1Rep.get(i);
+                    child1Rep.set(i, child2Rep.get(i));
+                    child2Rep.set(i, tmp);
+                }
+            }
+
+            visitedIndices.addAll(indices);
+            // find next starting index: last one + 1 until we find an unvisited index
+            idx = (indices.get(0) + 1) % length;
+            while (visitedIndices.contains(idx) && visitedIndices.size() < length) {
+                idx++;
+                if (idx >= length) {
+                    idx = 0;
+                }
+            }
+            indices.clear();
+        }
+
+        return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep));
+    }
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java
new file mode 100644
index 0000000..f0313c1
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java
@@ -0,0 +1,154 @@
+/*
+ * 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.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * N-point crossover policy. For each iteration 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 (2-point crossover):
+ * <pre>
+ * -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)
+ * </pre>
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @param <P> phenotype of chromosome
+ * @since 3.1
+ */
+public class NPointCrossover<T, P> extends AbstractListChromosomeCrossoverPolicy<T, P> {
+
+    /** The number of crossover points. */
+    private final int crossoverPoints;
+
+    /**
+     * Creates a new {@link NPointCrossover} policy using the given number of
+     * points.
+     * <p>
+     * <b>Note</b>: the number of crossover points must be &lt;
+     * <code>chromosome length - 1</code>. 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):
+     * <pre>
+     * -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)
+     * </pre>
+     *
+     * @param first  first parent (p1)
+     * @param second second parent (p2)
+     * @return pair of two children (c1,c2)
+     */
+    @Override
+    protected ChromosomePair<P> mate(final AbstractListChromosome<T, P> first,
+            final AbstractListChromosome<T, P> second) {
+
+        final int length = first.getLength();
+        if (crossoverPoints >= length) {
+            throw new GeneticException(GeneticException.TOO_LARGE, crossoverPoints, length);
+        }
+
+        // array representations of the parents
+        final List<T> parent1Rep = first.getRepresentation();
+        final List<T> parent2Rep = second.getRepresentation();
+        // and of the children
+        final List<T> child1Rep = new ArrayList<>(length);
+        final List<T> child2Rep = new ArrayList<>(length);
+
+        final UniformRandomProvider random = RandomProviderManager.getRandomProvider();
+
+        List<T> c1 = child1Rep;
+        List<T> c2 = child2Rep;
+
+        int remainingPoints = crossoverPoints;
+        int lastIndex = 0;
+        for (int i = 0; i < crossoverPoints; i++, remainingPoints--) {
+            // select the next crossover point at random
+            final int crossoverIndex = 1 + lastIndex + random.nextInt(length - lastIndex - remainingPoints);
+
+            // copy the current segment
+            for (int j = lastIndex; j < crossoverIndex; j++) {
+                c1.add(parent1Rep.get(j));
+                c2.add(parent2Rep.get(j));
+            }
+
+            // swap the children for the next segment
+            final List<T> tmp = c1;
+            c1 = c2;
+            c2 = tmp;
+
+            lastIndex = crossoverIndex;
+        }
+
+        // copy the last segment
+        for (int j = lastIndex; j < length; j++) {
+            c1.add(parent1Rep.get(j));
+            c2.add(parent2Rep.get(j));
+        }
+
+        return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep));
+    }
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java
new file mode 100644
index 0000000..f860798
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java
@@ -0,0 +1,115 @@
+/*
+ * 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.BinaryChromosome;
+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;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+
+/**
+ * OnePoint Crossover Policy for Binary chromosomes.
+ * @param <P> the phenotype
+ */
+public class OnePointBinaryCrossover<P> extends AbstractChromosomeCrossoverPolicy<P> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected ChromosomePair<P> crossover(Chromosome<P> first, Chromosome<P> second) {
+
+        if (!(first instanceof BinaryChromosome<?> && second instanceof BinaryChromosome<?>)) {
+            throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME);
+        }
+        final BinaryChromosome<P> firstChromosome = (BinaryChromosome<P>) first;
+        final BinaryChromosome<P> secondChromosome = (BinaryChromosome<P>) 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<P> childChromosome1 = new BinaryChromosome<>(child1Rep, alleleCount,
+                firstChromosome.getFitnessFunction(), firstChromosome.getDecoder());
+        final BinaryChromosome<P> 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 0000000..2c6da54
--- /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:
+ * <pre>
+ * -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)
+ * </pre>
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @param <P> phenotype of chromosome
+ * @since 2.0
+ *
+ */
+public class OnePointCrossover<T, P> extends AbstractListChromosomeCrossoverPolicy<T, P> {
+
+    /**
+     * Performs one point crossover. 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:
+     * <pre>
+     * -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)
+     * </pre>
+     *
+     * @param first  first parent (p1)
+     * @param second second parent (p2)
+     * @return pair of two children (c1,c2)
+     */
+    @Override
+    protected ChromosomePair<P> mate(final AbstractListChromosome<T, P> first,
+            final AbstractListChromosome<T, P> second) {
+        final int length = first.getLength();
+        // array representations of the parents
+        final List<T> parent1Rep = first.getRepresentation();
+        final List<T> parent2Rep = second.getRepresentation();
+        // and of the children
+        final List<T> child1Rep = new ArrayList<>(length);
+        final List<T> child2Rep = new ArrayList<>(length);
+
+        // select a crossover point at random (0 and length makes no sense)
+        final int crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextInt(length - 1));
+
+        // copy the first part
+        for (int i = 0; i < crossoverIndex; i++) {
+            child1Rep.add(parent1Rep.get(i));
+            child2Rep.add(parent2Rep.get(i));
+        }
+        // and switch the second part
+        for (int i = crossoverIndex; i < length; i++) {
+            child1Rep.add(parent2Rep.get(i));
+            child2Rep.add(parent1Rep.get(i));
+        }
+
+        return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep));
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java
new file mode 100644
index 0000000..24608f9
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java
@@ -0,0 +1,131 @@
+/*
+ * 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.Collections;
+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;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Order 1 Crossover [OX1] builds offspring from <b>ordered</b> chromosomes by
+ * copying a consecutive slice from one parent, and filling up the remaining
+ * genes from the other parent as they appear.
+ * <p>
+ * This policy works by applying the following rules:
+ * <ol>
+ * <li>select a random slice of consecutive genes from parent 1</li>
+ * <li>copy the slice to child 1 and mark out the genes in parent 2</li>
+ * <li>starting from the right side of the slice, copy genes from parent 2 as
+ * they appear to child 1 if they are not yet marked out.</li>
+ * </ol>
+ * <p>
+ * Example (random sublist from index 3 to 7, underlined):
+ * <pre>
+ * 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)
+ * </pre>
+ * <p>
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see <a href=
+ *      "http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/Order1CrossoverOperator.aspx">
+ *      Order 1 Crossover Operator</a>
+ *
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @param <P> phenotype of chromosome
+ * @since 3.1
+ */
+public class OrderedCrossover<T, P> extends AbstractListChromosomeCrossoverPolicy<T, P> {
+
+    /**
+     * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the
+     * actual crossover.
+     *
+     * @param first  the first chromosome
+     * @param second the second chromosome
+     * @return the pair of new chromosomes that resulted from the crossover
+     */
+    @Override
+    protected ChromosomePair<P> mate(final AbstractListChromosome<T, P> first,
+            final AbstractListChromosome<T, P> second) {
+
+        final int length = first.getLength();
+        // array representations of the parents
+        final List<T> parent1Rep = first.getRepresentation();
+        final List<T> parent2Rep = second.getRepresentation();
+        // and of the children
+        final List<T> child1 = new ArrayList<>(length);
+        final List<T> child2 = new ArrayList<>(length);
+        // sets of already inserted items for quick access
+        final Set<T> child1Set = new HashSet<>(length);
+        final Set<T> child2Set = new HashSet<>(length);
+
+        final UniformRandomProvider random = RandomProviderManager.getRandomProvider();
+        // choose random points, making sure that lb < ub.
+        final int a = random.nextInt(length);
+        int b;
+        do {
+            b = random.nextInt(length);
+        } while (a == b);
+        // determine the lower and upper bounds
+        final int lb = Math.min(a, b);
+        final int ub = Math.max(a, b);
+
+        // add the subLists that are between lb and ub
+        child1.addAll(parent1Rep.subList(lb, ub + 1));
+        child1Set.addAll(child1);
+        child2.addAll(parent2Rep.subList(lb, ub + 1));
+        child2Set.addAll(child2);
+
+        // iterate over every item in the parents
+        for (int i = 1; i <= length; i++) {
+            final int idx = (ub + i) % length;
+
+            // retrieve the current item in each parent
+            final T item1 = parent1Rep.get(idx);
+            final T item2 = parent2Rep.get(idx);
+
+            // if the first child already contains the item in the second parent add it
+            if (!child1Set.contains(item2)) {
+                child1.add(item2);
+                child1Set.add(item2);
+            }
+
+            // if the second child already contains the item in the first parent add it
+            if (!child2Set.contains(item1)) {
+                child2.add(item1);
+                child2Set.add(item1);
+            }
+        }
+
+        // rotate so that the original slice is in the same place as in the parents.
+        Collections.rotate(child1, lb);
+        Collections.rotate(child2, lb);
+
+        return new ChromosomePair<>(first.newChromosome(child1), second.newChromosome(child2));
+    }
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java
new file mode 100644
index 0000000..23292f7
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java
@@ -0,0 +1,123 @@
+/*
+ * 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.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Perform Uniform Crossover [UX] on the specified chromosomes. A fixed mixing
+ * ratio is used to combine genes from the first and second parents, e.g. using
+ * a ratio of 0.5 would result in approximately 50% of genes coming from each
+ * parent. This is typically a poor method of crossover, but empirical evidence
+ * suggests that it is more exploratory and results in a larger part of the
+ * problem space being searched.
+ * <p>
+ * 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}
+ * &lt; {@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).
+ * <p>
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is
+ * parameterized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see <a href=
+ *      "http://en.wikipedia.org/wiki/Crossover_%28genetic_algorithm%29">Crossover
+ *      techniques (Wikipedia)</a>
+ * @see <a href=
+ *      "http://www.obitko.com/tutorials/genetic-algorithms/crossover-mutation.php">Crossover
+ *      (Obitko.com)</a>
+ * @see <a href="http://www.tomaszgwiazda.com/uniformX.htm">Uniform
+ *      crossover</a>
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @param <P> phenotype of chromosome
+ * @since 3.1
+ */
+public class UniformCrossover<T, P> extends AbstractListChromosomeCrossoverPolicy<T, P> {
+
+    /** crossover rate. **/
+    public static final String CROSSOVER_RATE = "CROSSOVER_RATE";
+
+    /** The mixing ratio. */
+    private final double ratio;
+
+    /**
+     * Creates a new {@link UniformCrossover} policy using the given mixing ratio.
+     *
+     * @param ratio the mixing ratio
+     */
+    public UniformCrossover(final double ratio) {
+        if (ratio < 0.0d || ratio > 1.0d) {
+            throw new GeneticException(GeneticException.OUT_OF_RANGE, ratio, CROSSOVER_RATE, 0.0d, 1.0d);
+        }
+        this.ratio = ratio;
+    }
+
+    /**
+     * Returns the mixing ratio used by this {@link CrossoverPolicy}.
+     *
+     * @return the mixing ratio
+     */
+    public double getRatio() {
+        return ratio;
+    }
+
+    /**
+     * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the
+     * actual crossover.
+     *
+     * @param first  the first chromosome
+     * @param second the second chromosome
+     * @return the pair of new chromosomes that resulted from the crossover
+     */
+    @Override
+    protected ChromosomePair<P> mate(final AbstractListChromosome<T, P> first,
+            final AbstractListChromosome<T, P> second) {
+        final int length = first.getLength();
+        // array representations of the parents
+        final List<T> parent1Rep = first.getRepresentation();
+        final List<T> parent2Rep = second.getRepresentation();
+        // and of the children
+        final List<T> child1Rep = new ArrayList<>(length);
+        final List<T> child2Rep = new ArrayList<>(length);
+
+        final UniformRandomProvider random = RandomProviderManager.getRandomProvider();
+
+        for (int index = 0; index < length; index++) {
+
+            if (random.nextDouble() < ratio) {
+                // swap the bits -> take other parent
+                child1Rep.add(parent2Rep.get(index));
+                child2Rep.add(parent1Rep.get(index));
+            } else {
+                child1Rep.add(parent1Rep.get(index));
+                child2Rep.add(parent2Rep.get(index));
+            }
+        }
+
+        return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep));
+    }
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java
new file mode 100644
index 0000000..68c9282
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/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;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java
new file mode 100644
index 0000000..1b1057d
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.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 average rank of
+ * input chromosomes in population.
+ * @param <P> phenotype of chromosome
+ */
+public class AdaptiveLinearAverageRankBasedCrossoverRateGenerator<P> implements CrossoverRateGenerator<P> {
+
+    /** 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<P> first,
+            Chromosome<P> second,
+            PopulationStatisticalSummary<P> 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 0000000..c03f488
--- /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 <P> phenotype of chromosome
+ */
+public class AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<P> implements CrossoverRateGenerator<P> {
+
+    /** 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<P> first,
+            Chromosome<P> second,
+            PopulationStatisticalSummary<P> 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 0000000..2f77ce1
--- /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 <P> phenotype of chromosome
+ */
+public class ConstantCrossoverRateGenerator<P> implements CrossoverRateGenerator<P> {
+
+    /** 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<P> first,
+            Chromosome<P> second,
+            PopulationStatisticalSummary<P> 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 0000000..438aeed
--- /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 <P> phenotype of chromosome
+ */
+public interface CrossoverRateGenerator<P> {
+
+    /**
+     * 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<P> first,
+            Chromosome<P> second,
+            PopulationStatisticalSummary<P> 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 0000000..886eab7
--- /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 0000000..96a1920
--- /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 <T> genotype fo chromosome
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractListChromosomeDecoder<T, P> implements Decoder<P> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public P decode(Chromosome<P> chromosome) {
+        checkValidity(chromosome);
+
+        return decode((AbstractListChromosome<T, P>) chromosome);
+    }
+
+    /**
+     * Checks validity of {@link Chromosome}.
+     * @param chromosome the {@link Chromosome}
+     */
+    protected void checkValidity(Chromosome<P> chromosome) {
+        if (!AbstractListChromosome.class.isAssignableFrom(chromosome.getClass())) {
+            throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, chromosome.getClass().getSimpleName());
+        }
+    }
+
+    /**
+     * Decodes the chromosome genotype and returns the phenotype.
+     * @param chromosome The list chromosome to decode
+     * @return decoded phenotype of chromosome
+     */
+    protected abstract P decode(AbstractListChromosome<T, P> chromosome);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java
new file mode 100644
index 0000000..50f2ba9
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java
@@ -0,0 +1,35 @@
+/*
+ * 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.Chromosome;
+
+/**
+ * Decoder is responsible for converting chromosome genotype to phenotype.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public interface Decoder<P> {
+
+    /**
+     * Converts genotype to phenotype.
+     * @param chromosome The {@link Chromosome}
+     * @return phenotype The phenotype of chromosome
+     */
+    P decode(Chromosome<P> chromosome);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java
new file mode 100644
index 0000000..037ccfd
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java
@@ -0,0 +1,75 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.math4.ga.chromosome.AbstractListChromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+
+/**
+ * A concrete implementation of RandomKey decoder. This class is responsible for
+ * decoding permutation chromosome encoded with random key.
+ * @param <U> type of the permutation element
+ * @since 4.0
+ */
+public final class RandomKeyDecoder<U> extends AbstractListChromosomeDecoder<Double, List<U>> {
+
+    /** base sequence for decoding chromosome. **/
+    private final List<U> baseSequence;
+
+    /**
+     * @param baseSequence the unpermuted sequence
+     */
+    public RandomKeyDecoder(List<U> baseSequence) {
+        this.baseSequence = Collections.unmodifiableList(Objects.requireNonNull(baseSequence));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected List<U> decode(AbstractListChromosome<Double, List<U>> chromosome) {
+        final List<Double> representation = chromosome.getRepresentation();
+        final List<Double> sortedRepresentation = new ArrayList<>(representation);
+        Collections.sort(sortedRepresentation);
+
+        final int sequenceLength = baseSequence.size();
+
+        // the size of the three lists must be equal
+        if (representation.size() != sequenceLength) {
+            throw new GeneticException(GeneticException.SIZE_MISMATCH, representation.size(), sequenceLength);
+        }
+
+        // do not modify the original representation
+        final List<Double> representationCopy = new ArrayList<>(representation);
+
+        // now find the indices in the original repr and use them for permuting
+        final List<U> res = new ArrayList<>(sequenceLength);
+        for (int i = 0; i < sequenceLength; i++) {
+            final int index = representationCopy.indexOf(sortedRepresentation.get(i));
+            res.add(baseSequence.get(index));
+            representationCopy.set(index, null);
+        }
+
+        return res;
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java
new file mode 100644
index 0000000..b8d0be7
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java
@@ -0,0 +1,39 @@
+/*
+ * 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 java.util.List;
+
+import org.apache.commons.math4.ga.chromosome.AbstractListChromosome;
+
+/**
+ * A concrete implementation of transparent decoder for List Chromosome. Treats
+ * the gentype as phenotype.
+ * @param <T> the genotype of chromosome
+ * @since 4.0
+ */
+public final class TransparentListChromosomeDecoder<T> extends AbstractListChromosomeDecoder<T, List<T>> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected List<T> decode(AbstractListChromosome<T, List<T>> chromosome) {
+        return chromosome.getRepresentation();
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java
new file mode 100644
index 0000000..57e19a8
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/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.decoder;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java
new file mode 100644
index 0000000..ade6dea
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java
@@ -0,0 +1,34 @@
+/*
+ * 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.fitness;
+
+/**
+ * This interface represents fitness function.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public interface FitnessFunction<P> {
+
+    /**
+     * computes the fitness value of the input chromosome's phenotype.
+     * @param decodedChromosome chromosome decoded as phenotype
+     * @return fitness value
+     */
+    double compute(P decodedChromosome);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java
new file mode 100644
index 0000000..5b6d4c5
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/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.fitness;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java
new file mode 100644
index 0000000..7209472
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java
@@ -0,0 +1,120 @@
+/*
+ * 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.internal.exception;
+
+import java.text.MessageFormat;
+
+/**
+ * This class represents the Exception encountered during GA optimization.
+ * @since 4.0
+ */
+public class GeneticException extends RuntimeException {
+
+    /** Error message for "out of range" condition. */
+    public static final String OUT_OF_RANGE = "Value {0} of {1} is out of range [{2}, {3}]";
+
+    /** Error message for "not strictly positive" condition. */
+    public static final String NOT_STRICTLY_POSITIVE = "Number {0} is not strictly positive";
+
+    /** Error message for "too large" condition. */
+    public static final String TOO_LARGE = "Number {0} is larger than {1}";
+
+    /** Error message for "too small" condition. */
+    public static final String TOO_SMALL = "Number {0} is smaller than {1}";
+
+    /** Error message for "out of range" condition. */
+    public static final String NO_DATA = "No data";
+
+    /** Error message for "size mismatch" condition. */
+    public static final String SIZE_MISMATCH = "Size mismatch: {0} != {1}";
+
+    /** Error message for "generic illegal argument" condition. */
+    public static final String ILLEGAL_ARGUMENT = "Illegal Argument Exception: {0}";
+
+    /** Error message for "generic illegal argument" condition. */
+    public static final String ILLEGAL_RANGE = "Illegal Range of Value Exception: " +
+            "[Expected min-{0}, max-{1}], [Passed min-{2}, max-{3}]";
+
+    /** Error message for "generic illegal argument" condition. */
+    public static final String INVALID_FIXED_LENGTH_CHROMOSOME = "Invalid Fixed Length Chromosome.";
+
+    /** Error message for "NULL ARGUMENT" condition. */
+    public static final String NULL_ARGUMENT = "Null Argument Exception: {0}";
+
+    /**
+     * Error message for "List of Chromosome bigger than population size" condition.
+     */
+    public static final String LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE = "List of chromosome bigger than " +
+            "population size: {0} > {1}";
+
+    /**
+     * Error message for "population limit not positive" condition.
+     */
+    public static final String POPULATION_LIMIT_NOT_POSITIVE = "Population limit not positive :{0}";
+
+    /**
+     * Error message for " population limit less than list of chromosomes size"
+     * condition.
+     */
+    public static final String POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE = "Population limit is " +
+            " lesser than list of chromosomes size : {0} < {1}";
+
+    /**
+     * Error message for different origin and permuted data.
+     */
+    public static final String DIFFERENT_ORIG_AND_PERMUTED_DATA = "Different original and permuted data";
+
+    /**
+     * Error message for chromosome length larger than expected.
+     */
+    public static final String LENGTH_TOO_LARGE = "Chromosome length larger than {0} not supported for" +
+            " string representation.";
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 20210516L;
+
+    /**
+     * Create an exception where the message is constructed by applying the
+     * {@code format()} method from {@code java.text.MessageFormat}.
+     *
+     * @param message         Message format (with replaceable parameters).
+     * @param formatArguments Actual arguments to be displayed in the message.
+     */
+    public GeneticException(String message, Object... formatArguments) {
+        super(MessageFormat.format(message, formatArguments));
+    }
+
+    /**
+     * Create an exception.
+     * @param t instance of {@link Throwable}
+     */
+    public GeneticException(Throwable t) {
+        super(t);
+    }
+
+    /**
+     * Create an exception having both stacktrace and message.
+     * @param message         the exception message
+     * @param t               the instance of {@link Throwable}
+     * @param formatArguments arguments to format the exception message
+     */
+    public GeneticException(String message, Throwable t, Object... formatArguments) {
+        super(MessageFormat.format(message, formatArguments), t);
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java
new file mode 100644
index 0000000..b55e918
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/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.internal.exception;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java
new file mode 100644
index 0000000..b95b60f
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java
@@ -0,0 +1,177 @@
+/*
+ * 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.internal.stats;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+
+/**
+ * This class represents an implementation of population statistical summary.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class PopulationStatisticalSummaryImpl<P> implements PopulationStatisticalSummary<P> {
+
+    /** maximum fitness of the population. **/
+    private final double maxFitness;
+
+    /** minimum fitness of the population. **/
+    private final double minFitness;
+
+    /** mean fitness of the population. **/
+    private double meanFitness;
+
+    /** variance of population fitness. **/
+    private final double variance;
+
+    /** population size. **/
+    private final int populationSize;
+
+    /** a map of chromosome Id and corresponding rank in population. **/
+    private final Map<String, Integer> chromosomeIdRankMap = new HashMap<>();
+
+    /**
+     * @param population current population {@link Population} of chromosomes
+     */
+    public PopulationStatisticalSummaryImpl(Population<P> population) {
+
+        // Fetch all chromosomes.
+        List<Chromosome<P>> chromosomes = getChromosomes(Objects.requireNonNull(population));
+
+        // Sort all chromosomes.
+        Collections.sort(chromosomes);
+
+        this.populationSize = chromosomes.size();
+        this.maxFitness = chromosomes.get(chromosomes.size() - 1).evaluate();
+        this.minFitness = chromosomes.get(0).evaluate();
+        this.meanFitness = calculateMeanFitness(chromosomes);
+        this.variance = calculateVariance(chromosomes);
+
+        updateChromosomeIdRankMap(chromosomes);
+
+    }
+
+    /**
+     * Updates chromosome Id and rank.
+     * @param chromosomes list of chromosomes
+     */
+    private void updateChromosomeIdRankMap(List<Chromosome<P>> chromosomes) {
+        for (int rank = 0; rank < chromosomes.size(); rank++) {
+            this.chromosomeIdRankMap.put(chromosomes.get(rank).getId(), rank);
+        }
+    }
+
+    /**
+     * Fetches chromosomes.
+     * @param population
+     * @return list of chromosomes
+     */
+    private List<Chromosome<P>> getChromosomes(Population<P> population) {
+        List<Chromosome<P>> chromosomes = new ArrayList<>(population.getPopulationSize());
+        for (Chromosome<P> chromosome : population) {
+            chromosomes.add(chromosome);
+        }
+        return chromosomes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double getMeanFitness() {
+        return this.meanFitness;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double getFitnessVariance() {
+        return this.variance;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double getMaxFitness() {
+        return this.maxFitness;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double getMinFitness() {
+        return this.minFitness;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long getPopulationSize() {
+        return this.populationSize;
+    }
+
+    /**
+     * Calculate mean fitness.
+     * @param chromosomes list of chromosomes
+     * @return returns mean fitness
+     */
+    private double calculateMeanFitness(List<Chromosome<P>> chromosomes) {
+        double sum = 0.0;
+        for (Chromosome<P> chromosome : chromosomes) {
+            sum += chromosome.evaluate();
+        }
+        return sum / chromosomes.size();
+    }
+
+    /**
+     * Calculate variance of population fitness.
+     * @param chromosomes List of chromosomes
+     * @return fitness variance
+     */
+    private double calculateVariance(List<Chromosome<P>> chromosomes) {
+        if (this.meanFitness == 0) {
+            this.meanFitness = calculateMeanFitness(chromosomes);
+        }
+        double sumOfSquare = 0.0;
+        for (Chromosome<P> chromosome : chromosomes) {
+            sumOfSquare += Math.pow(chromosome.evaluate(), 2);
+        }
+
+        return (sumOfSquare / chromosomes.size()) - Math.pow(this.meanFitness, 2);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int findRank(Chromosome<P> chromosome) {
+        return chromosomeIdRankMap.get(chromosome.getId());
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java
new file mode 100644
index 0000000..a0ae55d
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/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.internal.stats;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java
new file mode 100644
index 0000000..f99d585
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.listener;
+
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * This interface represents a convergence listener. Any implementation of the
+ * same will be notified about the population statics.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public interface ConvergenceListener<P> {
+
+    /**
+     * Notifies about the population statistics.
+     * @param generation current generation
+     * @param population population of chromosome
+     */
+    void notify(int generation, Population<P> population);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java
new file mode 100644
index 0000000..7fce28e
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * 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.listener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * This class is the default implementation of ConvergenceListenerRegistry. It
+ * will be responsible for registering the interested listeners and notifying
+ * all when required.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public final class ConvergenceListenerRegistry<P> {
+
+    /**
+     * The instance of the singleton class.
+     */
+    @SuppressWarnings("rawtypes")
+    private static final ConvergenceListenerRegistry INSTANCE = new ConvergenceListenerRegistry<>();
+
+    /**
+     * List of registered listeners.
+     */
+    private final List<ConvergenceListener<P>> listeners = new ArrayList<>();
+
+    /**
+     * private constructor to construct the singleton instance.
+     */
+    private ConvergenceListenerRegistry() {
+    }
+
+    /**
+     * Registers the interested ConvergenceListener passed as an argument.
+     * @param convergenceListener The {@link ConvergenceListener}
+     */
+    public void addConvergenceListener(ConvergenceListener<P> convergenceListener) {
+        this.listeners.add(convergenceListener);
+    }
+
+    /**
+     * Notifies all registered ConvergenceListeners about the population statistics.
+     * @param generation current generation
+     * @param population population of chromosomes
+     */
+    public synchronized void notifyAll(int generation, Population<P> population) {
+        for (ConvergenceListener<P> convergenceListener : listeners) {
+            convergenceListener.notify(generation, population);
+        }
+    }
+
+    /**
+     * Add instance of convergence listener.
+     * @param convergenceListeners list of {@link ConvergenceListener}
+     */
+    public void addConvergenceListeners(List<ConvergenceListener<P>> convergenceListeners) {
+        for (ConvergenceListener<P> convergenceListener : Objects.requireNonNull(convergenceListeners)) {
+            addConvergenceListener(convergenceListener);
+        }
+    }
+
+    /**
+     * Returns instance of this class.
+     * @param <P> The phenotype of chromosome
+     * @return instance
+     */
+    @SuppressWarnings("unchecked")
+    public static <P> ConvergenceListenerRegistry<P> getInstance() {
+        return INSTANCE;
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java
new file mode 100644
index 0000000..b0f7276
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java
@@ -0,0 +1,49 @@
+/*
+ * 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.listener;
+
+import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Logs population statistics during the convergence process.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public final class PopulationStatisticsLogger<P> implements ConvergenceListener<P> {
+
+    /** instance of log4j logger. **/
+    private static final Logger LOGGER = LoggerFactory.getLogger(PopulationStatisticsLogger.class);
+
+    /**
+     * Logs the population statistics during the process of convergence.
+     */
+    @Override
+    public void notify(int generation, Population<P> population) {
+        final PopulationStatisticalSummary<P> populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>(
+                population);
+        LOGGER.info(
+                "Population statistics for generation %d ::: Mean Fitness: %f, Max Fitness: %f, Fitness Variance: %f",
+                generation, populationStatisticalSummary.getMeanFitness(), populationStatisticalSummary.getMaxFitness(),
+                populationStatisticalSummary.getFitnessVariance());
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java
new file mode 100644
index 0000000..f2e0d4a
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/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.listener;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java
new file mode 100644
index 0000000..3e41fc2
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java
@@ -0,0 +1,101 @@
+/*
+ * 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.mutation;
+
+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.Chromosome;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * This abstraction represents an abstract mutation policy for ListChromosomes.
+ * @param <T> genotype of chromosome
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public abstract class AbstractListChromosomeMutationPolicy<T, P> implements MutationPolicy<P> {
+
+    /**
+     * Mutate the given chromosome. Randomly changes few genes depending on mutation
+     * rate.
+     * @param original     the original chromosome.
+     * @param mutationRate the rate of mutation per gene
+     * @return the mutated chromosome.
+     */
+    @Override
+    public Chromosome<P> mutate(Chromosome<P> original, double mutationRate) {
+        // check for validity.
+        checkValidity(original);
+
+        @SuppressWarnings("unchecked")
+        final AbstractListChromosome<T, P> chromosome = (AbstractListChromosome<T, P>) original;
+        final List<T> newRep = new ArrayList<>(chromosome.getRepresentation());
+
+        final Set<Integer> mutableGeneIndexes = getMutableGeneIndexes(chromosome.getLength(), mutationRate);
+        for (int mutableGeneIndex : mutableGeneIndexes) {
+            newRep.set(mutableGeneIndex, mutateGene(newRep.get(mutableGeneIndex)));
+        }
+
+        return chromosome.newChromosome(newRep);
+    }
+
+    /**
+     * Checks input chromosome validity.
+     * @param original chromosome to be mutated
+     */
+    protected abstract void checkValidity(Chromosome<P> original);
+
+    /**
+     * Selects and returns mutable gene indexes based on mutation rate.
+     * @param length       no of alleles/genes in chromosome
+     * @param mutationRate mutation rate of the allele/gene
+     * @return mutable gene indexes
+     */
+    protected Set<Integer> getMutableGeneIndexes(int length, double mutationRate) {
+
+        // calculate the total mutation rate of all the alleles i.e. chromosome.
+        final double chromosomeMutationRate = mutationRate * length;
+        final Set<Integer> indexSet = new HashSet<>();
+        final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider();
+
+        // if chromosomeMutationRate >= 1 then more than one allele will be mutated.
+        if (chromosomeMutationRate >= 1) {
+            final int noOfMutation = (int) Math.round(chromosomeMutationRate);
+            while (indexSet.size() < noOfMutation) {
+                indexSet.add(randomProvider.nextInt(length));
+            }
+        } else if (randomProvider.nextDouble() < chromosomeMutationRate) {
+            indexSet.add(randomProvider.nextInt(length));
+        }
+
+        return indexSet;
+    }
+
+    /**
+     * Mutates an individual gene/allele.
+     * @param originalValue the original value of gene
+     * @return mutated value of gene
+     */
+    protected abstract T mutateGene(T originalValue);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java
new file mode 100644
index 0000000..2470f38
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.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.mutation;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+
+/**
+ * Mutation for {@link BinaryChromosome}s. Randomly changes few genes.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class BinaryMutation<P> implements MutationPolicy<P> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Chromosome<P> mutate(Chromosome<P> original, double mutationRate) {
+        // check for validity.
+        checkValidity(original);
+        final BinaryChromosome<P> chromosome = (BinaryChromosome<P>) original;
+        final long[] representation = chromosome.getRepresentation();
+        final long[] newRep = new long[representation.length];
+        System.arraycopy(representation, 0, newRep, 0, representation.length);
+
+        final Map<Integer, Set<Integer>> mutableGeneIndexMap = getMutableGeneIndexes(chromosome.getLength(),
+                mutationRate);
+        for (Entry<Integer, Set<Integer>> entry : mutableGeneIndexMap.entrySet()) {
+            final int alleleBlockIndex = entry.getKey();
+            long mask = 0;
+            final Set<Integer> alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex);
+            for (int index : alleleElementIndexes) {
+                mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index);
+            }
+            newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask;
+        }
+//        for (int alleleBlockIndex : mutableGeneIndexMap.keySet()) {
+//            long mask = 0;
+//            final Set<Integer> alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex);
+//            for (int index : alleleElementIndexes) {
+//                mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index);
+//            }
+//            newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask;
+//        }
+
+        return chromosome.newChromosome(newRep, chromosome.getLength());
+    }
+
+    /**
+     * Checks input chromosome validity.
+     * @param original chromosome to be mutated
+     */
+    protected void checkValidity(Chromosome<P> original) {
+        if (!BinaryChromosome.class.isAssignableFrom(original.getClass())) {
+            throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName());
+        }
+    }
+
+    /**
+     * Selects and returns mutable gene indexes based on mutation rate.
+     * @param length       no of alleles/genes in chromosome
+     * @param mutationRate mutation rate of the allele/gene
+     * @return mutable gene indexes
+     */
+    protected Map<Integer, Set<Integer>> getMutableGeneIndexes(long length, double mutationRate) {
+
+        // calculate the total mutation rate of all the alleles i.e. chromosome.
+        final double chromosomeMutationRate = mutationRate * length;
+        final Map<Integer, Set<Integer>> indexMap = new HashMap<>();
+
+        // if chromosomeMutationRate >= 1 then more than one allele will be mutated.
+        if (chromosomeMutationRate >= 1) {
+            final int noOfMutation = (int) Math.round(chromosomeMutationRate);
+            final Set<Long> mutationIndexes = new HashSet<>();
+            for (int i = 0; i < noOfMutation; i++) {
+                final long mutationIndex = generateMutationIndex(length, mutationIndexes);
+                mutationIndexes.add(mutationIndex);
+                updateIndexMap(indexMap, length, mutationIndex);
+            }
+        } else if (RandomProviderManager.getRandomProvider().nextDouble() < chromosomeMutationRate) {
+            updateIndexMap(indexMap, length);
+        }
+        return indexMap;
+    }
+
+    private long generateMutationIndex(long length, Set<Long> mutationIndexes) {
+        long mutationIndex = 0;
+        do {
+            mutationIndex = RandomProviderManager.getRandomProvider().nextLong(length);
+        } while (mutationIndexes.contains(mutationIndex));
+        return mutationIndex;
+    }
+
+    private void updateIndexMap(Map<Integer, Set<Integer>> indexMap, long length, long mutationIndex) {
+        final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE);
+        final long offsettedMutableAlleleIndex = offset + mutationIndex;
+
+        final int alleleBlockIndex = (int) (offsettedMutableAlleleIndex / Long.SIZE);
+
+        if (!indexMap.containsKey(alleleBlockIndex)) {
+            indexMap.put(alleleBlockIndex, new HashSet<>());
+        }
+        final int alleleElementIndex = (int) (offsettedMutableAlleleIndex % Long.SIZE);
+
+        indexMap.get(alleleBlockIndex).add(alleleElementIndex);
+    }
+
+    private void updateIndexMap(Map<Integer, Set<Integer>> indexMap, long length) {
+        updateIndexMap(indexMap, length, RandomProviderManager.getRandomProvider().nextLong(length));
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java
new file mode 100644
index 0000000..2ff7f0f
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java
@@ -0,0 +1,88 @@
+/*
+ * 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.mutation;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+
+/**
+ * Mutation for {@link IntegralValuedChromosome}. Randomly changes few genes.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class IntegralValuedMutation<P> extends AbstractListChromosomeMutationPolicy<Integer, P> {
+
+    /** minimum acceptable value of allele. **/
+    private final int min;
+
+    /** maximum acceptable value of allele. **/
+    private final int max;
+
+    /**
+     * @param min minimum value of allele
+     * @param max maximum value of allele
+     */
+    public IntegralValuedMutation(final int min, final int max) {
+        this.min = min;
+        this.max = max;
+        if (min >= max) {
+            throw new GeneticException(GeneticException.TOO_LARGE, min, max);
+        }
+    }
+
+    /**
+     * Returns the minimum acceptable value.
+     * @return minimum
+     */
+    public int getMin() {
+        return min;
+    }
+
+    /**
+     * Returns the maximum acceptable value.
+     * @return maximum
+     */
+    public int getMax() {
+        return max;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void checkValidity(Chromosome<P> original) {
+        if (!IntegralValuedChromosome.class.isAssignableFrom(original.getClass())) {
+            throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName());
+        }
+        final IntegralValuedChromosome<P> chromosome = (IntegralValuedChromosome<P>) original;
+        if (chromosome.getMin() != this.min || chromosome.getMax() != this.max) {
+            throw new GeneticException(GeneticException.ILLEGAL_RANGE, this.min, this.max, chromosome.getMin(),
+                    chromosome.getMax());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Integer mutateGene(Integer originalValue) {
+        return min + RandomProviderManager.getRandomProvider().nextInt(max - min);
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java
new file mode 100644
index 0000000..096e419
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java
@@ -0,0 +1,36 @@
+/*
+ * 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.mutation;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+
+/**
+ * Algorithm used to mutate a chromosome.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public interface MutationPolicy<P> {
+
+    /**
+     * Mutate the given chromosome.
+     * @param original     the original chromosome.
+     * @param mutationRate The probability of mutation
+     * @return the mutated chromosome.
+     */
+    Chromosome<P> mutate(Chromosome<P> original, double mutationRate);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java
new file mode 100644
index 0000000..3dcbdcf
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java
@@ -0,0 +1,97 @@
+/*
+ * 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.mutation;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.chromosome.RealValuedChromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+
+/**
+ * This class mutates real-valued chromosome.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public class RealValuedMutation<P> extends AbstractListChromosomeMutationPolicy<Double, P> {
+
+    /** minimum value of chromosome gene/allele. **/
+    private final double min;
+
+    /** maximum value of chromosome gene/allele. **/
+    private final double max;
+
+    /**
+     * Constructs the mutation operator with normalized range of double values.
+     */
+    public RealValuedMutation() {
+        this.min = 0d;
+        this.max = 1d;
+    }
+
+    /**
+     * Constructs the mutation operator with provided range of double values.
+     * @param min minimum inclusive value of allele
+     * @param max maximum exclusive value of allele
+     */
+    public RealValuedMutation(double min, double max) {
+        this.min = min;
+        this.max = max;
+        if (min >= max) {
+            throw new GeneticException(GeneticException.TOO_LARGE, min, max);
+        }
+    }
+
+    /**
+     * Returns the minimum acceptable value.
+     * @return minimum
+     */
+    public double getMin() {
+        return min;
+    }
+
+    /**
+     * Returns the maximum acceptable value.
+     * @return maximum
+     */
+    public double getMax() {
+        return max;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void checkValidity(Chromosome<P> original) {
+        if (!RealValuedChromosome.class.isAssignableFrom(original.getClass())) {
+            throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName());
+        }
+        final RealValuedChromosome<P> chromosome = (RealValuedChromosome<P>) original;
+        if (chromosome.getMin() != this.min || chromosome.getMax() != this.max) {
+            throw new GeneticException(GeneticException.ILLEGAL_RANGE, this.min, this.max, chromosome.getMin(),
+                    chromosome.getMax());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Double mutateGene(Double originalValue) {
+        return min + RandomProviderManager.getRandomProvider().nextDouble() * (max - min);
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java
new file mode 100644
index 0000000..30c90e4
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/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.mutation;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java
new file mode 100644
index 0000000..555465f
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.mutation.rategenerator;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+
+/**
+ * Generates mutation rate using linear function of relative rank of input
+ * chromosome in population.
+ * @param <P> phenotype of chromosome
+ */
+public class AdaptiveLinearMutationRateGenerator<P> implements MutationRateGenerator<P> {
+
+    /** minimum crossover rate. **/
+    private final double minimumRate;
+
+    /** maximum crossover rate. **/
+    private final double maximumRate;
+
+    /**
+     * @param minimumRate minimum mutation rate
+     * @param maximumRate maximum mutation rate
+     */
+    public AdaptiveLinearMutationRateGenerator(double minimumRate, double maximumRate) {
+        this.minimumRate = minimumRate;
+        this.maximumRate = maximumRate;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double generate(Chromosome<P> chromosome, PopulationStatisticalSummary<P> populationStats, int generation) {
+        return minimumRate + (maximumRate - minimumRate) *
+                (1.0 - (double) populationStats.findRank(chromosome) / (populationStats.getPopulationSize() - 1));
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java
new file mode 100644
index 0000000..097e98d
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java
@@ -0,0 +1,47 @@
+/*
+ * 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.mutation.rategenerator;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+
+/**
+ * This class represents a constant mutation rate generator.
+ * @param <P> phenotype of chromosome
+ */
+public class ConstantMutationRateGenerator<P> implements MutationRateGenerator<P> {
+
+    /** the constant mutationRate. **/
+    private final double mutationRate;
+
+    /**
+     * @param mutationRate mutation rate
+     */
+    public ConstantMutationRateGenerator(double mutationRate) {
+        this.mutationRate = mutationRate;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double generate(Chromosome<P> chromosome, PopulationStatisticalSummary<P> populationStats, int generation) {
+        return mutationRate;
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.java
new file mode 100644
index 0000000..a206e02
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.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.mutation.rategenerator;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary;
+
+/**
+ * This abstraction represents mutation rate generator.
+ * @param <P> phenotype of chromosome
+ */
+public interface MutationRateGenerator<P> {
+
+    /**
+     * Generates mutation rate based on input params.
+     * @param chromosome      chromosome
+     * @param populationStats population statictics summary
+     * @param generation      generation count
+     * @return mutation rate
+     */
+    double generate(Chromosome<P> chromosome, PopulationStatisticalSummary<P> populationStats, int generation);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java
new file mode 100644
index 0000000..c7e56ce
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/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.mutation.rategenerator;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java
new file mode 100644
index 0000000..40fa263
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/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;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java
new file mode 100644
index 0000000..1622d9c
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java
@@ -0,0 +1,220 @@
+/*
+ * 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.population;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+
+/**
+ * Population of chromosomes represented by a {@link List}.
+ *
+ * @param <P> phenotype of chromosome
+ * @since 2.0
+ */
+public class ListPopulation<P> implements Population<P> {
+
+    /** new line constant. **/
+    public static final String NEW_LINE = System.getProperty("line.separator");
+
+    /** List of chromosomes. */
+    private final List<Chromosome<P>> chromosomes;
+
+    /** maximal size of the population. */
+    private int populationLimit;
+
+    /**
+     * Creates a new ListPopulation instance and initializes its inner chromosome
+     * list.
+     *
+     * @param populationLimit maximal size of the population
+     */
+    public ListPopulation(final int populationLimit) {
+        this(Collections.<Chromosome<P>>emptyList(), populationLimit);
+    }
+
+    /**
+     * Creates a new ListPopulation instance.
+     * <p>
+     * Note: the chromosomes of the specified list are added to the population.
+     *
+     * @param chromosomes     list of chromosomes to be added to the population
+     * @param populationLimit maximal size of the population
+     */
+    public ListPopulation(final List<Chromosome<P>> chromosomes, final int populationLimit) {
+
+        Objects.requireNonNull(chromosomes);
+
+        if (populationLimit <= 0) {
+            throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, populationLimit);
+        }
+        if (chromosomes.size() > populationLimit) {
+            throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+                    chromosomes.size(), populationLimit);
+        }
+        this.populationLimit = populationLimit;
+        this.chromosomes = new ArrayList<>(populationLimit);
+        this.chromosomes.addAll(chromosomes);
+    }
+
+    /**
+     * Add a {@link Collection} of chromosomes to this {@link Population}.
+     * @param chromosomeColl a {@link Collection} of chromosomes
+     * @since 3.1
+     */
+    public void addChromosomes(final Collection<Chromosome<P>> chromosomeColl) {
+        if (chromosomes.size() + chromosomeColl.size() > populationLimit) {
+            throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+                    chromosomes.size(), populationLimit);
+        }
+        this.chromosomes.addAll(chromosomeColl);
+    }
+
+    /**
+     * Returns an unmodifiable list of the chromosomes in this population.
+     * @return the unmodifiable list of chromosomes
+     */
+    public List<Chromosome<P>> getChromosomes() {
+        return Collections.unmodifiableList(chromosomes);
+    }
+
+    /**
+     * Access the list of chromosomes.
+     * @return the list of chromosomes
+     * @since 3.1
+     */
+    protected List<Chromosome<P>> getChromosomeList() {
+        return chromosomes;
+    }
+
+    /**
+     * Add the given chromosome to the population.
+     * @param chromosome the chromosome to add.
+     */
+    @Override
+    public void addChromosome(final Chromosome<P> chromosome) {
+        if (chromosomes.size() >= populationLimit) {
+            throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+                    chromosomes.size(), populationLimit);
+        }
+        this.chromosomes.add(chromosome);
+    }
+
+    /**
+     * Access the fittest chromosome in this population.
+     * @return the fittest chromosome.
+     */
+    @Override
+    public Chromosome<P> getFittestChromosome() {
+        // best so far
+        return Collections.max(this.chromosomes);
+    }
+
+    /**
+     * Access the maximum population size.
+     * @return the maximum population size.
+     */
+    @Override
+    public int getPopulationLimit() {
+        return this.populationLimit;
+    }
+
+    /**
+     * Sets the maximal population size.
+     * @param populationLimit maximal population size.
+     */
+    public void setPopulationLimit(final int populationLimit) {
+        if (populationLimit <= 0) {
+            throw new GeneticException(GeneticException.POPULATION_LIMIT_NOT_POSITIVE, populationLimit);
+        }
+        if (populationLimit < chromosomes.size()) {
+            throw new GeneticException(GeneticException.POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE,
+                    populationLimit, chromosomes.size());
+        }
+        this.populationLimit = populationLimit;
+    }
+
+    /**
+     * Access the current population size.
+     * @return the current population size.
+     */
+    @Override
+    public int getPopulationSize() {
+        return this.chromosomes.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        final StringBuilder populationStrRepr = new StringBuilder();
+        for (Chromosome<P> chromosome : chromosomes) {
+            populationStrRepr.append(chromosome.toString());
+            populationStrRepr.append(NEW_LINE);
+        }
+        return populationStrRepr.toString();
+    }
+
+    /**
+     * Returns an iterator over the unmodifiable list of chromosomes.
+     * <p>
+     * Any call to {@link Iterator#remove()} will result in a
+     * {@link UnsupportedOperationException}.
+     * </p>
+     *
+     * @return chromosome iterator
+     */
+    @Override
+    public Iterator<Chromosome<P>> iterator() {
+        return getChromosomes().iterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Population<P> nextGeneration(final double elitismRate) {
+        final List<Chromosome<P>> oldChromosomes = getChromosomeList();
+
+        if ((int) (oldChromosomes.size() * elitismRate) == 0) {
+            // if no of elite chromosome is 0 crete and return an empty population instance.
+            return new ListPopulation<>(getPopulationLimit());
+        } else {
+            // create a new generation of chromosomes with same parameters and add the elit
+            // individuals.
+            final ListPopulation<P> nextGeneration = new ListPopulation<>(getPopulationLimit());
+
+            // Sort the chromosome according to ascending order of fitness.
+            Collections.sort(oldChromosomes);
+
+            // index of the last "not good enough" chromosome
+            final int boundIndex = (int) Math.ceil((1.0 - elitismRate) * oldChromosomes.size());
+            for (int i = boundIndex; i < oldChromosomes.size(); i++) {
+                nextGeneration.addChromosome(oldChromosomes.get(i));
+            }
+            return nextGeneration;
+        }
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java
new file mode 100644
index 0000000..65cfe5f
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.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.population;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+
+/**
+ * A collection of chromosomes that facilitates generational evolution.
+ *
+ * @param <P> phenotype of chromosome
+ * @since 2.0
+ */
+public interface Population<P> extends Iterable<Chromosome<P>> {
+
+    /**
+     * Access the current population size.
+     * @return the current population size.
+     */
+    int getPopulationSize();
+
+    /**
+     * Access the maximum population size.
+     * @return the maximum population size.
+     */
+    int getPopulationLimit();
+
+    /**
+     * Start the population for the next generation.
+     * @param elitismRate the Elitism Rate
+     * @return the beginnings of the next generation.
+     */
+    Population<P> nextGeneration(double elitismRate);
+
+    /**
+     * Add the given chromosome to the population.
+     * @param chromosome the chromosome to add.
+     */
+    void addChromosome(Chromosome<P> chromosome);
+
+    /**
+     * Access the fittest chromosome in this population.
+     * @return the fittest chromosome.
+     */
+    Chromosome<P> getFittestChromosome();
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java
new file mode 100644
index 0000000..4d77dc9
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/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.population;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java
new file mode 100644
index 0000000..7c8682a
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java
@@ -0,0 +1,35 @@
+/*
+ * 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.selection;
+
+import org.apache.commons.math4.ga.chromosome.ChromosomePair;
+import org.apache.commons.math4.ga.population.Population;
+
+/**
+ * Algorithm used to select a chromosome pair from a population.
+ * @param <P> phenotype of chromosome
+ * @since 2.0
+ */
+public interface SelectionPolicy<P> {
+
+    /**
+     * Select two chromosomes from the population.
+     * @param population the population from which the chromosomes are chosen.
+     * @return the selected chromosomes.
+     */
+    ChromosomePair<P> select(Population<P> population);
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java
new file mode 100644
index 0000000..8240f5d
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java
@@ -0,0 +1,108 @@
+/*
+ * 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.selection;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+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;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.utils.RandomProviderManager;
+
+/**
+ * Tournament selection scheme. Each of the two selected chromosomes is selected
+ * based on n-ary tournament -- this is done by drawing {@link #arity} random
+ * chromosomes without replacement from the population, and then selecting the
+ * fittest chromosome among them.
+ * @param <P> phenotype of chromosome
+ * @since 2.0
+ */
+public class TournamentSelection<P> implements SelectionPolicy<P> {
+
+    /** number of chromosomes included in the tournament selections. */
+    private final int arity;
+
+    /**
+     * Creates a new TournamentSelection instance.
+     *
+     * @param arity how many chromosomes will be drawn to the tournament
+     */
+    public TournamentSelection(final int arity) {
+        this.arity = arity;
+    }
+
+    /**
+     * Select two chromosomes from the population. Each of the two selected
+     * chromosomes is selected based on n-ary tournament -- this is done by drawing
+     * {@link #arity} random chromosomes without replacement from the population,
+     * and then selecting the fittest chromosome among them.
+     *
+     * @param population the population from which the chromosomes are chosen.
+     * @return the selected chromosomes.
+     */
+    @Override
+    public ChromosomePair<P> select(final Population<P> population) {
+        if (!(population instanceof ListPopulation<?>)) {
+            throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, population);
+        }
+        return new ChromosomePair<>(tournament((ListPopulation<P>) population),
+                tournament((ListPopulation<P>) population));
+    }
+
+    /**
+     * Helper for {@link #select(Population)}. Draw {@link #arity} random
+     * chromosomes without replacement from the population, and then select the
+     * fittest chromosome among them.
+     *
+     * @param population the population from which the chromosomes are chosen.
+     * @return the selected chromosome.
+     */
+    private Chromosome<P> tournament(final ListPopulation<P> population) {
+        if (population.getPopulationSize() < this.arity) {
+            throw new GeneticException(GeneticException.TOO_LARGE, arity, population.getPopulationSize());
+        }
+
+        // create a copy of the chromosome list
+        final List<Chromosome<P>> chromosomes = new ArrayList<>(population.getChromosomes());
+        final List<Chromosome<P>> selectedChromosomes = new ArrayList<>();
+
+        for (int i = 0; i < this.arity; i++) {
+            // select a random individual and add it to the tournament
+            final int rind = RandomProviderManager.getRandomProvider().nextInt(chromosomes.size());
+            selectedChromosomes.add(chromosomes.get(rind));
+            // do not select it again
+            chromosomes.remove(rind);
+        }
+
+        // the winner takes it all
+        return Collections.max(selectedChromosomes);
+    }
+
+    /**
+     * Gets the arity (number of chromosomes drawn to the tournament).
+     *
+     * @return arity of the tournament
+     */
+    public int getArity() {
+        return arity;
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java
new file mode 100644
index 0000000..b8ba3ae
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/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.selection;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java
new file mode 100644
index 0000000..e24f3a0
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java
@@ -0,0 +1,67 @@
+/*
+ * 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.stats;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+
+/**
+ * This interface represents the statistical summary for population fitness.
+ * @param <P> phenotype of chromosome
+ * @since 4.0
+ */
+public interface PopulationStatisticalSummary<P> {
+
+    /**
+     * Returns the arithmetic mean of population fitness.
+     * @return The mean or Double.NaN if no values have been added.
+     */
+    double getMeanFitness();
+
+    /**
+     * Returns the variance of the population fitness.
+     * @return The variance, Double.NaN if no values have been added or 0.0 for a
+     *         single value set.
+     */
+    double getFitnessVariance();
+
+    /**
+     * Returns the minimum fitness of the population.
+     * @return The max or Double.NaN if no values have been added.
+     */
+    double getMinFitness();
+
+    /**
+     * Returns the maximum fitness of the population.
+     * @return The max or Double.NaN if no values have been added.
+     */
+    double getMaxFitness();
+
+    /**
+     * Returns the population size.
+     * @return The number of available values
+     */
+    long getPopulationSize();
+
+    /**
+     * Calculates the rank of chromosome in population based on its fitness.
+     * @param chromosome chromosome, for which rank would be found
+     * @return the rank of chromosome
+     */
+    int findRank(Chromosome<P> chromosome);
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java
new file mode 100644
index 0000000..f40707d
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/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.stats;
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java
new file mode 100644
index 0000000..4750335
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java
@@ -0,0 +1,211 @@
+/*
+ * 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.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * This interface generates all random representations for chromosomes.
+ * @since 4.0
+ */
+public interface ChromosomeRepresentationUtils {
+
+    /**
+     * Generates a representation corresponding to a random permutation of length l
+     * which can be passed to the RandomKey constructor.
+     *
+     * @param l length of the permutation
+     * @return representation of a random permutation
+     */
+    static List<Double> randomPermutation(final int l) {
+        final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider();
+        final List<Double> repr = new ArrayList<>(l);
+        for (int i = 0; i < l; i++) {
+            repr.add(randomProvider.nextDouble());
+        }
+        return repr;
+    }
+
+    /**
+     * Generates a representation corresponding to an identity permutation of length
+     * l which can be passed to the RandomKey constructor.
+     *
+     * @param l length of the permutation
+     * @return representation of an identity permutation
+     */
+    static List<Double> identityPermutation(final int l) {
+        final List<Double> repr = new ArrayList<>(l);
+        for (int i = 0; i < l; i++) {
+            repr.add((double) i / l);
+        }
+        return repr;
+    }
+
+    /**
+     * Generates a representation of a permutation corresponding to the
+     * <code>data</code> sorted by <code>comparator</code>. The <code>data</code> is
+     * not modified during the process.
+     *
+     * This is useful if you want to inject some permutations to the initial
+     * population.
+     *
+     * @param <S>        type of the data
+     * @param data       list of data determining the order
+     * @param comparator how the data will be compared
+     * @return list representation of the permutation corresponding to the
+     *         parameters
+     */
+    static <S> List<Double> comparatorPermutation(final List<S> data, final Comparator<S> comparator) {
+        final List<S> sortedData = new ArrayList<>(data);
+        Collections.sort(sortedData, comparator);
+
+        return inducedPermutation(data, sortedData);
+    }
+
+    /**
+     * Generates a representation of a permutation corresponding to a permutation
+     * which yields <code>permutedData</code> when applied to
+     * <code>originalData</code>.
+     *
+     * This method can be viewed as an inverse to decode().
+     *
+     * @param <S>          type of the data
+     * @param originalData the original, unpermuted data
+     * @param permutedData the data, somehow permuted
+     * @return representation of a permutation corresponding to the permutation
+     *         {@code originalData -> permutedData}
+     */
+    static <S> List<Double> inducedPermutation(final List<S> originalData, final List<S> permutedData) {
+
+        if (originalData.size() != permutedData.size()) {
+            throw new GeneticException(GeneticException.SIZE_MISMATCH, permutedData.size(), originalData.size());
+        }
+        final int l = originalData.size();
+
+        final List<S> origDataCopy = new ArrayList<>(originalData);
+
+        final Double[] res = new Double[l];
+        for (int i = 0; i < l; i++) {
+            final int index = origDataCopy.indexOf(permutedData.get(i));
+            if (index == -1) {
+                throw new GeneticException(GeneticException.DIFFERENT_ORIG_AND_PERMUTED_DATA);
+            }
+            res[index] = (double) i / l;
+            origDataCopy.set(index, null);
+        }
+        return Arrays.asList(res);
+    }
+
+    /**
+     * Returns a representation of a random binary array of length
+     * <code>length</code>.
+     * @param length length of the array
+     * @param min    minimum inclusive value of allele
+     * @param max    maximum exclusive value of allele
+     * @return a random binary array of length <code>length</code>
+     */
+    static List<Integer> randomIntegralRepresentation(final int length, final int min, final int max) {
+        final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider();
+        final List<Integer> rList = new ArrayList<>(length);
+        for (int j = 0; j < length; j++) {
+            rList.add(min + randomProvider.nextInt(max - min));
+        }
+        return rList;
+    }
+
+    /**
+     * Returns a representation of a random binary array of length
+     * <code>length</code>.
+     * @param length length of the array
+     * @return a random binary array of length <code>length</code>
+     */
+    static long[] randomBinaryRepresentation(final long length) {
+        if (length > BinaryChromosome.MAX_LENGTH) {
+            throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT,
+                    "length exceeded the max length " + BinaryChromosome.MAX_LENGTH);
+        }
+        final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider();
+        int elementCount = (int) Math.ceil(length / (double)Long.SIZE);
+        // random binary list
+        final long[] representation = new long[elementCount];
+        int remainder = (int) (length % Long.SIZE);
+        representation[0] = remainder == 0 ? randomProvider.nextLong() :
+                randomProvider.nextLong((long) Math.pow(2, remainder));
+        for (int i = 1; i < elementCount; i++) {
+            representation[i] = randomProvider.nextLong();
+        }
+        return representation;
+    }
+
+    /**
+     * Generates a random string representation of chromosome with specified
+     * characters.
+     * @param alleles characters representing alleles
+     * @param length  length of chromosome
+     * @return returns chromosome representation as string
+     */
+    static String randomStringRepresentation(char[] alleles, final long length) {
+        Objects.requireNonNull(alleles);
+        final StringBuilder representationStr = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            representationStr
+                    .append(alleles[(int) (RandomProviderManager.getRandomProvider().nextInt(alleles.length))]);
+        }
+        return representationStr.toString();
+    }
+
+    /**
+     * Generates a representation corresponding to a random double values[0..1] of
+     * length l.
+     * @param l length of the permutation
+     * @return representation of a random permutation
+     */
+    static List<Double> randomNormalizedDoubleRepresentation(final int l) {
+        return randomDoubleRepresentation(l, 0, 1);
+    }
+
+    /**
+     * Generates a representation corresponding to a random double values of length
+     * l.
+     * @param l   length of representation
+     * @param min minimum inclusive value of chromosome gene
+     * @param max maximum exclusive value of chromosome gene
+     * @return representation as List of Double
+     */
+    static List<Double> randomDoubleRepresentation(final int l, double min, double max) {
+        if (min >= max) {
+            throw new GeneticException(GeneticException.TOO_LARGE, min, max);
+        }
+        final double range = max - min;
+        final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider();
+        final List<Double> repr = new ArrayList<>(l);
+        for (int i = 0; i < l; i++) {
+            repr.add(min + randomProvider.nextDouble() * range);
+        }
+        return repr;
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java
new file mode 100644
index 0000000..0c01896
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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.utils;
+
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.rng.simple.ThreadLocalRandomSource;
+
+/**
+ * An utility to generate per thread {@link UniformRandomProvider} instance.
+ * @since 4.0
+ */
+public final class RandomProviderManager {
+
+    /** The default RandomSource for random number generation. **/
+    private static RandomSource randomSource = RandomSource.XO_RO_SHI_RO_128_PP;
+
+    /**
+     * constructs the singleton instance.
+     */
+    private RandomProviderManager() {
+    }
+
+    /**
+     * Returns the (static) random generator.
+     * @return the static random generator shared by GA implementation classes
+     */
+    public static UniformRandomProvider getRandomProvider() {
+        return ThreadLocalRandomSource.current(RandomProviderManager.randomSource);
+    }
+
+}
diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java
new file mode 100644
index 0000000..0e8c79a
--- /dev/null
+++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/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.utils;
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java
new file mode 100644
index 0000000..99a3785
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java
@@ -0,0 +1,187 @@
+/*
+ * 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 java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.convergence.FixedGenerationCount;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover;
+import org.apache.commons.math4.ga.crossover.OnePointCrossover;
+import org.apache.commons.math4.ga.decoder.Decoder;
+import org.apache.commons.math4.ga.fitness.FitnessFunction;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.listener.ConvergenceListener;
+import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry;
+import org.apache.commons.math4.ga.mutation.BinaryMutation;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.selection.TournamentSelection;
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is also an example of usage.
+ */
+public class GeneticAlgorithmTestBinaryOneMax {
+
+    // parameters for the GA
+    private static final int DIMENSION = 50;
+    private static final int POPULATION_SIZE = 50;
+    private static final int NUM_GENERATIONS = 50;
+    private static final double CROSSOVER_RATE = 1;
+    private static final double MUTATION_RATE = 0.1;
+    private static final int TOURNAMENT_ARITY = 2;
+
+    @Test
+    public void test() {
+        removeListeners();
+
+        // initialize a new genetic algorithm
+        GeneticAlgorithm<List<Integer>> ga = new GeneticAlgorithm<>(new OnePointBinaryCrossover<List<Integer>>(),
+                CROSSOVER_RATE, new BinaryMutation<List<Integer>>(), MUTATION_RATE,
+                new TournamentSelection<List<Integer>>(TOURNAMENT_ARITY));
+
+        Assertions.assertEquals(0, ga.getGenerationsEvolved());
+
+        // initial population
+        Population<List<Integer>> initial = randomPopulation();
+        // stopping conditions
+        StoppingCondition<List<Integer>> stopCond = new FixedGenerationCount<>(NUM_GENERATIONS);
+
+        // best initial chromosome
+        Chromosome<List<Integer>> bestInitial = initial.getFittestChromosome();
+
+        // run the algorithm
+        Population<List<Integer>> finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        Chromosome<List<Integer>> bestFinal = finalPopulation.getFittestChromosome();
+
+        // the only thing we can test is whether the final solution is not worse than
+        // the initial one
+        // however, for some implementations of GA, this need not be true :)
+
+        Assertions.assertTrue(bestFinal.compareTo(bestInitial) > 0);
+        Assertions.assertEquals(NUM_GENERATIONS, ga.getGenerationsEvolved());
+
+    }
+
+    private void removeListeners() {
+        try {
+            ConvergenceListenerRegistry<String> registry = ConvergenceListenerRegistry.<String>getInstance();
+            Field listenersField = registry.getClass().getDeclaredField("listeners");
+            boolean accessible = listenersField.isAccessible();
+            if (!accessible) {
+                listenersField.setAccessible(true);
+            }
+            @SuppressWarnings("unchecked")
+            List<ConvergenceListener<String>> listeners = (List<ConvergenceListener<String>>) listenersField
+                    .get(ConvergenceListenerRegistry.getInstance());
+            listeners.clear();
+            listenersField.setAccessible(accessible);
+        } catch (NoSuchFieldException | SecurityException e) {
+            e.printStackTrace();
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Initializes a random population.
+     */
+    private ListPopulation<List<Integer>> randomPopulation() {
+        List<Chromosome<List<Integer>>> popList = new LinkedList<>();
+
+        for (int i = 0; i < POPULATION_SIZE; i++) {
+            BinaryChromosome<List<Integer>> randChrom = new FindOnes(
+                    ChromosomeRepresentationUtils.randomBinaryRepresentation(DIMENSION), DIMENSION);
+            popList.add(randChrom);
+        }
+        return new ListPopulation<>(popList, popList.size());
+    }
+
+    /**
+     * Chromosomes represented by a binary chromosome.
+     *
+     * The goal is to set all bits (genes) to 1.
+     */
+    private class FindOnes extends BinaryChromosome<List<Integer>> {
+
+        FindOnes(long[] representation, long length) {
+            super(representation, length, new OneMaxFitnessFunction(), new OneMaxDecoder());
+        }
+    }
+
+    private class OneMaxFitnessFunction implements FitnessFunction<List<Integer>> {
+
+        @Override
+        public double compute(List<Integer> decodedChromosome) {
+            double value = 0;
+            for (Integer allele : decodedChromosome) {
+                value += allele;
+            }
+            return value;
+        }
+
+    }
+
+    private class OneMaxDecoder implements Decoder<List<Integer>> {
+
+        @Override
+        public List<Integer> decode(Chromosome<List<Integer>> chromosome) {
+            BinaryChromosome<List<Integer>> binaryChromosome = (BinaryChromosome<List<Integer>>) chromosome;
+            List<Integer> phenotype = new ArrayList<>();
+            long[] representation = binaryChromosome.getRepresentation();
+            for (int i = 0; i < representation.length; i++) {
+                String value = Long.toUnsignedString(representation[i], 2);
+                for (int j = 64 - value.length(); j > 0; j--) {
+                    phenotype.add(Integer.valueOf(0));
+                }
+                for (int j = 0; j < value.length(); j++) {
+                    phenotype.add(Integer.parseInt("" + value.charAt(j)));
+                }
+            }
+            return phenotype;
+        }
+    }
+
+    @Test
+    public void testCrossoverRate() {
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new GeneticAlgorithm<>(new OnePointCrossover<>(), 1.5, new BinaryMutation<>(), .01,
+                    new TournamentSelection<>(10));
+        });
+    }
+
+    @Test
+    public void testMutationRate() {
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new GeneticAlgorithm<>(new OnePointCrossover<>(), .5, new BinaryMutation<>(), 1.5,
+                    new TournamentSelection<>(10));
+        });
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java
new file mode 100644
index 0000000..4bfb2ec
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java
@@ -0,0 +1,144 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.chromosome.RealValuedChromosome;
+import org.apache.commons.math4.ga.convergence.FixedGenerationCount;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.crossover.OnePointCrossover;
+import org.apache.commons.math4.ga.decoder.RandomKeyDecoder;
+import org.apache.commons.math4.ga.fitness.FitnessFunction;
+import org.apache.commons.math4.ga.mutation.RealValuedMutation;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.apache.commons.math4.ga.selection.TournamentSelection;
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is also an example of usage.
+ *
+ * This algorithm does "stochastic sorting" of a sequence 0,...,N.
+ *
+ */
+public class GeneticAlgorithmTestPermutations {
+
+    // parameters for the GA
+    private static final int DIMENSION = 20;
+    private static final int POPULATION_SIZE = 80;
+    private static final int NUM_GENERATIONS = 200;
+    private static final double ELITISM_RATE = 0.2;
+    private static final double CROSSOVER_RATE = 1;
+    private static final double MUTATION_RATE = 0.08;
+    private static final int TOURNAMENT_ARITY = 2;
+
+    // numbers from 0 to N-1
+    private static final List<Integer> sequence = new ArrayList<>();
+    static {
+        for (int i = 0; i < DIMENSION; i++) {
+            sequence.add(i);
+        }
+    }
+
+    @Test
+    public void test() {
+        // to test a stochastic algorithm is hard, so this will rather be an usage
+        // example
+
+        // initialize a new genetic algorithm
+        GeneticAlgorithm<List<Integer>> ga = new GeneticAlgorithm<>(new OnePointCrossover<Integer, List<Integer>>(),
+                CROSSOVER_RATE, new RealValuedMutation<List<Integer>>(), MUTATION_RATE,
+                new TournamentSelection<List<Integer>>(TOURNAMENT_ARITY), ELITISM_RATE);
+
+        // initial population
+        Population<List<Integer>> initial = randomPopulation();
+        // stopping conditions
+        StoppingCondition<List<Integer>> stopCond = new FixedGenerationCount<>(NUM_GENERATIONS);
+
+        // best initial chromosome
+        Chromosome<List<Integer>> bestInitial = initial.getFittestChromosome();
+
+        // run the algorithm
+        Population<List<Integer>> finalPopulation = ga.evolve(initial, stopCond);
+
+        // best chromosome from the final population
+        Chromosome<List<Integer>> bestFinal = finalPopulation.getFittestChromosome();
+
+        // the only thing we can test is whether the final solution is not worse than
+        // the initial one
+        // however, for some implementations of GA, this need not be true :)
+
+        Assertions.assertTrue(bestFinal.compareTo(bestInitial) > 0);
+
+    }
+
+    /**
+     * Initializes a random population
+     */
+    private static Population<List<Integer>> randomPopulation() {
+        List<Chromosome<List<Integer>>> popList = new ArrayList<>();
+        for (int i = 0; i < POPULATION_SIZE; i++) {
+            Chromosome<List<Integer>> randChrom = new MinPermutations(
+                    ChromosomeRepresentationUtils.randomPermutation(DIMENSION));
+            popList.add(randChrom);
+        }
+        return new ListPopulation<List<Integer>>(popList, popList.size());
+    }
+
+    /**
+     * Chromosomes representing a permutation of (0,1,2,...,DIMENSION-1).
+     *
+     * The goal is to sort the sequence.
+     */
+    private static class MinPermutations extends RealValuedChromosome<List<Integer>> {
+
+        MinPermutations(List<Double> representation) {
+            super(representation, new MinPermutationsFitnessFunction(), new RandomKeyDecoder<>(sequence));
+        }
+
+        @Override
+        public RealValuedChromosome<List<Integer>> newChromosome(List<Double> chromosomeRepresentation) {
+            return new MinPermutations(chromosomeRepresentation);
+        }
+
+    }
+
+    private static class MinPermutationsFitnessFunction implements FitnessFunction<List<Integer>> {
+
+        @Override
+        public double compute(List<Integer> decodedChromosome) {
+            double res = 0.0;
+            for (int i = 0; i < decodedChromosome.size(); i++) {
+                int value = decodedChromosome.get(i);
+                if (value != i) {
+                    // bad position found
+                    res += Math.abs(value - i);
+                }
+            }
+            // the most fitted chromosome is the one with minimal error
+            // therefore we must return negative value
+            return -res;
+        }
+
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java
new file mode 100644
index 0000000..a682024
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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 org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class AbstractChromosomeTest {
+
+    @Test
+    public void testGetFitness() {
+        Chromosome<String> c1 = new AbstractChromosome<String>(chromosome -> 1, chromosome -> "1") {
+        };
+        Assertions.assertEquals(1, c1.evaluate(), .001);
+    }
+
+    @Test
+    public void testDecode() {
+        Chromosome<String> c1 = new AbstractChromosome<String>(chromosome -> 1, chromosome -> "1") {
+        };
+        Assertions.assertEquals("1", c1.decode());
+    }
+
+    @Test
+    public void testCompareTo() {
+        Chromosome<String> c1 = new AbstractChromosome<String>(chromosome -> 0, chromosome -> "0") {
+        };
+        Chromosome<String> c2 = new AbstractChromosome<String>(chromosome -> 10, chromosome -> "10") {
+        };
+        Chromosome<String> c3 = new AbstractChromosome<String>(chromosome -> 10, chromosome -> "10") {
+        };
+
+        Assertions.assertTrue(c1.compareTo(c2) < 0);
+        Assertions.assertTrue(c2.compareTo(c1) > 0);
+        Assertions.assertEquals(0, c3.compareTo(c2));
+        Assertions.assertEquals(0, c2.compareTo(c3));
+    }
+
+    @Test
+    public void testIsSame() {
+        AbstractChromosome<String> c1 = new AbstractChromosome<String>(chromosome -> 1, chromosome -> "1") {
+        };
+        AbstractChromosome<String> c2 = new AbstractChromosome<String>(chromosome -> 2, chromosome -> "2") {
+        };
+        AbstractChromosome<String> c3 = new AbstractChromosome<String>(chromosome -> 3, chromosome -> "1") {
+        };
+        Assertions.assertTrue(c1.isSame(c3));
+        Assertions.assertFalse(c1.isSame(c2));
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java
new file mode 100644
index 0000000..ef6669a
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class BinaryChromosomeTest {
+
+    @Test
+    public void testInvalidConstructor() {
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new BinaryChromosome<String>(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), Long.MAX_VALUE,
+                c -> 0, c -> "0");
+        });
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new BinaryChromosome<String>(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), 100, c -> 0,
+                c -> "0");
+        });
+    }
+
+    @Test
+    public void testRandomConstructor() {
+        for (int i = 0; i < 20; i++) {
+            BinaryChromosome.<String>randomChromosome(10, c -> 1, new DummyListChromosomeDecoder<>("1"));
+        }
+    }
+
+    @Test
+    public void testGetStringRepresentation() {
+        int length = 10;
+        int startToEndGap = 1;
+        testStringRepresentationWithRanges(length, startToEndGap);
+
+        length = 64;
+        startToEndGap = 10;
+        testStringRepresentationWithRanges(length, startToEndGap);
+
+        length = 100;
+        startToEndGap = 50;
+        testStringRepresentationWithRanges(length, startToEndGap);
+
+        length = 128;
+        startToEndGap = 70;
+        testStringRepresentationWithRanges(length, startToEndGap);
+
+        length = 250;
+        startToEndGap = 128;
+        testStringRepresentationWithRanges(length, startToEndGap);
+
+        length = 350;
+        startToEndGap = 228;
+        testStringRepresentationWithRanges(length, startToEndGap);
+
+        length = 450;
+        startToEndGap = 108;
+        testStringRepresentationWithRanges(length, startToEndGap);
+
+    }
+
+    private void testStringRepresentationWithRanges(int length, int startToEndGap) {
+        for (int i = 0; i < 50; i++) {
+            String representationStr = ChromosomeRepresentationUtils.randomStringRepresentation(new char[] {'0', '1'},
+                    length);
+            BinaryChromosome<String> chromosome = new BinaryChromosome<>(representationStr, c -> 0, c -> "0");
+            Assertions.assertEquals(representationStr, chromosome.getStringRepresentation());
+            for (int j = 0; j < length - startToEndGap; j++) {
+                int index = (int) ((length + 1 - startToEndGap) * Math.random());
+                Assertions.assertEquals(representationStr.substring(index, index + startToEndGap),
+                        chromosome.getStringRepresentation(index, index + startToEndGap));
+            }
+        }
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java
new file mode 100644
index 0000000..f43a27c
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.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.chromosome;
+
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+
+public class ChromosomePairTest {
+
+    @Test
+    public void testChromosomePair() {
+        Chromosome<String> chromosome1 = new AbstractChromosome<String>(c -> 0, c -> "0") {
+        };
+        Chromosome<String> chromosome2 = new AbstractChromosome<String>(c -> 1, c -> "1") {
+        };
+        ChromosomePair<String> chromosomePair = new ChromosomePair<>(chromosome1, chromosome2);
+
+        Assertions.assertEquals(chromosomePair.getFirst(), chromosome1);
+        Assertions.assertEquals(chromosomePair.getSecond(), chromosome2);
+
+        Assertions.assertNotNull(chromosomePair.toString());
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java
new file mode 100644
index 0000000..e2a89fe
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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 org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class IntegralValuedChromosomeTest {
+
+    @Test
+    public void testIntegralValuedChromosome() {
+        int min = 0;
+        int max = 10;
+        IntegralValuedChromosome<String> chromosome = new IntegralValuedChromosome<>(
+                ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0,
+                new DummyListChromosomeDecoder<>("0"), min, max);
+        Assertions.assertEquals(min, chromosome.getMin());
+        Assertions.assertEquals(max, chromosome.getMax());
+
+        IntegralValuedChromosome<String> chromosome1 = new IntegralValuedChromosome<>(
+                ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max).toArray(new Integer[10]),
+            c -> 0, new DummyListChromosomeDecoder<>("0"), min, max);
+        Assertions.assertEquals(min, chromosome1.getMin());
+        Assertions.assertEquals(max, chromosome1.getMax());
+    }
+
+    @Test
+    public void testCheckValidity() {
+        int min = 0;
+        int max = 10;
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new IntegralValuedChromosome<>(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max),
+                c -> 0, new DummyListChromosomeDecoder<>("0"), max, min);
+        });
+    }
+
+    @Test
+    public void testCheckValidity1() {
+        int min = 0;
+        int max = 10;
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new IntegralValuedChromosome<>(
+                    ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min - 10, max + 10), c -> 0,
+                    new DummyListChromosomeDecoder<>("0"), min, max);
+        });
+
+    }
+
+    @Test
+    public void testNewChromosome() {
+        int min = 0;
+        int max = 10;
+        IntegralValuedChromosome<String> chromosome = new IntegralValuedChromosome<>(
+                ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0,
+                new DummyListChromosomeDecoder<>("0"), min, max);
+        IntegralValuedChromosome<String> newChromosome = chromosome
+                .newChromosome(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max));
+        Assertions.assertEquals(chromosome.getMin(), newChromosome.getMin());
+        Assertions.assertEquals(chromosome.getMax(), newChromosome.getMax());
+        Assertions.assertEquals(chromosome.getDecoder(), newChromosome.getDecoder());
+        Assertions.assertEquals(chromosome.getFitnessFunction(), newChromosome.getFitnessFunction());
+
+    }
+
+    @Test
+    public void testRandomChromosome() {
+        int min = 0;
+        int max = 10;
+        IntegralValuedChromosome<String> chromosome = IntegralValuedChromosome.<String>randomChromosome(10, c -> 0,
+                new DummyListChromosomeDecoder<>("0"), min, max);
+        Assertions.assertEquals(min, chromosome.getMin());
+        Assertions.assertEquals(max, chromosome.getMax());
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java
new file mode 100644
index 0000000..333e2ca
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class RealValuedChromosomeTest {
+
+    @Test
+    public void test() {
+        for (int i = 0; i < 10; i++) {
+            new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1,
+                    new DummyListChromosomeDecoder<>("1"));
+            new RealValuedChromosome<>(
+                    ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1).toArray(new Double[10]), c -> 0,
+                    new DummyListChromosomeDecoder<>("0"));
+        }
+    }
+
+    @Test
+    public void testNewChromosome() {
+        for (int i = 0; i < 10; i++) {
+            RealValuedChromosome<String> chromosome = new RealValuedChromosome<>(
+                    ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1,
+                    new DummyListChromosomeDecoder<>("1"));
+            chromosome.newChromosome(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1));
+        }
+    }
+
+    @Test
+    public void testRandomChromosome() {
+        for (int i = 0; i < 10; i++) {
+            RealValuedChromosome.randomChromosome(5, c -> 0, new DummyListChromosomeDecoder<>("0"), 0, 2);
+        }
+    }
+
+    @Test
+    public void testCheckValidity() {
+        int min = 0;
+        int max = 10;
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0,
+                    new DummyListChromosomeDecoder<>("0"), max, min);
+        });
+    }
+
+    @Test
+    public void testCheckValidity1() {
+        int min = 0;
+        int max = 10;
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min - 10, max + 10),
+                c -> 0, new DummyListChromosomeDecoder<>("0"), min, max);
+        });
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java
new file mode 100644
index 0000000..e2681ab
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.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.convergencecond;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.math4.ga.convergence.FixedElapsedTime;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class FixedElapsedTimeTest {
+
+    @Test
+    public void testIsSatisfied() {
+        final Population<String> pop = new ListPopulation<>(10);
+
+        final long start = System.nanoTime();
+        final long duration = 3;
+        final FixedElapsedTime<String> tec = new FixedElapsedTime<String>(duration);
+
+        while (!tec.isSatisfied(pop)) {
+            try {
+                Thread.sleep(50);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+
+        final long end = System.nanoTime();
+        final long elapsedTime = end - start;
+        final long diff = Math.abs(elapsedTime - TimeUnit.SECONDS.toNanos(duration));
+
+        Assertions.assertTrue(diff < TimeUnit.MILLISECONDS.toNanos(100));
+    }
+
+    @Test
+    public void testNegativeTime() {
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new FixedElapsedTime<>(-10);
+        });
+    }
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java
new file mode 100644
index 0000000..5bf1567
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.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.convergencecond;
+
+import org.apache.commons.math4.ga.convergence.FixedGenerationCount;
+
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class FixedGenerationCountTest {
+
+    @Test
+    public void testIsSatisfied() {
+        FixedGenerationCount<String> fgc = new FixedGenerationCount<String>(20);
+
+        int cnt = 0;
+        Population<String> pop = new ListPopulation<>(10);
+
+        while (!fgc.isSatisfied(pop)) {
+            cnt++;
+        }
+        Assertions.assertEquals(cnt, fgc.getNumGenerations());
+    }
+
+    @Test
+    public void testNegativeGenerationCount() {
+        Assertions.assertThrows(GeneticException.class, () -> {
+            new FixedGenerationCount<String>(-1);
+        });
+    }
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java
new file mode 100644
index 0000000..5fffd06
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.convergencecond;
+
+import java.util.ArrayList;
+
+import java.util.List;
+
+import org.apache.commons.math4.ga.chromosome.AbstractChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.convergence.UnchangedBestFitness;
+import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class UnchangedBestFitnessTest {
+
+    @Test
+    public void testIsSatisfied() {
+
+        final int noOfGenerationsWithUnchangedBestFitness = 5;
+        StoppingCondition<String> stoppingCondition = new UnchangedBestFitness<>(
+                noOfGenerationsWithUnchangedBestFitness);
+
+        double[] fitnesses = new double[10];
+        for (int i = 0; i < 10; i++) {
+            fitnesses[i] = i;
+        }
+        List<Chromosome<String>> chromosomes = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            final double fitness = fitnesses[i];
+            Chromosome<String> ch = new AbstractChromosome<String>(c -> fitness, c -> "Fixed") {
+            };
+            chromosomes.add(ch);
+        }
+        Population<String> pop = new ListPopulation<>(chromosomes, 10);
+
+        double initialMaxFitness = new PopulationStatisticalSummaryImpl<>(pop).getMaxFitness();
+
+        int counter = 0;
+        while (!stoppingCondition.isSatisfied(pop)) {
+            counter++;
+        }
+
+        double maxFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMaxFitness();
+
+        Assertions.assertEquals(initialMaxFitness, maxFitnessAfterConvergence, .001);
+        Assertions.assertEquals(noOfGenerationsWithUnchangedBestFitness, counter);
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java
new file mode 100644
index 0000000..6e4042c
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.convergencecond;
+
+import java.util.ArrayList;
+
+import java.util.List;
+
+import org.apache.commons.math4.ga.chromosome.AbstractChromosome;
+import org.apache.commons.math4.ga.chromosome.Chromosome;
+import org.apache.commons.math4.ga.convergence.StoppingCondition;
+import org.apache.commons.math4.ga.convergence.UnchangedMeanFitness;
+import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl;
+import org.apache.commons.math4.ga.population.ListPopulation;
+import org.apache.commons.math4.ga.population.Population;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class UnchangedMeanFitnessTest {
+
+    @Test
+    public void testIsSatisfied() {
+
+        final int noOfGenerationsWithUnchangedMeanFitness = 5;
+        StoppingCondition<String> stoppingCondition = new UnchangedMeanFitness<>(
+                noOfGenerationsWithUnchangedMeanFitness);
+
+        double[] fitnesses = new double[10];
+        for (int i = 0; i < 10; i++) {
+            fitnesses[i] = i;
+        }
+        List<Chromosome<String>> chromosomes = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            final double fitness = fitnesses[i];
+            Chromosome<String> ch = new AbstractChromosome<String>(c -> fitness, c -> "Fixed") {
+            };
+            chromosomes.add(ch);
+        }
+        Population<String> pop = new ListPopulation<>(chromosomes, 10);
+
+        double initialAverageFitness = new PopulationStatisticalSummaryImpl<>(pop).getMeanFitness();
+
+        int counter = 0;
+        while (!stoppingCondition.isSatisfied(pop)) {
+            counter++;
+        }
+
+        double averageFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMeanFitness();
+
+        Assertions.assertEquals(initialAverageFitness, averageFitnessAfterConvergence, .001);
+        Assertions.assertEquals(noOfGenerationsWithUnchangedMeanFitness, counter);
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java
new file mode 100644
index 0000000..c1249b5
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.dummy.DummyChromosome;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class AbstractChromosomeCrossoverPolicyTest {
+
+    @Test
+    public void testCrossoverProbability() {
+
+        CrossoverPolicy<String> crossoverPolicy = new AbstractChromosomeCrossoverPolicy<String>() {
+            @Override
+            protected ChromosomePair<String> crossover(Chromosome<String> first, Chromosome<String> second) {
+                return null;
+            }
+        };
+
+        Chromosome<String> ch1 = new DummyChromosome();
+
+        Chromosome<String> ch2 = new DummyChromosome();
+
+        Assertions.assertNull(crossoverPolicy.crossover(ch1, ch2, 1.0));
+        Assertions.assertNotNull(crossoverPolicy.crossover(ch1, ch2, 0.0));
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java
new file mode 100644
index 0000000..0739f0d
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.AbstractChromosome;
+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.dummy.DummyListChromosome;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class AbstractListChromosomeCrossoverPolicyTest {
+
+    @Test
+    public void testCrossoverWithNonListChromosome() {
+
+        CrossoverPolicy<String> crossoverPolicy = new AbstractListChromosomeCrossoverPolicy<Integer, String>() {
+
+            @Override
+            protected ChromosomePair<String> mate(AbstractListChromosome<Integer, String> first,
+                    AbstractListChromosome<Integer, String> second) {
+                return new ChromosomePair<>(first, second);
+            }
+        };
+        Chromosome<String> ch1 = new AbstractChromosome<String>(c -> 0, c -> "0") {
+        };
+
+        Chromosome<String> ch2 = new AbstractChromosome<String>(c -> 1, c -> "1") {
+        };
+
+        Assertions.assertThrows(GeneticException.class, () -> {
+            crossoverPolicy.crossover(ch1, ch2, 1.0);
+        });
+
+    }
+
+    @Test
+    public void testCrossoverWithUnEqualLengthChromosome() {
+
+        CrossoverPolicy<String> crossoverPolicy = new AbstractListChromosomeCrossoverPolicy<Integer, String>() {
+
+            @Override
+            protected ChromosomePair<String> mate(AbstractListChromosome<Integer, String> first,
+                    AbstractListChromosome<Integer, String> second) {
+                return new ChromosomePair<>(first, second);
+            }
+        };
+        Chromosome<String> ch1 = new DummyListChromosome(
+                ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2));
+
+        Chromosome<String> ch2 = new DummyListChromosome(
+                ChromosomeRepresentationUtils.randomIntegralRepresentation(12, 0, 2));
+
+        Assertions.assertThrows(GeneticException.class, () -> {
+            crossoverPolicy.crossover(ch1, ch2, 1.0);
+        });
+
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java
new file mode 100644
index 0000000..e8ac820
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.ChromosomePair;
+import org.apache.commons.math4.ga.dummy.DummyListChromosome;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class CycleCrossoverTest {
+
+    @Test
+    public void testCrossoverExample() {
+        // taken from
+        // http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx
+        final Integer[] p1 = new Integer[] {8, 4, 7, 3, 6, 2, 5, 1, 9, 0};
+        final Integer[] p2 = new Integer[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        final DummyListChromosome p1c = new DummyListChromosome(p1);
+        final DummyListChromosome p2c = new DummyListChromosome(p2);
+
+        final CrossoverPolicy<String> cp = new CycleCrossover<Integer, String>();
+        final ChromosomePair<String> pair = cp.crossover(p1c, p2c, 1.0);
+
+        final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation()
+                .toArray(new Integer[p1.length]);
+        final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation()
+                .toArray(new Integer[p2.length]);
+
+        final Integer[] c1e = new Integer[] {8, 1, 2, 3, 4, 5, 6, 7, 9, 0};
+        final Integer[] c2e = new Integer[] {0, 4, 7, 3, 6, 2, 5, 1, 8, 9};
+
+        Assertions.assertArrayEquals(c1e, c1);
+        Assertions.assertArrayEquals(c2e, c2);
+    }
+
+    @Test
+    public void testCrossoverExample2() {
+        // taken from http://www.scribd.com/doc/54206412/32/Cycle-crossover
+        final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
+        final Integer[] p2 = new Integer[] {9, 3, 7, 8, 2, 6, 5, 1, 4};
+        final DummyListChromosome p1c = new DummyListChromosome(p1);
+        final DummyListChromosome p2c = new DummyListChromosome(p2);
+
+        final CrossoverPolicy<String> cp = new CycleCrossover<Integer, String>();
+        final ChromosomePair<String> pair = cp.crossover(p1c, p2c, 1.0);
+
+        final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation()
+                .toArray(new Integer[p1.length]);
+        final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation()
+                .toArray(new Integer[p2.length]);
+
+        final Integer[] c1e = new Integer[] {1, 3, 7, 4, 2, 6, 5, 8, 9};
+        final Integer[] c2e = new Integer[] {9, 2, 3, 8, 5, 6, 7, 1, 4};
+
+        Assertions.assertArrayEquals(c1e, c1);
+        Assertions.assertArrayEquals(c2e, c2);
+    }
+
+    @Test
+    public void testCrossover() {
+        final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        final Integer[] p2 = new Integer[] {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+        final DummyListChromosome p1c = new DummyListChromosome(p1);
+        final DummyListChromosome p2c = new DummyListChromosome(p2);
+
+        final CrossoverPolicy<String> cp = new CycleCrossover<Integer, String>(true);
+
+        for (int i = 0; i < 20; i++) {
+            final ChromosomePair<String> pair = cp.crossover(p1c, p2c, 1.0);
+
+            final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation()
+                    .toArray(new Integer[p1.length]);
+            final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation()
+                    .toArray(new Integer[p2.length]);
+
+            int index = 0;
+            // Determine if it is in the same spot as in the first parent, if
+            // not it comes from the second parent.
+            for (final Integer j : c1) {
+                if (!p1[index].equals(j)) {
+                    Assertions.assertEquals(j, p2[index]);
+                } else {
+                    Assertions.assertEquals(j, p1[index]);
+                }
+                index++;
+            }
+
+            // Same as above only for the second parent.
+            index = 0;
+            for (final Integer k : c2) {
+                if (p2[index] != k) {
+                    Assertions.assertEquals(k, p1[index]);
+                } else {
+                    Assertions.assertEquals(k, p2[index]);
+                }
+                index++;
+            }
+        }
+    }
+
+}
diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java
new file mode 100644
index 0000000..35a55b0
--- /dev/null
+++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.List;
+
+import org.apache.commons.math4.ga.chromosome.AbstractChromosome;
+import org.apache.commons.math4.ga.chromosome.ChromosomePair;
+import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome;
+import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder;
+import org.apache.commons.math4.ga.internal.exception.GeneticException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class NPointCrossoverTest {
+
+    @Test
+    public void testNumberIsTooLargeException() {
+        final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1};
+        final Integer[] p2 = new Integer[] {0, 1, 1, 0, 1, 0, 1, 1, 1};
+
+        final IntegralValuedChromosome<String> p1c = new IntegralValuedChromosome<String>(p1, c -> 0,
+                new DummyListChromosomeDecoder<Integer>("0"), 0, 2);
+        final IntegralValuedChromosome<String> p2c = new IntegralValuedChromosome<String>(p2, c -> 0,
+                new DummyListChromosomeDecoder<Integer>("0"), 0, 2);
+
+        final CrossoverPolicy<String> cp = new NPointCrossover<Integer, String>(15);
+        Assertions.assertThrows(GeneticException.class, () -> {
+            cp.crossover(p1c, p2c, 1.0);
+        });
+    }
+
+    @Test
+    public void testCrossoverInvalidFixedLengthChromosomeFirst() {
+        final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1};
+        final IntegralValuedChromosome<String> p1c = new IntegralValuedChromosome<String>(p1, chromosome -> 0,
+                new DummyListChromosomeDecoder<>("0"), 0, 2);
+        final AbstractChromosome<String> p2c = new AbstractChromosome<String>(chromosome -> 0,
+                new DummyListChromosomeDecoder<>("0")) {
+        };
+
... 1951 lines suppressed ...