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/05/31 21:45:48 UTC

[commons-math] branch feature__MATH-1563__genetic_algorithm updated: MATH-1618: Design proposal (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


The following commit(s) were added to refs/heads/feature__MATH-1563__genetic_algorithm by this push:
     new ddfd5bf85 MATH-1618: Design proposal (WIP).
ddfd5bf85 is described below

commit ddfd5bf859d04cc5da604b20021ceaba9de7def6
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Sun May 1 22:19:08 2022 +0200

    MATH-1618: Design proposal (WIP).
---
 .../{Coordinate.java => Coordinates.java}          |  25 +--
 .../ga/mathfunctions/MathFunctionOptimizer.java    |  45 ++--
 .../ga/mathfunctions/MathFunctionOptimizer2.java   | 189 ++++++++++++++++
 .../examples/ga/mathfunctions/StandAlone.java      |  81 +++++--
 commons-math-examples/examples-ga/pom.xml          |   5 +
 commons-math-examples/pom.xml                      |   6 +
 commons-math-ga2/LICENCE                           | 201 +++++++++++++++++
 commons-math-ga2/NOTICE                            |   5 +
 .../examples-ga => commons-math-ga2}/pom.xml       |  36 ++--
 .../commons/math4/ga2/AbstractCrossover.java       |  78 +++++++
 .../apache/commons/math4/ga2/AbstractMutation.java |  95 ++++++++
 .../apache/commons/math4/ga2/ApplicationRate.java  |  92 ++++++++
 .../commons/math4/ga2/ChromosomeFactory.java       |  40 +---
 .../apache/commons/math4/ga2/FitnessService.java   |  47 ++--
 .../commons/math4/ga2/GenerationCallback.java      |  41 +---
 .../commons/math4/ga2/GeneticAlgorithmFactory.java | 240 +++++++++++++++++++++
 .../apache/commons/math4/ga2/GeneticOperator.java  |  45 ++--
 .../org/apache/commons/math4/ga2/Population.java   | 191 ++++++++++++++++
 .../org/apache/commons/math4/ga2/Selection.java    |  43 ++--
 .../ga2/fitness/FitnessConcurrentCalculator.java   |  98 +++++++++
 .../commons/math4/ga2/fitness/package-info.java    |  38 +---
 .../commons/math4/ga2/gene/binary/Chromosome.java  | 109 ++++++++++
 .../commons/math4/ga2/gene/binary/Mutation.java    |  50 ++---
 .../math4/ga2/gene/binary/OnePointCrossover.java   |  75 +++++++
 .../commons/math4/ga2/gene/binary/Operators.java   |  41 ++--
 .../math4/ga2/gene/binary/package-info.java        |  38 +---
 .../org/apache/commons/math4/ga2/package-info.java |  38 +---
 .../math4/ga2/rate/AverageRankLinearRate.java      |  52 +++++
 .../commons/math4/ga2/rate/ConstantRate.java       |  38 ++--
 .../commons/math4/ga2/rate/RateGenerators.java     |  46 ++--
 .../commons/math4/ga2/rate/package-info.java       |  38 +---
 .../commons/math4/ga2/select/Tournament.java       |  77 +++++++
 .../commons/math4/ga2/select/package-info.java     |  38 +---
 .../commons/math4/ga2/stop/UnchangedFitness.java   | 116 ++++++++++
 .../commons/math4/ga2/stop/package-info.java       |  38 +---
 pom.xml                                            |   1 +
 .../checkstyle/checkstyle-suppressions.xml         |   3 +
 37 files changed, 1892 insertions(+), 547 deletions(-)

diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinates.java
similarity index 69%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinates.java
index eee31fbc8..30ce0d63d 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinates.java
@@ -16,31 +16,28 @@
  */
 package org.apache.commons.math4.examples.ga.mathfunctions;
 
-import java.util.List;
+import java.util.Arrays;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Chromosome phenotype.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
+public class Coordinates {
+    /** Coordinates. **/
+    private final double[] values;
 
     /**
-     * constructor.
-     * @param values coordinates of all dimensions.
+     * @param values Coordinates.
      */
-    public Coordinate(List<Double> values) {
-        this.values = values;
+    public Coordinates(double[] values) {
+        this.values = values.clone();
     }
 
     /**
      * Returns the values of all coordinates.
      * @return values of coordinates
      */
-    public List<Double> getValues() {
-        return values;
+    public double[] getValues() {
+        return values.clone();
     }
 
     /**
@@ -48,7 +45,7 @@ public class Coordinate {
      */
     @Override
     public String toString() {
-        return "Coordinate [values=" + values + "]";
+        return "Coordinates [values=" + Arrays.toString(values) + "]";
     }
 
 }
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/MathFunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/MathFunctionOptimizer.java
index 73b00a08e..4d8543ba6 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/MathFunctionOptimizer.java
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/MathFunctionOptimizer.java
@@ -17,9 +17,6 @@
 
 package org.apache.commons.math4.examples.ga.mathfunctions;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import org.apache.commons.math4.ga.GeneticAlgorithm;
 import org.apache.commons.math4.ga.chromosome.BinaryChromosome;
 import org.apache.commons.math4.ga.chromosome.Chromosome;
@@ -65,50 +62,52 @@ public final class MathFunctionOptimizer {
             int populationSize) {
 
         // initialize a new genetic algorithm
-        final GeneticAlgorithm<Coordinate> ga = new GeneticAlgorithm<Coordinate>(
-                new OnePointBinaryCrossover<Coordinate>(), crossoverRate, new BinaryMutation<Coordinate>(),
-                mutationRate, new TournamentSelection<Coordinate>(tournamentSize), elitismRate,
-                new PopulationStatisticsLogger<Coordinate>());
+        final GeneticAlgorithm<Coordinates> ga = new GeneticAlgorithm<Coordinates>(
+                new OnePointBinaryCrossover<Coordinates>(), crossoverRate, new BinaryMutation<Coordinates>(),
+                mutationRate, new TournamentSelection<Coordinates>(tournamentSize), elitismRate,
+                new PopulationStatisticsLogger<Coordinates>());
 
         // stopping condition
-        final StoppingCondition<Coordinate> stopCond = new UnchangedBestFitness<>(
+        final StoppingCondition<Coordinates> stopCond = new UnchangedBestFitness<>(
                 generationCountWithUnchangedBestFitness);
 
         // run the algorithm
-        final Population<Coordinate> finalPopulation = ga.evolve(getInitialPopulation(dimension, populationSize),
+        final Population<Coordinates> finalPopulation = ga.evolve(getInitialPopulation(dimension, populationSize),
                 stopCond, Runtime.getRuntime().availableProcessors());
 
         // best chromosome from the final population
-        final Chromosome<Coordinate> bestFinal = finalPopulation.getFittestChromosome();
+        final Chromosome<Coordinates> bestFinal = finalPopulation.getFittestChromosome();
 
         logger.info(bestFinal.toString());
     }
 
-    private static Population<Coordinate> getInitialPopulation(int dimension, int populationSize) {
-        final Population<Coordinate> population = new ListPopulation<>(populationSize);
+    private static Population<Coordinates> getInitialPopulation(int dimension, int populationSize) {
+        final Population<Coordinates> population = new ListPopulation<>(populationSize);
 
         for (int i = 0; i < populationSize; i++) {
             population.addChromosome(BinaryChromosome
-                    .<Coordinate>randomChromosome(dimension * CHROMOSOME_LENGTH_PER_DIMENSION, coordinate -> {
+                    .<Coordinates>randomChromosome(dimension * CHROMOSOME_LENGTH_PER_DIMENSION, coordinate -> {
                         double sumOfSquare = 0.0;
-                        for (Double value : coordinate.getValues()) {
-                            sumOfSquare += Math.pow(value, 2);
+                        for (double value : coordinate.getValues()) {
+                            sumOfSquare += Math.pow(value - 10, 2);
                         }
                         return -Math.pow(sumOfSquare, .25) *
                                 (Math.pow(Math.sin(50 * Math.pow(sumOfSquare, .1)), 2) + 1);
 
                     }, chromosome -> {
-                            final BinaryChromosome<Coordinate> binaryChromosome =
-                                    (BinaryChromosome<Coordinate>) chromosome;
+                            final BinaryChromosome<Coordinates> binaryChromosome =
+                                    (BinaryChromosome<Coordinates>) chromosome;
                             final long length = binaryChromosome.getLength();
-                            final List<Double> coordinates = new ArrayList<>();
-
-                            for (int j = 0; j < length; j += 12) {
-                                final String dimensionStrValue = binaryChromosome.getStringRepresentation(j, j + 12);
-                                coordinates.add(Integer.parseUnsignedInt(dimensionStrValue, 2) / 100d);
+                            final double[] coordinates = new double[dimension];
+                            for (int j = 0; j < dimension; j++) {
+                                final int start = j * CHROMOSOME_LENGTH_PER_DIMENSION;
+                                final String dimensionStrValue =
+                                    binaryChromosome.getStringRepresentation(start,
+                                                                             start + CHROMOSOME_LENGTH_PER_DIMENSION);
+                                coordinates[j] = Integer.parseInt(dimensionStrValue, 2) / 100d;
                             }
 
-                            return new Coordinate(coordinates);
+                            return new Coordinates(coordinates);
                         }));
         }
         return population;
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/MathFunctionOptimizer2.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/MathFunctionOptimizer2.java
new file mode 100644
index 000000000..e8a16a375
--- /dev/null
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/MathFunctionOptimizer2.java
@@ -0,0 +1,189 @@
+/*
+ * 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;
+
+import java.util.BitSet;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.ToDoubleFunction;
+import java.util.function.Predicate;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Callable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math4.ga2.gene.binary.Chromosome;
+import org.apache.commons.math4.ga2.gene.binary.Operators;
+import org.apache.commons.math4.ga2.Population;
+import org.apache.commons.math4.ga2.GeneticOperator;
+import org.apache.commons.math4.ga2.FitnessService;
+import org.apache.commons.math4.ga2.Selection;
+import org.apache.commons.math4.ga2.GeneticAlgorithmFactory;
+import org.apache.commons.math4.ga2.ChromosomeFactory;
+import org.apache.commons.math4.ga2.GenerationCallback;
+import org.apache.commons.math4.ga2.ApplicationRate;
+import org.apache.commons.math4.ga2.stop.UnchangedFitness;
+import org.apache.commons.math4.ga2.select.Tournament;
+import org.apache.commons.math4.ga2.fitness.FitnessConcurrentCalculator;
+import org.apache.commons.math4.ga2.rate.RateGenerators;
+
+/**
+ * Optimizer example.
+ */
+public final class MathFunctionOptimizer2 {
+    /** Chromosome length. */
+    private static final int GENES_PER_DIMENSION = 12;
+    /** Scaling factor. */
+    private static final double SCALE = 0.01;
+
+    /**
+     * @param dim Dimension.
+     * @param crossover Crossover rate.
+     * @param mutation Mutation rate.
+     * @param elitism Elitism rate.
+     * @param tournament Tournament size.
+     * @param numGeneration Number of generations unchanged best fitness.
+     * @param popSize Population size.
+     * @param jobs Number of threads for computing the fitnesses.
+     */
+    public void optimize(int dim,
+                         double crossover,
+                         double mutation,
+                         double elitism,
+                         int tournament,
+                         int numGeneration,
+                         int popSize,
+                         int jobs) {
+        final int numGenes = dim * GENES_PER_DIMENSION;
+
+        // Random genotypes generator.
+        final ChromosomeFactory<Chromosome> initFactory =
+            new Chromosome.RandomFactory(RandomSource.ISAAC);
+
+        // Genotype to phenotype decoder.
+        final Function<Chromosome, Coordinates> decoder = gene -> {
+            final BitSet rep = gene.asBitSet();
+            final double[] coord = new double[dim];
+            for (int i = 0; i < dim; i++) {
+                final int start = i * GENES_PER_DIMENSION;
+                final long[] coordRep = rep.get(start, start + GENES_PER_DIMENSION).toLongArray();
+                if (coordRep.length == 0) {
+                    coord[i] = 0;
+                } else if (coordRep.length == 1) {
+                    coord[i] = coordRep[0] * SCALE;
+                } else {
+                    // Should not happen.
+                    throw new IllegalStateException("Unsupported representation size: " +
+                                                    coordRep.length);
+                }
+            }
+            return new Coordinates(coord);
+        };
+
+        // Stopping condition (not thread-safe).
+        final Predicate<Population<Chromosome, Coordinates>> stop =
+            new UnchangedFitness(UnchangedFitness.Type.BEST,
+                                 numGeneration);
+
+        final ToDoubleFunction<Coordinates> fitnessFunction = coord -> {
+            double s = 0;
+            for (double v : coord.getValues()) {
+                final double vM10 = v - 10;
+                s += vM10 * vM10;
+            }
+            final double a = Math.sin(50 * Math.pow(s, 0.1));
+            final double b = -Math.pow(s, 0.25) * (a * a + 1);
+            return b;
+        };
+
+        // Setup for parallel computation of fitnesses.
+        final ExecutorService exec = Executors.newFixedThreadPool(jobs);
+        final FitnessService<Chromosome, Coordinates> fitness =
+            new FitnessConcurrentCalculator(fitnessFunction,
+                                            exec);
+
+        // Parents selection scheme.
+        final Selection<Chromosome, Coordinates> selection =
+            new Tournament(tournament,
+                           RandomSource.SPLIT_MIX_64);
+
+        // Offspring generators.
+        final Map<GeneticOperator<Chromosome>, ApplicationRate> operators = new HashMap<>();
+        operators.put(Operators.mutation(mutation), RateGenerators.constant(1));
+        operators.put(Operators.onePointCrossover(), RateGenerators.constant(crossover));
+
+        final Callable<Population<Chromosome, Coordinates>> ga =
+            GeneticAlgorithmFactory.<Chromosome, Coordinates>create(numGenes,
+                                                                    initFactory,
+                                                                    popSize,
+                                                                    decoder,
+                                                                    stop,
+                                                                    fitness,
+                                                                    selection,
+                                                                    operators,
+                                                                    elitism,
+                                                                    RandomSource.KISS,
+                                                                    new GenerationLogger());
+
+        try {
+            // Run the GA and retrieve contents of the last generation.
+            final List<Map.Entry<Chromosome, Double>> lastGen = ga.call().contents(true);
+            final Map.Entry<Chromosome, Double> top = lastGen.get(0);
+            final Coordinates best = decoder.apply(top.getKey());
+            final double bestValue = fitnessFunction.applyAsDouble(best);
+
+            // CHECKSTYLE: stop all
+            System.out.println("fitness=" + bestValue + " for " + best.toString());
+            // CHECKSTYLE: resume all
+        } catch (Exception e) {
+            // Rethrow.
+            throw new RuntimeException(e);
+        } finally {
+            exec.shutdown();
+        }
+    }
+}
+
+/**
+ * Log evolution.
+ */
+class GenerationLogger implements GenerationCallback<Chromosome> {
+    /** Logger. */
+    private static final Logger LOG = LoggerFactory.getLogger(GenerationLogger.class);
+
+    /** {@inheritDoc} */
+    @Override
+    public void update(List<Map.Entry<Chromosome, Double>> contents,
+                       int generation) {
+        final SummaryStatistics stats = new SummaryStatistics();
+        for (Map.Entry<Chromosome, Double> e : contents) {
+            stats.addValue(e.getValue());
+        }
+
+        LOG.info("fitness at generation {}: min={} max={} mean={} stddev={}",
+                 generation,
+                 stats.getMin(),
+                 stats.getMax(),
+                 stats.getMean(),
+                 stats.getStandardDeviation());
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/StandAlone.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/StandAlone.java
index 4d54ac58f..ea4f406e0 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/StandAlone.java
+++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/StandAlone.java
@@ -25,63 +25,100 @@ import picocli.CommandLine.Option;
 @Command(name = "mathfunction-optimizer", mixinStandardHelpOptions = true, version = "mathfunction-optimizer 1.0")
 public class StandAlone implements Runnable {
 
-    /** number of dimension. **/
+    /** number of dimension. */
     @Option(names = "-d", paramLabel = "DIMENSION", required = true, description = "Dimension of problem domain.")
     private int dimension;
 
-    /** size of tournament. **/
+    /** size of tournament. */
     @Option(names = "-t", paramLabel = "TOURNAMENT_SIZE", required = true, description = "Tournament size.")
     private int tournamentSize;
 
-    /** size of population. **/
+    /** size of population. */
     @Option(names = "-p", paramLabel = "POPULATION_SIZE", required = true, description = "Size of population.")
     private int populationSize;
 
-    /** rate of crossover. **/
+    /** rate of crossover. */
     @Option(names = "-c", paramLabel = "CROSSOVER_RATE", description = "Crossover rate (default: ${DEFAULT-VALUE}).")
     private double crossoverRate = 1.0;
 
-    /** rate of elitism. **/
+    /** rate of elitism. */
     @Option(names = "-e", paramLabel = "ELITISM_RATE", description = "Elitism rate (default: ${DEFAULT-VALUE}).")
     private double elitismRate = 0.25;
 
-    /** rate of mutation. **/
+    /** rate of mutation. */
     @Option(names = "-m", paramLabel = "MUTATION_RATE", description = "Mutation rate (default: ${DEFAULT-VALUE}).")
     private double mutationRate = 0.01;
 
-    /** number of generations with unchanged best fitness. **/
+    /** number of generations with unchanged best fitness. */
     @Option(names = "-g", paramLabel = "GENERATIONS_EVOLVED_WITH_UNCHANGED_BEST_FITNESS",
             description = "No of generations evolved with unchanged best fitness (default: ${DEFAULT-VALUE}).")
     private int generationsEvolvedWithUnchangedBestFitness = 50;
 
-    /** indicates whether the algorithm should be legacy or not. **/
-    @Option(names = "--legacy", description = "Indicates which version of algorithm to execute current or legacy.")
-    private boolean legacy;
+    /** indicates what to run. */
+    @Option(names = "--api",
+            description = "Indicates which version of the algorithm to execute: ${COMPLETION-CANDIDATES}.")
+    private API variant;
+
+    /** Number of threads for computing fitnesses. */
+    @Option(names = { "--jobs", "-j" }, description = "Number of fitness threads (default: ${DEFAULT-VALUE}).")
+    private int numThreads = 1;
 
     public static void main(String[] args) {
         CommandLine.run(new StandAlone(), args);
     }
 
-    /**
-     * This method is responsible for validating input and then invoking math
-     * function optimizer.
-     */
+    enum API {
+        /** Legacy. */
+        LEGACY,
+        /** Proposal. */
+        AVIJIT,
+        /** Proposal. */
+        GILLES
+    }
+
+    /** {@inheritDoc} */
     @Override
     public void run() {
-
-        // validate all input options.
         validateInput();
 
-        if (!legacy) {
-            new MathFunctionOptimizer().optimize(dimension, crossoverRate, mutationRate, elitismRate, tournamentSize,
-                    generationsEvolvedWithUnchangedBestFitness, populationSize);
-        } else {
-            new LegacyMathFunctionOptimizer().optimize(dimension, crossoverRate, mutationRate, elitismRate,
-                    tournamentSize, generationsEvolvedWithUnchangedBestFitness, populationSize);
+        switch (variant) {
+        case LEGACY:
+            new LegacyMathFunctionOptimizer().optimize(dimension,
+                                                       crossoverRate,
+                                                       mutationRate,
+                                                       elitismRate,
+                                                       tournamentSize,
+                                                       generationsEvolvedWithUnchangedBestFitness,
+                                                       populationSize);
+            break;
+        case AVIJIT:
+            new MathFunctionOptimizer().optimize(dimension,
+                                                 crossoverRate,
+                                                 mutationRate,
+                                                 elitismRate,
+                                                 tournamentSize,
+                                                 generationsEvolvedWithUnchangedBestFitness,
+                                                 populationSize);
+            break;
+        case GILLES:
+            new MathFunctionOptimizer2().optimize(dimension,
+                                                  crossoverRate,
+                                                  mutationRate,
+                                                  elitismRate,
+                                                  tournamentSize,
+                                                  generationsEvolvedWithUnchangedBestFitness,
+                                                  populationSize,
+                                                  numThreads);
+            break;
+        default:
+            throw new IllegalStateException();
         }
 
     }
 
+    /**
+     * Validate algorithm parameters.
+     */
     private void validateInput() {
         if (this.dimension < 1) {
             throw new IllegalArgumentException("Dimension should be > 0.");
diff --git a/commons-math-examples/examples-ga/pom.xml b/commons-math-examples/examples-ga/pom.xml
index 462daf2c4..51a9f207b 100644
--- a/commons-math-examples/examples-ga/pom.xml
+++ b/commons-math-examples/examples-ga/pom.xml
@@ -40,6 +40,11 @@
       <artifactId>commons-math4-ga</artifactId>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-math4-ga2</artifactId>
+    </dependency>
+
     <dependency>
       <groupId>info.picocli</groupId>
       <artifactId>picocli</artifactId>
diff --git a/commons-math-examples/pom.xml b/commons-math-examples/pom.xml
index c848836c1..2be02a1b8 100644
--- a/commons-math-examples/pom.xml
+++ b/commons-math-examples/pom.xml
@@ -75,6 +75,12 @@
         <version>${math.version}</version>
       </dependency>
 
+      <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-math4-ga2</artifactId>
+        <version>${math.version}</version>
+      </dependency>
+
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-math4-legacy</artifactId>
diff --git a/commons-math-ga2/LICENCE b/commons-math-ga2/LICENCE
new file mode 100644
index 000000000..261eeb9e9
--- /dev/null
+++ b/commons-math-ga2/LICENCE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/commons-math-ga2/NOTICE b/commons-math-ga2/NOTICE
new file mode 100644
index 000000000..28031e8ef
--- /dev/null
+++ b/commons-math-ga2/NOTICE
@@ -0,0 +1,5 @@
+Apache Commons Math
+Copyright 2001-2022 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/commons-math-examples/examples-ga/pom.xml b/commons-math-ga2/pom.xml
similarity index 61%
copy from commons-math-examples/examples-ga/pom.xml
copy to commons-math-ga2/pom.xml
index 462daf2c4..fe9407878 100644
--- a/commons-math-examples/examples-ga/pom.xml
+++ b/commons-math-ga2/pom.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -21,42 +21,44 @@
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.apache.commons</groupId>
-    <artifactId>commons-math-examples</artifactId>
+    <artifactId>commons-math-parent</artifactId>
     <version>4.0-SNAPSHOT</version>
   </parent>
-  <artifactId>examples-ga</artifactId>
-  <packaging>pom</packaging>
-  <name>GA</name>
+  <artifactId>commons-math4-ga2</artifactId>
+  <name>Genetic Algorithms</name>
+
+  <description>Genetic Algorithms</description>
 
   <properties>
+    <!-- The Java Module System Name -->
+    <commons.module.name>org.apache.commons.math4.ga2</commons.module.name>
+    <!-- This value must reflect the current name of the base package. -->
+    <commons.osgi.symbolicName>org.apache.commons.math4.ga2</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>
+    <math.parent.dir>${basedir}/..</math.parent.dir>
+    <math.slf4j.version>1.7.32</math.slf4j.version>
   </properties>
 
   <dependencies>
 
     <dependency>
       <groupId>org.apache.commons</groupId>
-      <artifactId>commons-math4-ga</artifactId>
+      <artifactId>commons-rng-simple</artifactId>
     </dependency>
 
     <dependency>
-      <groupId>info.picocli</groupId>
-      <artifactId>picocli</artifactId>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-rng-sampling</artifactId>
     </dependency>
 
     <dependency>
       <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-simple</artifactId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${math.slf4j.version}</version>
     </dependency>
 
   </dependencies>
 
-  <modules>
-    <module>examples-ga-math-functions</module>
-    <module>examples-ga-math-functions-adaptive</module>
-    <module>examples-ga-tsp</module>
-    <module>examples-parallel-ga-math-functions</module>
-  </modules>
-
 </project>
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/AbstractCrossover.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/AbstractCrossover.java
new file mode 100644
index 000000000..df003b79c
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/AbstractCrossover.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ga2;
+
+import java.util.List;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Base class for "crossover" operators.
+ *
+ * @param <G> Genotype.
+ */
+public abstract class AbstractCrossover<G> implements GeneticOperator<G> {
+    /** Number of parents and number of offsprings. */
+    private static final int NUM_CHROMOSOMES = 2;
+
+    /** {@inheritDoc} */
+    @Override
+    public final int numberOfParents() {
+        return NUM_CHROMOSOMES;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int numberOfOffsprings() {
+        return NUM_CHROMOSOMES;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if there are less than two parents
+     * or they are of different sizes.
+     */
+    @Override
+    public List<G> apply(List<G> parents,
+                         UniformRandomProvider rng) {
+        if (parents.size() != NUM_CHROMOSOMES) {
+            throw new IllegalArgumentException("Unexpected number of parents");
+        }
+
+        final List<G> offsprings = apply(parents.get(0),
+                                         parents.get(1),
+                                         rng);
+        if (offsprings.size() != NUM_CHROMOSOMES) {
+            throw new IllegalArgumentException("Unexpected number of offsprings");
+        }
+
+        return offsprings;
+    }
+
+    /**
+     * Unconditionally apply operator.
+     *
+     * @param parent1 Parent.
+     * @param parent2 Parent.
+     * @param rng RNG.
+     * @return a list containing two offpsrings.
+     */
+    protected abstract List<G> apply(G parent1,
+                                     G parent2,
+                                     UniformRandomProvider rng);
+}
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/AbstractMutation.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/AbstractMutation.java
new file mode 100644
index 000000000..c00e72ab6
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/AbstractMutation.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ga2;
+
+import java.util.List;
+import java.util.ArrayList;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Base class for "mutation" operators.
+ *
+ * @param <G> Genotype.
+ */
+public abstract class AbstractMutation<G> implements GeneticOperator<G> {
+    /** Number of parents and number of offsprings. */
+    private static final int NUM_CHROMOSOMES = 1;
+    /** Mutation probability, per gene. */
+    private final double probability;
+
+    /**
+     * @param probability Probability that any gene should mutate.
+     */
+    protected AbstractMutation(double probability) {
+        if (probability <= 0 ||
+            probability > 1) {
+            throw new IllegalArgumentException("Out of range");
+        }
+
+        this.probability = probability;
+    }
+
+    /** @return the probability of gene mutation. */
+    protected double getProbability() {
+        return probability;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int numberOfParents() {
+        return NUM_CHROMOSOMES;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int numberOfOffsprings() {
+        return NUM_CHROMOSOMES;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if there are less than two parents
+     * or they are of different sizes.
+     */
+    @Override
+    public List<G> apply(List<G> parents,
+                         UniformRandomProvider rng) {
+        if (parents.size() != NUM_CHROMOSOMES) {
+            throw new IllegalArgumentException("Incompatible number of parents");
+        }
+
+        final G mutated = apply(parents.get(0),
+                                rng);
+
+        final List<G> offsprings = new ArrayList<>(1);
+        offsprings.add(mutated);
+
+        return offsprings;
+    }
+
+    /**
+     * Apply operator.
+     *
+     * @param parent Parent.
+     * @param rng RNG.
+     * @return a list containing one offpsring.
+     */
+    protected abstract G apply(G parent,
+                               UniformRandomProvider rng);
+}
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/ApplicationRate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/ApplicationRate.java
new file mode 100644
index 000000000..46c148657
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/ApplicationRate.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.ga2;
+
+import java.util.List;
+
+/**
+ * Computes the probability to apply a genetic operator (the higher the
+ * probability, the higher the chance that offsrpings will be different
+ * from their parents.
+ */
+public abstract class ApplicationRate {
+    /** Minimum probability. */
+    private final double min;
+    /** Maximum probability. */
+    private final double max;
+
+    /**
+     * @param min Minimum probability.
+     * @param max Maximum probability.
+     * @throws IllegalArgumentException if either probability is out of
+     * the {@code [0 1]} interval or {@code min > max}.
+     */
+    protected ApplicationRate(double min,
+                              double max) {
+        if (min < 0 ||
+            min > 1) {
+            throw new IllegalArgumentException("Probability (min) is out of range");
+        }
+        if (max < 0 ||
+            max > 1) {
+            throw new IllegalArgumentException("Probability (max) is out of range");
+        }
+        if (min >= max) {
+            throw new IllegalArgumentException("max <= min");
+        }
+
+        this.min = min;
+        this.max = max;
+    }
+
+    /**
+     * @param p Probability.
+     * @throws IllegalArgumentException if the probability is out of
+     * the {@code [0 1]} interval.
+     */
+    protected ApplicationRate(double p) {
+        if (p < 0 ||
+            p > 1) {
+            throw new IllegalArgumentException("Probability is out of range");
+        }
+        this.min = p;
+        this.max = p;
+    }
+
+    /** @return the minimum probability. */
+    protected double min() {
+        return min;
+    }
+    /** @return the maximum probability. */
+    protected double max() {
+        return max;
+    }
+
+    /**
+     * Computes the probability that some operator will be applied to the
+     * given {@code chromosomes}.
+     *
+     * @param population Population.
+     * @param chromosomes Chromosomes that belong to the {@code population}.
+     * @return the probability to apply the operator.
+     *
+     * @param <G> Genotype.
+     * @param <P> Phenotype.
+     */
+    public abstract <G, P> double compute(Population<G, P> population,
+                                          List<G> chromosomes);
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/ChromosomeFactory.java
similarity index 52%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/ChromosomeFactory.java
index eee31fbc8..4a615eef9 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/ChromosomeFactory.java
@@ -14,41 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
-
-import java.util.List;
+package org.apache.commons.math4.ga2;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Chromosome generator.
+ *
+ * @param <G> Genotype.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
+public interface ChromosomeFactory<G> {
     /**
-     * constructor.
-     * @param values coordinates of all dimensions.
+     * @param size Number of genes.
+     * @return a chromosome of the specified {@code size}.
      */
-    public Coordinate(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 + "]";
-    }
-
+    G with(int size);
 }
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/FitnessService.java
similarity index 52%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/FitnessService.java
index eee31fbc8..84ba0b6d0 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/FitnessService.java
@@ -14,41 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
 
-import java.util.List;
+package org.apache.commons.math4.ga2;
+
+import java.util.Collection;
+import java.util.function.Function;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Fitness function.
+ *
+ * @param <G> Genotype.
+ * @param <P> Phenotype.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
+public interface FitnessService<G, P> {
     /**
-     * constructor.
-     * @param values coordinates of all dimensions.
+     * Computes the fitness value.
+     *
+     * @param decoder Genotype to phenotype converter.
+     * @param chromosomes Chromosomes.
+     * @return the fitness values, in the same order as the input.
      */
-    public Coordinate(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 + "]";
-    }
-
+    double[] apply(Function<G, P> decoder,
+                   Collection<G> chromosomes);
 }
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GenerationCallback.java
similarity index 53%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GenerationCallback.java
index eee31fbc8..6e7163176 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GenerationCallback.java
@@ -14,41 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
+package org.apache.commons.math4.ga2;
 
 import java.util.List;
+import java.util.Map;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Function for passing a copy of the current population contents to
+ * external code.
+ *
+ * @param <G> Genotype.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(List<Double> values) {
-        this.values = values;
-    }
-
+public interface GenerationCallback<G> {
     /**
-     * Returns the values of all coordinates.
-     * @return values of coordinates
+     * @param contents Population (sorted in decreasing order of fitness).
+     * @param generation Current generation.
      */
-    public List<Double> getValues() {
-        return values;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        return "Coordinate [values=" + values + "]";
-    }
-
+    void update(List<Map.Entry<G, Double>> contents,
+                int generation);
 }
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GeneticAlgorithmFactory.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GeneticAlgorithmFactory.java
new file mode 100644
index 000000000..40ec98249
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GeneticAlgorithmFactory.java
@@ -0,0 +1,240 @@
+/*
+ * 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.ga2;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Genetic algorithm factory.
+ *
+ * @param <G> Genotype.
+ * @param <P> Phenotype.
+ */
+public final class GeneticAlgorithmFactory<G, P> implements Callable<Population<G, P>> {
+    /** Logger. */
+    private static final Logger LOG = LoggerFactory.getLogger(GeneticAlgorithmFactory.class);
+    /** Initial list of chromosomes. */
+    private final Collection<G> init;
+    /** Genotype to phenotype converter. */
+    private final Function<G, P> decoder;
+    /** Criterion for stopping the evolution. */
+    private final Predicate<Population<G, P>> stop;
+    /** Fitness calculator. */
+    private final FitnessService<G, P> fitness;
+    /** Chromosome selector. */
+    private final Selection<G, P> selection;
+    /** Offspring generators. */
+    private final Map<GeneticOperator<G>, ApplicationRate> operators;
+    /** Elitism. */
+    private final double elitism;
+    /** RNG. */
+    private final RandomSource random;
+    /** Callback. */
+    private final GenerationCallback<G> callback;
+
+    /**
+     * @param init Initial list of chromosomes.
+     * @param decoder Genotype to phenotype converter.
+     * @param stop Criterion for stopping the evolution.
+     * @param fitness Fitness calculator.
+     * @param selection Chromosome selector.
+     * @param operators Offspring generators.
+     * @param elitism Fraction of the population that will be unconditionally
+     * transferred to the next generation.
+     * @param random Source of randomness.
+     * @param callback Callback.
+     */
+    private GeneticAlgorithmFactory(Collection<G> init,
+                                    Function<G, P> decoder,
+                                    Predicate<Population<G, P>> stop,
+                                    FitnessService<G, P> fitness,
+                                    Selection<G, P> selection,
+                                    Map<GeneticOperator<G>, ApplicationRate> operators,
+                                    double elitism,
+                                    RandomSource random,
+                                    GenerationCallback<G> callback) {
+        this.init = init;
+        this.decoder = decoder;
+        this.stop = stop;
+        this.fitness = fitness;
+        this.selection = selection;
+        this.operators = Collections.unmodifiableMap(operators);
+        this.elitism = elitism;
+        this.random = random;
+        this.callback = callback;
+    }
+
+    /**
+     * @param init Initial list of chromosomes.
+     * @param decoder Genotype to phenotype converter.
+     * @param stop Criterion for stopping the evolution.
+     * @param fitness Fitness calculator.
+     * @param selection Chromosome selector.
+     * @param operators Offspring generators.
+     * @param elitism Fraction of the population that will be unconditionally
+     * transferred to the next generation.
+     * @param random Source of randomness.
+     * @param callback Callback.
+     * @return a new instance.
+     *
+     * @param <G> Genotype.
+     * @param <P> Phenotype.
+     */
+    public static <G, P> Callable<Population<G, P>> create(Collection<G> init,
+                                                           Function<G, P> decoder,
+                                                           Predicate<Population<G, P>> stop,
+                                                           FitnessService<G, P> fitness,
+                                                           Selection<G, P> selection,
+                                                           Map<GeneticOperator<G>, ApplicationRate> operators,
+                                                           double elitism,
+                                                           RandomSource random,
+                                                           GenerationCallback<G> callback) {
+        return new GeneticAlgorithmFactory<>(init,
+                                             decoder,
+                                             stop,
+                                             fitness,
+                                             selection,
+                                             operators,
+                                             elitism,
+                                             random,
+                                             callback);
+    }
+
+    /**
+     * @param chromosomeSize Number of genes.
+     * @param initFactory Factory for creating the initial chromosomes.
+     * @param populationSize Number of chromosomes per generation.
+     * @param decoder Genotype to phenotype converter.
+     * @param stop Criterion for stopping the evolution.
+     * @param fitness Fitness calculator.
+     * @param selection Chromosome selector.
+     * @param operators Offspring generators.
+     * @param elitism Fraction of the population that will be unconditionally
+     * transferred to the next generation.
+     * @param random Source of randomness.
+     * @param callback Callback.
+     * @return a new instance.
+     *
+     * @param <G> Genotype.
+     * @param <P> Phenotype.
+     */
+    public static <G, P> Callable<Population<G, P>> create(int chromosomeSize,
+                                                           ChromosomeFactory<G> initFactory,
+                                                           int populationSize,
+                                                           Function<G, P> decoder,
+                                                           Predicate<Population<G, P>> stop,
+                                                           FitnessService<G, P> fitness,
+                                                           Selection<G, P> selection,
+                                                           Map<GeneticOperator<G>, ApplicationRate> operators,
+                                                           double elitism,
+                                                           RandomSource random,
+                                                           GenerationCallback<G> callback) {
+        // Create initial population.
+        final List<G> init = new ArrayList<>(populationSize);
+        for (int i = 0; i < populationSize; i++) {
+            init.add(initFactory.with(chromosomeSize));
+        }
+
+        return create(init,
+                      decoder,
+                      stop,
+                      fitness,
+                      selection,
+                      operators,
+                      elitism,
+                      random,
+                      callback);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Population<G, P> call() {
+        int generation = 0;
+        final int popSize = init.size();
+
+        Population<G, P> currentGen = new Population<>(popSize, decoder, fitness);
+        currentGen.add(init);
+        final UniformRandomProvider rng = random.create();
+
+        while (!stop.test(currentGen)) {
+            ++generation;
+            final Population<G, P> nextGen = new Population<>(popSize, decoder, fitness);
+
+            applyElitism(currentGen, nextGen);
+
+            // Loop for allowing that some fitnesses are "NaN" (thus
+            // avoiding that the new generation is not fuly populated).
+            while (true) {
+                final int numCandidates = nextGen.allowedInsertions();
+                if (numCandidates == 0) {
+                    break;
+                }
+
+                // Generate and compute fitness.
+                nextGen.add(currentGen.offsprings(numCandidates,
+                                                  selection,
+                                                  operators,
+                                                  rng));
+
+                if (nextGen.allowedInsertions() == numCandidates) {
+                    LOG.error("Zero insertion at generation {}",
+                              generation);
+                }
+            }
+
+            if (callback != null) {
+                // Notify caller.
+                callback.update(nextGen.contents(true), generation);
+            }
+
+            // Replace with new generation.
+            currentGen = nextGen;
+        }
+
+        return currentGen;
+    }
+
+    /**
+     * @param from Population from which to retrieve the best individuals.
+     * @param to Population to which the individuals must be added.
+     */
+    private void applyElitism(Population<G, P> from,
+                              Population<G, P> to) {
+        final List<Map.Entry<G, Double>> contents = from.contents(true);
+
+        // Number of elite individuals to be transferred.
+        final int elite = (int) (contents.size() * elitism);
+
+        final List<G> eliteList = new ArrayList<>(elite);
+        for (int i = 0; i < elite; i++) {
+            eliteList.add(contents.get(i).getKey());
+        }
+
+        to.add(eliteList);
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GeneticOperator.java
similarity index 54%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GeneticOperator.java
index eee31fbc8..31ae216b3 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/GeneticOperator.java
@@ -14,41 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
+
+package org.apache.commons.math4.ga2;
 
 import java.util.List;
+import org.apache.commons.rng.UniformRandomProvider;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Represents any operator that applies on chromosomes of the
+ * appropriate type.
+ *
+ * @param <G> Genotype.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
+public interface GeneticOperator<G> {
+    /** @return the number of parents. */
+    int numberOfParents();
 
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(List<Double> values) {
-        this.values = values;
-    }
+    /** @return the number of offsprings. */
+    int numberOfOffsprings();
 
     /**
-     * Returns the values of all coordinates.
-     * @return values of coordinates
+     * @param parents Parents.
+     * @param rng Source of randomness.
+     * @return the offsprings.
      */
-    public List<Double> getValues() {
-        return values;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        return "Coordinate [values=" + values + "]";
-    }
-
+    List<G> apply(List<G> parents,
+                  UniformRandomProvider rng);
 }
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/Population.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/Population.java
new file mode 100644
index 000000000..a75979acc
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/Population.java
@@ -0,0 +1,191 @@
+/*
+ * 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.ga2;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.AbstractMap;
+import java.util.HashMap;
+import java.util.function.Function;
+import org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Collection of chromosomes and associated fitness.
+ *
+ * Class is <em>not</em> thread-safe.
+ *
+ * @param <G> Genotype.
+ * @param <P> Phenotype.
+ */
+public class Population<G, P> {
+    /** Population data (fitness). */
+    private final Map<G, Double> chromo2fit = new HashMap<>();
+    /** Population data (rank). */
+    private final Map<G, Integer> chromo2rank = new HashMap<>();
+    /** Decoder. */
+    private final Function<G, P> decoder;
+    /** Fitness function. */
+    private final FitnessService<G, P> fitnessCalculator;
+    /** Maximum population size. */
+    private final int maxSize;
+
+    /**
+     * @param max Maximum allowed number of chromosomes.
+     * @param geno2pheno Genotype to phenotype converter.
+     * @param function Fitness calculator.
+     */
+    public Population(int max,
+                      Function<G, P> geno2pheno,
+                      FitnessService<G, P> function) {
+        maxSize = max;
+        decoder = geno2pheno;
+        fitnessCalculator = function;
+    }
+
+    /** @return the number of free slots in the population. */
+    public int allowedInsertions() {
+        return Math.max(0, maxSize - chromo2fit.size());
+    }
+
+    /** @return the number of individuals in the population. */
+    public int size() {
+        return chromo2fit.size();
+    }
+
+    /**
+     * Insert chromosomes into the population.
+     * Fitness and rank are calculated.
+     * If the fitness is {@code NaN}, the corresponding chromosome is
+     * <em>not</em> added to the population.
+     *
+     * @param chromosomes Chromosomes.
+     */
+    public void add(Collection<G> chromosomes) {
+        if (chromosomes.size() > allowedInsertions()) {
+            throw new IllegalArgumentException("Too many chromosomes");
+        }
+
+        final double[] fitness = fitnessCalculator.apply(decoder, chromosomes);
+        int c = 0;
+        boolean atLeastOneInsertion = false;
+        for (G chromosome : chromosomes) {
+            final double value = fitness[c++];
+            if (!Double.isNaN(value)) {
+                // Insert.
+                chromo2fit.put(chromosome, value);
+                atLeastOneInsertion = true;
+            }
+        }
+
+        if (atLeastOneInsertion) { // Recompute ranks.
+            final List<Map.Entry<G, Double>> list = contents(true);
+            for (int i = 0; i < list.size(); i++) {
+                chromo2rank.put(list.get(i).getKey(), i);
+            }
+        }
+    }
+
+    /**
+     *
+    /**
+     * Retrieves the rank.
+     * Ranks are attributed in the range [0, N - 1] (where N is the
+     * number of individuals in the population) in inverse order of
+     * fitness (i.e. the chromosome with highest fitness has rank 0).
+     *
+     * @param chromosome Chromosome.
+     * @return the rank of the given {@code chromosome}.
+     * @throws IllegalArgumentException if the {@code chromosome} does
+     * not belong to this population.
+     */
+    public int rank(G chromosome) {
+        final Integer r = chromo2rank.get(chromosome);
+
+        if (r == null) {
+            throw new IllegalArgumentException("Chromosome not found");
+        }
+
+        return r;
+    }
+
+    /**
+     * @param sorted If {@code true}, the contents will be sorted in decreasing
+     * order of fitness.
+     * @return a copy of the population contents.
+     */
+    public List<Map.Entry<G, Double>> contents(boolean sorted) {
+        final List<Map.Entry<G, Double>> out = new ArrayList<>(chromo2fit.size());
+
+        for (Map.Entry<G, Double> e : chromo2fit.entrySet()) {
+            out.add(new AbstractMap.SimpleImmutableEntry<>(e));
+        }
+
+        if (sorted) {
+            sort(out);
+        }
+
+        return out;
+    }
+
+    /**
+     * @param numOffsprings Number of offsprings to generate.
+     * @param selection Parents selector.
+     * @param operators Generators to be applied on the parents.
+     * @param rng RNG.
+     * @return the offsprings.
+     */
+    public Collection<G> offsprings(int numOffsprings,
+                                    Selection<G, P> selection,
+                                    Map<GeneticOperator<G>, ApplicationRate> operators,
+                                    UniformRandomProvider rng) {
+        final List<G> candidates = new ArrayList<>(numOffsprings);
+        OFFSPRING: while (true) {
+            for (Map.Entry<GeneticOperator<G>, ApplicationRate> e : operators.entrySet()) {
+                final GeneticOperator<G> op = e.getKey();
+                final List<G> parents = selection.apply(op.numberOfParents(), this);
+                final double prob = e.getValue().compute(this, parents);
+                final List<G> offsprings = rng.nextDouble() < prob ?
+                    op.apply(parents, rng) :
+                    parents;
+
+                for (int i = 0; i < offsprings.size(); i++) {
+                    if (candidates.size() >= numOffsprings) {
+                        break OFFSPRING;
+                    }
+                    candidates.add(offsprings.get(i));
+                }
+            }
+        }
+
+        return Collections.unmodifiableList(candidates);
+    }
+
+    /**
+     * Sort (in-place) in decreasing order of fitness.
+     *
+     * @param contents List of individuals.
+     *
+     * @param <G> Genotype.
+     */
+    public static <G> void sort(List<Map.Entry<G, Double>> contents) {
+        Collections.sort(contents,
+                         Map.Entry.<G, Double>comparingByValue().reversed());
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/Selection.java
similarity index 53%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/Selection.java
index eee31fbc8..9ed47ae28 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/Selection.java
@@ -14,41 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
+package org.apache.commons.math4.ga2;
 
 import java.util.List;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Select chromosomes from a population.
+ *
+ * @param <G> Genotype.
+ * @param <P> Phenotype.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(List<Double> values) {
-        this.values = values;
-    }
-
+public interface Selection<G, P> {
     /**
-     * Returns the values of all coordinates.
-     * @return values of coordinates
+     * Select chromosomes from a population.
+     *
+     * @param population Population from which chromosomes are chosen.
+     * @param n Number of chromosomes to select.
+     * @return the selected chromosomes.
      */
-    public List<Double> getValues() {
-        return values;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        return "Coordinate [values=" + values + "]";
-    }
-
+    List<G> apply(int n,
+                  Population<G, P> population);
 }
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/fitness/FitnessConcurrentCalculator.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/fitness/FitnessConcurrentCalculator.java
new file mode 100644
index 000000000..93c616966
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/fitness/FitnessConcurrentCalculator.java
@@ -0,0 +1,98 @@
+/*
+ * 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.ga2.fitness;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.concurrent.Future;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
+import java.util.function.ToDoubleFunction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.commons.math4.ga2.FitnessService;
+
+/**
+ * Perform parallel computation of fitnesses.
+ *
+ * @param <G> Genotype.
+ * @param <P> Phenotype.
+ */
+public class FitnessConcurrentCalculator<G, P> implements FitnessService<G, P> {
+    /** Logger. */
+    private static final Logger LOG = LoggerFactory.getLogger(FitnessConcurrentCalculator.class);
+    /** Fitness function. */
+    private final ToDoubleFunction<P> function;
+    /** Executor. */
+    private final ExecutorService executor;
+
+    /**
+     * @param fitness Fitness function.
+     * Note: This class <em>must</em> be thrad-safe.
+     * @param service Executor.
+     */
+    public FitnessConcurrentCalculator(ToDoubleFunction<P> fitness,
+                                       ExecutorService service) {
+        function = fitness;
+        executor = service;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Note: If the fitness fails, or is interrupted, the corresponding slot
+     * will contain {@link Double#NaN NaN}.
+     */
+    @Override
+    public double[] apply(Function<G, P> decoder,
+                          Collection<G> chromosomes) {
+        final double[] fitnesses = new double[chromosomes.size()];
+        int c = 0;
+        for (Future<Double> f : compute(decoder, chromosomes)) {
+            double value = Double.NaN; // Default.
+            try {
+                // Fitness computations were submitted to the executor:
+                // Wait for all results.
+                value = f.get();
+            } catch (InterruptedException |
+                     ExecutionException e) {
+                LOG.error("Unexpected failure: {}", e.getMessage());
+            }
+            fitnesses[c++] = value;
+        }
+        return fitnesses;
+    }
+
+    /**
+     * Compute fitness of every chromosome, using all available threads.
+     *
+     * @param decoder Genotype to phenotype converter.
+     * @param chromosomes Chromosomes.
+     * @return the fitness values, in the same order as the input.
+     */
+    private List<Future<Double>> compute(Function<G, P> decoder,
+                                         Collection<G> chromosomes) {
+        final List<Future<Double>> futures = new ArrayList<>();
+        for (G chromosome : chromosomes) {
+            futures.add(executor.submit(() -> function.applyAsDouble(decoder.apply(chromosome))));
+        }
+        return futures;
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/fitness/package-info.java
similarity index 51%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/fitness/package-info.java
index eee31fbc8..05e68cda2 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/fitness/package-info.java
@@ -14,41 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
-
-import java.util.List;
-
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Functionality for computation of fitness.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(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 + "]";
-    }
-
-}
+package org.apache.commons.math4.ga2.fitness;
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Chromosome.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Chromosome.java
new file mode 100644
index 000000000..9878ccbc7
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Chromosome.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ga2.gene.binary;
+
+import java.util.BitSet;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.math4.ga2.ChromosomeFactory;
+
+/**
+ * Represents a chromosome.
+ */
+public final class Chromosome {
+    /** Length. */
+    private final int size;
+    /** Internal representation. */
+    private final BitSet representation;
+
+    /**
+     * Forbid direct instantiation.
+     *
+     * @param representation Representation.  Reference is copied: Factory
+     * methods that call this constructor <em>must</em> pass a defensive copy.
+     * @param size Number of genes.
+     */
+    private Chromosome(BitSet representation,
+                       int size) {
+        this.size = size;
+        this.representation = representation;
+    }
+
+    /** @return the number of genes. */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Factory method.
+     *
+     * @param r Representation.
+     * @param size Number of genes.
+     * @return a new instance whose internal representation is a copy of the
+     * bits of {@code r} (truncating, or expanding with {@code false}, so that
+     * the number of genes is equal to {@code size}).
+     */
+    public static Chromosome from(BitSet r,
+                                  int size) {
+        return new Chromosome((BitSet) r.clone(), size);
+    }
+
+    /**
+     * Factory method.
+     *
+     * @param rng Random generator.
+     * @param size Number of genes.
+     * @return a new instance whose genes are drawn from the given generator.
+     */
+    public static Chromosome from(UniformRandomProvider rng,
+                                  int size) {
+        final BitSet r = new BitSet(size);
+        for (int i = 0; i < size; i++) {
+            r.set(i, rng.nextBoolean());
+        }
+
+        return new Chromosome(r, size);
+    }
+
+    /** @return a copy of the internal representation. */
+    public BitSet asBitSet() {
+        return (BitSet) representation.clone();
+    }
+
+    /**
+     * Generate chromosomes with random genes.
+     * Class is <em>not</em> thread-safe.
+     */
+    public static class RandomFactory implements ChromosomeFactory<Chromosome> {
+        /** RNG. */
+        private final UniformRandomProvider rng;
+
+        /**
+         * @param random Source of randomness.
+         */
+        public RandomFactory(RandomSource random) {
+            rng = random.create();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Chromosome with(int size) {
+            return Chromosome.from(rng, size);
+        }
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Mutation.java
similarity index 50%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Mutation.java
index eee31fbc8..193f79809 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Mutation.java
@@ -14,41 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
 
-import java.util.List;
+package org.apache.commons.math4.ga2.gene.binary;
+
+import java.util.BitSet;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.math4.ga2.AbstractMutation;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Genetic operator.
+ * Class is immutable.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
+/* package-private */ class Mutation extends AbstractMutation<Chromosome> {
     /**
-     * constructor.
-     * @param values coordinates of all dimensions.
+     * @param probability Probability that a gene will mutate.
      */
-    public Coordinate(List<Double> values) {
-        this.values = values;
+    Mutation(double probability) {
+        super(probability);
     }
 
-    /**
-     * Returns the values of all coordinates.
-     * @return values of coordinates
-     */
-    public List<Double> getValues() {
-        return values;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    public String toString() {
-        return "Coordinate [values=" + values + "]";
-    }
+    protected Chromosome apply(Chromosome parent,
+                               UniformRandomProvider rng) {
+        final int size = parent.size();
+        final BitSet c = parent.asBitSet();
+        for (int i = 0; i < size; i++) {
+            if (rng.nextDouble() < getProbability()) {
+                c.flip(i);
+            }
+        }
 
+        return Chromosome.from(c, size);
+    }
 }
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/OnePointCrossover.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/OnePointCrossover.java
new file mode 100644
index 000000000..95342a7bb
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/OnePointCrossover.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.ga2.gene.binary;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.math4.ga2.AbstractCrossover;
+
+/**
+ * Genetic operator.
+ * Class is immutable.
+ */
+/* package-private */ class OnePointCrossover extends AbstractCrossover<Chromosome> {
+    /** {@inheritDoc} */
+    @Override
+    protected List<Chromosome> apply(Chromosome parent1,
+                                     Chromosome parent2,
+                                     UniformRandomProvider rng) {
+        final int size = parent1.size();
+        if (size != parent2.size()) {
+            throw new IllegalArgumentException("Parents of unequal sizes: " +
+                                               size + " != " +
+                                               parent2.size());
+        }
+
+        // Index of crossover point.
+        final int xIndex = 1 + rng.nextInt(size - 1);
+        final BitSet p1 = parent1.asBitSet();
+        final BitSet p2 = parent2.asBitSet();
+        final BitSet c1;
+        final BitSet c2;
+
+        final int midIndex = size / 2;
+        if (xIndex > midIndex) {
+            c1 = parent1.asBitSet();
+            c2 = parent2.asBitSet();
+
+            for (int i = xIndex; i < size; i++) {
+                c1.set(i, p2.get(i));
+                c2.set(i, p1.get(i));
+            }
+        } else {
+            c1 = parent2.asBitSet();
+            c2 = parent1.asBitSet();
+
+            for (int i = 0; i < xIndex; i++) {
+                c1.set(i, p1.get(i));
+                c2.set(i, p2.get(i));
+            }
+        }
+
+        final List<Chromosome> offsprings = new ArrayList<>(2);
+        offsprings.add(Chromosome.from(c1, size));
+        offsprings.add(Chromosome.from(c2, size));
+
+        return offsprings;
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Operators.java
similarity index 54%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Operators.java
index eee31fbc8..7030134f5 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/Operators.java
@@ -14,41 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
 
-import java.util.List;
+package org.apache.commons.math4.ga2.gene.binary;
+
+import org.apache.commons.math4.ga2.GeneticOperator;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Genetic operators factory.
+ * It creates instances that operate on "binary" genotypes.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(List<Double> values) {
-        this.values = values;
-    }
+public final class Operators {
+    /** Prevent instantiation of utility class. */
+    private Operators() {}
 
     /**
-     * Returns the values of all coordinates.
-     * @return values of coordinates
+     * @param probability Probability that a gene flip will occur.
+     * @return a mutation operator.
      */
-    public List<Double> getValues() {
-        return values;
+    public static GeneticOperator<Chromosome> mutation(double probability) {
+        return new Mutation(probability);
     }
-
     /**
-     * {@inheritDoc}
+     * @return a one-point crossover operator.
      */
-    @Override
-    public String toString() {
-        return "Coordinate [values=" + values + "]";
+    public static GeneticOperator<Chromosome> onePointCrossover() {
+        return new OnePointCrossover();
     }
-
 }
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/package-info.java
similarity index 51%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/package-info.java
index eee31fbc8..960ed5302 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/gene/binary/package-info.java
@@ -14,41 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
-
-import java.util.List;
-
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Classes that define functionality specific to "binary" genotypes.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(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 + "]";
-    }
-
-}
+package org.apache.commons.math4.ga2.gene.binary;
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/package-info.java
similarity index 51%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/package-info.java
index eee31fbc8..5930d9eb3 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/package-info.java
@@ -14,41 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
-
-import java.util.List;
-
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Genetic Algorithm.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(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 + "]";
-    }
-
-}
+package org.apache.commons.math4.ga2;
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/AverageRankLinearRate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/AverageRankLinearRate.java
new file mode 100644
index 000000000..c1e056328
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/AverageRankLinearRate.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.ga2.rate;
+
+import java.util.List;
+import org.apache.commons.math4.ga2.ApplicationRate;
+import org.apache.commons.math4.ga2.Population;
+
+/**
+ * Average-rank-weighted linear interpolation.
+ */
+/* package-private */ class AverageRankLinearRate extends ApplicationRate {
+    /**
+     * @param min Minimum probability.
+     * @param max Maximum probability.
+     */
+    AverageRankLinearRate(double min,
+                          double max) {
+        super(min, max);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public <G, P> double compute(Population<G, P> population,
+                                 List<G> chromosomes) {
+        if (chromosomes.isEmpty()) {
+            throw new IllegalArgumentException("Empty list");
+        }
+
+        double avg = 0;
+        for (G c : chromosomes) {
+            avg += population.rank(c);
+        }
+        avg /= chromosomes.size();
+
+        return min() + (max() - min()) * avg / (population.size() - 1);
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/ConstantRate.java
similarity index 55%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/ConstantRate.java
index eee31fbc8..41d1e9333 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/ConstantRate.java
@@ -14,41 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
+package org.apache.commons.math4.ga2.rate;
 
 import java.util.List;
+import org.apache.commons.math4.ga2.ApplicationRate;
+import org.apache.commons.math4.ga2.Population;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Constant.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
+/* package-private */ class ConstantRate extends ApplicationRate {
     /**
-     * constructor.
-     * @param values coordinates of all dimensions.
+     * @param value Probability.
      */
-    public Coordinate(List<Double> values) {
-        this.values = values;
+    ConstantRate(double value) {
+        super(value);
     }
 
-    /**
-     * Returns the values of all coordinates.
-     * @return values of coordinates
-     */
-    public List<Double> getValues() {
-        return values;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    public String toString() {
-        return "Coordinate [values=" + values + "]";
+    public <G, P> double compute(Population<G, P> population,
+                                 List<G> chromosomes) {
+        return min();
     }
-
 }
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/RateGenerators.java
similarity index 50%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/RateGenerators.java
index eee31fbc8..5ea563606 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/RateGenerators.java
@@ -14,41 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
 
-import java.util.List;
+package org.apache.commons.math4.ga2.rate;
+
+import org.apache.commons.math4.ga2.ApplicationRate;
 
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Rate generator factory.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
+public final class RateGenerators {
+    /** Prevent instantiation of utility class. */
+    private RateGenerators() {}
 
     /**
-     * constructor.
-     * @param values coordinates of all dimensions.
+     * Creates a generator that always returns the given {@code value}.
+     *
+     * @param value Probability.
+     * @return a new instance.
      */
-    public Coordinate(List<Double> values) {
-        this.values = values;
+    public static ApplicationRate constant(double value) {
+        return new ConstantRate(value);
     }
 
     /**
-     * Returns the values of all coordinates.
-     * @return values of coordinates
+     * Creates a generator that returns a linear interpolation.
+     *
+     * @param min Minimum probability.
+     * @param max Maximum probability.
+     * @return a new instance.
      */
-    public List<Double> getValues() {
-        return values;
+    public static ApplicationRate averageRankLinear(double min,
+                                                    double max) {
+        return new AverageRankLinearRate(min, max);
     }
-
-    /**
-     * {@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/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/package-info.java
similarity index 51%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/package-info.java
index eee31fbc8..8eebb1622 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/rate/package-info.java
@@ -14,41 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
-
-import java.util.List;
-
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Probability generators.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(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 + "]";
-    }
-
-}
+package org.apache.commons.math4.ga2.rate;
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/select/Tournament.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/select/Tournament.java
new file mode 100644
index 000000000..8e2f06179
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/select/Tournament.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.ga2.select;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.rng.sampling.ListSampler;
+import org.apache.commons.math4.ga2.Population;
+import org.apache.commons.math4.ga2.Selection;
+
+/**
+ * Select chromosomes from a population.
+ *
+ * @param <G> Genotype.
+ * @param <P> Phenotype.
+ */
+public class Tournament<G, P> implements Selection<G, P> {
+    /** RNG. */
+    private final UniformRandomProvider rng;
+    /** Number of chromosomes that take part in a tournament. */
+    private final int poolSize;
+
+    /**
+     * @param poolSize Number of chromosomes that take part in each tournament.
+     * @param source Source of randomness.
+     */
+    public Tournament(int poolSize,
+                      RandomSource source) {
+        this.poolSize = poolSize;
+        rng = source.create();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<G> apply(int n,
+                         Population<G, P> population) {
+        final List<Map.Entry<G, Double>> all = population.contents(false);
+
+        final List<G> selected = new ArrayList<G>(n);
+        for (int i = 0; i < n; i++) {
+            selected.add(selectFrom(all));
+        }
+
+        return selected;
+    }
+
+    /**
+     * @param population Population.
+     * @return the winner of a tournament.
+     */
+    private G selectFrom(List<Map.Entry<G, Double>> population) {
+        // Randomly draw participants to the tournament.
+        final List<Map.Entry<G, Double>> pool = ListSampler.sample(rng,
+                                                                   population,
+                                                                   poolSize);
+        // Within the pool, choose the best individual.
+        Population.sort(pool);
+        return pool.get(0).getKey();
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/select/package-info.java
similarity index 51%
copy from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
copy to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/select/package-info.java
index eee31fbc8..c771dee00 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/select/package-info.java
@@ -14,41 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
-
-import java.util.List;
-
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Selecting individuals from a population.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(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 + "]";
-    }
-
-}
+package org.apache.commons.math4.ga2.select;
diff --git a/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/stop/UnchangedFitness.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/stop/UnchangedFitness.java
new file mode 100644
index 000000000..c261c434f
--- /dev/null
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/stop/UnchangedFitness.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.ga2.stop;
+
+import java.util.Map;
+import java.util.function.ToDoubleFunction;
+import java.util.function.Predicate;
+import org.apache.commons.math4.ga2.Population;
+
+/**
+ * Criterion for asserting convergence of a population.
+ * Note: Class is <em>not</em> thread-safe.
+ *
+ * @param <G> Genotype.
+ * @param <P> Phenotype.
+ */
+public class UnchangedFitness<G, P> implements Predicate<Population<G, P>> {
+    /** Function that computes the reference value. */
+    private final ToDoubleFunction<Population<G, P>> calculator;
+    /** Number of generations during which no change has happened. */
+    private final int maxGenerations;
+    /** Value for previous population. */
+    private double previousFitness = Double.NaN;
+    /** Number of generations without changes. */
+    private int generations = 0;
+
+    /** What needs to be unchanged. */
+    public enum Type {
+        /** Best fitness. */
+        BEST,
+        /** Mean fitness. */
+        MEAN;
+    }
+
+    /**
+     * @param criterion Reference value that when unchanged for the given
+     * number of {@code generations}, signals convergence.
+     * @param maxGenerations Number of generations during which the reference
+     * value must have been the same.
+     */
+    public UnchangedFitness(Type criterion,
+                            int maxGenerations) {
+        switch (criterion) {
+        case BEST:
+            calculator = p -> bestFitness(p);
+            break;
+        case MEAN:
+            calculator = p -> meanFitness(p);
+            break;
+        default:
+            calculator = null;
+            throw new IllegalStateException(); // Should never happen.
+        }
+
+        this.maxGenerations = maxGenerations;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean test(Population<G, P> population) {
+        final double fitness = calculator.applyAsDouble(population);
+        if (fitness == previousFitness) {
+            if (++generations > maxGenerations) {
+                return true;
+            }
+        } else {
+            generations = 0;
+            previousFitness = fitness;
+        }
+
+        return false;
+    }
+
+    /**
+     * @param population Population.
+     * @return the best fitness.
+     *
+     * @param <G> Genotype.
+     * @param <P> Phenotype.
+     */
+    private static <G, P> double bestFitness(Population<G, P> population) {
+        return population.contents(true).get(0).getValue();
+    }
+
+    /**
+     * @param population Population.
+     * @return the mean fitness.
+     *
+     * @param <G> Genotype.
+     * @param <P> Phenotype.
+     */
+    private static <G, P> double meanFitness(Population<G, P> population) {
+        double mean = 0;
+        int count = 0;
+
+        for (Map.Entry<G, Double> e : population.contents(false)) {
+            mean += e.getValue();
+        }
+
+        return mean / count;
+    }
+}
diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/stop/package-info.java
similarity index 51%
rename from commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
rename to commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/stop/package-info.java
index eee31fbc8..bb0b7772d 100644
--- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java
+++ b/commons-math-ga2/src/main/java/org/apache/commons/math4/ga2/stop/package-info.java
@@ -14,41 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.examples.ga.mathfunctions;
-
-import java.util.List;
-
 /**
- * This class represents the coordinate of the problem domain i.e. the phenotype
- * of chromosome.
+ * Convergence criteria.
  */
-public class Coordinate {
-
-    /** coordinate of all dimensions. **/
-    private final List<Double> values;
-
-    /**
-     * constructor.
-     * @param values coordinates of all dimensions.
-     */
-    public Coordinate(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 + "]";
-    }
-
-}
+package org.apache.commons.math4.ga2.stop;
diff --git a/pom.xml b/pom.xml
index 1fef46725..23e2a096d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -117,6 +117,7 @@
     <module>commons-math-neuralnet</module>
     <module>commons-math-transform</module>
     <module>commons-math-ga</module>
+    <module>commons-math-ga2</module>
 
     <!-- 2. Modularized (but not refactored) legacy functionalities. -->
     <module>commons-math-legacy-exception</module>
diff --git a/src/main/resources/checkstyle/checkstyle-suppressions.xml b/src/main/resources/checkstyle/checkstyle-suppressions.xml
index 64edbff2b..00efd1ccf 100644
--- a/src/main/resources/checkstyle/checkstyle-suppressions.xml
+++ b/src/main/resources/checkstyle/checkstyle-suppressions.xml
@@ -36,6 +36,9 @@
   <suppress checks="TypeName" files=".*[/\\]AccurateMath\.java" />
   <suppress checks="ParameterName" files=".*[/\\]AccurateMathCalc\.java" />
   <suppress checks="ParameterNumber" files=".*[/\\]AdaptiveMathFunctionOptimizer\.java" />
+  <suppress checks="ParameterNumber" files=".*[/\\]GeneticAlgorithmFactory\.java" />
+  <suppress checks="ParameterNumber" files=".*[/\\]MathFunctionOptimizer2\.java" />
+  <suppress checks="IllegalCatch" files=".*[/\\]MathFunctionOptimizer2\.java" />
 
   <!-- Be more lenient on tests. -->
   <suppress checks="Javadoc" files=".*[/\\]test[/\\].*" />