diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml new file mode 100644 index 0000000000..1089efd06f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + org.apache.commons + examples-ga + 4.0-SNAPSHOT + + examples-ga-math-functions + + + 1.8 + 1.8 + + + org.apache.commons.math4.examples.ga.mathfunctions + org.apache.commons.math4.examples.ga.mathfunctions + + org.apache.commons.math4.examples.ga.mathfunctions + + ${basedir}/../../.. + + examples-ga-mathfunctions + org.apache.commons.math4.examples.ga.mathfunctions.Dimension2FunctionOptimizer + + + \ No newline at end of file diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java new file mode 100644 index 0000000000..e19eb6addf --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Coordinate.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions; + +/** + * This class represents the coordinate of the problem domain i.e. the phenotype of chromosome. + */ +public class Coordinate { + + /** coordinate of first dimension. **/ + private final double x; + + /** coordinate of second dimension. **/ + private final double y; + + /** + * constructor. + * @param x coordinate of first dimension + * @param y coordinate of second dimension + */ + public Coordinate(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * returns the coordinate of first dimension. + * @return coordinate of first dimension + */ + public double getX() { + return x; + } + + /** + * returns the coordinate of second dimension. + * @return coordinate of second dimension + */ + public double getY() { + return y; + } + + /** + * Returns a string representation of coordinate. + */ + @Override + public String toString() { + return "Coordinate [x=" + x + ", y=" + y + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2Decoder.java new file mode 100644 index 0000000000..01290e5bac --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2Decoder.java @@ -0,0 +1,51 @@ +/* + * 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.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; + +/** + * Decoder to convert chromosome's binary genotype to phenotype + * {@link Coordinate}. + */ +public class Dimension2Decoder extends AbstractListChromosomeDecoder { + + /** + * decode the binary representation of chromosome to {@link Coordinate}. + * @param chromosome The {@link AbstractListChromosome} + */ + @Override + protected Coordinate decode(AbstractListChromosome chromosome) { + final BinaryChromosome binaryChromosome = (BinaryChromosome) chromosome; + final List alleles = binaryChromosome.getRepresentation(); + + final StringBuilder allelesStr = new StringBuilder(); + for (Integer allele : alleles) { + allelesStr.append(Integer.toBinaryString(allele)); + } + + final double x = Integer.parseInt(allelesStr.substring(0, 12), 2) / 100.0; + final double y = Integer.parseInt(allelesStr.substring(12, 24), 2) / 100.0; + + return new Coordinate(x, y); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2FitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2FitnessFunction.java new file mode 100644 index 0000000000..d55b810859 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2FitnessFunction.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions; + +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the mathematical fitness function for optimizing a 2 + * dimension mathematical function. + */ +public class Dimension2FitnessFunction implements FitnessFunction { + + /** + * Computes the fitness value based on the decoded chromosome. + * @param coordinate The {@link Coordinate} + * @return the fitness value + */ + @Override + public double compute(Coordinate coordinate) { + return -Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .25) * + (Math.pow(Math.sin(50 * Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .1)), + 2) + 1); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2FunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2FunctionOptimizer.java new file mode 100644 index 0000000000..feae63d768 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/Dimension2FunctionOptimizer.java @@ -0,0 +1,104 @@ +/* + * 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 org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.convergencecond.UnchangedBestFitness; +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.examples.ga.mathfunctions.utils.GraphPlotter; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergencecond.StoppingCondition; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ConsoleLogger; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class Dimension2FunctionOptimizer { + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final Dimension2FunctionOptimizer optimizer = new Dimension2FunctionOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = ConvergenceListenerRegistry + .getInstance(); + convergenceListenerRegistry + .addConvergenceListener(new PopulationStatisticsLogger(Constants.ENCODING)); + convergenceListenerRegistry + .addConvergenceListener(new GraphPlotter("Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + /** + * Optimizes the population. + * @param initial The {@link Population} + */ + public void optimize(Population initial) { + + // initialize a new genetic algorithm + final GeneticAlgorithm ga = new GeneticAlgorithm<>(new OnePointCrossover(), + Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(Constants.TOURNAMENT_SIZE), Constants.ELITISM_RATE); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness<>( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + final ConsoleLogger consoleLogger = ConsoleLogger.getInstance(Constants.ENCODING); + consoleLogger.log("*********************************************"); + consoleLogger.log("***********Optimization Result***************"); + + consoleLogger.log(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>(Constants.POPULATION_SIZE); + final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction(); + final Dimension2Decoder decoder = new Dimension2Decoder(); + for (int i = 0; i < Constants.POPULATION_SIZE; i++) { + population.addChromosome(BinaryChromosome.randomChromosome(Constants.CHROMOSOME_LENGTH, + fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/Dimension2FunctionOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/Dimension2FunctionOptimizerLegacy.java new file mode 100644 index 0000000000..fdddcd3765 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/Dimension2FunctionOptimizerLegacy.java @@ -0,0 +1,96 @@ +/* + * 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.legacy; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import org.apache.commons.math3.genetics.BinaryChromosome; +import org.apache.commons.math3.genetics.BinaryMutation; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.exception.GeneticException; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * the legacy genetic algorithm. + */ +public class Dimension2FunctionOptimizerLegacy { + + /** + * Optimizes the 2-dimensional fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + final Dimension2FunctionOptimizerLegacy simulation = new Dimension2FunctionOptimizerLegacy(); + + simulation.optimize(initPopulation); + } + + /** + * Optimizes the initial population using legacy genetic algorithm. + * @param initial initial {@link Population} + */ + public void optimize(Population initial) { + + // initialize a new genetic algorithm + final GeneticAlgorithm geneticAlgorithm = new GeneticAlgorithm(new OnePointCrossover<>(), + Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(Constants.TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = geneticAlgorithm.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) { + writer.write("*********************************************"); + writer.newLine(); + writer.write("***********Optimization Result***************"); + writer.write(bestFinal.toString()); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + /** + * Generates the initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ElitisticListPopulation(Constants.POPULATION_SIZE, Constants.ELITISM_RATE); + for (int i = 0; i < Constants.POPULATION_SIZE; i++) { + population.addChromosome(new LegacyBinaryChromosome( + BinaryChromosome.randomBinaryRepresentation(Constants.CHROMOSOME_LENGTH))); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/LegacyBinaryChromosome.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/LegacyBinaryChromosome.java new file mode 100644 index 0000000000..06790735c2 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/LegacyBinaryChromosome.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.legacy; + +import java.util.List; + +import org.apache.commons.math3.genetics.AbstractListChromosome; +import org.apache.commons.math3.genetics.BinaryChromosome; + +/** + * A representation of concrete binary chromosome. + */ +public class LegacyBinaryChromosome extends BinaryChromosome { + + /** + * constructor. + * @param representation the internal representation + */ + public LegacyBinaryChromosome(List representation) { + super(representation); + } + + /** + * {@inheritDoc} + */ + @Override + public double fitness() { + final List alleles = getRepresentation(); + + final StringBuilder allelesStr = new StringBuilder(); + for (Integer allele : alleles) { + allelesStr.append(Integer.toBinaryString(allele)); + } + + final double x = Integer.parseInt(allelesStr.substring(0, 12), 2) / 100.0; + final double y = Integer.parseInt(allelesStr.substring(12, 24), 2) / 100.0; + + return -Math.pow(Math.pow(x, 2) + Math.pow(y, 2), .25) * + (Math.pow(Math.sin(50 * Math.pow(Math.pow(x, 2) + Math.pow(y, 2), .1)), 2) + 1); + } + + /** + * {@inheritDoc} + */ + @Override + public AbstractListChromosome newFixedLengthChromosome(List chromosomeRepresentation) { + return new LegacyBinaryChromosome(chromosomeRepresentation); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/UnchangedBestFitness.java new file mode 100644 index 0000000000..eabb11db55 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/UnchangedBestFitness.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.legacy; + +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; + +/** + * This class represents the stopping condition based on unchanged best fitness. + */ +public class UnchangedBestFitness implements StoppingCondition { + + /** last best fitness. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** maximum number of generations evolved with unchanged best fitness. **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** generations having unchanged best fitness. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations evolved with + * unchanged best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population population) { + final double currentBestFitness = population.getFittestChromosome().getFitness(); + + if (lastBestFitness == currentBestFitness) { + this.generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/package-info.java new file mode 100644 index 0000000000..24ca3d70a0 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/legacy/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.legacy; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/package-info.java new file mode 100644 index 0000000000..f2344be6ce --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java new file mode 100644 index 0000000000..1149c7d8ff --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.utils; + +/** + * This abstraction maintains constants used by this module. + */ +public final class Constants { + + /** size of population. **/ + public static final int POPULATION_SIZE = 20; + + /** size of tournament. **/ + public static final int TOURNAMENT_SIZE = 2; + + /** length of chromosome. **/ + public static final int CHROMOSOME_LENGTH = 24; + + /** rate of crossover. **/ + public static final double CROSSOVER_RATE = 1.0; + + /** rate of elitism. **/ + public static final double ELITISM_RATE = 0.25; + + /** rate of mutation. **/ + public static final double AVERAGE_MUTATION_RATE = 0.05; + + /** number of generations with unchanged best fitness. **/ + public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50; + + /** encoding for console logger. **/ + public static final String ENCODING = "UTF-8"; + + /** + * constructor. + */ + private Constants() { + + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/GraphPlotter.java new file mode 100644 index 0000000000..7b47e35073 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/GraphPlotter.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.utils; + +import java.awt.BorderLayout; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.examples.ga.mathfunctions.Coordinate; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.apache.commons.math4.ga.stats.internal.PopulationStatisticalSummaryImpl; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class GraphPlotter extends JFrame implements ConvergenceListener { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List seriesList = dataset.getSeries(); + if (seriesList == null || seriesList.isEmpty()) { + return false; + } + for (XYSeries series : seriesList) { + if (series.getKey().compareTo(graphName) == 0) { + return true; + } + } + return false; + } + + /** + * Creates chart panel. + * @param chartTitle chart title + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + * @return panel + */ + private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) { + + final boolean showLegend = true; + final boolean createURL = false; + final boolean createTooltip = false; + + final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset, + PlotOrientation.VERTICAL, showLegend, createTooltip, createURL); + final XYPlot plot = chart.getXYPlot(); + final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + + plot.setRenderer(renderer); + + return new ChartPanel(chart); + + } + + /** + * {@inheritDoc} + */ + @Override + public void notify(int generation, Population population) { + PopulationStatisticalSummary populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>( + population); + this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness()); + this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness()); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java new file mode 100644 index 0000000000..360a1c38fb --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.utils; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml new file mode 100644 index 0000000000..533cb5a89a --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + org.apache.commons + examples-ga + 4.0-SNAPSHOT + + examples-ga-tsp + + + 1.8 + 1.8 + + + org.apache.commons.math4.examples.ga.tsp + org.apache.commons.math4.examples.ga.tsp + + org.apache.commons.math4.examples.ga.tsp + + ${basedir}/../../.. + + examples-ga-mathfunctions + org.apache.commons.math4.examples.ga.tsp.TSPOptimizer + + + + + org.apache.commons + commons-csv + 1.9.0 + + + \ No newline at end of file diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java new file mode 100644 index 0000000000..9b06312e0a --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp; + +import java.util.List; + + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.commons.DistanceMatrix; +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the fitness function for tsp. + */ +public class TSPFitnessFunction implements FitnessFunction> { + + /** + * {@inheritDoc} + */ + @Override + public double compute(List cities) { + double totalDistance = 0.0; + int index1 = 0; + int index2 = 0; + for (int i = 0; i < cities.size(); i++) { + index1 = i; + index2 = (i == cities.size() - 1) ? 0 : i + 1; + totalDistance += calculateNodeDistance(cities.get(index1), cities.get(index2)); + } + return -totalDistance; + } + + private double calculateNodeDistance(City node1, City node2) { + final DistanceMatrix distanceMatrix = DistanceMatrix.getInstance(); + return distanceMatrix.getDistance(node1, node2); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java new file mode 100644 index 0000000000..e0bbaa3d02 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp; + +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.examples.ga.tsp.utils.GraphPlotter; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergencecond.StoppingCondition; +import org.apache.commons.math4.ga.convergencecond.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.ConsoleLogger; + +/** + * This class represents the optimizer for traveling salesman problem. + */ +public class TSPOptimizer { + + /** + * Main method to initiate the optimization process. + * @param args arguments + */ + public static void main(String[] args) { + try { + final Population> initPopulation = getInitialPopulation(Constants.CITIES); + + final TSPOptimizer optimizer = new TSPOptimizer(); + + final ConvergenceListenerRegistry> convergenceListenerRegistry = ConvergenceListenerRegistry + .getInstance(); + convergenceListenerRegistry + .addConvergenceListener(new PopulationStatisticsLogger<>(Constants.ENCODING)); + convergenceListenerRegistry + .addConvergenceListener(new GraphPlotter("Convergence", "generation", "total-distance")); + + optimizer.optimizeSGA(initPopulation, Constants.CITIES); + + Thread.sleep(5000); + + } catch (InterruptedException e) { + throw new GeneticException(e); + } + } + + /** + * Optimizes the tsp problem. + * @param initial initial population + * @param cities cities + */ + public void optimizeSGA(Population> initial, List cities) { + + // initialize a new genetic algorithm + final GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointCrossover>(), + Constants.CROSSOVER_RATE, new RealValuedMutation>(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection>(Constants.TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition> stopCond = new UnchangedBestFitness<>( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population> finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + final RealValuedChromosome> bestFinal = (RealValuedChromosome>) finalPopulation + .getFittestChromosome(); + + final ConsoleLogger consoleLogger = ConsoleLogger.getInstance(Constants.ENCODING); + consoleLogger.log("*********************************************"); + consoleLogger.log("***********Optimization Result***************"); + + consoleLogger.log(bestFinal.decode().toString()); + consoleLogger.log("Best Fitness: %.6f", bestFinal.evaluate()); + + } + + private static Population> getInitialPopulation(List cities) { + final Population> simulationPopulation = new ListPopulation<>(Constants.POPULATION_SIZE); + + for (int i = 0; i < Constants.POPULATION_SIZE; i++) { + simulationPopulation.addChromosome(new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomPermutation(Constants.CHROMOSOME_LENGTH), + new TSPFitnessFunction(), new RandomKeyDecoder(cities))); + } + + return simulationPopulation; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java new file mode 100644 index 0000000000..7a6d8d110b --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.commons; + +/** + * This class represents a city with location coordinate. + */ +public final class City { + + /** index of city. **/ + private final int index; + + /** x coordinate. **/ + private final double x; + + /** y coordinate. **/ + private final double y; + + /** + * constructor. + * @param index index of city + * @param x x coordinate + * @param y y coordinate + */ + public City(int index, double x, double y) { + this.index = index; + this.x = x; + this.y = y; + } + + /** + * Returns city index. + * @return city index + */ + public int getIndex() { + return index; + } + + /** + * Returns x coordinate. + * @return x coordinate + */ + public double getX() { + return x; + } + + /** + * Returns y coordinate. + * @return y coordinate + */ + public double getY() { + return y; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Node [index=" + index + ", x=" + x + ", y=" + y + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java new file mode 100644 index 0000000000..175c7c695e --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.commons; + +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; + +/** + * This class represents the distance matrix between cities. + */ +public final class DistanceMatrix { + + /** instance of the class. **/ + private static final DistanceMatrix INSTANCE = new DistanceMatrix(); + + /** distances between cities. **/ + private double[][] distances; + + private DistanceMatrix() { + initialize(Constants.CITIES); + } + + /** + * Returns distances between two cities. + * @param city1 first city + * @param city2 second city + * @return distance + */ + public double getDistance(City city1, City city2) { + return distances[city1.getIndex() - 1][city2.getIndex() - 1]; + } + + /** + * Initializes the distance matrix. + * @param cities list of cities + */ + private void initialize(List cities) { + final int len = cities.size(); + this.distances = new double[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + distances[i][j] = Math.pow(Math.pow(cities.get(i).getX() - cities.get(j).getX(), 2) + + Math.pow(cities.get(i).getY() - cities.get(j).getY(), 2), .5); + } + } + } + + /** + * Returns the instance of this class. + * @return instance + */ + public static DistanceMatrix getInstance() { + return INSTANCE; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java new file mode 100644 index 0000000000..e0422fd72e --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp.commons; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java new file mode 100644 index 0000000000..9a42d39582 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import java.util.List; + +import org.apache.commons.math3.genetics.RandomKey; +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.commons.DistanceMatrix; + +/** + * This class represents chromosome for tsp problem. + */ +public class TSPChromosome extends RandomKey { + + /** list of cities. **/ + private final List cities; + + /** + * constructor. + * @param representation internal representation of chromosome + * @param cities list of cities + */ + public TSPChromosome(List representation, List cities) { + super(representation); + this.cities = cities; + } + + /** + * {@inheritDoc} + */ + @Override + public double fitness() { + final List permutatedNodes = decode(cities); + return -calculateTotalDistance(permutatedNodes); + } + + /** + * {@inheritDoc} + */ + @Override + public TSPChromosome newFixedLengthChromosome(List representation) { + return new TSPChromosome(representation, cities); + } + + private double calculateTotalDistance(List permutedCities) { + double totalDistance = 0.0; + int index1 = 0; + int index2 = 0; + for (int i = 0; i < permutedCities.size(); i++) { + index1 = i; + index2 = (i == permutedCities.size() - 1) ? 0 : i + 1; + totalDistance += calculateNodeDistance(permutedCities.get(index1), permutedCities.get(index2)); + } + return totalDistance; + } + + private double calculateNodeDistance(City node1, City node2) { + return DistanceMatrix.getInstance().getDistance(node1, node2); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java new file mode 100644 index 0000000000..35410a21bd --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import java.io.BufferedWriter; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; + +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.RandomKey; +import org.apache.commons.math3.genetics.RandomKeyMutation; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.ga.exception.GeneticException; + +/** + * This class represents the tsp optimizer based on legacy implementation of + * Genetic Algorithm. + */ +public class TSPOptimizerLegacy { + + /** + * Main method to initiate optimization. + * @param args arguments + */ + public static void main(String[] args) { + try { + final Population initPopulation = getInitialPopulation(Constants.CITIES); + + final TSPOptimizerLegacy optimizer = new TSPOptimizerLegacy(); + + optimizer.optimize(initPopulation, Constants.CITIES); + + Thread.sleep(5000); + + } catch (InterruptedException e) { + throw new GeneticException(e); + } + } + + /** + * Optimizes the tsp problem using legacy GA. + * @param initial initial population + * @param cities cities + */ + public void optimize(Population initial, List cities) { + + // initialize a new genetic algorithm + final GeneticAlgorithm ga = new GeneticAlgorithm(new OnePointCrossover(), Constants.CROSSOVER_RATE, + new RandomKeyMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(Constants.TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + @SuppressWarnings("unchecked") + final RandomKey bestFinal = (RandomKey) finalPopulation.getFittestChromosome(); + + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) { + writer.write("*********************************************"); + writer.newLine(); + writer.write("***********Optimization Result***************"); + writer.write(bestFinal.toString()); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + private static Population getInitialPopulation(List cities) { + final Population simulationPopulation = new ElitisticListPopulation(Constants.POPULATION_SIZE, .25); + + for (int i = 0; i < Constants.POPULATION_SIZE; i++) { + simulationPopulation.addChromosome(new TSPChromosome(RandomKey.randomPermutation(cities.size()), cities)); + } + + return simulationPopulation; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java new file mode 100644 index 0000000000..44fe93a325 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; + +/** + * This class represents the stopping condition based on unchanged best fitness. + */ +public class UnchangedBestFitness implements StoppingCondition { + + /** last best fitness. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** maximum number of generations evolved with unchanged best fitness. **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** generations having unchanged best fitness. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations evolved with + * unchanged best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population population) { + final double currentBestFitness = population.getFittestChromosome().getFitness(); + + if (lastBestFitness == currentBestFitness) { + this.generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java new file mode 100644 index 0000000000..d46c6fd7b5 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java new file mode 100644 index 0000000000..938b8f916a --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java new file mode 100644 index 0000000000..c41cc31203 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.tsp.utils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; + +/** + * This class contains all required constants for this example. + */ +public final class Constants { + + /** size of population. **/ + public static final int POPULATION_SIZE = 100; + + /** size of tournament. **/ + public static final int TOURNAMENT_SIZE = 5; + + /** length of chromosome. **/ + public static final int CHROMOSOME_LENGTH = 14; + + /** rate of crossover. **/ + public static final double CROSSOVER_RATE = 1.0; + + /** rate of elitism. **/ + public static final double ELITISM_RATE = 0.25; + + /** rate of mutation. **/ + public static final double AVERAGE_MUTATION_RATE = 0.05; + + /** maximum number of generations with unchanged best fitness. **/ + public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50; + + /** list of cities. **/ + public static final List CITIES = Collections.unmodifiableList( + Arrays.asList(new City[] {new City(1, 0, 0), new City(2, 1, 0), new City(3, 2, 0), new City(4, 3, 0), + new City(5, 3, 1), new City(6, 3, 2), new City(7, 3, 3), new City(8, 2, 3), new City(9, 1, 3), + new City(10, 0, 3), new City(11, 1, 2), new City(12, 2, 2), new City(13, 2, 1), new City(14, 1, 1)})); + + /** encoding for console logger. **/ + public static final String ENCODING = "UTF-8"; + + private Constants() { + + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java new file mode 100644 index 0000000000..bc84a7900b --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.utils; + +import java.awt.BorderLayout; + +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.apache.commons.math4.ga.stats.internal.PopulationStatisticalSummaryImpl; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class GraphPlotter extends JFrame implements ConvergenceListener> { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List seriesList = dataset.getSeries(); + if (seriesList == null || seriesList.isEmpty()) { + return false; + } + for (XYSeries series : seriesList) { + if (series.getKey().compareTo(graphName) == 0) { + return true; + } + } + return false; + } + + /** + * Creates chart panel. + * @param chartTitle chart title + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + * @return panel + */ + private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) { + + final boolean showLegend = true; + final boolean createURL = false; + final boolean createTooltip = false; + + final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset, + PlotOrientation.VERTICAL, showLegend, createTooltip, createURL); + final XYPlot plot = chart.getXYPlot(); + final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + + plot.setRenderer(renderer); + + return new ChartPanel(chart); + + } + + /** + * {@inheritDoc} + */ + @Override + public void notify(int generation, Population> population) { + PopulationStatisticalSummary> populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>( + population); + this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness()); + this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness()); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java new file mode 100644 index 0000000000..5caadde4b3 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp.utils; diff --git a/commons-math-examples/examples-ga/pom.xml b/commons-math-examples/examples-ga/pom.xml new file mode 100644 index 0000000000..81ddf842b9 --- /dev/null +++ b/commons-math-examples/examples-ga/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + org.apache.commons + commons-math-examples + 4.0-SNAPSHOT + + examples-ga + pom + examples-genetic-algorithm + + + + ${basedir}/../.. + + + + + org.apache.commons + commons-math-ga + 4.0-SNAPSHOT + + + org.apache.commons + commons-math3 + + + org.jfree + jfreechart + 1.5.3 + + + + examples-ga-math-functions + examples-ga-tsp + + \ No newline at end of file diff --git a/commons-math-examples/examples-ga/src/main/resources/spotbugs/spotbugs-exclude-filter.xml b/commons-math-examples/examples-ga/src/main/resources/spotbugs/spotbugs-exclude-filter.xml new file mode 100644 index 0000000000..7337a51c50 --- /dev/null +++ b/commons-math-examples/examples-ga/src/main/resources/spotbugs/spotbugs-exclude-filter.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/commons-math-examples/pom.xml b/commons-math-examples/pom.xml index e182af04ac..3cc1f50f23 100644 --- a/commons-math-examples/pom.xml +++ b/commons-math-examples/pom.xml @@ -150,6 +150,7 @@ examples-sofm + examples-ga diff --git a/commons-math-ga/pom.xml b/commons-math-ga/pom.xml new file mode 100644 index 0000000000..b6b3f62a81 --- /dev/null +++ b/commons-math-ga/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + org.apache.commons + commons-math-parent + 4.0-SNAPSHOT + + commons-math-ga + genetic algorithm + + + + + + org.apache.commons.math4.ga + + org.apache.commons.math4.ga + + org.apache.commons.math4.ga + + ${basedir}/.. + + + + + org.apache.commons + commons-numbers-core + + + org.apache.commons + commons-rng-simple + + + + \ No newline at end of file diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java new file mode 100644 index 0000000000..6aeb3564d1 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.convergencecond.StoppingCondition; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; + +/** + * This class represents an abstraction for all Genetic algorithm implementation + * comprising the basic properties and operations. + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractGeneticAlgorithm

{ + + /** the crossover policy used by the algorithm. */ + private final CrossoverPolicy

crossoverPolicy; + + /** the mutation policy used by the algorithm. */ + private final MutationPolicy

mutationPolicy; + + /** the selection policy used by the algorithm. */ + private final SelectionPolicy

selectionPolicy; + + /** + * the number of generations evolved to reach {@link StoppingCondition} in the + * last run. + */ + private int generationsEvolved; + + /** The elitism rate having default value of .25. */ + private double elitismRate = .25; + + /** + * constructor. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param mutationPolicy The {@link MutationPolicy} + * @param selectionPolicy The {@link SelectionPolicy} + */ + protected AbstractGeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, final MutationPolicy

mutationPolicy, + final SelectionPolicy

selectionPolicy) { + this.crossoverPolicy = crossoverPolicy; + this.mutationPolicy = mutationPolicy; + this.selectionPolicy = selectionPolicy; + } + + /** + * constructor. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param mutationPolicy The {@link MutationPolicy} + * @param selectionPolicy The {@link SelectionPolicy} + * @param elitismRate The elitism rate + */ + protected AbstractGeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, final MutationPolicy

mutationPolicy, + final SelectionPolicy

selectionPolicy, double elitismRate) { + this.crossoverPolicy = crossoverPolicy; + this.mutationPolicy = mutationPolicy; + this.selectionPolicy = selectionPolicy; + this.elitismRate = elitismRate; + } + + /** + * Returns the crossover policy. + * @return crossover policy + */ + public CrossoverPolicy

getCrossoverPolicy() { + return crossoverPolicy; + } + + /** + * Returns the mutation policy. + * @return mutation policy + */ + public MutationPolicy

getMutationPolicy() { + return mutationPolicy; + } + + /** + * Returns the selection policy. + * @return selection policy + */ + public SelectionPolicy

getSelectionPolicy() { + return selectionPolicy; + } + + /** + * Returns the number of generations evolved to reach {@link StoppingCondition} + * in the last run. + * + * @return number of generations evolved + * @since 2.1 + */ + public int getGenerationsEvolved() { + return generationsEvolved; + } + + /** + * Evolve the given population. Evolution stops when the stopping condition is + * satisfied. Updates the {@link #getGenerationsEvolved() generationsEvolved} + * property with the number of generations evolved before the StoppingCondition + * is satisfied. + * + * @param initial the initial, seed population. + * @param condition the stopping condition used to stop evolution. + * @return the population that satisfies the stopping condition. + */ + public Population

evolve(final Population

initial, final StoppingCondition

condition) { + Population

current = initial; + // check if stopping condition is satisfied otherwise produce the next + // generation of population. + while (!condition.isSatisfied(current)) { + // notify interested listener + ConvergenceListenerRegistry.

getInstance().notifyAll(generationsEvolved, current); + + current = nextGeneration(current); + this.generationsEvolved++; + } + + return current; + } + + /** + * Evolve the given population into the next generation. + *

    + *
  1. Get nextGeneration population to fill from current + * generation, using its nextGeneration method
  2. + *
  3. Loop until new generation is filled: + *
      + *
    • Apply configured SelectionPolicy to select a pair of parents from + * current,
    • + *
    • apply configured {@link CrossoverPolicy} to parents,
    • + *
    • apply configured {@link MutationPolicy} to each of the offspring
    • + *
    • Add offspring individually to nextGeneration, space permitting
    • + *
    + *
  4. + *
  5. Return nextGeneration
  6. + *
+ * + * @param current the current population + * @return the population for the next generation. + */ + protected abstract Population

nextGeneration(Population

current); + + /** + * Returns the elitism rate. + * @return elitism rate + */ + public double getElitismRate() { + return elitismRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java new file mode 100644 index 0000000000..3f97072ca9 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.apache.commons.math4.ga.utils.Constants; + +/** + * Implementation of a genetic algorithm. All factors that govern the operation + * of the algorithm can be configured for a specific problem. + * + * @param

phenotype of chromosome + * @since 4.0 + */ +public class GeneticAlgorithm

extends AbstractGeneticAlgorithm

{ + + /** the rate of crossover for the algorithm. */ + private final double crossoverRate; + + /** the rate of mutation for the algorithm. */ + private final double mutationRate; + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + */ + public GeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, final double crossoverRate, + final MutationPolicy

mutationPolicy, final double mutationRate, + final SelectionPolicy

selectionPolicy) { + super(crossoverPolicy, mutationPolicy, selectionPolicy); + + checkValidity(crossoverRate, mutationRate); + this.crossoverRate = crossoverRate; + this.mutationRate = mutationRate; + } + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + * @param elitismRate The rate of elitism + */ + public GeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, final double crossoverRate, + final MutationPolicy

mutationPolicy, final double mutationRate, final SelectionPolicy

selectionPolicy, + final double elitismRate) { + super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate); + + checkValidity(crossoverRate, mutationRate); + this.crossoverRate = crossoverRate; + this.mutationRate = mutationRate; + } + + private void checkValidity(final double crossoverRateInput, final double inputMutationRate) { + if (crossoverRateInput < 0 || crossoverRateInput > 1) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, crossoverRateInput, Constants.CROSSOVER_RATE, 0, + 1); + } + if (inputMutationRate < 0 || inputMutationRate > 1) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, inputMutationRate, Constants.MUTATION_RATE, 0, 1); + } + } + + /** + * Evolve the given population into the next generation. + *

    + *
  1. Get nextGeneration population to fill from current + * generation, using its nextGeneration method
  2. + *
  3. Loop until new generation is filled: + *
      + *
    • Apply configured SelectionPolicy to select a pair of parents from + * current
    • + *
    • With probability = {@link #getCrossoverRate()}, apply configured + * {@link CrossoverPolicy} to parents
    • + *
    • With probability = {@link #getMutationRate()}, apply configured + * {@link MutationPolicy} to each of the offspring
    • + *
    • Add offspring individually to nextGeneration, space permitting
    • + *
    + *
  4. + *
  5. Return nextGeneration
  6. + *
+ * + * @param current the current population. + * @return the population for the next generation. + */ + @Override + protected Population

nextGeneration(final Population

current) { + final Population

nextGeneration = current.nextGeneration(getElitismRate()); + + while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit() - 1) { + // select parent chromosomes + ChromosomePair

pair = getSelectionPolicy().select(current); + + // apply crossover policy to create two offspring + pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond(), crossoverRate); + + // apply mutation policy to the chromosomes + pair = new ChromosomePair<>(getMutationPolicy().mutate(pair.getFirst(), mutationRate), + getMutationPolicy().mutate(pair.getSecond(), mutationRate)); + + // add the chromosomes to the population + nextGeneration.addChromosome(pair.getFirst()); + nextGeneration.addChromosome(pair.getSecond()); + } + + return nextGeneration; + } + + /** + * Returns the crossover rate. + * @return crossover rate + */ + public double getCrossoverRate() { + return crossoverRate; + } + + /** + * Returns the mutation rate. + * @return mutation rate + */ + public double getMutationRate() { + return mutationRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java new file mode 100644 index 0000000000..a3f5abc3fd --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * Individual in a population. Chromosomes are compared based on their fitness. + *

+ * The chromosomes are IMMUTABLE, and so their fitness is also immutable and + * therefore it can be cached. + * + * @param

The phenotype of chromosome. The type should override hashCode() + * and equals() methods. + * @since 4.0 + */ +public abstract class AbstractChromosome

implements Chromosome

{ + + /** Value assigned when no fitness has been computed yet. */ + private static final double NO_FITNESS = Double.NEGATIVE_INFINITY; + + /** Cached value of the fitness of this chromosome. */ + private double fitness = NO_FITNESS; + + /** Fitness function to evaluate fitness of chromosome. **/ + private final FitnessFunction

fitnessFunction; + + /** decoder to deode the chromosome's genotype representation. **/ + private final Decoder

decoder; + + /** + * constructor. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + protected AbstractChromosome(final FitnessFunction

fitnessFunction, final Decoder

decoder) { + ValidationUtils.checkForNull("fitness-function", fitnessFunction); + ValidationUtils.checkForNull("decoder", decoder); + this.fitnessFunction = fitnessFunction; + this.decoder = decoder; + } + + /** + * returns fitness function. + * @return fitnessFunction + */ + protected FitnessFunction

getFitnessFunction() { + return fitnessFunction; + } + + /** + * Returns the decoder instance. + * @return decoder + */ + protected Decoder

getDecoder() { + return decoder; + } + + /** + * Access the fitness of this chromosome. The bigger the fitness, the better the + * chromosome. + *

+ * Computation of fitness is usually very time-consuming task, therefore the + * fitness is cached. + * @return the fitness + */ + @Override + public double evaluate() { + if (this.fitness == NO_FITNESS) { + // no cache - compute the fitness + this.fitness = fitnessFunction.compute(decode()); + } + return this.fitness; + } + + /** + * Decodes the chromosome genotype and returns the phenotype. + * @return phenotype + */ + @Override + public P decode() { + return this.decoder.decode(this); + } + + /** + * Compares two chromosomes based on their fitness. The bigger the fitness, the + * better the chromosome. + * @param another another chromosome to compare + * @return + *

    + *
  • -1 if another is better than this
  • + *
  • 1 if another is worse than this
  • + *
  • 0 if the two chromosomes have the same fitness
  • + *
+ */ + @Override + public int compareTo(final Chromosome

another) { + return Double.compare(evaluate(), another.evaluate()); + } + + /** + * Returns true iff another has the same + * representation and therefore the same fitness. By default, it returns false + * -- override it in your implementation if you need it. + * @param another chromosome to compare + * @return true if another is equivalent to this chromosome + */ + public boolean isSame(final AbstractChromosome

another) { + final P decodedChromosome = decode(); + final P otherDecodedChromosome = another.decode(); + return decodedChromosome.equals(otherDecodedChromosome); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return String.format("(f=%s %s)", evaluate(), decode()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java new file mode 100644 index 0000000000..df3003ce96 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * This class represents an abstract chromosome containing an immutable list of + * allele/genes. + * @param type of the allele/gene in the representation list. T should be + * immutable. + * @param

phenotype of chromosome + * @since 2.0 + */ +public abstract class AbstractListChromosome extends AbstractChromosome

{ + + /** List of allele/genes. */ + private final List representation; + + /** + * constructor. + * @param representation The representation of chromosome genotype as + * {@link List} of generic T + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder An instance of {@link AbstractListChromosomeDecoder}, + * to decode list chromosome. + */ + protected AbstractListChromosome(final List representation, final FitnessFunction

fitnessFunction, + final AbstractListChromosomeDecoder decoder) { + this(representation, true, fitnessFunction, decoder); + } + + /** + * constructor. + * @param representation The representation of chromosome genotype as an array + * of generic T + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder An instance of {@link AbstractListChromosomeDecoder}, + * to decode list chromosome. + */ + protected AbstractListChromosome(final T[] representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + this(Arrays.asList(representation), fitnessFunction, decoder); + } + + /** + * constructor. + * @param representation Internal representation of chromosome genotype as an + * array of generic T + * @param copyList if {@code true}, the representation will be copied, + * otherwise it will be referenced. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The instance of {@link AbstractListChromosomeDecoder} + */ + protected AbstractListChromosome(final List representation, final boolean copyList, + final FitnessFunction

fitnessFunction, final AbstractListChromosomeDecoder decoder) { + super(fitnessFunction, decoder); + ValidationUtils.checkForNull("representation", representation); + this.representation = Collections.unmodifiableList(copyList ? new ArrayList<>(representation) : representation); + } + + /** + * Returns the (immutable) inner representation of the chromosome. + * @return the representation of the chromosome + */ + public List getRepresentation() { + return representation; + } + + /** + * Returns the length of the chromosome. + * @return the length of the chromosome + */ + public int getLength() { + return getRepresentation().size(); + } + + /** + * returns the decoder. + * @return decoder + */ + @SuppressWarnings("unchecked") + @Override + protected AbstractListChromosomeDecoder getDecoder() { + return (AbstractListChromosomeDecoder) super.getDecoder(); + } + + /** + * Creates a new instance of the same class as this is, with a + * given arrayRepresentation. This is needed in crossover and + * mutation operators, where we need a new instance of the same class, but with + * different array representation. + *

+ * Usually, this method just calls a constructor of the class. + * + * @param chromosomeRepresentation the inner array representation of the new + * chromosome. + * @return new instance extended from FixedLengthChromosome with the given + * arrayRepresentation + */ + public abstract AbstractListChromosome newChromosome(List chromosomeRepresentation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java new file mode 100644 index 0000000000..4830f81d0b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.List; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; + +/** + * Chromosome represented by a vector of 0s and 1s. + * @param

phenotype of chromosome + * @since 2.0 + */ +public class BinaryChromosome

extends IntegralValuedChromosome

{ + + /** + * constructor. + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + */ + public BinaryChromosome(List representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + super(representation, fitnessFunction, decoder, 0, 2); + } + + /** + * constructor. + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + */ + public BinaryChromosome(Integer[] representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + super(representation, fitnessFunction, decoder, 0, 2); + } + + /** + * {@inheritDoc} + */ + @Override + public BinaryChromosome

newChromosome(List chromosomeRepresentation) { + return new BinaryChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder()); + } + + /** + * Creates an instance of Binary Chromosome with random binary representation. + * @param

phenotype fo chromosome + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @return a binary chromosome + */ + public static

BinaryChromosome

randomChromosome(int length, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + return new BinaryChromosome<>(ChromosomeRepresentationUtils.randomBinaryRepresentation(length), fitnessFunction, + decoder); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java new file mode 100644 index 0000000000..8931a638d5 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.chromosome; + +/** + * This abstraction represents a chromosome. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface Chromosome

extends Comparable> { + + /** + * Access the fitness of this chromosome. The bigger the fitness, the better the + * chromosome. + *

+ * Computation of fitness is usually very time-consuming task, therefore the + * fitness is cached. + * @return the fitness + */ + double evaluate(); + + /** + * Decodes the chromosome genotype and returns the phenotype. + * @return phenotype + */ + P decode(); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java new file mode 100644 index 0000000000..1157a00822 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +/** + * A pair of {@link Chromosome} objects. + * @param

phenotype of chromosome + * @since 2.0 + */ +public class ChromosomePair

{ + + /** the first chromosome in the pair. */ + private final Chromosome

first; + + /** the second chromosome in the pair. */ + private final Chromosome

second; + + /** + * Create a chromosome pair. + * @param c1 the first chromosome. + * @param c2 the second chromosome. + */ + public ChromosomePair(final Chromosome

c1, final Chromosome

c2) { + super(); + first = c1; + second = c2; + } + + /** + * Access the first chromosome. + * + * @return the first chromosome. + */ + public Chromosome

getFirst() { + return first; + } + + /** + * Access the second chromosome. + * + * @return the second chromosome. + */ + public Chromosome

getSecond() { + return second; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return String.format("(%s,%s)", getFirst(), getSecond()); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java new file mode 100644 index 0000000000..91c08c7e18 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.List; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * Chromosome represented by a list of integral values. The acceptable integral + * values should belong to the range min(inclusive) to max(exclusive). + * @param

phenotype of chromosome + * @since 4.0 + */ +public class IntegralValuedChromosome

extends AbstractListChromosome { + + /** minimum acceptable value of allele. **/ + private final int min; + + /** maximum acceptable value of allele. **/ + private final int max; + + /** + * constructor. + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public IntegralValuedChromosome(List representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, int min, int max) { + super(representation, fitnessFunction, decoder); + this.min = min; + this.max = max; + checkValidity(); + } + + /** + * constructor. + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public IntegralValuedChromosome(Integer[] representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, int min, int max) { + super(representation, fitnessFunction, decoder); + this.min = min; + this.max = max; + checkValidity(); + } + + /** + * Returns the minimum acceptable value of allele. + * @return minimum value + */ + public int getMin() { + return min; + } + + /** + * Returns the maximum acceptable value of allele. + * @return maximum value + */ + public int getMax() { + return max; + } + + /** + * Asserts that representation can represent a valid chromosome. + */ + private void checkValidity() { + ValidationUtils.checkForMinMax(min, max); + for (int i : getRepresentation()) { + if (i < min || i >= max) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public IntegralValuedChromosome

newChromosome(List chromosomeRepresentation) { + return new IntegralValuedChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder(), this.min, + this.max); + } + + /** + * Creates an instance of Integral valued Chromosome with random binary + * representation. + * @param

phenotype fo chromosome + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + * @return an integral-valued chromosome + */ + public static

IntegralValuedChromosome

randomChromosome(int length, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, int min, int max) { + return new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(length, min, max), fitnessFunction, decoder, + min, max); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java new file mode 100644 index 0000000000..18bbb7fab4 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * DoubleEncodedChromosome is used for representing chromosome encoded as + * Double. It is a vector of a fixed length of real numbers.The acceptable real + * values should belong to the range min(inclusive) to max(exclusive). + *

+ * @param

phenotype of chromosome + * @since 4.0 + */ +public class RealValuedChromosome

extends AbstractListChromosome { + + /** minimum acceptable value of allele. **/ + private final double min; + + /** maximum acceptable value of allele. **/ + private final double max; + + /** + * constructor. + * @param representation an array of real values + * @param fitnessFunction the fitness function + * @param decoder the {@link AbstractListChromosomeDecoder} + */ + public RealValuedChromosome(final List representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + super(representation, fitnessFunction, decoder); + this.min = 0; + this.max = 1d; + checkValidity(); + } + + /** + * constructor. + * @param representation an array of real values + * @param fitnessFunction the fitness function + * @param decoder the {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public RealValuedChromosome(final List representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, double min, double max) { + super(representation, fitnessFunction, decoder); + this.min = min; + this.max = max; + checkValidity(); + } + + /** + * constructor. + * @param representation Internal representation of chromosome as genotype + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + */ + public RealValuedChromosome(final Double[] representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + this(Arrays.asList(representation), fitnessFunction, decoder); + } + + /** + * constructor. + * @param representation Internal representation of chromosome as genotype + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public RealValuedChromosome(final Double[] representation, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, double min, double max) { + this(Arrays.asList(representation), fitnessFunction, decoder, min, max); + } + + /** + * Return the minimum allele value. + * @return minimum + */ + public double getMin() { + return min; + } + + /** + * Returns the maximum allele value. + * @return maximum + */ + public double getMax() { + return max; + } + + /** + * Asserts that representation can represent a valid chromosome. + */ + private void checkValidity() { + ValidationUtils.checkForMinMax(min, max); + for (double i : getRepresentation()) { + if (i < min || i >= max) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public RealValuedChromosome

newChromosome(List chromosomeRepresentation) { + return new RealValuedChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder(), this.min, + this.max); + } + + /** + * Creates an instance of RealValued chromosome with randomly generated + * representation. + * @param

phenotype of chromosome + * @param length length of chromosome genotype + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value generated as allele + * @param max maximum exclusive value generated as allele + * @return A real-valued chromosome + */ + public static

RealValuedChromosome

randomChromosome(int length, FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, double min, double max) { + return new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(length, min, max), + fitnessFunction, decoder, min, max); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java new file mode 100644 index 0000000000..196ddd9e25 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.chromosome; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTime.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTime.java new file mode 100644 index 0000000000..c6f53a91cb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTime.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.population.Population; + +/** + * Stops after a fixed amount of time has elapsed. + *

+ * The first time {@link #isSatisfied(Population)} is invoked, the end time of + * the evolution is determined based on the provided maxTime value. + * Once the elapsed time reaches the configured maxTime value, + * {@link #isSatisfied(Population)} returns true. + * + * @param

phenotype of chromosome + * @since 3.1 + */ +public class FixedElapsedTime

implements StoppingCondition

{ + + /** Maximum allowed time period (in nanoseconds). */ + private final long maxTimePeriod; + + /** The predetermined termination time (stopping condition). */ + private long endTime = -1; + + /** + * Create a new {@link FixedElapsedTime} instance. + * + * @param maxTime maximum number of seconds generations are allowed to evolve + */ + public FixedElapsedTime(final long maxTime) { + this(maxTime, TimeUnit.SECONDS); + } + + /** + * Create a new {@link FixedElapsedTime} instance. + * + * @param maxTime maximum time generations are allowed to evolve + * @param unit {@link TimeUnit} of the maxTime argument + */ + public FixedElapsedTime(final long maxTime, final TimeUnit unit) { + if (maxTime < 0) { + throw new GeneticException(GeneticException.TOO_SMALL, maxTime, 0); + } + maxTimePeriod = unit.toNanos(maxTime); + } + + /** + * Determine whether or not the maximum allowed time has passed. The termination + * time is determined after the first generation. + * + * @return true IFF the maximum allowed time period has elapsed + */ + @Override + public boolean isSatisfied(Population

population) { + if (endTime < 0) { + endTime = System.nanoTime() + maxTimePeriod; + } + + return System.nanoTime() >= endTime; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCount.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCount.java new file mode 100644 index 0000000000..c1f1503ac9 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCount.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.population.Population; + +/** + * Stops after a fixed number of generations. + *

+ * Each time {@link #isSatisfied(Population)} is invoked, a generation counter + * is incremented. Once the counter reaches the configured + * {@code maxGenerations} value, {@link #isSatisfied(Population)} returns true. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public class FixedGenerationCount

implements StoppingCondition

{ + /** Number of generations that have passed. */ + private int numGenerations; + + /** Maximum number of generations (stopping criteria). */ + private final int maxGenerations; + + /** + * Create a new FixedGenerationCount instance. + * + * @param maxGenerations number of generations to evolve + */ + public FixedGenerationCount(final int maxGenerations) { + if (maxGenerations <= 0) { + throw new GeneticException(GeneticException.TOO_SMALL, maxGenerations, 1); + } + this.maxGenerations = maxGenerations; + } + + /** + * Determine whether or not the given number of generations have passed. + * Increments the number of generations counter if the maximum has not been + * reached. + * + * @return true IFF the maximum number of generations has been + * exceeded + */ + @Override + public boolean isSatisfied(Population

population) { + if (this.numGenerations < this.maxGenerations) { + numGenerations++; + return false; + } + return true; + } + + /** + * Returns the number of generations that have already passed. + * @return the number of generations that have passed + */ + public int getNumGenerations() { + return numGenerations; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/StoppingCondition.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/StoppingCondition.java new file mode 100644 index 0000000000..1e85c9b329 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/StoppingCondition.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import org.apache.commons.math4.ga.population.Population; + +/** + * Algorithm used to determine when to stop evolution. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface StoppingCondition

{ + + /** + * Determine whether or not the given population satisfies the stopping + * condition. + * @param population population of chromosome + * + * @return true if this stopping condition is met by the given + * population, false otherwise. + */ + boolean isSatisfied(Population

population); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitness.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitness.java new file mode 100644 index 0000000000..f41ad54ac4 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitness.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.convergencecond; + +import org.apache.commons.math4.ga.population.Population; + +/** + * This class represents a stopping condition based on best fitness value. + * Convergence will be stopped once best fitness remains unchanged for + * predefined number of generations. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class UnchangedBestFitness

implements StoppingCondition

{ + + /** best fitness of previous generation. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** + * The configured number of generations for which optimization process will + * continue with unchanged best fitness value. + **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** Number of generations the best fitness value has not been changed. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations with unchanged + * best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population

population) { + final double currentBestFitness = population.getFittestChromosome().evaluate(); + + if (lastBestFitness == currentBestFitness) { + generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitness.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitness.java new file mode 100644 index 0000000000..21255b2333 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitness.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.convergencecond; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.Population; + +/** + * This class represents a stopping condition based on mean fitness value. + * Convergence will be stopped once mean fitness remains unchanged for + * predefined number of generations. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class UnchangedMeanFitness

implements StoppingCondition

{ + + /** Mean fitness of previous generation. **/ + private double lastMeanFitness = Double.MIN_VALUE; + + /** + * The configured number of generations for which optimization process will + * continue with unchanged best fitness value. + **/ + private final int maxGenerationsWithUnchangedMeanFitness; + + /** Number of generations the mean fitness value has not been changed. **/ + private int generationsHavingUnchangedMeanFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedMeanFitness maximum number of generations + * with unchanged mean fitness + */ + public UnchangedMeanFitness(final int maxGenerationsWithUnchangedMeanFitness) { + this.maxGenerationsWithUnchangedMeanFitness = maxGenerationsWithUnchangedMeanFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population

population) { + + final double currentMeanFitness = calculateMeanFitness(population); + + if (lastMeanFitness == currentMeanFitness) { + generationsHavingUnchangedMeanFitness++; + if (generationsHavingUnchangedMeanFitness == maxGenerationsWithUnchangedMeanFitness) { + return true; + } + } else { + this.generationsHavingUnchangedMeanFitness = 0; + lastMeanFitness = currentMeanFitness; + } + + return false; + } + + /** + * calculates mean fitness of the population. + * @param population + * @return mean fitness + */ + private double calculateMeanFitness(Population

population) { + double totalFitness = 0.0; + for (Chromosome

chromosome : population) { + totalFitness += chromosome.evaluate(); + } + return totalFitness / population.getPopulationSize(); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/package-info.java new file mode 100644 index 0000000000..df73f6996a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergencecond/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.convergencecond; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java new file mode 100644 index 0000000000..e254a621b4 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.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.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomGenerator; + +/** + * An abstraction to represent the base crossover policy. + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractChromosomeCrossoverPolicy

implements CrossoverPolicy

{ + + /** + * {@inheritDoc} + */ + @Override + public ChromosomePair

crossover(final Chromosome

first, final Chromosome

second, + final double crossoverRate) { + if (RandomGenerator.getRandomGenerator().nextDouble() < crossoverRate) { + return crossover(first, second); + } else { + return new ChromosomePair<>(first, second); + } + } + + /** + * Performs crossover of two chromosomes. + * @param first The first parent chromosome participating in crossover + * @param second The second parent chromosome participating in crossover + * @return chromosome pair + */ + protected abstract ChromosomePair

crossover(Chromosome

first, Chromosome

second); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java new file mode 100644 index 0000000000..6136282e3e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.exception.GeneticException; + +/** + * An abstraction of crossover policy for list chromosomes. + * @param genetype of chromosome + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractListChromosomeCrossoverPolicy extends AbstractChromosomeCrossoverPolicy

{ + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + @Override + public ChromosomePair

crossover(final Chromosome

first, final Chromosome

second) { + // check for validity. + checkValidity(first, second); + + final AbstractListChromosome firstListChromosome = (AbstractListChromosome) first; + final AbstractListChromosome secondListChromosome = (AbstractListChromosome) second; + + return mate(firstListChromosome, secondListChromosome); + } + + /** + * Validates the chromosome pair. + * @param first first chromosome + * @param second second chromosome + */ + @SuppressWarnings("unchecked") + protected void checkValidity(final Chromosome

first, final Chromosome

second) { + if (!(first instanceof AbstractListChromosome && second instanceof AbstractListChromosome)) { + throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME); + } + final AbstractListChromosome firstListChromosome = (AbstractListChromosome) first; + final AbstractListChromosome secondListChromosome = (AbstractListChromosome) second; + + final int length = firstListChromosome.getLength(); + if (length != secondListChromosome.getLength()) { + throw new GeneticException(GeneticException.SIZE_MISMATCH, secondListChromosome.getLength(), length); + } + + } + + /** + * Performs mating between two chromosomes and returns the offspring pair. + * @param first The first parent chromosome participating in crossover + * @param second The second parent chromosome participating in crossover + * @return chromosome pair + */ + protected abstract ChromosomePair

mate(AbstractListChromosome first, AbstractListChromosome second); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java new file mode 100644 index 0000000000..65aa19111a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; + +/** + * Policy used to create a pair of new chromosomes by performing a crossover + * operation on a source pair of chromosomes. + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface CrossoverPolicy

{ + + /** + * Perform a crossover operation on the given chromosomes. + * + * @param first the first chromosome. + * @param second the second chromosome. + * @param crossoverRate the probability of crossover + * @return the pair of new chromosomes that resulted from the crossover. + */ + ChromosomePair

crossover(Chromosome

first, Chromosome

second, double crossoverRate); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java new file mode 100644 index 0000000000..c0a1678e8b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomGenerator; + +/** + * Cycle Crossover [CX] builds offspring from ordered chromosomes by + * identifying cycles between two parent chromosomes. To form the children, the + * cycles are copied from the respective parents. + *

+ * To form a cycle the following procedure is applied: + *

    + *
  1. start with the first gene of parent 1
  2. + *
  3. look at the gene at the same position of parent 2
  4. + *
  5. go to the position with the same gene in parent 1
  6. + *
  7. add this gene index to the cycle
  8. + *
  9. repeat the steps 2-5 until we arrive at the starting gene of this + * cycle
  10. + *
+ * The indices that form a cycle are then used to form the children in + * alternating order, i.e. in cycle 1, the genes of parent 1 are copied to child + * 1, while in cycle 2 the genes of parent 1 are copied to child 2, and so forth + * ... + * + * Example (zero-start cycle): + *
+ * p1 = (8 4 7 3 6 2 5 1 9 0)    X   c1 = (8 1 2 3 4 5 6 7 9 0)
+ * p2 = (0 1 2 3 4 5 6 7 8 9)    X   c2 = (0 4 7 3 6 2 5 1 8 9)
+ *
+ * cycle 1: 8 0 9
+ * cycle 2: 4 1 7 2 5 6
+ * cycle 3: 3
+ * 
+ * + * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @see + * Cycle Crossover Operator + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class CycleCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** If the start index shall be chosen randomly. */ + private final boolean randomStart; + + /** + * Creates a new {@link CycleCrossover} policy. + */ + public CycleCrossover() { + this(false); + } + + /** + * Creates a new {@link CycleCrossover} policy using the given + * {@code randomStart} behavior. + * + * @param randomStart whether the start index shall be chosen randomly or be set + * to 0 + */ + public CycleCrossover(final boolean randomStart) { + this.randomStart = randomStart; + } + + /** + * Returns whether the starting index is chosen randomly or set to zero. + * + * @return {@code true} if the starting index is chosen randomly, {@code false} + * otherwise + */ + public boolean isRandomStart() { + return randomStart; + } + + /** + * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the + * actual crossover. + * + * @param first the first chromosome + * @param second the second chromosome + * @return the pair of new chromosomes that resulted from the crossover + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children: do a crossover copy to simplify the later processing + final List child1Rep = new ArrayList<>(second.getRepresentation()); + final List child2Rep = new ArrayList<>(first.getRepresentation()); + + // the set of all visited indices so far + final Set visitedIndices = new HashSet<>(length); + // the indices of the current cycle + final List indices = new ArrayList<>(length); + + // determine the starting index + int idx = randomStart ? RandomGenerator.getRandomGenerator().nextInt(length) : 0; + int cycle = 1; + + while (visitedIndices.size() < length) { + indices.add(idx); + + T item = parent2Rep.get(idx); + idx = parent1Rep.indexOf(item); + + while (idx != indices.get(0)) { + // add that index to the cycle indices + indices.add(idx); + // get the item in the second parent at that index + item = parent2Rep.get(idx); + // get the index of that item in the first parent + idx = parent1Rep.indexOf(item); + } + + // for even cycles: swap the child elements on the indices found in this cycle + if (cycle++ % 2 != 0) { + for (int i : indices) { + final T tmp = child1Rep.get(i); + child1Rep.set(i, child2Rep.get(i)); + child2Rep.set(i, tmp); + } + } + + visitedIndices.addAll(indices); + // find next starting index: last one + 1 until we find an unvisited index + idx = (indices.get(0) + 1) % length; + while (visitedIndices.contains(idx) && visitedIndices.size() < length) { + idx++; + if (idx >= length) { + idx = 0; + } + } + indices.clear(); + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java new file mode 100644 index 0000000000..d5a786f21f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * N-point crossover policy. For each iteration a random crossover point is + * selected and the first part from each parent is copied to the corresponding + * child, and the second parts are copied crosswise. + * + * Example (2-point crossover): + *

+ * -C- denotes a crossover point
+ *           -C-       -C-                         -C-        -C-
+ * p1 = (1 0  | 1 0 0 1 | 0 1 1)    X    p2 = (0 1  | 1 0 1 0  | 1 1 1)
+ *      \----/ \-------/ \-----/              \----/ \--------/ \-----/
+ *        ||      (*)       ||                  ||      (**)       ||
+ *        VV      (**)      VV                  VV      (*)        VV
+ *      /----\ /--------\ /-----\             /----\ /--------\ /-----\
+ * c1 = (1 0  | 1 0 1 0  | 0 1 1)    X   c2 = (0 1  | 1 0 0 1  | 0 1 1)
+ * 
+ * + * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class NPointCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** The number of crossover points. */ + private final int crossoverPoints; + + /** + * Creates a new {@link NPointCrossover} policy using the given number of + * points. + *

+ * Note: the number of crossover points must be < + * chromosome length - 1. This condition can only be checked at + * runtime, as the chromosome length is not known in advance. + * + * @param crossoverPoints the number of crossover points + */ + public NPointCrossover(final int crossoverPoints) { + if (crossoverPoints <= 0) { + throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, crossoverPoints); + } + this.crossoverPoints = crossoverPoints; + } + + /** + * Returns the number of crossover points used by this {@link CrossoverPolicy}. + * + * @return the number of crossover points + */ + public int getCrossoverPoints() { + return crossoverPoints; + } + + /** + * Performs a N-point crossover. N random crossover points are selected and are + * used to divide the parent chromosomes into segments. The segments are copied + * in alternate order from the two parents to the corresponding child + * chromosomes. + * + * Example (2-point crossover): + *

+     * -C- denotes a crossover point
+     *           -C-       -C-                         -C-        -C-
+     * p1 = (1 0  | 1 0 0 1 | 0 1 1)    X    p2 = (0 1  | 1 0 1 0  | 1 1 1)
+     *      \----/ \-------/ \-----/              \----/ \--------/ \-----/
+     *        ||      (*)       ||                  ||      (**)       ||
+     *        VV      (**)      VV                  VV      (*)        VV
+     *      /----\ /--------\ /-----\             /----\ /--------\ /-----\
+     * c1 = (1 0  | 1 0 1 0  | 0 1 1)    X   c2 = (0 1  | 1 0 0 1  | 0 1 1)
+     * 
+ * + * @param first first parent (p1) + * @param second second parent (p2) + * @return pair of two children (c1,c2) + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + + final int length = first.getLength(); + if (crossoverPoints >= length) { + throw new GeneticException(GeneticException.TOO_LARGE, crossoverPoints, length); + } + + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1Rep = new ArrayList<>(length); + final List child2Rep = new ArrayList<>(length); + + final UniformRandomProvider random = RandomGenerator.getRandomGenerator(); + + List c1 = child1Rep; + List c2 = child2Rep; + + int remainingPoints = crossoverPoints; + int lastIndex = 0; + for (int i = 0; i < crossoverPoints; i++, remainingPoints--) { + // select the next crossover point at random + final int crossoverIndex = 1 + lastIndex + random.nextInt(length - lastIndex - remainingPoints); + + // copy the current segment + for (int j = lastIndex; j < crossoverIndex; j++) { + c1.add(parent1Rep.get(j)); + c2.add(parent2Rep.get(j)); + } + + // swap the children for the next segment + final List tmp = c1; + c1 = c2; + c2 = tmp; + + lastIndex = crossoverIndex; + } + + // copy the last segment + for (int j = lastIndex; j < length; j++) { + c1.add(parent1Rep.get(j)); + c2.add(parent2Rep.get(j)); + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java new file mode 100644 index 0000000000..5beefa01fe --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomGenerator; + +/** + * One point crossover policy. A random crossover point is selected and the + * first part from each parent is copied to the corresponding child, and the + * second parts are copied crosswise. + * + * Example: + *

+ * -C- denotes a crossover point
+ *                   -C-                                 -C-
+ * p1 = (1 0 1 0 0 1  | 0 1 1)    X    p2 = (0 1 1 0 1 0  | 1 1 1)
+ *      \------------/ \-----/              \------------/ \-----/
+ *            ||         (*)                       ||        (**)
+ *            VV         (**)                      VV        (*)
+ *      /------------\ /-----\              /------------\ /-----\
+ * c1 = (1 0 1 0 0 1  | 1 1 1)    X    c2 = (0 1 1 0 1 0  | 0 1 1)
+ * 
+ * + * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 2.0 + * + */ +public class OnePointCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** + * Performs one point crossover. A random crossover point is selected and the + * first part from each parent is copied to the corresponding child, and the + * second parts are copied crosswise. + * + * Example: + *

+     * -C- denotes a crossover point
+     *                   -C-                                 -C-
+     * p1 = (1 0 1 0 0 1  | 0 1 1)    X    p2 = (0 1 1 0 1 0  | 1 1 1)
+     *      \------------/ \-----/              \------------/ \-----/
+     *            ||         (*)                       ||        (**)
+     *            VV         (**)                      VV        (*)
+     *      /------------\ /-----\              /------------\ /-----\
+     * c1 = (1 0 1 0 0 1  | 1 1 1)    X    c2 = (0 1 1 0 1 0  | 0 1 1)
+     * 
+ * + * @param first first parent (p1) + * @param second second parent (p2) + * @return pair of two children (c1,c2) + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1Rep = new ArrayList<>(length); + final List child2Rep = new ArrayList<>(length); + + // select a crossover point at random (0 and length makes no sense) + final int crossoverIndex = 1 + (RandomGenerator.getRandomGenerator().nextInt(length - 2)); + + // copy the first part + for (int i = 0; i < crossoverIndex; i++) { + child1Rep.add(parent1Rep.get(i)); + child2Rep.add(parent2Rep.get(i)); + } + // and switch the second part + for (int i = crossoverIndex; i < length; i++) { + child1Rep.add(parent2Rep.get(i)); + child2Rep.add(parent1Rep.get(i)); + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java new file mode 100644 index 0000000000..3cbdd2ed4d --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * Order 1 Crossover [OX1] builds offspring from ordered chromosomes by + * copying a consecutive slice from one parent, and filling up the remaining + * genes from the other parent as they appear. + *

+ * This policy works by applying the following rules: + *

    + *
  1. select a random slice of consecutive genes from parent 1
  2. + *
  3. copy the slice to child 1 and mark out the genes in parent 2
  4. + *
  5. starting from the right side of the slice, copy genes from parent 2 as + * they appear to child 1 if they are not yet marked out.
  6. + *
+ *

+ * Example (random sublist from index 3 to 7, underlined): + *

+ * p1 = (8 4 7 3 6 2 5 1 9 0)   X   c1 = (0 4 7 3 6 2 5 1 8 9)
+ *             ---------                        ---------
+ * p2 = (0 1 2 3 4 5 6 7 8 9)   X   c2 = (8 1 2 3 4 5 6 7 9 0)
+ * 
+ *

+ * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @see + * Order 1 Crossover Operator + * + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class OrderedCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** + * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the + * actual crossover. + * + * @param first the first chromosome + * @param second the second chromosome + * @return the pair of new chromosomes that resulted from the crossover + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1 = new ArrayList<>(length); + final List child2 = new ArrayList<>(length); + // sets of already inserted items for quick access + final Set child1Set = new HashSet<>(length); + final Set child2Set = new HashSet<>(length); + + final UniformRandomProvider random = RandomGenerator.getRandomGenerator(); + // choose random points, making sure that lb < ub. + final int a = random.nextInt(length); + int b; + do { + b = random.nextInt(length); + } while (a == b); + // determine the lower and upper bounds + final int lb = Math.min(a, b); + final int ub = Math.max(a, b); + + // add the subLists that are between lb and ub + child1.addAll(parent1Rep.subList(lb, ub + 1)); + child1Set.addAll(child1); + child2.addAll(parent2Rep.subList(lb, ub + 1)); + child2Set.addAll(child2); + + // iterate over every item in the parents + for (int i = 1; i <= length; i++) { + final int idx = (ub + i) % length; + + // retrieve the current item in each parent + final T item1 = parent1Rep.get(idx); + final T item2 = parent2Rep.get(idx); + + // if the first child already contains the item in the second parent add it + if (!child1Set.contains(item2)) { + child1.add(item2); + child1Set.add(item2); + } + + // if the second child already contains the item in the first parent add it + if (!child2Set.contains(item1)) { + child2.add(item1); + child2Set.add(item1); + } + } + + // rotate so that the original slice is in the same place as in the parents. + Collections.rotate(child1, lb); + Collections.rotate(child2, lb); + + return new ChromosomePair<>(first.newChromosome(child1), second.newChromosome(child2)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java new file mode 100644 index 0000000000..3c77f5cd91 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.Constants; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * Perform Uniform Crossover [UX] on the specified chromosomes. A fixed mixing + * ratio is used to combine genes from the first and second parents, e.g. using + * a ratio of 0.5 would result in approximately 50% of genes coming from each + * parent. This is typically a poor method of crossover, but empirical evidence + * suggests that it is more exploratory and results in a larger part of the + * problem space being searched. + *

+ * This crossover policy evaluates each gene of the parent chromosomes by + * choosing a uniform random number {@code p} in the range [0, 1]. If {@code p} + * < {@code ratio}, the parent genes are swapped. This means with a ratio of + * 0.7, 30% of the genes from the first parent and 70% from the second parent + * will be selected for the first offspring (and vice versa for the second + * offspring). + *

+ * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @see Crossover + * techniques (Wikipedia) + * @see Crossover + * (Obitko.com) + * @see Uniform + * crossover + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class UniformCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** The mixing ratio. */ + private final double ratio; + + /** + * Creates a new {@link UniformCrossover} policy using the given mixing ratio. + * + * @param ratio the mixing ratio + */ + public UniformCrossover(final double ratio) { + if (ratio < 0.0d || ratio > 1.0d) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, ratio, Constants.CROSSOVER_RATE, 0.0d, 1.0d); + } + this.ratio = ratio; + } + + /** + * Returns the mixing ratio used by this {@link CrossoverPolicy}. + * + * @return the mixing ratio + */ + public double getRatio() { + return ratio; + } + + /** + * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the + * actual crossover. + * + * @param first the first chromosome + * @param second the second chromosome + * @return the pair of new chromosomes that resulted from the crossover + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1Rep = new ArrayList<>(length); + final List child2Rep = new ArrayList<>(length); + + final UniformRandomProvider random = RandomGenerator.getRandomGenerator(); + + for (int index = 0; index < length; index++) { + + if (random.nextDouble() < ratio) { + // swap the bits -> take other parent + child1Rep.add(parent2Rep.get(index)); + child2Rep.add(parent1Rep.get(index)); + } else { + child1Rep.add(parent1Rep.get(index)); + child2Rep.add(parent2Rep.get(index)); + } + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java new file mode 100644 index 0000000000..68c92826b1 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.crossover; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java new file mode 100644 index 0000000000..37c7cf927e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.exception.GeneticException; + +/** + * An abstract Decoder of ListChromosome. + * @param genotype fo chromosome + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractListChromosomeDecoder implements Decoder

{ + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + @Override + public P decode(Chromosome

chromosome) { + checkValidity(chromosome); + + return decode((AbstractListChromosome) chromosome); + } + + /** + * Checks validity of {@link Chromosome}. + * @param chromosome the {@link Chromosome} + */ + protected void checkValidity(Chromosome

chromosome) { + if (!AbstractListChromosome.class.isAssignableFrom(chromosome.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, chromosome.getClass().getSimpleName()); + } + } + + /** + * Decodes the chromosome genotype and returns the phenotype. + * @param chromosome The list chromosome to decode + * @return decoded phenotype of chromosome + */ + protected abstract P decode(AbstractListChromosome chromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java new file mode 100644 index 0000000000..4583cc90c4 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * Decoder is responsible for converting chromosome genotype to phenotype. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface Decoder

{ + + /** + * Converts genotype to phenotype. + * @param chromosome The {@link Chromosome} + * @return phenotype The phenotype of chromosome + */ + P decode(Chromosome

chromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java new file mode 100644 index 0000000000..8a096828e2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * A concrete implementation of RandomKey decoder. This class is responsible for + * decoding permutation chromosome encoded with random key. + * @param type of the permutation element + * @since 4.0 + */ +public final class RandomKeyDecoder extends AbstractListChromosomeDecoder> { + + /** base sequence for decoding chromosome. **/ + private final List baseSequence; + + /** + * constructor. + * @param baseSequence the unpermuted sequence + */ + public RandomKeyDecoder(List baseSequence) { + ValidationUtils.checkForNull("baseSequence", baseSequence); + this.baseSequence = Collections.unmodifiableList(baseSequence); + } + + /** + * {@inheritDoc} + */ + @Override + protected List decode(AbstractListChromosome> chromosome) { + final List representation = chromosome.getRepresentation(); + final List sortedRepresentation = new ArrayList<>(representation); + Collections.sort(sortedRepresentation); + + final int sequenceLength = baseSequence.size(); + + // the size of the three lists must be equal + if (representation.size() != sequenceLength) { + throw new GeneticException(GeneticException.SIZE_MISMATCH, representation.size(), sequenceLength); + } + + // do not modify the original representation + final List representationCopy = new ArrayList<>(representation); + + // now find the indices in the original repr and use them for permuting + final List res = new ArrayList<>(sequenceLength); + for (int i = 0; i < sequenceLength; i++) { + final int index = representationCopy.indexOf(sortedRepresentation.get(i)); + res.add(baseSequence.get(index)); + representationCopy.set(index, null); + } + + return res; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java new file mode 100644 index 0000000000..b8d0be72a3 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; + +/** + * A concrete implementation of transparent decoder for List Chromosome. Treats + * the gentype as phenotype. + * @param the genotype of chromosome + * @since 4.0 + */ +public final class TransparentListChromosomeDecoder extends AbstractListChromosomeDecoder> { + + /** + * {@inheritDoc} + */ + @Override + protected List decode(AbstractListChromosome> chromosome) { + return chromosome.getRepresentation(); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java new file mode 100644 index 0000000000..57e19a8131 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.decoder; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/exception/GeneticException.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/exception/GeneticException.java new file mode 100644 index 0000000000..876d3881e5 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/exception/GeneticException.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.exception; + +import java.text.MessageFormat; + +/** + * This class represents the Exception encountered during GA optimization. + * @since 4.0 + */ +public class GeneticException extends RuntimeException { + + /** Error message for "out of range" condition. */ + public static final String OUT_OF_RANGE = "Value {0} of {1} is out of range [{2}, {3}]"; + + /** Error message for "not strictly positive" condition. */ + public static final String NOT_STRICTLY_POSITIVE = "Number {0} is not strictly positive"; + + /** Error message for "too large" condition. */ + public static final String TOO_LARGE = "Number {0} is larger than {1}"; + + /** Error message for "too small" condition. */ + public static final String TOO_SMALL = "Number {0} is smaller than {1}"; + + /** Error message for "out of range" condition. */ + public static final String NO_DATA = "No data"; + + /** Error message for "size mismatch" condition. */ + public static final String SIZE_MISMATCH = "Size mismatch: {0} != {1}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_ARGUMENT = "Illegal Argument Exception: {0}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_RANGE = "Illegal Range of Value Exception: " + + "[Expected min-{0}, max-{1}], [Passed min-{2}, max-{3}]"; + + /** Error message for "generic illegal argument" condition. */ + public static final String INVALID_FIXED_LENGTH_CHROMOSOME = "Invalid Fixed Length Chromosome."; + + /** Error message for "NULL ARGUMENT" condition. */ + public static final String NULL_ARGUMENT = "Null Argument Exception: {0}"; + + /** + * Error message for "List of Chromosome bigger than population size" condition. + */ + public static final String LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE = "List of chromosome bigger than " + + "population size: {0} > {1}"; + + /** + * Error message for "population limit not positive" condition. + */ + public static final String POPULATION_LIMIT_NOT_POSITIVE = "Population limit not positive :{0}"; + + /** + * Error message for " population limit less than list of chromosomes size" + * condition. + */ + public static final String POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE = "Population limit is " + + " lesser than list of chromosomes size : {0} < {1}"; + + /** + * Error message for different origin and permuted data. + */ + public static final String DIFFERENT_ORIG_AND_PERMUTED_DATA = "Different original and permuted data"; + + /** Serializable version identifier. */ + private static final long serialVersionUID = 20210516L; + + /** + * Create an exception where the message is constructed by applying the + * {@code format()} method from {@code java.text.MessageFormat}. + * + * @param message Message format (with replaceable parameters). + * @param formatArguments Actual arguments to be displayed in the message. + */ + public GeneticException(String message, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments)); + } + + /** + * Create an exception. + * @param t instance of {@link Throwable} + */ + public GeneticException(Throwable t) { + super(t); + } + + /** + * Create an exception having both stacktrace and message. + * @param message the exception message + * @param t the instance of {@link Throwable} + * @param formatArguments arguments to format the exception message + */ + public GeneticException(String message, Throwable t, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments), t); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/exception/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/exception/package-info.java new file mode 100644 index 0000000000..e815d63093 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/exception/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.exception; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java new file mode 100644 index 0000000000..ade6dea0d2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.fitness; + +/** + * This interface represents fitness function. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface FitnessFunction

{ + + /** + * computes the fitness value of the input chromosome's phenotype. + * @param decodedChromosome chromosome decoded as phenotype + * @return fitness value + */ + double compute(P decodedChromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java new file mode 100644 index 0000000000..5b6d4c5203 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.fitness; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java new file mode 100644 index 0000000000..f99d585687 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import org.apache.commons.math4.ga.population.Population; + +/** + * This interface represents a convergence listener. Any implementation of the + * same will be notified about the population statics. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface ConvergenceListener

{ + + /** + * Notifies about the population statistics. + * @param generation current generation + * @param population population of chromosome + */ + void notify(int generation, Population

population); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java new file mode 100644 index 0000000000..033060e57b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * This class is the default implementation of ConvergenceListenerRegistry. It + * will be responsible for registering the interested listeners and notifying + * all when required. + * @param

phenotype of chromosome + * @since 4.0 + */ +public final class ConvergenceListenerRegistry

{ + + /** + * The instance of the singleton class. + */ + @SuppressWarnings("rawtypes") + private static final ConvergenceListenerRegistry INSTANCE = new ConvergenceListenerRegistry<>(); + + /** + * List of registered listeners. + */ + private final List> listeners = new ArrayList<>(); + + /** + * constructor. + */ + private ConvergenceListenerRegistry() { + } + + /** + * Registers the interested ConvergenceListener passed as an argument. + * @param convergenceListener The {@link ConvergenceListener} + */ + public void addConvergenceListener(ConvergenceListener

convergenceListener) { + this.listeners.add(convergenceListener); + } + + /** + * Notifies all registered ConvergenceListeners about the population statistics. + * @param generation current generation + * @param population population of chromosomes + */ + public void notifyAll(int generation, Population

population) { + for (ConvergenceListener

convergenceListener : listeners) { + convergenceListener.notify(generation, population); + } + } + + /** + * Add instance of convergence listener. + * @param convergenceListeners list of {@link ConvergenceListener} + */ + public void addConvergenceListeners(List> convergenceListeners) { + ValidationUtils.checkForNull("Null convergenceListeners", convergenceListeners); + for (ConvergenceListener

convergenceListener : convergenceListeners) { + addConvergenceListener(convergenceListener); + } + } + + /** + * Returns instance of this class. + * @param

The phenotype of chromosome + * @return instance + */ + @SuppressWarnings("unchecked") + public static

ConvergenceListenerRegistry

getInstance() { + return INSTANCE; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java new file mode 100644 index 0000000000..ec76df3817 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.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.ga.listener; + +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.apache.commons.math4.ga.stats.internal.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.utils.ConsoleLogger; + +/** + * Logs population statistics during the convergence process. + * @param

phenotype of chromosome + * @since 4.0 + */ +public final class PopulationStatisticsLogger

implements ConvergenceListener

{ + + /** instance of consolelogger. **/ + private ConsoleLogger consoleLogger; + + public PopulationStatisticsLogger(String encoding) { + this.consoleLogger = ConsoleLogger.getInstance(encoding); + } + + /** + * Logs the population statistics to console during the process of convergence. + */ + @Override + public void notify(int generation, Population

population) { + final PopulationStatisticalSummary

populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>( + population); + consoleLogger.log( + "Population statistics for generation %d ::: Mean Fitness: %f, Max Fitness: %f, Fitness Variance: %f", + generation, populationStatisticalSummary.getMeanFitness(), populationStatisticalSummary.getMaxFitness(), + populationStatisticalSummary.getFitnessVariance()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java new file mode 100644 index 0000000000..f2e0d4a83b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.listener; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java new file mode 100644 index 0000000000..a38c348ec5 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.utils.RandomGenerator; + +/** + * This abstraction represents an abstract mutation policy for ListChromosomes. + * @param genotype of chromosome + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractListChromosomeMutationPolicy implements MutationPolicy

{ + + /** + * Mutate the given chromosome. Randomly changes few genes depending on mutation + * rate. + * @param original the original chromosome. + * @param mutationRate the rate of mutation per gene + * @return the mutated chromosome. + */ + @Override + public Chromosome

mutate(Chromosome

original, double mutationRate) { + // check for validity. + checkValidity(original); + + @SuppressWarnings("unchecked") + final AbstractListChromosome chromosome = (AbstractListChromosome) original; + final List newRep = new ArrayList<>(chromosome.getRepresentation()); + + final Set mutableGeneIndexes = getMutableGeneIndexes(chromosome.getLength(), mutationRate); + for (int mutableGeneIndex : mutableGeneIndexes) { + newRep.set(mutableGeneIndex, mutateGene(newRep.get(mutableGeneIndex))); + } + + return chromosome.newChromosome(newRep); + } + + /** + * Checks input chromosome validity. + * @param original chromosome to be mutated + */ + protected abstract void checkValidity(Chromosome

original); + + /** + * Selects and returns mutable gene indexes based on mutation rate. + * @param length no of alleles/genes in chromosome + * @param mutationRate mutation rate of the allele/gene + * @return mutable gene indexes + */ + protected Set getMutableGeneIndexes(int length, double mutationRate) { + + // calculate the total mutation rate of all the alleles i.e. chromosome. + final double chromosomeMutationRate = mutationRate * length; + final Set indexSet = new HashSet<>(); + + // if chromosomeMutationRate >= 1 then more than one allele will be mutated. + if (chromosomeMutationRate >= 1) { + final int noOfMutation = (int) Math.round(chromosomeMutationRate); + while (indexSet.size() < noOfMutation) { + indexSet.add(RandomGenerator.getRandomGenerator().nextInt(length)); + } + } else if (RandomGenerator.getRandomGenerator().nextDouble() < chromosomeMutationRate) { + indexSet.add(RandomGenerator.getRandomGenerator().nextInt(length)); + } + + return indexSet; + } + + /** + * Mutates an individual gene/allele. + * @param originalValue the original value of gene + * @return mutated value of gene + */ + protected abstract T mutateGene(T originalValue); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java new file mode 100644 index 0000000000..557770f40e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.exception.GeneticException; + +/** + * Mutation for {@link BinaryChromosome}s. Randomly changes few genes. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class BinaryMutation

extends IntegralValuedMutation

{ + + public BinaryMutation() { + super(0, 2); + } + + /** + * {@inheritDoc} + */ + @Override + protected void checkValidity(Chromosome

original) { + super.checkValidity(original); + if (!BinaryChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Integer mutateGene(Integer originalValue) { + return originalValue == 0 ? 1 : 0; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java new file mode 100644 index 0000000000..5ae9d794e4 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * Mutation for {@link IntegralValuedChromosome}. Randomly changes few genes. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class IntegralValuedMutation

extends AbstractListChromosomeMutationPolicy { + + /** minimum acceptable value of allele. **/ + private final int min; + + /** maximum acceptable value of allele. **/ + private final int max; + + /** + * constructor. + * @param min minimum value of allele + * @param max maximum value of allele + */ + public IntegralValuedMutation(final int min, final int max) { + this.min = min; + this.max = max; + ValidationUtils.checkForMinMax(min, max); + } + + /** + * Returns the minimum acceptable value. + * @return minimum + */ + public int getMin() { + return min; + } + + /** + * Returns the maximum acceptable value. + * @return maximum + */ + public int getMax() { + return max; + } + + /** + * {@inheritDoc} + */ + @Override + protected void checkValidity(Chromosome

original) { + if (!IntegralValuedChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + IntegralValuedChromosome

chromosome = (IntegralValuedChromosome

) original; + if (chromosome.getMin() != this.min || chromosome.getMax() != this.max) { + throw new GeneticException(GeneticException.ILLEGAL_RANGE, this.min, this.max, chromosome.getMin(), + chromosome.getMax()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Integer mutateGene(Integer originalValue) { + return min + RandomGenerator.getRandomGenerator().nextInt(max - min); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java new file mode 100644 index 0000000000..66bdbc3516 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * Algorithm used to mutate a chromosome. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface MutationPolicy

{ + + /** + * Mutate the given chromosome. + * @param original the original chromosome. + * @param mutationRate The probability of mutation + * @return the mutated chromosome. + */ + Chromosome

mutate(Chromosome

original, double mutationRate); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java new file mode 100644 index 0000000000..c999dde8d3 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * This class mutates real-valued chromosome. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class RealValuedMutation

extends AbstractListChromosomeMutationPolicy { + + /** minimum value of chromosome gene/allele. **/ + private final double min; + + /** maximum value of chromosome gene/allele. **/ + private final double max; + + /** + * Constructs the mutation operator with normalized range of double values. + */ + public RealValuedMutation() { + this.min = 0d; + this.max = 1d; + } + + /** + * Constructs the mutation operator with provided range of double values. + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public RealValuedMutation(double min, double max) { + this.min = min; + this.max = max; + ValidationUtils.checkForMinMax(min, max); + } + + /** + * Returns the minimum acceptable value. + * @return minimum + */ + public double getMin() { + return min; + } + + /** + * Returns the maximum acceptable value. + * @return maximum + */ + public double getMax() { + return max; + } + + /** + * {@inheritDoc} + */ + @Override + protected void checkValidity(Chromosome

original) { + if (!RealValuedChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + RealValuedChromosome

chromosome = (RealValuedChromosome

) original; + if (chromosome.getMin() != this.min || chromosome.getMax() != this.max) { + throw new GeneticException(GeneticException.ILLEGAL_RANGE, this.min, this.max, chromosome.getMin(), + chromosome.getMax()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Double mutateGene(Double originalValue) { + return min + RandomGenerator.getRandomGenerator().nextDouble() * (max - min); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java new file mode 100644 index 0000000000..30c90e46cb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.mutation; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java new file mode 100644 index 0000000000..40fa263981 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java new file mode 100644 index 0000000000..15518bb0db --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.Constants; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * Population of chromosomes represented by a {@link List}. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public class ListPopulation

implements Population

{ + + /** List of chromosomes. */ + private final List> chromosomes; + + /** maximal size of the population. */ + private int populationLimit; + + /** + * Creates a new ListPopulation instance and initializes its inner chromosome + * list. + * + * @param populationLimit maximal size of the population + */ + public ListPopulation(final int populationLimit) { + this(Collections.>emptyList(), populationLimit); + } + + /** + * Creates a new ListPopulation instance. + *

+ * Note: the chromosomes of the specified list are added to the population. + * + * @param chromosomes list of chromosomes to be added to the population + * @param populationLimit maximal size of the population + */ + public ListPopulation(final List> chromosomes, final int populationLimit) { + + ValidationUtils.checkForNull("chromosomes", chromosomes); + + if (populationLimit <= 0) { + throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, populationLimit); + } + if (chromosomes.size() > populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.populationLimit = populationLimit; + this.chromosomes = new ArrayList<>(populationLimit); + this.chromosomes.addAll(chromosomes); + } + + /** + * Add a {@link Collection} of chromosomes to this {@link Population}. + * @param chromosomeColl a {@link Collection} of chromosomes + * @since 3.1 + */ + public void addChromosomes(final Collection> chromosomeColl) { + if (chromosomes.size() + chromosomeColl.size() > populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.chromosomes.addAll(chromosomeColl); + } + + /** + * Returns an unmodifiable list of the chromosomes in this population. + * @return the unmodifiable list of chromosomes + */ + public List> getChromosomes() { + return Collections.unmodifiableList(chromosomes); + } + + /** + * Access the list of chromosomes. + * @return the list of chromosomes + * @since 3.1 + */ + protected List> getChromosomeList() { + return chromosomes; + } + + /** + * Add the given chromosome to the population. + * @param chromosome the chromosome to add. + */ + @Override + public void addChromosome(final Chromosome

chromosome) { + if (chromosomes.size() >= populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.chromosomes.add(chromosome); + } + + /** + * Access the fittest chromosome in this population. + * @return the fittest chromosome. + */ + @Override + public Chromosome

getFittestChromosome() { + // best so far + return Collections.max(this.chromosomes); + } + + /** + * Access the maximum population size. + * @return the maximum population size. + */ + @Override + public int getPopulationLimit() { + return this.populationLimit; + } + + /** + * Sets the maximal population size. + * @param populationLimit maximal population size. + */ + public void setPopulationLimit(final int populationLimit) { + if (populationLimit <= 0) { + throw new GeneticException(GeneticException.POPULATION_LIMIT_NOT_POSITIVE, populationLimit); + } + if (populationLimit < chromosomes.size()) { + throw new GeneticException(GeneticException.POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE, + populationLimit, chromosomes.size()); + } + this.populationLimit = populationLimit; + } + + /** + * Access the current population size. + * @return the current population size. + */ + @Override + public int getPopulationSize() { + return this.chromosomes.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder populationStrRepr = new StringBuilder(); + for (Chromosome

chromosome : chromosomes) { + populationStrRepr.append(chromosome.toString()); + populationStrRepr.append(Constants.NEW_LINE); + } + return populationStrRepr.toString(); + } + + /** + * Returns an iterator over the unmodifiable list of chromosomes. + *

+ * Any call to {@link Iterator#remove()} will result in a + * {@link UnsupportedOperationException}. + *

+ * + * @return chromosome iterator + */ + @Override + public Iterator> iterator() { + return getChromosomes().iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public Population

nextGeneration(final double elitismRate) { + final List> oldChromosomes = getChromosomeList(); + + if ((int) (oldChromosomes.size() * elitismRate) == 0) { + // if no of elite chromosome is 0 crete and return an empty population instance. + return new ListPopulation<>(getPopulationLimit()); + } else { + // create a new generation of chromosomes with same parameters and add the elit + // individuals. + final ListPopulation

nextGeneration = new ListPopulation<>(getPopulationLimit()); + + // Sort the chromosome according to ascending order of fitness. + Collections.sort(oldChromosomes); + + // index of the last "not good enough" chromosome + final int boundIndex = (int) Math.ceil((1.0 - elitismRate) * oldChromosomes.size()); + for (int i = boundIndex; i < oldChromosomes.size(); i++) { + nextGeneration.addChromosome(oldChromosomes.get(i)); + } + return nextGeneration; + } + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java new file mode 100644 index 0000000000..65cfe5fbcc --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * A collection of chromosomes that facilitates generational evolution. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface Population

extends Iterable> { + + /** + * Access the current population size. + * @return the current population size. + */ + int getPopulationSize(); + + /** + * Access the maximum population size. + * @return the maximum population size. + */ + int getPopulationLimit(); + + /** + * Start the population for the next generation. + * @param elitismRate the Elitism Rate + * @return the beginnings of the next generation. + */ + Population

nextGeneration(double elitismRate); + + /** + * Add the given chromosome to the population. + * @param chromosome the chromosome to add. + */ + void addChromosome(Chromosome

chromosome); + + /** + * Access the fittest chromosome in this population. + * @return the fittest chromosome. + */ + Chromosome

getFittestChromosome(); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java new file mode 100644 index 0000000000..4d77dc9d30 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.population; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java new file mode 100644 index 0000000000..7c8682ac78 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.selection; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.population.Population; + +/** + * Algorithm used to select a chromosome pair from a population. + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface SelectionPolicy

{ + + /** + * Select two chromosomes from the population. + * @param population the population from which the chromosomes are chosen. + * @return the selected chromosomes. + */ + ChromosomePair

select(Population

population); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java new file mode 100644 index 0000000000..098b400b5b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.selection; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.RandomGenerator; + +/** + * Tournament selection scheme. Each of the two selected chromosomes is selected + * based on n-ary tournament -- this is done by drawing {@link #arity} random + * chromosomes without replacement from the population, and then selecting the + * fittest chromosome among them. + * @param

phenotype of chromosome + * @since 2.0 + */ +public class TournamentSelection

implements SelectionPolicy

{ + + /** number of chromosomes included in the tournament selections. */ + private int arity; + + /** + * Creates a new TournamentSelection instance. + * + * @param arity how many chromosomes will be drawn to the tournament + */ + public TournamentSelection(final int arity) { + this.arity = arity; + } + + /** + * Select two chromosomes from the population. Each of the two selected + * chromosomes is selected based on n-ary tournament -- this is done by drawing + * {@link #arity} random chromosomes without replacement from the population, + * and then selecting the fittest chromosome among them. + * + * @param population the population from which the chromosomes are chosen. + * @return the selected chromosomes. + */ + @Override + public ChromosomePair

select(final Population

population) { + if (!(population instanceof ListPopulation)) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, population); + } + return new ChromosomePair<>(tournament((ListPopulation

) population), + tournament((ListPopulation

) population)); + } + + /** + * Helper for {@link #select(Population)}. Draw {@link #arity} random + * chromosomes without replacement from the population, and then select the + * fittest chromosome among them. + * + * @param population the population from which the chromosomes are chosen. + * @return the selected chromosome. + */ + private Chromosome

tournament(final ListPopulation

population) { + if (population.getPopulationSize() < this.arity) { + throw new GeneticException(GeneticException.TOO_LARGE, arity, population.getPopulationSize()); + } + + // create a copy of the chromosome list + final List> chromosomes = new ArrayList<>(population.getChromosomes()); + final List> selectedChromosomes = new ArrayList<>(); + + for (int i = 0; i < this.arity; i++) { + // select a random individual and add it to the tournament + final int rind = RandomGenerator.getRandomGenerator().nextInt(chromosomes.size()); + selectedChromosomes.add(chromosomes.get(rind)); + // do not select it again + chromosomes.remove(rind); + } + + // the winner takes it all + return Collections.max(selectedChromosomes); + } + + /** + * Gets the arity (number of chromosomes drawn to the tournament). + * + * @return arity of the tournament + */ + public int getArity() { + return arity; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java new file mode 100644 index 0000000000..b8ba3ae248 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.selection; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java new file mode 100644 index 0000000000..e24f3a0d07 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.stats; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * This interface represents the statistical summary for population fitness. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface PopulationStatisticalSummary

{ + + /** + * Returns the arithmetic mean of population fitness. + * @return The mean or Double.NaN if no values have been added. + */ + double getMeanFitness(); + + /** + * Returns the variance of the population fitness. + * @return The variance, Double.NaN if no values have been added or 0.0 for a + * single value set. + */ + double getFitnessVariance(); + + /** + * Returns the minimum fitness of the population. + * @return The max or Double.NaN if no values have been added. + */ + double getMinFitness(); + + /** + * Returns the maximum fitness of the population. + * @return The max or Double.NaN if no values have been added. + */ + double getMaxFitness(); + + /** + * Returns the population size. + * @return The number of available values + */ + long getPopulationSize(); + + /** + * Calculates the rank of chromosome in population based on its fitness. + * @param chromosome chromosome, for which rank would be found + * @return the rank of chromosome + */ + int findRank(Chromosome

chromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/internal/PopulationStatisticalSummaryImpl.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/internal/PopulationStatisticalSummaryImpl.java new file mode 100644 index 0000000000..3f59c189c2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/internal/PopulationStatisticalSummaryImpl.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.stats.internal; + +import java.util.Arrays; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.apache.commons.math4.ga.utils.ValidationUtils; + +/** + * This class represents an implementation of population statistical summary. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class PopulationStatisticalSummaryImpl

implements PopulationStatisticalSummary

{ + + /** array of fitness of the population. **/ + private final double[] fitnesses; + + /** maximum fitness of the population. **/ + private final double maxFitness; + + /** minimum fitness of the population. **/ + private final double minFitness; + + /** mean fitness of the population. **/ + private double meanFitness; + + /** variance of population fitness. **/ + private final double variance; + + /** + * constructor. + * @param population current population {@link Population} of chromosomes + */ + public PopulationStatisticalSummaryImpl(Population

population) { + ValidationUtils.checkForNull("population", population); + final double[] populationFitnesses = getFitnesses(population); + Arrays.sort(populationFitnesses); + this.fitnesses = populationFitnesses; + this.maxFitness = populationFitnesses[populationFitnesses.length - 1]; + this.minFitness = populationFitnesses[0]; + this.meanFitness = calculateMeanFitness(populationFitnesses); + this.variance = calculateVariance(populationFitnesses); + } + + /** + * Fetches array of chromosome fitness of the population. + * @param population + * @return fitness array + */ + private double[] getFitnesses(Population

population) { + double[] populationFitnesses = new double[population.getPopulationSize()]; + int index = 0; + for (Chromosome

chromosome : population) { + populationFitnesses[index++] = chromosome.evaluate(); + } + return populationFitnesses; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMeanFitness() { + return this.meanFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getFitnessVariance() { + return this.variance; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMaxFitness() { + return this.maxFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMinFitness() { + return this.minFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public long getPopulationSize() { + return this.fitnesses.length; + } + + /** + * calculate mean fitness. + * @param populationFitnesses + * @return mean fitness + */ + private double calculateMeanFitness(double[] populationFitnesses) { + double sum = 0.0; + for (double fitness : populationFitnesses) { + sum += fitness; + } + return sum / populationFitnesses.length; + } + + /** + * calculate variance of population fitness. + * @param populationFitnesses + * @return variance + */ + private double calculateVariance(double[] populationFitnesses) { + if (this.meanFitness == 0) { + this.meanFitness = calculateMeanFitness(populationFitnesses); + } + double sumOfSquare = 0.0; + for (double fitness : populationFitnesses) { + sumOfSquare += Math.pow(fitness, 2); + } + + return (sumOfSquare / populationFitnesses.length) - Math.pow(this.meanFitness, 2); + } + + /** + * {@inheritDoc} + */ + @Override + public int findRank(Chromosome

chromosome) { + return Arrays.binarySearch(fitnesses, chromosome.evaluate()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/internal/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/internal/package-info.java new file mode 100644 index 0000000000..35cf7dc2c3 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/internal/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.stats.internal; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java new file mode 100644 index 0000000000..f40707d99c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.stats; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java new file mode 100644 index 0000000000..068e57c1aa --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * This interface generates all random representations for chromosomes. + * @since 4.0 + */ +public interface ChromosomeRepresentationUtils { + + /** + * Generates a representation corresponding to a random permutation of length l + * which can be passed to the RandomKey constructor. + * + * @param l length of the permutation + * @return representation of a random permutation + */ + static List randomPermutation(final int l) { + final UniformRandomProvider randomProvider = RandomGenerator.getRandomGenerator(); + final List repr = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + repr.add(randomProvider.nextDouble()); + } + return repr; + } + + /** + * Generates a representation corresponding to an identity permutation of length + * l which can be passed to the RandomKey constructor. + * + * @param l length of the permutation + * @return representation of an identity permutation + */ + static List identityPermutation(final int l) { + final List repr = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + repr.add((double) i / l); + } + return repr; + } + + /** + * Generates a representation of a permutation corresponding to the + * data sorted by comparator. The data is + * not modified during the process. + * + * This is useful if you want to inject some permutations to the initial + * population. + * + * @param type of the data + * @param data list of data determining the order + * @param comparator how the data will be compared + * @return list representation of the permutation corresponding to the + * parameters + */ + static List comparatorPermutation(final List data, final Comparator comparator) { + final List sortedData = new ArrayList<>(data); + Collections.sort(sortedData, comparator); + + return inducedPermutation(data, sortedData); + } + + /** + * Generates a representation of a permutation corresponding to a permutation + * which yields permutedData when applied to + * originalData. + * + * This method can be viewed as an inverse to decode(). + * + * @param type of the data + * @param originalData the original, unpermuted data + * @param permutedData the data, somehow permuted + * @return representation of a permutation corresponding to the permutation + * {@code originalData -> permutedData} + */ + static List inducedPermutation(final List originalData, final List permutedData) { + + if (originalData.size() != permutedData.size()) { + throw new GeneticException(GeneticException.SIZE_MISMATCH, permutedData.size(), originalData.size()); + } + final int l = originalData.size(); + + final List origDataCopy = new ArrayList<>(originalData); + + final Double[] res = new Double[l]; + for (int i = 0; i < l; i++) { + final int index = origDataCopy.indexOf(permutedData.get(i)); + if (index == -1) { + throw new GeneticException(GeneticException.DIFFERENT_ORIG_AND_PERMUTED_DATA); + } + res[index] = (double) i / l; + origDataCopy.set(index, null); + } + return Arrays.asList(res); + } + + /** + * Returns a representation of a random binary array of length + * length. + * @param length length of the array + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + * @return a random binary array of length length + */ + static List randomIntegralRepresentation(final int length, final int min, final int max) { + final UniformRandomProvider randomProvider = RandomGenerator.getRandomGenerator(); + final List rList = new ArrayList<>(length); + for (int j = 0; j < length; j++) { + rList.add(min + randomProvider.nextInt(max - min)); + } + return rList; + } + + /** + * Returns a representation of a random binary array of length + * length. + * @param length length of the array + * @return a random binary array of length length + */ + static List randomBinaryRepresentation(final int length) { + final UniformRandomProvider randomProvider = RandomGenerator.getRandomGenerator(); + // random binary list + final List rList = new ArrayList<>(length); + for (int j = 0; j < length; j++) { + rList.add(randomProvider.nextInt(2)); + } + return rList; + } + + /** + * Generates a representation corresponding to a random double values[0..1] of + * length l. + * @param l length of the permutation + * @return representation of a random permutation + */ + static List randomNormalizedDoubleRepresentation(final int l) { + return randomDoubleRepresentation(l, 0, 1); + } + + /** + * Generates a representation corresponding to a random double values of length + * l. + * @param l length of representation + * @param min minimum inclusive value of chromosome gene + * @param max maximum exclusive value of chromosome gene + * @return representation as List of Double + */ + static List randomDoubleRepresentation(final int l, double min, double max) { + ValidationUtils.checkForMinMax(min, max); + final double range = max - min; + final UniformRandomProvider randomProvider = RandomGenerator.getRandomGenerator(); + final List repr = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + repr.add(min + randomProvider.nextDouble() * range); + } + return repr; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ConsoleLogger.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ConsoleLogger.java new file mode 100644 index 0000000000..8af43a0164 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ConsoleLogger.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import org.apache.commons.math4.ga.exception.GeneticException; + +/** + * This class is responsible for logging messages to console. + * @since 4.0 + */ +public final class ConsoleLogger { + + /** instance of ConsoleLogger. **/ + private static volatile ConsoleLogger instance; + + /** writer instance to log messages to system console. **/ + private final BufferedWriter writer; + + /** + * constructor. + * @param encoding + */ + private ConsoleLogger(String encoding) { + try { + writer = new BufferedWriter(new OutputStreamWriter(System.out, encoding)); + + // Create a shutdown hook to close the writer. + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + writer.close(); + } catch (IOException e) { + throw new GeneticException(e); + } + })); + } catch (UnsupportedEncodingException e1) { + throw new GeneticException(e1); + } + } + + /** + * Logs a message. + * @param message message to log + */ + public void log(String message) { + try { + writer.write(message); + writer.newLine(); + writer.flush(); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + /** + * Logs the message after formatting with the args. + * @param message message to log + * @param args args to format the message + */ + public void log(String message, Object... args) { + try { + writer.write(String.format(message, args)); + writer.newLine(); + writer.flush(); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + /** + * Returns the instance of ConsoleLogger. + * @param encoding encoding to be used with writing + * @return instance of ConsoleLogger + */ + public static ConsoleLogger getInstance(final String encoding) { + ValidationUtils.checkForNull("Encoding of ConsoleLogger", encoding); + if (instance == null) { + synchronized (ConsoleLogger.class) { + if (instance == null) { + instance = new ConsoleLogger(encoding); + } + } + } + return instance; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/Constants.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/Constants.java new file mode 100644 index 0000000000..4fa705453e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/Constants.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.utils; + +/** + * Contains all constants required for the library. + * @since 4.0 + */ +public final class Constants { + + /** crossover rate. **/ + public static final String CROSSOVER_RATE = "CROSSOVER_RATE"; + + /** mutation rate. **/ + public static final String MUTATION_RATE = "MUTATION_RATE"; + + /** elitism rate. **/ + public static final String ELITISM_RATE = "ELITISM_RATE"; + + /** allele value. **/ + public static final String ALLELE_VALUE = "ALLELE_VALUE"; + + /** new line constant. **/ + public static final String NEW_LINE = System.getProperty("line.separator"); + + private Constants() { + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomGenerator.java new file mode 100644 index 0000000000..6bf1908add --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomGenerator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.utils; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.simple.RandomSource; +import org.apache.commons.rng.simple.ThreadLocalRandomSource; + +/** + * An utility to generate per thread {@link UniformRandomProvider} instance. + * @since 4.0 + */ +public final class RandomGenerator { + + /** {@link ThreadLocal} instances of {@link UniformRandomProvider}. **/ + private static ThreadLocal randomSources = new ThreadLocal<>(); + + /** + * constructor. + */ + private RandomGenerator() { + } + + /** + * Returns the (static) random generator. + * @return the static random generator shared by GA implementation classes + */ + public static UniformRandomProvider getRandomGenerator() { + if (randomSources.get() == null) { + randomSources.set(ThreadLocalRandomSource.current(RandomSource.XO_RO_SHI_RO_128_PP)); + } + return randomSources.get(); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ValidationUtils.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ValidationUtils.java new file mode 100644 index 0000000000..0c0dcf7fb8 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ValidationUtils.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import org.apache.commons.math4.ga.exception.GeneticException; + +/** + * This class contains common validation methods. + * @since 4.0 + */ +public interface ValidationUtils { + + /** + * Checks for Null value. + * @param name alias of the parameter + * @param value value of the parameter + */ + static void checkForNull(String name, Object value) { + if (value == null) { + throw new GeneticException(GeneticException.NULL_ARGUMENT, name); + } + } + + /** + * Checks for min and max, throws error if min is greater than or equals to max. + * @param min minimum value + * @param max maximum value + */ + static void checkForMinMax(int min, int max) { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + } + + /** + * Checks for min and max, throws error if min is greater than or equals to max. + * @param min minimum value + * @param max maximum value + */ + static void checkForMinMax(double min, double max) { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java new file mode 100644 index 0000000000..0e8c79adc3 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.utils; diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinary.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinary.java new file mode 100644 index 0000000000..3a58f7c7de --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinary.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import java.lang.reflect.Field; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergencecond.FixedGenerationCount; +import org.apache.commons.math4.ga.convergencecond.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.TransparentListChromosomeDecoder; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * This is also an example of usage. + */ +public class GeneticAlgorithmTestBinary { + + // parameters for the GA + private static final int DIMENSION = 50; + private static final int POPULATION_SIZE = 50; + private static final int NUM_GENERATIONS = 50; + private static final double CROSSOVER_RATE = 1; + private static final double MUTATION_RATE = 0.1; + private static final int TOURNAMENT_ARITY = 2; + + @Before + public void reset() + throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + Field listenersField = registry.getClass().getDeclaredField("listeners"); + boolean accessible = listenersField.isAccessible(); + if (!accessible) { + listenersField.setAccessible(true); + } + @SuppressWarnings("unchecked") + List> listeners = (List>) listenersField + .get(ConvergenceListenerRegistry.getInstance()); + listeners.clear(); + listenersField.setAccessible(accessible); + } + + @Test + public void test() { + // to test a stochastic algorithm is hard, so this will rather be an usage + // example + + // initialize a new genetic algorithm + GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointCrossover>(), + CROSSOVER_RATE, new BinaryMutation>(), MUTATION_RATE, + new TournamentSelection>(TOURNAMENT_ARITY)); + + Assert.assertEquals(0, ga.getGenerationsEvolved()); + + // initial population + Population> initial = randomPopulation(); + // stopping conditions + StoppingCondition> stopCond = new FixedGenerationCount<>(NUM_GENERATIONS); + + // best initial chromosome + Chromosome> bestInitial = initial.getFittestChromosome(); + + // run the algorithm + Population> finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + Chromosome> bestFinal = finalPopulation.getFittestChromosome(); + + // the only thing we can test is whether the final solution is not worse than + // the initial one + // however, for some implementations of GA, this need not be true :) + + Assert.assertTrue(bestFinal.compareTo(bestInitial) > 0); + Assert.assertEquals(NUM_GENERATIONS, ga.getGenerationsEvolved()); + + } + + /** + * Initializes a random population. + */ + private static ListPopulation> randomPopulation() { + List>> popList = new LinkedList<>(); + + for (int i = 0; i < POPULATION_SIZE; i++) { + BinaryChromosome> randChrom = new FindOnes( + ChromosomeRepresentationUtils.randomBinaryRepresentation(DIMENSION)); + popList.add(randChrom); + } + return new ListPopulation<>(popList, popList.size()); + } + + /** + * Chromosomes represented by a binary chromosome. + * + * The goal is to set all bits (genes) to 1. + */ + private static class FindOnes extends BinaryChromosome> { + + FindOnes(List representation) { + super(representation, phenotype -> { + Integer val = 0; + for (Integer num : phenotype) { + val += num; + } + return val; + }, new TransparentListChromosomeDecoder<>()); + } + } + + @Test(expected = GeneticException.class) + public void testCrossoverRate() { + new GeneticAlgorithm<>(new OnePointCrossover<>(), 1.5, new BinaryMutation<>(), .01, + new TournamentSelection<>(10)); + } + + @Test(expected = GeneticException.class) + public void testMutationRate() { + new GeneticAlgorithm<>(new OnePointCrossover<>(), .5, new BinaryMutation<>(), 1.5, + new TournamentSelection<>(10)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java new file mode 100644 index 0000000000..f9f01a2326 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergencecond.FixedGenerationCount; +import org.apache.commons.math4.ga.convergencecond.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.Assert; +import org.junit.Test; + +/** + * This is also an example of usage. + * + * This algorithm does "stochastic sorting" of a sequence 0,...,N. + * + */ +public class GeneticAlgorithmTestPermutations { + + // parameters for the GA + private static final int DIMENSION = 20; + private static final int POPULATION_SIZE = 80; + private static final int NUM_GENERATIONS = 200; + private static final double ELITISM_RATE = 0.2; + private static final double CROSSOVER_RATE = 1; + private static final double MUTATION_RATE = 0.08; + private static final int TOURNAMENT_ARITY = 2; + + // numbers from 0 to N-1 + private static final List sequence = new ArrayList<>(); + static { + for (int i = 0; i < DIMENSION; i++) { + sequence.add(i); + } + } + + @Test + public void test() { + // to test a stochastic algorithm is hard, so this will rather be an usage + // example + + // initialize a new genetic algorithm + GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointCrossover>(), + CROSSOVER_RATE, new RealValuedMutation>(), MUTATION_RATE, + new TournamentSelection>(TOURNAMENT_ARITY), ELITISM_RATE); + + // initial population + Population> initial = randomPopulation(); + // stopping conditions + StoppingCondition> stopCond = new FixedGenerationCount<>(NUM_GENERATIONS); + + // best initial chromosome + Chromosome> bestInitial = initial.getFittestChromosome(); + + // run the algorithm + Population> finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + Chromosome> bestFinal = finalPopulation.getFittestChromosome(); + + // the only thing we can test is whether the final solution is not worse than + // the initial one + // however, for some implementations of GA, this need not be true :) + + Assert.assertTrue(bestFinal.compareTo(bestInitial) > 0); + + } + + /** + * Initializes a random population + */ + private static Population> randomPopulation() { + List>> popList = new ArrayList<>(); + for (int i = 0; i < POPULATION_SIZE; i++) { + Chromosome> randChrom = new MinPermutations( + ChromosomeRepresentationUtils.randomPermutation(DIMENSION)); + popList.add(randChrom); + } + return new ListPopulation>(popList, popList.size()); + } + + /** + * Chromosomes representing a permutation of (0,1,2,...,DIMENSION-1). + * + * The goal is to sort the sequence. + */ + private static class MinPermutations extends RealValuedChromosome> { + + MinPermutations(List representation) { + super(representation, new MinPermutationsFitnessFunction(), new RandomKeyDecoder<>(sequence)); + } + + @Override + public RealValuedChromosome> newChromosome(List chromosomeRepresentation) { + return new MinPermutations(chromosomeRepresentation); + } + + } + + private static class MinPermutationsFitnessFunction implements FitnessFunction> { + + @Override + public double compute(List decodedChromosome) { + double res = 0.0; + for (int i = 0; i < decodedChromosome.size(); i++) { + int value = decodedChromosome.get(i); + if (value != i) { + // bad position found + res += Math.abs(value - i); + } + } + // the most fitted chromosome is the one with minimal error + // therefore we must return negative value + return -res; + } + + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java new file mode 100644 index 0000000000..047c17939a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.junit.Assert; +import org.junit.Test; + +public class AbstractChromosomeTest { + + @Test + public void testGetFitness() { + Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + Assert.assertEquals(1, c1.evaluate(), .001); + } + + @Test + public void testDecode() { + Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + Assert.assertEquals("1", c1.decode()); + } + + @Test + public void testCompareTo() { + Chromosome c1 = new AbstractChromosome(chromosome -> 0, chromosome -> "0") { + }; + Chromosome c2 = new AbstractChromosome(chromosome -> 10, chromosome -> "10") { + }; + Chromosome c3 = new AbstractChromosome(chromosome -> 10, chromosome -> "10") { + }; + + Assert.assertTrue(c1.compareTo(c2) < 0); + Assert.assertTrue(c2.compareTo(c1) > 0); + Assert.assertEquals(0, c3.compareTo(c2)); + Assert.assertEquals(0, c2.compareTo(c3)); + } + + @Test + public void testIsSame() { + AbstractChromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + AbstractChromosome c2 = new AbstractChromosome(chromosome -> 2, chromosome -> "2") { + }; + AbstractChromosome c3 = new AbstractChromosome(chromosome -> 3, chromosome -> "1") { + }; + Assert.assertTrue(c1.isSame(c3)); + Assert.assertFalse(c1.isSame(c2)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java new file mode 100644 index 0000000000..8378051b0e --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class BinaryChromosomeTest { + + @Test(expected = GeneticException.class) + public void testInvalidConstructor() { + Integer[][] reprs = new Integer[][] {new Integer[] {0, 1, 0, 1, 2}, new Integer[] {0, 1, 0, 1, -1}}; + + for (Integer[] repr : reprs) { + new BinaryChromosome<>(repr, c -> 0, new DummyListChromosomeDecoder<>("0")); + Assert.fail("Exception not caught"); + } + } + + @Test + public void testRandomConstructor() { + for (int i = 0; i < 20; i++) { + BinaryChromosome.randomChromosome(10, c -> 1, new DummyListChromosomeDecoder<>("1")); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java new file mode 100644 index 0000000000..d1ea46dd34 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class ChromosomePairTest { + + @Test + public void testChromosomePair() { + BinaryChromosome chromosome1 = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(10), c -> 0, + new DummyListChromosomeDecoder<>("0")); + Chromosome chromosome2 = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(10), c -> 1, + new DummyListChromosomeDecoder<>("1")); + ChromosomePair chromosomePair = new ChromosomePair<>(chromosome1, chromosome2); + + Assert.assertEquals(chromosomePair.getFirst(), chromosome1); + Assert.assertEquals(chromosomePair.getSecond(), chromosome2); + + Assert.assertNotNull(chromosomePair.toString()); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java new file mode 100644 index 0000000000..194979f54b --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class IntegralValuedChromosomeTest { + + @Test + public void testIntegralValuedChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + Assert.assertEquals(min, chromosome.getMin()); + Assert.assertEquals(max, chromosome.getMax()); + + IntegralValuedChromosome chromosome1 = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max) + .toArray(new Integer[10]), c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + Assert.assertEquals(min, chromosome1.getMin()); + Assert.assertEquals(max, chromosome1.getMax()); + } + + @Test(expected = GeneticException.class) + public void testCheckValidity() { + int min = 0; + int max = 10; + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), max, min); + } + + @Test(expected = GeneticException.class) + public void testCheckValidity1() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min - 10, max + 10), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + } + + @Test + public void testNewChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + IntegralValuedChromosome newChromosome = chromosome + .newChromosome(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max)); + Assert.assertEquals(chromosome.getMin(), newChromosome.getMin()); + Assert.assertEquals(chromosome.getMax(), newChromosome.getMax()); + Assert.assertEquals(chromosome.getDecoder(), newChromosome.getDecoder()); + Assert.assertEquals(chromosome.getFitnessFunction(), newChromosome.getFitnessFunction()); + + } + + @Test + public void testRandomChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + Assert.assertEquals(min, chromosome.getMin()); + Assert.assertEquals(max, chromosome.getMax()); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java new file mode 100644 index 0000000000..295b7e4c44 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Test; + +public class RealValuedChromosomeTest { + + @Test + public void test() { + for (int i = 0; i < 10; i++) { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1, + new DummyListChromosomeDecoder<>("1")); + new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1).toArray(new Double[10]), c -> 0, + new DummyListChromosomeDecoder<>("0")); + } + } + + @Test + public void testNewChromosome() { + for (int i = 0; i < 10; i++) { + RealValuedChromosome chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1, + new DummyListChromosomeDecoder<>("1")); + chromosome.newChromosome(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1)); + } + } + + @Test + public void testRandomChromosome() { + for (int i = 0; i < 10; i++) { + RealValuedChromosome.randomChromosome(5, c -> 0, new DummyListChromosomeDecoder<>("0"), 0, 2); + } + } + + @Test(expected = GeneticException.class) + public void testCheckValidity() { + int min = 0; + int max = 10; + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), max, min); + } + + @Test(expected = GeneticException.class) + public void testCheckValidity1() { + int min = 0; + int max = 10; + new RealValuedChromosome<>(ChromosomeRepresentationUtils + .randomDoubleRepresentation(10, min - 10, max + 10), c -> 0, new DummyListChromosomeDecoder<>("0"), + min, max); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java new file mode 100644 index 0000000000..7f4489c880 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.Assert; +import org.junit.Test; + +public class FixedElapsedTimeTest { + + @Test + public void testIsSatisfied() { + final Population pop = new ListPopulation<>(10); + + final long start = System.nanoTime(); + final long duration = 3; + final FixedElapsedTime tec = new FixedElapsedTime(duration); + + while (!tec.isSatisfied(pop)) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // ignore + } + } + + final long end = System.nanoTime(); + final long elapsedTime = end - start; + final long diff = Math.abs(elapsedTime - TimeUnit.SECONDS.toNanos(duration)); + + Assert.assertTrue(diff < TimeUnit.MILLISECONDS.toNanos(100)); + } + + @Test(expected = GeneticException.class) + public void testNegativeTime() { + new FixedElapsedTime<>(-10); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java new file mode 100644 index 0000000000..aa7f7151dd --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.Assert; +import org.junit.Test; + +public class FixedGenerationCountTest { + + @Test + public void testIsSatisfied() { + FixedGenerationCount fgc = new FixedGenerationCount(20); + + int cnt = 0; + Population pop = new ListPopulation<>(10); + + while (!fgc.isSatisfied(pop)) { + cnt++; + } + Assert.assertEquals(cnt, fgc.getNumGenerations()); + } + + @Test(expected = GeneticException.class) + public void testNegativeGenerationCount() { + new FixedGenerationCount(-1); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java new file mode 100644 index 0000000000..48ee560fee --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.internal.PopulationStatisticalSummaryImpl; +import org.junit.Assert; +import org.junit.Test; + +public class UnchangedBestFitnessTest { + + @Test + public void testIsSatisfied() { + + final int noOfGenerationsWithUnchangedBestFitness = 5; + StoppingCondition stoppingCondition = new UnchangedBestFitness<>( + noOfGenerationsWithUnchangedBestFitness); + + double[] fitnesses = new double[10]; + for (int i = 0; i < 10; i++) { + fitnesses[i] = i; + } + List> chromosomes = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + final double fitness = fitnesses[i]; + Chromosome ch = new Chromosome() { + + @Override + public int compareTo(Chromosome o) { + double diff = this.evaluate() - o.evaluate(); + return diff > 0 ? 1 : (diff < 0 ? -1 : 0); + } + + @Override + public double evaluate() { + return fitness; + } + + @Override + public String decode() { + return "Fixed"; + } + }; + chromosomes.add(ch); + } + Population pop = new ListPopulation<>(chromosomes, 10); + + double initialMaxFitness = new PopulationStatisticalSummaryImpl<>(pop).getMaxFitness(); + + int counter = 0; + while (!stoppingCondition.isSatisfied(pop)) { + counter++; + } + + double maxFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMaxFitness(); + + Assert.assertEquals(initialMaxFitness, maxFitnessAfterConvergence, .001); + Assert.assertEquals(noOfGenerationsWithUnchangedBestFitness, counter); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java new file mode 100644 index 0000000000..d2a8544087 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.internal.PopulationStatisticalSummaryImpl; +import org.junit.Assert; +import org.junit.Test; + +public class UnchangedMeanFitnessTest { + + @Test + public void testIsSatisfied() { + + final int noOfGenerationsWithUnchangedMeanFitness = 5; + StoppingCondition stoppingCondition = new UnchangedMeanFitness<>( + noOfGenerationsWithUnchangedMeanFitness); + + double[] fitnesses = new double[10]; + for (int i = 0; i < 10; i++) { + fitnesses[i] = i; + } + List> chromosomes = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + final double fitness = fitnesses[i]; + Chromosome ch = new Chromosome() { + + @Override + public int compareTo(Chromosome o) { + double diff = this.evaluate() - o.evaluate(); + return diff > 0 ? 1 : (diff < 0 ? -1 : 0); + } + + @Override + public double evaluate() { + return fitness; + } + + @Override + public String decode() { + return "Fixed"; + } + }; + chromosomes.add(ch); + } + Population pop = new ListPopulation<>(chromosomes, 10); + + double initialAverageFitness = new PopulationStatisticalSummaryImpl<>(pop).getMeanFitness(); + + int counter = 0; + while (!stoppingCondition.isSatisfied(pop)) { + counter++; + } + + double averageFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMeanFitness(); + + Assert.assertEquals(initialAverageFitness, averageFitnessAfterConvergence, .001); + Assert.assertEquals(noOfGenerationsWithUnchangedMeanFitness, counter); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java new file mode 100644 index 0000000000..5ea57aacb9 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyChromosome; +import org.junit.Assert; +import org.junit.Test; + +public class AbstractChromosomeCrossoverPolicyTest { + + @Test + public void testCrossoverProbability() { + + CrossoverPolicy crossoverPolicy = new AbstractChromosomeCrossoverPolicy() { + @Override + protected ChromosomePair crossover(Chromosome first, Chromosome second) { + return null; + } + }; + + Chromosome ch1 = new DummyChromosome(); + + Chromosome ch2 = new DummyChromosome(); + + Assert.assertNull(crossoverPolicy.crossover(ch1, ch2, 1.0)); + Assert.assertNotNull(crossoverPolicy.crossover(ch1, ch2, 0.0)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java new file mode 100644 index 0000000000..f2855cd462 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.Test; + +public class AbstractListChromosomeCrossoverPolicyTest { + + @Test(expected = GeneticException.class) + public void testCrossoverWithNonListChromosome() { + + CrossoverPolicy crossoverPolicy = new AbstractListChromosomeCrossoverPolicy() { + + @Override + protected ChromosomePair mate(AbstractListChromosome first, + AbstractListChromosome second) { + return new ChromosomePair<>(first, second); + } + }; + Chromosome ch1 = new AbstractChromosome(c -> 0, c -> "0") { + }; + + Chromosome ch2 = new AbstractChromosome(c -> 1, c -> "1") { + }; + + crossoverPolicy.crossover(ch1, ch2, 1.0); + } + + @Test(expected = GeneticException.class) + public void testCrossoverWithUnEqualLengthChromosome() { + + CrossoverPolicy crossoverPolicy = new AbstractListChromosomeCrossoverPolicy() { + + @Override + protected ChromosomePair mate(AbstractListChromosome first, + AbstractListChromosome second) { + return new ChromosomePair<>(first, second); + } + }; + Chromosome ch1 = new DummyListChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10)); + + Chromosome ch2 = new DummyListChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(20)); + + crossoverPolicy.crossover(ch1, ch2, 1.0); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java new file mode 100644 index 0000000000..1f85a31a4d --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.junit.Assert; +import org.junit.Test; + +public class CycleCrossoverTest { + + @Test + public void testCrossoverExample() { + // taken from + // http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx + final Integer[] p1 = new Integer[] {8, 4, 7, 3, 6, 2, 5, 1, 9, 0}; + final Integer[] p2 = new Integer[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new CycleCrossover(); + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + final Integer[] c1e = new Integer[] {8, 1, 2, 3, 4, 5, 6, 7, 9, 0}; + final Integer[] c2e = new Integer[] {0, 4, 7, 3, 6, 2, 5, 1, 8, 9}; + + Assert.assertArrayEquals(c1e, c1); + Assert.assertArrayEquals(c2e, c2); + } + + @Test + public void testCrossoverExample2() { + // taken from http://www.scribd.com/doc/54206412/32/Cycle-crossover + final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9}; + final Integer[] p2 = new Integer[] {9, 3, 7, 8, 2, 6, 5, 1, 4}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new CycleCrossover(); + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + final Integer[] c1e = new Integer[] {1, 3, 7, 4, 2, 6, 5, 8, 9}; + final Integer[] c2e = new Integer[] {9, 2, 3, 8, 5, 6, 7, 1, 4}; + + Assert.assertArrayEquals(c1e, c1); + Assert.assertArrayEquals(c2e, c2); + } + + @Test + public void testCrossover() { + final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + final Integer[] p2 = new Integer[] {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new CycleCrossover(true); + + for (int i = 0; i < 20; i++) { + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + int index = 0; + // Determine if it is in the same spot as in the first parent, if + // not it comes from the second parent. + for (final Integer j : c1) { + if (!p1[index].equals(j)) { + Assert.assertEquals(j, p2[index]); + } else { + Assert.assertEquals(j, p1[index]); + } + index++; + } + + // Same as above only for the second parent. + index = 0; + for (final Integer k : c2) { + if (p2[index] != k) { + Assert.assertEquals(k, p1[index]); + } else { + Assert.assertEquals(k, p2[index]); + } + index++; + } + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java new file mode 100644 index 0000000000..f5d8d77d0f --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class NPointCrossoverTest { + + @Test(expected = GeneticException.class) + public void testNumberIsTooLargeException() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final Integer[] p2 = new Integer[] {0, 1, 1, 0, 1, 0, 1, 1, 1}; + + final BinaryChromosome p1c = new BinaryChromosome(p1, c -> 0, + new DummyListChromosomeDecoder("0")); + final BinaryChromosome p2c = new BinaryChromosome(p2, c -> 0, + new DummyListChromosomeDecoder("0")); + + final CrossoverPolicy cp = new NPointCrossover(15); + cp.crossover(p1c, p2c, 1.0); + } + + @Test(expected = GeneticException.class) + public void testCrossoverInvalidFixedLengthChromosomeFirst() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final BinaryChromosome p1c = new BinaryChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + final AbstractChromosome p2c = new AbstractChromosome(chromosome -> 0, + new DummyListChromosomeDecoder<>("0")) { + }; + + final CrossoverPolicy cp = new NPointCrossover(1); + cp.crossover(p1c, p2c, 1.0); + } + + @Test(expected = GeneticException.class) + public void testCrossoverInvalidFixedLengthChromosomeSecond() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final BinaryChromosome p2c = new BinaryChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + final AbstractChromosome p1c = new AbstractChromosome(chromosome -> 0, + new DummyListChromosomeDecoder<>("0")) { + }; + + final CrossoverPolicy cp = new NPointCrossover(1); + cp.crossover(p1c, p2c, 1.0); + } + + @Test + public void testCrossover() { + Integer[] p1 = new Integer[] {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Integer[] p2 = new Integer[] {0, 1, 0, 1, 0, 1, 0, 1, 0}; + + BinaryChromosome p1c = new BinaryChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + BinaryChromosome p2c = new BinaryChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + + final int order = 3; + NPointCrossover npc = new NPointCrossover<>(order); + + // the two parent chromosomes are different at each position, so it is easy to + // detect + // the number of crossovers that happened for each child + for (int i = 0; i < 20; i++) { + ChromosomePair pair = npc.crossover(p1c, p2c, 1.0); + Assert.assertEquals(order, detectCrossoverPoints(p1c, p2c, (BinaryChromosome) pair.getFirst())); + Assert.assertEquals(order, detectCrossoverPoints(p2c, p1c, (BinaryChromosome) pair.getSecond())); + } + } + + private int detectCrossoverPoints(BinaryChromosome p1, BinaryChromosome p2, + BinaryChromosome c) { + int crossovers = 0; + final int length = p1.getLength(); + + final List p1Rep = p1.getRepresentation(); + final List p2Rep = p2.getRepresentation(); + final List cRep = c.getRepresentation(); + + List rep = p1Rep; + for (int i = 0; i < length; i++) { + if (rep.get(i) != cRep.get(i)) { + crossovers++; + rep = rep == p1Rep ? p2Rep : p1Rep; + } + } + + return crossovers; + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java new file mode 100644 index 0000000000..5e99706c79 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class OnePointCrossoverTest { + + @Test + public void testCrossover() { + @SuppressWarnings("boxing") + Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + @SuppressWarnings("boxing") + Integer[] p2 = new Integer[] {0, 1, 1, 0, 1, 0, 1, 1, 1}; + + BinaryChromosome p1c = new BinaryChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + BinaryChromosome p2c = new BinaryChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + + OnePointCrossover opc = new OnePointCrossover<>(); + + // how to test a stochastic method? + for (int i = 0; i < 20; i++) { + ChromosomePair pair = opc.crossover(p1c, p2c, 1.0); + + Integer[] c1 = new Integer[p1.length]; + Integer[] c2 = new Integer[p2.length]; + + c1 = ((BinaryChromosome) pair.getFirst()).getRepresentation().toArray(c1); + c2 = ((BinaryChromosome) pair.getSecond()).getRepresentation().toArray(c2); + + // first and last values will be the same + Assert.assertEquals(p1[0], c1[0]); + Assert.assertEquals(p2[0], c2[0]); + Assert.assertEquals(p1[p1.length - 1], c1[c1.length - 1]); + Assert.assertEquals(p2[p2.length - 1], c2[c2.length - 1]); + // moreover, in the above setting, the 2nd, 3rd and 7th values will be the same + Assert.assertEquals(p1[2], c1[2]); + Assert.assertEquals(p2[2], c2[2]); + Assert.assertEquals(p1[3], c1[3]); + Assert.assertEquals(p2[3], c2[3]); + Assert.assertEquals(p1[7], c1[7]); + Assert.assertEquals(p2[7], c2[7]); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java new file mode 100644 index 0000000000..437cfbd01c --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.junit.Assert; +import org.junit.Test; + +public class OrderedCrossoverTest { + + @Test + public void testCrossover() { + final Integer[] p1 = new Integer[] {8, 4, 7, 3, 6, 2, 5, 1, 9, 0}; + final Integer[] p2 = new Integer[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new OrderedCrossover(); + + for (int i = 0; i < 20; i++) { + final Set parentSet1 = new HashSet<>(Arrays.asList(p1)); + final Set parentSet2 = new HashSet<>(Arrays.asList(p2)); + + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + Assert.assertNotSame(p1c, pair.getFirst()); + Assert.assertNotSame(p2c, pair.getSecond()); + + // make sure that the children have exactly the same elements as their parents + for (int j = 0; j < c1.length; j++) { + Assert.assertTrue(parentSet1.contains(c1[j])); + parentSet1.remove(c1[j]); + Assert.assertTrue(parentSet2.contains(c2[j])); + parentSet2.remove(c2[j]); + } + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java new file mode 100644 index 0000000000..e3852e1ef1 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.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.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class UniformCrossoverTest { + private static final int LEN = 10000; + private static final List p1 = new ArrayList<>(LEN); + private static final List p2 = new ArrayList<>(LEN); + + @SuppressWarnings("boxing") + @BeforeClass + public static void setUpBeforeClass() { + for (int i = 0; i < LEN; i++) { + p1.add(0); + p2.add(1); + } + } + + @Test(expected = GeneticException.class) + public void testRatioTooLow() { + new UniformCrossover(-0.5d); + } + + @Test(expected = GeneticException.class) + public void testRatioTooHigh() { + new UniformCrossover(1.5d); + } + + @Test + public void testCrossover() { + // test crossover with different ratios + performCrossover(0.5); + performCrossover(0.7); + performCrossover(0.2); + } + + private void performCrossover(double ratio) { + final BinaryChromosome p1c = new BinaryChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + final BinaryChromosome p2c = new BinaryChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + + final CrossoverPolicy cp = new UniformCrossover(ratio); + + // make a number of rounds + for (int i = 0; i < 20; i++) { + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final List c1 = ((BinaryChromosome) pair.getFirst()).getRepresentation(); + final List c2 = ((BinaryChromosome) pair.getSecond()).getRepresentation(); + + int from1 = 0; + int from2 = 0; + + // check first child + for (int val : c1) { + if (val == 0) { + from1++; + } else { + from2++; + } + } + + Assert.assertEquals(1.0 - ratio, (double) from1 / LEN, 0.1); + Assert.assertEquals(ratio, (double) from2 / LEN, 0.1); + + from1 = 0; + from2 = 0; + + // check second child + for (int val : c2) { + if (val == 0) { + from1++; + } else { + from2++; + } + } + + Assert.assertEquals(ratio, (double) from1 / LEN, 0.1); + Assert.assertEquals(1.0 - ratio, (double) from2 / LEN, 0.1); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java new file mode 100644 index 0000000000..00b0cc6f19 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.dummy.DummyChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.junit.Test; + +public class AbstractListChromosomeDecoderTest { + + @Test(expected = GeneticException.class) + public void testDecodeWithInvalidChromosomeInstance() { + Decoder decoder = new AbstractListChromosomeDecoder() { + + @Override + protected String decode(AbstractListChromosome chromosome) { + return null; + } + }; + Chromosome ch = new DummyChromosome(); + decoder.decode(ch); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java new file mode 100644 index 0000000000..b37247502e --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.junit.Assert; +import org.junit.Test; + +public class RandomKeyDecoderTest { + + @Test + public void testDecodeChromosome() { + + List sequence = Arrays.asList(new String[] {"a", "b", "c", "d", "e"}); + Double[] keys = new Double[] {0.4, 0.1, 0.5, 0.8, 0.2}; + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(keys, c -> 0, decoder); + List decodedSequence = chromosome.decode(); + + Assert.assertEquals("b", decodedSequence.get(0)); + Assert.assertEquals("e", decodedSequence.get(1)); + Assert.assertEquals("a", decodedSequence.get(2)); + Assert.assertEquals("c", decodedSequence.get(3)); + Assert.assertEquals("d", decodedSequence.get(4)); + + } + + @Test(expected = GeneticException.class) + public void testSequenceLength() { + List sequence = Arrays.asList(new String[] {"a", "b", "c", "d", "e", "f"}); + Double[] keys = new Double[] {0.4, 0.1, 0.5, 0.8, 0.2}; + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(keys, c -> 0, decoder); + chromosome.decode(); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java new file mode 100644 index 0000000000..b2de920888 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.Assert; +import org.junit.Test; + +public class TransparentListChromosomeDecoderTest { + + @Test + public void testDecode() { + List rp = ChromosomeRepresentationUtils.randomBinaryRepresentation(10); + Chromosome> chromosome = new BinaryChromosome<>(rp, c -> 0, + new TransparentListChromosomeDecoder<>()); + List decodedRp = chromosome.decode(); + Assert.assertTrue(Objects.equals(rp, decodedRp)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java new file mode 100644 index 0000000000..3583b3561c --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.dummy; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; + +public class DummyChromosome extends AbstractChromosome { + + public DummyChromosome() { + super(c -> 0, c -> "0"); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java new file mode 100644 index 0000000000..eaddb90207 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.dummy; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; + +/** + * Implementation of ListChromosome for testing purposes + */ +public class DummyListChromosome extends AbstractListChromosome { + + public DummyListChromosome(final Integer[] representation) { + super(representation, chromosome -> 0, new DummyListChromosomeDecoder<>("0")); + } + + public DummyListChromosome() { + super(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + } + + public DummyListChromosome(final List representation) { + super(representation, chromosome -> 0, new DummyListChromosomeDecoder<>("0")); + } + + @Override + public DummyListChromosome newChromosome(final List chromosomeRepresentation) { + return new DummyListChromosome(chromosomeRepresentation); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java new file mode 100644 index 0000000000..9e6f8d3b78 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.exception; + +import org.junit.Test; + +public class GeneticExceptionTest { + + @Test(expected = GeneticException.class) + public void testGeneticExceptionThrowable() { + throw new GeneticException(new NullPointerException()); + } + + @Test(expected = GeneticException.class) + public void testGeneticExceptionStringThrowableObjectArray() { + throw new GeneticException("Nullpointer Exception", new NullPointerException()); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java new file mode 100644 index 0000000000..034a3b5f0d --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.listener; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.Assert; +import org.junit.Test; + +public class ConvergenceListenerRegistryTest { + + @Test + public void testRegister() { + try { + reset(); + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + + List> listeners = new ArrayList<>(); + ConvergenceListener convergenceListener = new ConvergenceListener() { + + @Override + public void notify(int generation, Population population) { + // No op + } + }; + listeners.add(convergenceListener); + ConvergenceListener convergenceListener1 = new ConvergenceListener() { + + @Override + public void notify(int generation, Population population) { + // No op + } + }; + listeners.add(convergenceListener1); + registry.addConvergenceListeners(listeners); + Field listenersField = registry.getClass().getDeclaredField("listeners"); + boolean accessible = listenersField.isAccessible(); + if (!accessible) { + listenersField.setAccessible(true); + } + @SuppressWarnings("unchecked") + List> listeners1 = (List>) listenersField + .get(registry); + Assert.assertSame(listeners1.get(0), convergenceListener); + listenersField.setAccessible(accessible); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + // No op + } + } + + private void reset() + throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + Field listenersField = registry.getClass().getDeclaredField("listeners"); + boolean accessible = listenersField.isAccessible(); + if (!accessible) { + listenersField.setAccessible(true); + } + @SuppressWarnings("unchecked") + List> listeners = (List>) listenersField + .get(ConvergenceListenerRegistry.getInstance()); + listeners.clear(); + listenersField.setAccessible(accessible); + } + + @Test(expected = GeneticException.class) + public void testNotifyAll() { + try { + reset(); + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + ConvergenceListener convergenceListener = new ConvergenceListener() { + + @Override + public void notify(int generation, Population population) { + throw new GeneticException("Test Notify"); + } + }; + registry.addConvergenceListener(convergenceListener); + registry.notifyAll(0, new ListPopulation<>(10)); + Assert.assertTrue(true); + } catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + // No op + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java new file mode 100644 index 0000000000..14654b171f --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.listener; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class PopulationStatisticsLoggerTest { + + @Test + public void testPopulationStatisticsLogger() { + Population population = new ListPopulation(2); + population.addChromosome( + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils + .randomIntegralRepresentation(10, 0, 10), c -> 0, new DummyListChromosomeDecoder<>("0"), + 0, 10)); + population.addChromosome( + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils + .randomIntegralRepresentation(10, 0, 10), c -> 0, new DummyListChromosomeDecoder<>("0"), + 0, 10)); + PopulationStatisticsLogger logger = new PopulationStatisticsLogger<>("UTF-8"); + logger.notify(1, population); + Assert.assertTrue(true); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java new file mode 100644 index 0000000000..05016e5b8b --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.junit.Assert; +import org.junit.Test; + +public class AbstractListChromosomeMutationPolicyTest { + + @Test + public void testGetMutableGeneIndexes() { + AbstractListChromosomeMutationPolicy chromosomeMutationPolicy = new AbstractListChromosomeMutationPolicy() { + + @Override + protected Integer mutateGene(Integer originalValue) { + return RandomGenerator.getRandomGenerator().nextInt(2); + } + + @Override + protected void checkValidity(Chromosome original) { + // No Op + } + }; + Assert.assertEquals(1, chromosomeMutationPolicy.getMutableGeneIndexes(10, .1).size()); + chromosomeMutationPolicy.getMutableGeneIndexes(10, .001); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java new file mode 100644 index 0000000000..8f66129bd0 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class BinaryMutationTest { + + @Test(expected = GeneticException.class) + public void testCheckValidity() { + BinaryMutation mutation = new BinaryMutation<>(); + mutation.checkValidity( + new RealValuedChromosome<>(ChromosomeRepresentationUtils + .randomNormalizedDoubleRepresentation(0), c -> 0, new DummyListChromosomeDecoder<>("0"))); + } + + @Test + public void testMutate() { + BinaryMutation mutation = new BinaryMutation<>(); + + // stochastic testing for single gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .1); + + // one gene should be different + int numDifferent = 0; + for (int j = 0; j < original.getRepresentation().size(); j++) { + if (original.getRepresentation().get(j) != mutated.getRepresentation().get(j)) { + numDifferent++; + } + } + Assert.assertEquals(1, numDifferent); + } + + // stochastic testing for two gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .2); + + // one gene should be different + int numDifferent = 0; + for (int j = 0; j < original.getRepresentation().size(); j++) { + if (original.getRepresentation().get(j) != mutated.getRepresentation().get(j)) { + numDifferent++; + } + } + Assert.assertEquals(2, numDifferent); + } + + // stochastic testing for three gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .3); + + // one gene should be different + int numDifferent = 0; + for (int j = 0; j < original.getRepresentation().size(); j++) { + if (original.getRepresentation().get(j) != mutated.getRepresentation().get(j)) { + numDifferent++; + } + } + Assert.assertEquals(3, numDifferent); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java new file mode 100644 index 0000000000..7b04b2ac04 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.junit.Assert; +import org.junit.Test; + +public class IntegralValuedMutationTest { + + @Test(expected = GeneticException.class) + public void testCheckValidity() { + int min = 0; + int max = 10; + Chromosome chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(10), c -> 0, + new DummyListChromosomeDecoder<>("0")); + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min - 10, max); + mutation.checkValidity(chromosome); + } + + @Test(expected = GeneticException.class) + public void testCheckValidity1() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min - 10, max); + mutation.checkValidity(chromosome); + } + + @Test(expected = GeneticException.class) + public void testIntegralValuedMutation() { + new IntegralValuedMutation<>(10, 5); + } + + @Test + public void testGetMinMax() { + int min = 0; + int max = 10; + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min, max); + Assert.assertEquals(min, mutation.getMin()); + Assert.assertEquals(max, mutation.getMax()); + } + + @Test + public void testMutateGene() { + int min = 0; + int max = 10; + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min, max); + for (int i = 0; i < 100; i++) { + int origValue = min + RandomGenerator.getRandomGenerator().nextInt(max - min); + int mutatedValued = mutation.mutateGene(origValue); + Assert.assertTrue(min <= mutatedValued && mutatedValued < max); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java new file mode 100644 index 0000000000..304137e2fe --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.junit.Assert; +import org.junit.Test; + +public class RealValuedMutationTest { + + @Test(expected = GeneticException.class) + public void testCheckValidity() { + int min = 0; + int max = 10; + Chromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + RealValuedMutation mutation = new RealValuedMutation<>(min - 10, max); + mutation.checkValidity(chromosome); + } + + @Test(expected = GeneticException.class) + public void testCheckValidity1() { + double min = 0; + double max = 10; + RealValuedChromosome chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + RealValuedMutation mutation = new RealValuedMutation<>(min - 10, max); + mutation.checkValidity(chromosome); + } + + @Test(expected = GeneticException.class) + public void testIntegralValuedMutation() { + new RealValuedMutation<>(10, 5); + } + + @Test + public void testGetMinMax() { + double min = 0; + double max = 10; + RealValuedMutation mutation = new RealValuedMutation<>(min, max); + Assert.assertEquals(min, mutation.getMin(), .001); + Assert.assertEquals(max, mutation.getMax(), .001); + } + + @Test + public void testMutateGene() { + double min = 0; + double max = 10; + RealValuedMutation mutation = new RealValuedMutation<>(min, max); + for (int i = 0; i < 100; i++) { + double origValue = min + (max - min) * RandomGenerator.getRandomGenerator().nextDouble(); + double mutatedValue = mutation.mutateGene(origValue); + Assert.assertTrue(min <= mutatedValue && mutatedValue < max); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java new file mode 100644 index 0000000000..256b10efe4 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class ListPopulationTest { + + @Test + public void testGetFittestChromosome() { + AbstractChromosome c1 = new AbstractChromosome(chromosome -> 0, chromosome -> "0") { + }; + AbstractChromosome c2 = new AbstractChromosome(chromosome -> 10, chromosome -> "10") { + }; + AbstractChromosome c3 = new AbstractChromosome(chromosome -> 15, chromosome -> "15") { + }; + + ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(c1); + chromosomes.add(c2); + chromosomes.add(c3); + + ListPopulation population = new ListPopulation(chromosomes, 10); + + Assert.assertEquals(c3, population.getFittestChromosome()); + Assert.assertNotNull(population.toString()); + } + + @Test + public void testChromosomes() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + + final ListPopulation population = new ListPopulation<>(10); + + population.addChromosomes(chromosomes); + + Assert.assertEquals(chromosomes, population.getChromosomes()); + + population.setPopulationLimit(50); + Assert.assertEquals(50, population.getPopulationLimit()); + } + + @Test(expected = GeneticException.class) + public void testSetPopulationLimit() { + final ListPopulation population = new ListPopulation<>(10); + + population.setPopulationLimit(-50); + } + + @Test(expected = GeneticException.class) + public void testConstructorPopulationLimitNotPositive() { + new ListPopulation(-10); + } + + @Test(expected = GeneticException.class) + public void testChromosomeListConstructorPopulationLimitNotPositive() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + new ListPopulation(chromosomes, -10); + } + + @Test(expected = GeneticException.class) + public void testConstructorListOfChromosomesBiggerThanPopulationSize() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + + new ListPopulation(chromosomes, 1); + } + + @Test(expected = GeneticException.class) + public void testAddTooManyChromosomes() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + + final ListPopulation population = new ListPopulation<>(2); + + population.addChromosomes(chromosomes); + } + + @Test(expected = GeneticException.class) + public void testAddTooManyChromosomesSingleCall() { + + final ListPopulation population = new ListPopulation<>(2); + + for (int i = 0; i <= population.getPopulationLimit(); i++) { + population.addChromosome( + BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void testIterator() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + + final ListPopulation population = new ListPopulation<>(10); + + population.addChromosomes(chromosomes); + + final Iterator> iter = population.iterator(); + while (iter.hasNext()) { + iter.next(); + iter.remove(); + } + } + + @Test(expected = GeneticException.class) + public void testSetPopulationLimitTooSmall() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + + final ListPopulation population = new ListPopulation<>(chromosomes, 3); + + population.setPopulationLimit(2); + } + + @Test + public void testNextGeneration() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + chromosomes.add(BinaryChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"))); + + final ListPopulation population = new ListPopulation<>(chromosomes, 3); + + Assert.assertEquals(1, population.nextGeneration(.4).getPopulationSize()); + Assert.assertEquals(0, population.nextGeneration(.1).getPopulationSize()); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java new file mode 100644 index 0000000000..094775fcfa --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.selection; + +import java.util.Iterator; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.Assert; +import org.junit.Test; + +public class TournamentSelectionTest { + + private static int counter; + + @Test + public void testSelect() { + TournamentSelection ts = new TournamentSelection<>(2); + + Assert.assertEquals(2, ts.getArity()); + + ListPopulation pop = new ListPopulation<>(100); + + for (int i = 0; i < pop.getPopulationLimit(); i++) { + pop.addChromosome(new DummyChromosome()); + } + // how to write a test for stochastic method? + for (int i = 0; i < 20; i++) { + ChromosomePair pair = ts.select(pop); + // the worst chromosome should NEVER be selected + Assert.assertTrue(pair.getFirst().evaluate() > 0); + Assert.assertTrue(pair.getSecond().evaluate() > 0); + } + } + + private static class DummyChromosome extends AbstractChromosome { + DummyChromosome() { + super(c -> counter++, c -> "0"); + } + + } + + @Test(expected = GeneticException.class) + public void testNonListPopulation() { + + Population population = new Population() { + + @Override + public Iterator> iterator() { + return null; + } + + @Override + public int getPopulationSize() { + return 0; + } + + @Override + public int getPopulationLimit() { + return 0; + } + + @Override + public Population nextGeneration(double elitismRate) { + return null; + } + + @Override + public void addChromosome(Chromosome chromosome) { + } + + @Override + public Chromosome getFittestChromosome() { + return null; + } + }; + new TournamentSelection(5).select(population); + } + + @Test(expected = GeneticException.class) + public void testInvalidArity() { + Population population = new ListPopulation<>(2); + new TournamentSelection(2).select(population); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java new file mode 100644 index 0000000000..3ae6a7d6db --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.exception.GeneticException; +import org.junit.Assert; +import org.junit.Test; + +public class ChromosomeRepresentationUtilsTest { + + @Test + public void testRandomPermutation() { + // never generate an invalid one + for (int i = 0; i < 10; i++) { + List representation = ChromosomeRepresentationUtils.randomPermutation(10); + Assert.assertNotNull(representation); + } + } + + @Test + public void testIdentityPermutation() { + List identityPermutation = ChromosomeRepresentationUtils.identityPermutation(5); + List sequence = Arrays.asList(new String[] {"a", "b", "c", "d", "e"}); + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(identityPermutation, c -> 0, + decoder); + List decoded = decoder.decode(chromosome); + + Assert.assertEquals("a", decoded.get(0)); + Assert.assertEquals("b", decoded.get(1)); + Assert.assertEquals("c", decoded.get(2)); + Assert.assertEquals("d", decoded.get(3)); + Assert.assertEquals("e", decoded.get(4)); + } + + @Test + public void testComparatorPermutation() { + List sequence = Arrays.asList(new String[] {"x", "b", "c", "z", "b"}); + + List permutation = ChromosomeRepresentationUtils.comparatorPermutation(sequence, + new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } + }); + Double[] permArr = new Double[sequence.size()]; + permArr = permutation.toArray(permArr); + + Assert.assertArrayEquals(new Double[] {0.6, 0.0, 0.4, 0.8, 0.2}, permArr); + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + List decodedData = decoder.decode(new RealValuedChromosome<>(permutation, c -> 0, decoder)); + + Assert.assertEquals("b", decodedData.get(0)); + Assert.assertEquals("b", decodedData.get(1)); + Assert.assertEquals("c", decodedData.get(2)); + Assert.assertEquals("x", decodedData.get(3)); + Assert.assertEquals("z", decodedData.get(4)); + + permutation = ChromosomeRepresentationUtils.comparatorPermutation(sequence, new Comparator() { + @Override + public int compare(String o1, String o2) { + return o2.compareTo(o1); + } + }); + permArr = new Double[sequence.size()]; + permArr = permutation.toArray(permArr); + + Assert.assertArrayEquals(new Double[] {0.2, 0.6, 0.4, 0.0, 0.8}, permArr); + + decodedData = decoder.decode(new RealValuedChromosome<>(permutation, c -> 0, decoder)); + + Assert.assertEquals("z", decodedData.get(0)); + Assert.assertEquals("x", decodedData.get(1)); + Assert.assertEquals("c", decodedData.get(2)); + Assert.assertEquals("b", decodedData.get(3)); + Assert.assertEquals("b", decodedData.get(4)); + } + + @Test + public void testInducedPermutation() { + List origData = Arrays.asList(new String[] {"a", "b", "c", "d", "d"}); + List permutedData = Arrays.asList(new String[] {"d", "b", "c", "a", "d"}); + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(origData); + RealValuedChromosome> chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.inducedPermutation(origData, permutedData), c -> 0, decoder); + List decoded = decoder.decode(chromosome); + + Assert.assertEquals("d", decoded.get(0)); + Assert.assertEquals("b", decoded.get(1)); + Assert.assertEquals("c", decoded.get(2)); + Assert.assertEquals("a", decoded.get(3)); + Assert.assertEquals("d", decoded.get(4)); + + try { + ChromosomeRepresentationUtils.inducedPermutation(Arrays.asList(new String[] {"a", "b", "c", "d", "d"}), + Arrays.asList(new String[] {"a", "b", "c", "d"})); + Assert.fail("Uncaught exception"); + } catch (GeneticException e) { + // no-op + } + try { + ChromosomeRepresentationUtils.inducedPermutation(Arrays.asList(new String[] {"a", "b", "c", "d", "d"}), + Arrays.asList(new String[] {"a", "b", "c", "d", "f"})); + Assert.fail("Uncaught exception"); + } catch (GeneticException e) { + // no-op + } + } + + @Test + public void testEqualRepr() { + RandomKeyDecoder decoder = new RandomKeyDecoder<>(Arrays.asList(new String[] {"a", "b", "c"})); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(new Double[] {0.2, 0.2, 0.5}, c -> 0, + decoder); + + List decodedData = decoder.decode(chromosome); + Assert.assertEquals("a", decodedData.get(0)); + Assert.assertEquals("b", decodedData.get(1)); + Assert.assertEquals("c", decodedData.get(2)); + } + + @Test + public void testIntegralRepresentation() { + int min = 0; + int max = 10; + List values = ChromosomeRepresentationUtils.randomIntegralRepresentation(100, min, max); + for (Integer value : values) { + Assert.assertTrue(min <= value && value < max); + } + } + + @Test + public void testNormalizedDoubleRepresentation() { + List values = ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(100); + for (Double value : values) { + Assert.assertTrue(0 <= value && value < 1); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ConsoleLoggerTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ConsoleLoggerTest.java new file mode 100644 index 0000000000..dbd9b33960 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ConsoleLoggerTest.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import org.junit.Test; + +public class ConsoleLoggerTest { + + @Test + public void testLogString() { + ConsoleLogger logger = ConsoleLogger.getInstance("UTF-8"); + logger.log("Test Message"); + logger.log("Test %s ", "message"); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ConstantsTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ConstantsTest.java new file mode 100644 index 0000000000..43aab162ac --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ConstantsTest.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import org.junit.Assert; +import org.junit.Test; + +public class ConstantsTest { + + @Test + public void test() { + Assert.assertNotNull(Constants.NEW_LINE); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/DummyListChromosomeDecoder.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/DummyListChromosomeDecoder.java new file mode 100644 index 0000000000..28601220f6 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/DummyListChromosomeDecoder.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; + +public class DummyListChromosomeDecoder extends AbstractListChromosomeDecoder { + + private String value; + + public DummyListChromosomeDecoder(String value) { + this.value = value; + } + + @Override + protected String decode(AbstractListChromosome chromosome) { + return value; + } + + @Override + protected void checkValidity(Chromosome chromosome) { + // No op + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ValidationUtilsTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ValidationUtilsTest.java new file mode 100644 index 0000000000..5f5db570af --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ValidationUtilsTest.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import org.apache.commons.math4.ga.exception.GeneticException; +import org.junit.Test; + +public class ValidationUtilsTest { + + @Test(expected = GeneticException.class) + public void testCheckForNull() { + ValidationUtils.checkForNull("Value", null); + } + +} diff --git a/pom.xml b/pom.xml index a80f25888a..e9e3eecf03 100644 --- a/pom.xml +++ b/pom.xml @@ -126,6 +126,7 @@ commons-math-examples + commons-math-ga