From 93fe6a2c6578b75d185dcea4ac3bacaf426b4326 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Sun, 8 Aug 2021 21:38:28 +0530 Subject: [PATCH 1/9] Developed the new genetic algorithm module following the JIRA MATH-1563. --- .../examples-ga-math-functions/pom.xml | 45 ++++ .../dimension2/Dim2GraphPlotter.java | 145 ++++++++++++ .../dimension2/Dimension2Coordinate.java | 64 +++++ .../dimension2/Dimension2Decoder.java | 53 +++++ .../dimension2/Dimension2FitnessFunction.java | 40 ++++ .../Dimension2FunctionAdaptiveOptimizer.java | 114 +++++++++ .../Dimension2FunctionOptimizer.java | 114 +++++++++ .../Dimension2FunctionOptimizerLegacy.java | 103 ++++++++ .../legacy/LegacyBinaryChromosome.java | 65 ++++++ .../legacy/UnchangedBestFitness.java | 67 ++++++ .../dimension2/legacy/package-info.java | 20 ++ .../dimension2/package-info.java | 20 ++ .../dimensionN/DimNGraphPlotter.java | 145 ++++++++++++ .../dimensionN/DimensionNCoordinate.java | 54 +++++ .../dimensionN/DimensionNDecoder.java | 55 +++++ .../dimensionN/DimensionNFitnessFunction.java | 42 ++++ .../DimensionNFunctionAdaptiveOptimizer.java | 114 +++++++++ .../DimensionNFunctionOptimizer.java | 113 +++++++++ .../dimensionN/package-info.java | 20 ++ .../ga/mathfunctions/utils/Constants.java | 55 +++++ .../ga/mathfunctions/utils/package-info.java | 20 ++ .../examples-ga/examples-ga-tsp/pom.xml | 52 +++++ .../examples/ga/tsp/TSPFitnessFunction.java | 52 +++++ .../math4/examples/ga/tsp/TSPOptimizer.java | 116 +++++++++ .../math4/examples/ga/tsp/commons/City.java | 77 ++++++ .../ga/tsp/commons/DistanceMatrix.java | 71 ++++++ .../examples/ga/tsp/commons/package-info.java | 20 ++ .../examples/ga/tsp/legacy/TSPChromosome.java | 76 ++++++ .../ga/tsp/legacy/TSPOptimizerLegacy.java | 99 ++++++++ .../ga/tsp/legacy/UnchangedBestFitness.java | 67 ++++++ .../examples/ga/tsp/legacy/package-info.java | 20 ++ .../math4/examples/ga/tsp/package-info.java | 20 ++ .../examples/ga/tsp/utils/Constants.java | 65 ++++++ .../examples/ga/tsp/utils/GraphPlotter.java | 146 ++++++++++++ .../examples/ga/tsp/utils/package-info.java | 20 ++ commons-math-examples/examples-ga/pom.xml | 56 +++++ commons-math-examples/pom.xml | 1 + commons-math-ga/pom.xml | 65 ++++++ .../math4/ga/AbstractGeneticAlgorithm.java | 180 ++++++++++++++ .../math4/ga/AdaptiveGeneticAlgorithm.java | 164 +++++++++++++ .../commons/math4/ga/GeneticAlgorithm.java | 171 ++++++++++++++ .../ga/chromosome/AbstractChromosome.java | 148 ++++++++++++ .../ga/chromosome/AbstractListChromosome.java | 125 ++++++++++ .../math4/ga/chromosome/BinaryChromosome.java | 77 ++++++ .../math4/ga/chromosome/Chromosome.java | 48 ++++ .../math4/ga/chromosome/ChromosomePair.java | 66 ++++++ .../chromosome/IntegralValuedChromosome.java | 135 +++++++++++ .../ga/chromosome/RealValuedChromosome.java | 159 +++++++++++++ .../math4/ga/chromosome/package-info.java | 20 ++ .../ga/convergence/FixedElapsedTime.java | 80 +++++++ .../ga/convergence/FixedGenerationCount.java | 76 ++++++ .../ga/convergence/StoppingCondition.java | 38 +++ .../ga/convergence/UnchangedBestFitness.java | 72 ++++++ .../ga/convergence/UnchangedMeanFitness.java | 85 +++++++ .../math4/ga/convergence/package-info.java | 20 ++ .../AbstractChromosomeCrossoverPolicy.java | 53 +++++ ...AbstractListChromosomeCrossoverPolicy.java | 76 ++++++ .../math4/ga/crossover/CrossoverPolicy.java | 39 ++++ .../math4/ga/crossover/CycleCrossover.java | 168 +++++++++++++ .../math4/ga/crossover/NPointCrossover.java | 154 ++++++++++++ .../math4/ga/crossover/OnePointCrossover.java | 102 ++++++++ .../math4/ga/crossover/OrderedCrossover.java | 131 +++++++++++ .../math4/ga/crossover/UniformCrossover.java | 122 ++++++++++ .../math4/ga/crossover/package-info.java | 20 ++ ...verageRankBasedCrossoverRateGenerator.java | 58 +++++ ...aximumRankBasedCrossoverRateGenerator.java | 58 +++++ .../ConstantCrossoverRateGenerator.java | 50 ++++ .../rategenerator/CrossoverRateGenerator.java | 42 ++++ .../crossover/rategenerator/package-info.java | 20 ++ .../AbstractListChromosomeDecoder.java | 59 +++++ .../commons/math4/ga/decoder/Decoder.java | 35 +++ .../math4/ga/decoder/RandomKeyDecoder.java | 75 ++++++ .../TransparentListChromosomeDecoder.java | 39 ++++ .../math4/ga/decoder/package-info.java | 20 ++ .../math4/ga/fitness/FitnessFunction.java | 34 +++ .../math4/ga/fitness/package-info.java | 20 ++ .../internal/exception/GeneticException.java | 114 +++++++++ .../ga/internal/exception/package-info.java | 20 ++ .../PopulationStatisticalSummaryImpl.java | 177 ++++++++++++++ .../math4/ga/internal/stats/package-info.java | 20 ++ .../ga/internal/utils/ValidationUtils.java | 38 +++ .../math4/ga/internal/utils/package-info.java | 20 ++ .../ga/listener/ConvergenceListener.java | 37 +++ .../listener/ConvergenceListenerRegistry.java | 91 ++++++++ .../listener/PopulationStatisticsLogger.java | 49 ++++ .../math4/ga/listener/package-info.java | 20 ++ .../AbstractListChromosomeMutationPolicy.java | 99 ++++++++ .../math4/ga/mutation/BinaryMutation.java | 53 +++++ .../ga/mutation/IntegralValuedMutation.java | 87 +++++++ .../math4/ga/mutation/MutationPolicy.java | 35 +++ .../math4/ga/mutation/RealValuedMutation.java | 96 ++++++++ .../math4/ga/mutation/package-info.java | 20 ++ .../AdaptiveLinearMutationRateGenerator.java | 54 +++++ .../ConstantMutationRateGenerator.java | 47 ++++ .../rategenerator/MutationRateGenerator.java | 38 +++ .../mutation/rategenerator/package-info.java | 20 ++ .../apache/commons/math4/ga/package-info.java | 20 ++ .../math4/ga/population/ListPopulation.java | 220 ++++++++++++++++++ .../math4/ga/population/Population.java | 59 +++++ .../math4/ga/population/package-info.java | 20 ++ .../math4/ga/selection/SelectionPolicy.java | 35 +++ .../ga/selection/TournamentSelection.java | 108 +++++++++ .../math4/ga/selection/package-info.java | 20 ++ .../stats/PopulationStatisticalSummary.java | 67 ++++++ .../commons/math4/ga/stats/package-info.java | 20 ++ .../utils/ChromosomeRepresentationUtils.java | 183 +++++++++++++++ .../math4/ga/utils/RandomGenerator.java | 44 ++++ .../commons/math4/ga/utils/package-info.java | 20 ++ .../math4/ga/GeneticAlgorithmTestBinary.java | 147 ++++++++++++ .../ga/GeneticAlgorithmTestPermutations.java | 144 ++++++++++++ .../ga/chromosome/AbstractChromosomeTest.java | 65 ++++++ .../ga/chromosome/BinaryChromosomeTest.java | 43 ++++ .../ga/chromosome/ChromosomePairTest.java | 38 +++ .../IntegralValuedChromosomeTest.java | 87 +++++++ .../chromosome/RealValuedChromosomeTest.java | 71 ++++++ .../convergencecond/FixedElapsedTimeTest.java | 57 +++++ .../FixedGenerationCountTest.java | 45 ++++ .../UnchangedBestFitnessTest.java | 67 ++++++ .../UnchangedMeanFitnessTest.java | 67 ++++++ ...AbstractChromosomeCrossoverPolicyTest.java | 45 ++++ ...ractListChromosomeCrossoverPolicyTest.java | 68 ++++++ .../ga/crossover/CycleCrossoverTest.java | 115 +++++++++ .../ga/crossover/NPointCrossoverTest.java | 117 ++++++++++ .../ga/crossover/OnePointCrossoverTest.java | 66 ++++++ .../ga/crossover/OrderedCrossoverTest.java | 63 +++++ .../ga/crossover/UniformCrossoverTest.java | 109 +++++++++ ...umRankBasedCrossoverRateGeneratorTest.java | 68 ++++++ .../AbstractListChromosomeDecoderTest.java | 40 ++++ .../ga/decoder/RandomKeyDecoderTest.java | 56 +++++ .../TransparentListChromosomeDecoderTest.java | 39 ++++ .../math4/ga/dummy/DummyChromosome.java | 27 +++ .../math4/ga/dummy/DummyListChromosome.java | 48 ++++ .../ga/exception/GeneticExceptionTest.java | 34 +++ .../ConvergenceListenerRegistryTest.java | 105 +++++++++ .../PopulationStatisticsLoggerTest.java | 45 ++++ ...tractListChromosomeMutationPolicyTest.java | 44 ++++ .../math4/ga/mutation/BinaryMutationTest.java | 90 +++++++ .../mutation/IntegralValuedMutationTest.java | 79 +++++++ .../ga/mutation/RealValuedMutationTest.java | 79 +++++++ ...aptiveLinearMutationRateGeneratorTest.java | 61 +++++ .../ConstantMutationRateGeneratorTest.java | 45 ++++ .../ga/population/ListPopulationTest.java | 182 +++++++++++++++ .../ga/selection/TournamentSelectionTest.java | 104 +++++++++ .../ChromosomeRepresentationUtilsTest.java | 163 +++++++++++++ .../ga/utils/DummyListChromosomeDecoder.java | 41 ++++ .../math4/ga/utils/ValidationUtilsTest.java | 35 +++ pom.xml | 1 + .../spotbugs/spotbugs-exclude-filter.xml | 6 + 148 files changed, 10287 insertions(+) create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/pom.xml create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java create mode 100644 commons-math-examples/examples-ga/pom.xml create mode 100644 commons-math-ga/pom.xml create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinary.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/DummyListChromosomeDecoder.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ValidationUtilsTest.java 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..a387377b80 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml @@ -0,0 +1,45 @@ + + + + 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/dimension2/Dim2GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java new file mode 100644 index 0000000000..eca37e8798 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import java.awt.BorderLayout; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class Dim2GraphPlotter extends JFrame implements ConvergenceListener { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public Dim2GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List 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/dimension2/Dimension2Coordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java new file mode 100644 index 0000000000..307f1efff2 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +/** + * This class represents the coordinate of the problem domain i.e. the phenotype of chromosome. + */ +public class Dimension2Coordinate { + + /** coordinate of first dimension. **/ + private final double x; + + /** coordinate of second dimension. **/ + private final double y; + + /** + * constructor. + * @param x coordinate of first dimension + * @param y coordinate of second dimension + */ + public Dimension2Coordinate(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * returns the coordinate of first dimension. + * @return coordinate of first dimension + */ + public double getX() { + return x; + } + + /** + * returns the coordinate of second dimension. + * @return coordinate of second dimension + */ + public double getY() { + return y; + } + + /** + * Returns a string representation of coordinate. + */ + @Override + public String toString() { + return "Coordinate [x=" + x + ", y=" + y + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java new file mode 100644 index 0000000000..a8ef79e17b --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java @@ -0,0 +1,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.examples.ga.mathfunctions.dimension2; + +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 Dimension2Coordinate}. + */ +public class Dimension2Decoder extends AbstractListChromosomeDecoder { + + /** + * decode the binary representation of chromosome to + * {@link Dimension2Coordinate}. + * @param chromosome The {@link AbstractListChromosome} + */ + @Override + protected Dimension2Coordinate 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 Dimension2Coordinate(x, y); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java new file mode 100644 index 0000000000..33e8f73018 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the mathematical fitness function for optimizing a 2 + * dimension mathematical function. + */ +public class Dimension2FitnessFunction implements FitnessFunction { + + /** + * Computes the fitness value based on the decoded chromosome. + * @param coordinate The {@link Dimension2Coordinate} + * @return the fitness value + */ + @Override + public double compute(Dimension2Coordinate coordinate) { + return -Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .25) * + (Math.pow(Math.sin(50 * Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .1)), + 2) + 1); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java new file mode 100644 index 0000000000..6f5de86d29 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.crossover.rategenerator.ConstantCrossoverRateGenerator; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class Dimension2FunctionAdaptiveOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final Dimension2FunctionAdaptiveOptimizer optimizer = new Dimension2FunctionAdaptiveOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new Dim2GraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + private void optimize(Population initial) { + + // initialize a new genetic algorithm + final AdaptiveGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm<>( + new OnePointCrossover(), new ConstantCrossoverRateGenerator<>(1), + new BinaryMutation(), + new AdaptiveLinearMutationRateGenerator<>(Constants.AVERAGE_MUTATION_RATE / 2, + Constants.AVERAGE_MUTATION_RATE * 2), + new TournamentSelection<>(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 Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction(); + final Dimension2Decoder decoder = new Dimension2Decoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java new file mode 100644 index 0000000000..c96751afd4 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class Dimension2FunctionOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final Dimension2FunctionOptimizer optimizer = new Dimension2FunctionOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new Dim2GraphPlotter("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(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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction(); + final Dimension2Decoder decoder = new Dimension2Decoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java new file mode 100644 index 0000000000..b523d41f74 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import org.apache.commons.math3.genetics.BinaryChromosome; +import org.apache.commons.math3.genetics.BinaryMutation; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * the legacy genetic algorithm. + */ +public class Dimension2FunctionOptimizerLegacy { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** + * Optimizes the 2-dimensional fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + final Dimension2FunctionOptimizerLegacy simulation = new Dimension2FunctionOptimizerLegacy(); + + simulation.optimize(initPopulation); + } + + /** + * Optimizes the initial population using legacy genetic algorithm. + * @param initial initial {@link Population} + */ + public void optimize(Population initial) { + + // initialize a new genetic algorithm + final GeneticAlgorithm geneticAlgorithm = new GeneticAlgorithm(new OnePointCrossover<>(), + Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = geneticAlgorithm.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) { + writer.write("*********************************************"); + writer.newLine(); + writer.write("***********Optimization Result***************"); + writer.write(bestFinal.toString()); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + /** + * Generates the initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ElitisticListPopulation(Constants.POPULATION_SIZE_PER_DIMENSION, + Constants.ELITISM_RATE); + for (int i = 0; i < Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(new LegacyBinaryChromosome(BinaryChromosome + .randomBinaryRepresentation(DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION))); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java new file mode 100644 index 0000000000..66c4615abd --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import java.util.List; + +import org.apache.commons.math3.genetics.AbstractListChromosome; +import org.apache.commons.math3.genetics.BinaryChromosome; + +/** + * A representation of concrete binary chromosome. + */ +public class LegacyBinaryChromosome extends BinaryChromosome { + + /** + * constructor. + * @param representation the internal representation + */ + public LegacyBinaryChromosome(List 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/dimension2/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java new file mode 100644 index 0000000000..8800f3978b --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; + +/** + * This class represents the stopping condition based on unchanged best fitness. + */ +public class UnchangedBestFitness implements StoppingCondition { + + /** last best fitness. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** maximum number of generations evolved with unchanged best fitness. **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** generations having unchanged best fitness. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations evolved with + * unchanged best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population population) { + final double currentBestFitness = population.getFittestChromosome().getFitness(); + + if (lastBestFitness == currentBestFitness) { + this.generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java new file mode 100644 index 0000000000..7965791c5c --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java new file mode 100644 index 0000000000..432651f230 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java new file mode 100644 index 0000000000..52571e3825 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.awt.BorderLayout; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class DimNGraphPlotter extends JFrame implements ConvergenceListener { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public DimNGraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List 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/dimensionN/DimensionNCoordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java new file mode 100644 index 0000000000..98a31bcffc --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.util.List; + +/** + * This class represents the coordinate of the problem domain i.e. the phenotype + * of chromosome. + */ +public class DimensionNCoordinate { + + /** coordinate of all dimensions. **/ + private final List values; + + /** + * constructor. + * @param values coordinates of all dimensions. + */ + public DimensionNCoordinate(List values) { + this.values = values; + } + + /** + * Returns the values of all coordinates. + * @return values of coordinates + */ + public List getValues() { + return values; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Coordinate [values=" + values + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java new file mode 100644 index 0000000000..7146be6f54 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java @@ -0,0 +1,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.dimensionN; + +import java.util.ArrayList; +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 DimensionNCoordinate}. + */ +public class DimensionNDecoder extends AbstractListChromosomeDecoder { + + /** + * decode the binary representation of chromosome to {@link DimensionNCoordinate}. + * @param chromosome The {@link AbstractListChromosome} + */ + @Override + protected DimensionNCoordinate 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)); + } + + List values = new ArrayList<>(); + for (int i = 0; i < allelesStr.length(); i += 12) { + values.add(Integer.parseInt(allelesStr.substring(i, i + 12), 2) / 100.0); + } + + return new DimensionNCoordinate(values); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java new file mode 100644 index 0000000000..1c0d7db94f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the mathematical fitness function for optimizing a 2 + * dimension mathematical function. + */ +public class DimensionNFitnessFunction implements FitnessFunction { + + /** + * Computes the fitness value based on the decoded chromosome. + * @param coordinate The {@link DimensionNCoordinate} + * @return the fitness value + */ + @Override + public double compute(DimensionNCoordinate coordinate) { + double sumOfSquare = 0.0; + for (Double value : coordinate.getValues()) { + sumOfSquare += Math.pow(value, 2); + } + return -Math.pow(sumOfSquare, .25) * (Math.pow(Math.sin(50 * Math.pow(sumOfSquare, .1)), 2) + 1); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java new file mode 100644 index 0000000000..a7e88ff5ed --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.AbstractGeneticAlgorithm; +import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.crossover.rategenerator.AdaptiveLinearMaximumRankBasedCrossoverRateGenerator; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class DimensionNFunctionAdaptiveOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 10; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 4; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final DimensionNFunctionAdaptiveOptimizer optimizer = new DimensionNFunctionAdaptiveOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new DimNGraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + private void optimize(Population initial) { + + // initialize a new genetic algorithm + final AbstractGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm( + new OnePointCrossover(), + new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>(Constants.CROSSOVER_RATE / 2, + Constants.CROSSOVER_RATE), + new BinaryMutation<>(), new AdaptiveLinearMutationRateGenerator<>(0, Constants.AVERAGE_MUTATION_RATE), + new TournamentSelection<>(TOURNAMENT_SIZE), Constants.ELITISM_RATE); + + // stopping condition + final StoppingCondition 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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction(); + final DimensionNDecoder decoder = new DimensionNDecoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java new file mode 100644 index 0000000000..e039e6e436 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class DimensionNFunctionOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 10; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 4; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final DimensionNFunctionOptimizer optimizer = new DimensionNFunctionOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new DimNGraphPlotter("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<>(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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction(); + final DimensionNDecoder decoder = new DimensionNDecoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java new file mode 100644 index 0000000000..6a15fed226 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java new file mode 100644 index 0000000000..1389c75418 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.utils; + +/** + * This abstraction maintains constants used by this module. + */ +public final class Constants { + + /** size of population. **/ + public static final int POPULATION_SIZE_PER_DIMENSION = 10; + + /** size of tournament. **/ + public static final int TOURNAMENT_SIZE_PER_DIMENSION = 2; + + /** length of chromosome. **/ + public static final int CHROMOSOME_LENGTH_PER_DIMENSION = 12; + + /** rate of crossover. **/ + public static final double CROSSOVER_RATE = 1.0; + + /** rate of elitism. **/ + public static final double ELITISM_RATE = 0.25; + + /** rate of mutation. **/ + public static final double AVERAGE_MUTATION_RATE = 0.05; + + /** number of generations with unchanged best fitness. **/ + public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50; + + /** encoding for console logger. **/ + public static final String ENCODING = "UTF-8"; + + /** + * constructor. + */ + private Constants() { + + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java new file mode 100644 index 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..db88cd0756 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml @@ -0,0 +1,52 @@ + + + + 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..21833ef3de --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp; + +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.examples.ga.tsp.utils.GraphPlotter; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents the optimizer for traveling salesman problem. + */ +public class TSPOptimizer { + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(TSPOptimizer.class); + + /** + * Main method to initiate the optimization process. + * @param args arguments + */ + public static void main(String[] args) { + try { + final Population> initPopulation = getInitialPopulation(Constants.CITIES); + + final TSPOptimizer optimizer = new TSPOptimizer(); + + final ConvergenceListenerRegistry> convergenceListenerRegistry = ConvergenceListenerRegistry + .getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<>()); + convergenceListenerRegistry + .addConvergenceListener(new GraphPlotter("Convergence", "generation", "total-distance")); + + optimizer.optimizeSGA(initPopulation, Constants.CITIES); + + Thread.sleep(5000); + + } catch (InterruptedException e) { + throw new GeneticException(e); + } + } + + /** + * Optimizes the tsp problem. + * @param initial initial population + * @param cities cities + */ + public void optimizeSGA(Population> 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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.decode().toString()); + + } + + 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..81ae597601 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import java.io.BufferedWriter; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; + +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.RandomKey; +import org.apache.commons.math3.genetics.RandomKeyMutation; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * This class represents the tsp optimizer based on legacy implementation of + * Genetic Algorithm. + */ +public class TSPOptimizerLegacy { + + /** + * MainThread.sleep(5000) method to initiate optimization. + * @param args arguments + */ + public static void main(String[] args) { + + final Population initPopulation = getInitialPopulation(Constants.CITIES); + final TSPOptimizerLegacy optimizer = new TSPOptimizerLegacy(); + + optimizer.optimize(initPopulation, Constants.CITIES); + + } + + /** + * Optimizes the tsp problem using legacy GA. + * @param initial initial population + * @param cities cities + */ + public void optimize(Population initial, List 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..701380bcea --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.utils; + +import java.awt.BorderLayout; + +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class GraphPlotter extends JFrame implements ConvergenceListener> { + + /** + * 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..84324837ad --- /dev/null +++ b/commons-math-examples/examples-ga/pom.xml @@ -0,0 +1,56 @@ + + + + 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/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..6afdd5f572 --- /dev/null +++ b/commons-math-ga/pom.xml @@ -0,0 +1,65 @@ + + + + 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}/.. + 1.7.32 + + + + + org.apache.commons + commons-numbers-core + + + org.apache.commons + commons-rng-simple + + + org.slf4j + slf4j-api + ${slf4jVersion} + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + + + \ 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..7f9359f8ec --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an abstraction for all Genetic algorithm implementation + * comprising the basic properties and operations. + * @param

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

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** the crossover policy used by the algorithm. */ + private final CrossoverPolicy

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

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

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

crossoverPolicy, + final MutationPolicy

mutationPolicy, + final SelectionPolicy

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

crossoverPolicy, + final MutationPolicy

mutationPolicy, + final SelectionPolicy

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

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

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

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

evolve(final Population

initial, final StoppingCondition

condition) { + Population

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

getInstance().notifyAll(generationsEvolved, current); + + current = nextGeneration(current); + this.generationsEvolved++; + } + LOGGER.info("Population convergence achieved after generations: " + generationsEvolved); + + return current; + } + + /** + * Evolve the given population into the next generation. + *

    + *
  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/AdaptiveGeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java new file mode 100644 index 0000000000..07e891316f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.crossover.rategenerator.CrossoverRateGenerator; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.mutation.rategenerator.MutationRateGenerator; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An implementation of Genetic Algorithm. The probability of crossover and + * mutation is generated in an adaptive way. This implementation allows + * configuration of dynamic crossover and mutation rate generator along with + * crossover policy, mutation policy, selection policy and optionally elitism + * rate. + * @param

phenotype of chromosome + */ +public class AdaptiveGeneticAlgorithm

extends AbstractGeneticAlgorithm

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** The crossover rate generator. **/ + private final CrossoverRateGenerator

crossoverRateGenerator; + + /** The mutation rate generator. **/ + private final MutationRateGenerator

mutationRateGenerator; + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy

crossoverPolicy, + CrossoverRateGenerator

crossoverProbabilityGenerator, + MutationPolicy

mutationPolicy, + MutationRateGenerator

mutationProbabilityGenerator, + SelectionPolicy

selectionPolicy) { + super(crossoverPolicy, mutationPolicy, selectionPolicy); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + * @param elitismRate elitism rate + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy

crossoverPolicy, + CrossoverRateGenerator

crossoverProbabilityGenerator, + MutationPolicy

mutationPolicy, + MutationRateGenerator

mutationProbabilityGenerator, + SelectionPolicy

selectionPolicy, + double elitismRate) { + super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * {@inheritDoc} + */ + @Override + protected Population

nextGeneration(Population

current) { + + LOGGER.debug("Reproducing next generation."); + + // compute statistics of current generation chromosomes. + PopulationStatisticalSummary

populationStats = new PopulationStatisticalSummaryImpl<>(current); + + // Initialize the next generation with elit chromosomes from previous + // generation. + final Population

nextGeneration = current.nextGeneration(getElitismRate()); + + LOGGER.debug( + "No of Elite chromosomes selected from previous generation: " + nextGeneration.getPopulationSize()); + + final int maxOffspringCount = nextGeneration.getPopulationLimit() - nextGeneration.getPopulationSize(); + + // Initialize an empty population for offsprings. + final Population

offspringPopulation = current.nextGeneration(0); + + // perform crossover and generate new offsprings + while (offspringPopulation.getPopulationSize() < maxOffspringCount) { + + // select parent chromosomes + ChromosomePair

pair = getSelectionPolicy().select(current); + LOGGER.debug("Selected Chromosomes: \r\n" + pair.toString()); + + final double crossoverRate = crossoverRateGenerator.generate(pair.getFirst(), pair.getSecond(), + populationStats, getGenerationsEvolved()); + // apply crossover policy to create two offspring + pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond(), crossoverRate); + LOGGER.debug("Offsprings after Crossover: \r\n" + pair.toString()); + + // add the first chromosome to the population + offspringPopulation.addChromosome(pair.getFirst()); + // is there still a place for the second chromosome? + if (offspringPopulation.getPopulationSize() < maxOffspringCount) { + // add the second chromosome to the population + offspringPopulation.addChromosome(pair.getSecond()); + } + } + LOGGER.debug("Performing adaptive mutation of offsprings."); + + // recompute the statistics of the offspring population. + populationStats = new PopulationStatisticalSummaryImpl<>(offspringPopulation); + + // apply mutation policy to the offspring chromosomes and add the mutated + // chromosomes to next generation. + for (Chromosome

chromosome : offspringPopulation) { + nextGeneration.addChromosome(getMutationPolicy().mutate(chromosome, + mutationRateGenerator.generate(chromosome, populationStats, getGenerationsEvolved()))); + } + LOGGER.debug("New Generation: \r\n" + nextGeneration.toString()); + + return nextGeneration; + } + + /** + * Returns crossover probability generator. + * @return crossover probability generator + */ + public CrossoverRateGenerator

getCrossoverProbabilityGenerator() { + return crossoverRateGenerator; + } + + /** + * Returns mutation probability generator. + * @return mutation probability generator + */ + public MutationRateGenerator

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

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

extends AbstractGeneticAlgorithm

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** crossover rate string. **/ + private static final String CROSSOVER_RATE = "CROSSOVER_RATE"; + + /** mutation rate string. **/ + private static final String MUTATION_RATE = "MUTATION_RATE"; + + /** the rate of crossover for the algorithm. */ + private final double crossoverRate; + + /** the rate of mutation for the algorithm. */ + private final double mutationRate; + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + */ + public GeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, + final double crossoverRate, + final MutationPolicy

mutationPolicy, + final double mutationRate, + final SelectionPolicy

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

crossoverPolicy, + final double crossoverRate, + final MutationPolicy

mutationPolicy, + final double mutationRate, + final SelectionPolicy

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

    + *
  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) { + + LOGGER.debug("Reproducing next generation."); + final Population

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

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

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

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

implements Chromosome

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

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

decoder; + + /** Id of chromosome. **/ + private final String id; + + /** + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + protected AbstractChromosome(final FitnessFunction

fitnessFunction, final Decoder

decoder) { + this.fitnessFunction = Objects.requireNonNull(fitnessFunction); + this.decoder = Objects.requireNonNull(decoder); + this.id = UUID.randomUUID().toString(); + } + + /** + * returns fitness function. + * @return fitnessFunction + */ + protected FitnessFunction

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

getDecoder() { + return decoder; + } + + /** + * Returns id of chromosome. + * @return id + */ + @Override + public String getId() { + return id; + } + + /** + * Access the fitness of this chromosome. The bigger the fitness, the better the + * chromosome. + *

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

    + *
  • -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..ac85e553a2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents an abstract chromosome containing an immutable list of + * allele/genes. + * @param 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; + + /** + * @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); + } + + /** + * @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); + } + + /** + * @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); + Objects.requireNonNull(representation); + this.representation = Collections.unmodifiableList(copyList ? new ArrayList<>(representation) : representation); + } + + /** + * Returns the (immutable) inner representation of the chromosome. + * @return the representation of the chromosome + */ + public List 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..84975c7a39 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.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.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

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

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

extends Comparable> { + + /** + * 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(); + + /** + * Returns unique Id of chromosome. + * @return id + */ + String getId(); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java new file mode 100644 index 0000000000..1157a00822 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +/** + * A pair of {@link Chromosome} objects. + * @param

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

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

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

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

c1, final Chromosome

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

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

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

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

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

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

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

+ * @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; + + /** + * @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(); + } + + /** + * @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(); + } + + /** + * @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); + } + + /** + * @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/convergence/FixedElapsedTime.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java new file mode 100644 index 0000000000..8fda02bd0c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergence; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.Population; + +/** + * Stops after a fixed amount of time has elapsed. + *

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

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

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

implements StoppingCondition

{ + /** Number of generations that have passed. */ + private int numGenerations; + + /** Maximum number of generations (stopping criteria). */ + private final int maxGenerations; + + /** + * Create a new FixedGenerationCount instance. + * + * @param maxGenerations number of generations to evolve + */ + public FixedGenerationCount(final int maxGenerations) { + if (maxGenerations <= 0) { + throw new GeneticException(GeneticException.TOO_SMALL, maxGenerations, 1); + } + this.maxGenerations = maxGenerations; + } + + /** + * Determine whether or not the given number of generations have passed. + * Increments the number of generations counter if the maximum has not been + * reached. + * + * @return 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/convergence/StoppingCondition.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java new file mode 100644 index 0000000000..601c098711 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergence; + +import org.apache.commons.math4.ga.population.Population; + +/** + * Algorithm used to determine when to stop evolution. + * + * @param

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

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

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

implements StoppingCondition

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

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

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

implements StoppingCondition

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

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

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

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

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

implements CrossoverPolicy

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

crossover(final Chromosome

first, + final Chromosome

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

crossover(Chromosome

first, Chromosome

second); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java new file mode 100644 index 0000000000..2184310c6d --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * An abstraction of crossover policy for list chromosomes. + * @param 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..1a567a07ad --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.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..8ad9d9aa35 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.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 { + + /** crossover rate. **/ + public static final String CROSSOVER_RATE = "CROSSOVER_RATE"; + + /** The mixing ratio. */ + private final double ratio; + + /** + * Creates a new {@link UniformCrossover} policy using the given mixing ratio. + * + * @param ratio the mixing ratio + */ + public UniformCrossover(final double ratio) { + if (ratio < 0.0d || ratio > 1.0d) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, ratio, CROSSOVER_RATE, 0.0d, 1.0d); + } + this.ratio = ratio; + } + + /** + * Returns the mixing ratio used by this {@link CrossoverPolicy}. + * + * @return the mixing ratio + */ + public double getRatio() { + return ratio; + } + + /** + * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the + * actual crossover. + * + * @param first the first chromosome + * @param second the second chromosome + * @return the pair of new chromosomes that resulted from the crossover + */ + @Override + protected ChromosomePair

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/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java new file mode 100644 index 0000000000..1b1057de0f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates crossover rate based on linear function of relative average rank of + * input chromosomes in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearAverageRankBasedCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum crossover rate + * @param maximumRate maximum crossover rate + */ + public AdaptiveLinearAverageRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) { + this.maximumRate = maximumRate; + this.minimumRate = minimumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation) { + final int averageRank = (populationStats.findRank(first) + populationStats.findRank(second)) / 2; + return minimumRate + + (maximumRate - minimumRate) * (1.0 - (double) averageRank / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java new file mode 100644 index 0000000000..c03f488f5f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates crossover rate based on linear function of relative maximum rank of + * input chromosomes in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearMaximumRankBasedCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum crossover rate + * @param maximumRate maximum crossover rate + */ + public AdaptiveLinearMaximumRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) { + this.maximumRate = maximumRate; + this.minimumRate = minimumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation) { + final int maximumRank = Math.max(populationStats.findRank(first), populationStats.findRank(second)); + return minimumRate + + (maximumRate - minimumRate) * (1.0 - (double) maximumRank / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java new file mode 100644 index 0000000000..2f77ce12f0 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents a constant crossover rate generator. + * @param

phenotype of chromosome + */ +public class ConstantCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** the fixed value of crossover rate. **/ + private final double crossoverRate; + + /** + * @param crossoverRate crossover rate + */ + public ConstantCrossoverRateGenerator(double crossoverRate) { + this.crossoverRate = crossoverRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

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

phenotype of chromosome + */ +public interface CrossoverRateGenerator

{ + + /** + * Generates crossover rate. + * @param first first chromosome + * @param second second chromosome + * @param populationStats population statistics summary + * @param generation generation number + * @return crossover rate rate of crossover + */ + double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java new file mode 100644 index 0000000000..886eab7704 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.crossover.rategenerator; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java new file mode 100644 index 0000000000..96a1920533 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * An abstract Decoder of ListChromosome. + * @param 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..50f2ba935d --- /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..037ccfde03 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * A concrete implementation of RandomKey decoder. This class is responsible for + * decoding permutation chromosome encoded with random key. + * @param type of the permutation element + * @since 4.0 + */ +public final class RandomKeyDecoder extends AbstractListChromosomeDecoder> { + + /** base sequence for decoding chromosome. **/ + private final List baseSequence; + + /** + * @param baseSequence the unpermuted sequence + */ + public RandomKeyDecoder(List baseSequence) { + this.baseSequence = Collections.unmodifiableList(Objects.requireNonNull(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/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/internal/exception/GeneticException.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java new file mode 100644 index 0000000000..bb0666460e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/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.internal.exception; + +import java.text.MessageFormat; + +/** + * This class represents the Exception encountered during GA optimization. + * @since 4.0 + */ +public class GeneticException extends RuntimeException { + + /** Error message for "out of range" condition. */ + public static final String OUT_OF_RANGE = "Value {0} of {1} is out of range [{2}, {3}]"; + + /** Error message for "not strictly positive" condition. */ + public static final String NOT_STRICTLY_POSITIVE = "Number {0} is not strictly positive"; + + /** Error message for "too large" condition. */ + public static final String TOO_LARGE = "Number {0} is larger than {1}"; + + /** Error message for "too small" condition. */ + public static final String TOO_SMALL = "Number {0} is smaller than {1}"; + + /** Error message for "out of range" condition. */ + public static final String NO_DATA = "No data"; + + /** Error message for "size mismatch" condition. */ + public static final String SIZE_MISMATCH = "Size mismatch: {0} != {1}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_ARGUMENT = "Illegal Argument Exception: {0}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_RANGE = "Illegal Range of Value Exception: " + + "[Expected min-{0}, max-{1}], [Passed min-{2}, max-{3}]"; + + /** Error message for "generic illegal argument" condition. */ + public static final String INVALID_FIXED_LENGTH_CHROMOSOME = "Invalid Fixed Length Chromosome."; + + /** Error message for "NULL ARGUMENT" condition. */ + public static final String NULL_ARGUMENT = "Null Argument Exception: {0}"; + + /** + * Error message for "List of Chromosome bigger than population size" condition. + */ + public static final String LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE = "List of chromosome bigger than " + + "population size: {0} > {1}"; + + /** + * Error message for "population limit not positive" condition. + */ + public static final String POPULATION_LIMIT_NOT_POSITIVE = "Population limit not positive :{0}"; + + /** + * Error message for " population limit less than list of chromosomes size" + * condition. + */ + public static final String POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE = "Population limit is " + + " lesser than list of chromosomes size : {0} < {1}"; + + /** + * Error message for different origin and permuted data. + */ + public static final String DIFFERENT_ORIG_AND_PERMUTED_DATA = "Different original and permuted data"; + + /** Serializable version identifier. */ + private static final long serialVersionUID = 20210516L; + + /** + * Create an exception where the message is constructed by applying the + * {@code format()} method from {@code java.text.MessageFormat}. + * + * @param message Message format (with replaceable parameters). + * @param formatArguments Actual arguments to be displayed in the message. + */ + public GeneticException(String message, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments)); + } + + /** + * Create an exception. + * @param t instance of {@link Throwable} + */ + public GeneticException(Throwable t) { + super(t); + } + + /** + * Create an exception having both stacktrace and message. + * @param message the exception message + * @param t the instance of {@link Throwable} + * @param formatArguments arguments to format the exception message + */ + public GeneticException(String message, Throwable t, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments), t); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java new file mode 100644 index 0000000000..b55e918a60 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.internal.exception; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java new file mode 100644 index 0000000000..b95b60f1a6 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.internal.stats; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents an implementation of population statistical summary. + * @param

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

implements PopulationStatisticalSummary

{ + + /** maximum fitness of the population. **/ + private final double maxFitness; + + /** minimum fitness of the population. **/ + private final double minFitness; + + /** mean fitness of the population. **/ + private double meanFitness; + + /** variance of population fitness. **/ + private final double variance; + + /** population size. **/ + private final int populationSize; + + /** a map of chromosome Id and corresponding rank in population. **/ + private final Map chromosomeIdRankMap = new HashMap<>(); + + /** + * @param population current population {@link Population} of chromosomes + */ + public PopulationStatisticalSummaryImpl(Population

population) { + + // Fetch all chromosomes. + List> chromosomes = getChromosomes(Objects.requireNonNull(population)); + + // Sort all chromosomes. + Collections.sort(chromosomes); + + this.populationSize = chromosomes.size(); + this.maxFitness = chromosomes.get(chromosomes.size() - 1).evaluate(); + this.minFitness = chromosomes.get(0).evaluate(); + this.meanFitness = calculateMeanFitness(chromosomes); + this.variance = calculateVariance(chromosomes); + + updateChromosomeIdRankMap(chromosomes); + + } + + /** + * Updates chromosome Id and rank. + * @param chromosomes list of chromosomes + */ + private void updateChromosomeIdRankMap(List> chromosomes) { + for (int rank = 0; rank < chromosomes.size(); rank++) { + this.chromosomeIdRankMap.put(chromosomes.get(rank).getId(), rank); + } + } + + /** + * Fetches chromosomes. + * @param population + * @return list of chromosomes + */ + private List> getChromosomes(Population

population) { + List> chromosomes = new ArrayList<>(population.getPopulationSize()); + for (Chromosome

chromosome : population) { + chromosomes.add(chromosome); + } + return chromosomes; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMeanFitness() { + return this.meanFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getFitnessVariance() { + return this.variance; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMaxFitness() { + return this.maxFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMinFitness() { + return this.minFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public long getPopulationSize() { + return this.populationSize; + } + + /** + * Calculate mean fitness. + * @param chromosomes list of chromosomes + * @return returns mean fitness + */ + private double calculateMeanFitness(List> chromosomes) { + double sum = 0.0; + for (Chromosome

chromosome : chromosomes) { + sum += chromosome.evaluate(); + } + return sum / chromosomes.size(); + } + + /** + * Calculate variance of population fitness. + * @param chromosomes List of chromosomes + * @return fitness variance + */ + private double calculateVariance(List> chromosomes) { + if (this.meanFitness == 0) { + this.meanFitness = calculateMeanFitness(chromosomes); + } + double sumOfSquare = 0.0; + for (Chromosome

chromosome : chromosomes) { + sumOfSquare += Math.pow(chromosome.evaluate(), 2); + } + + return (sumOfSquare / chromosomes.size()) - Math.pow(this.meanFitness, 2); + } + + /** + * {@inheritDoc} + */ + @Override + public int findRank(Chromosome

chromosome) { + return chromosomeIdRankMap.get(chromosome.getId()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java new file mode 100644 index 0000000000..a0ae55d50e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.internal.stats; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.java new file mode 100644 index 0000000000..af92b3170a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.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.internal.utils; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * This class contains common validation methods. + * @since 4.0 + */ +public interface ValidationUtils { + + /** + * 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/internal/utils/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/package-info.java new file mode 100644 index 0000000000..c0e5560400 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/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.internal.utils; 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..7fce28e510 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.population.Population; + +/** + * This class is the default implementation of ConvergenceListenerRegistry. It + * will be responsible for registering the interested listeners and notifying + * all when required. + * @param

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<>(); + + /** + * private constructor to construct the singleton instance. + */ + private ConvergenceListenerRegistry() { + } + + /** + * Registers the interested ConvergenceListener passed as an argument. + * @param convergenceListener The {@link ConvergenceListener} + */ + public void addConvergenceListener(ConvergenceListener

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

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) { + for (ConvergenceListener

convergenceListener : Objects.requireNonNull(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..b0f7276f8a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Logs population statistics during the convergence process. + * @param

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

implements ConvergenceListener

{ + + /** instance of log4j logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(PopulationStatisticsLogger.class); + + /** + * Logs the population statistics during the process of convergence. + */ + @Override + public void notify(int generation, Population

population) { + final PopulationStatisticalSummary

populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>( + population); + LOGGER.info( + "Population statistics for generation %d ::: Mean Fitness: %f, Max Fitness: %f, Fitness Variance: %f", + generation, populationStatisticalSummary.getMeanFitness(), populationStatisticalSummary.getMaxFitness(), + populationStatisticalSummary.getFitnessVariance()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java new file mode 100644 index 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..138a90f323 --- /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.internal.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..5147d25067 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.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.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.internal.utils.ValidationUtils; +import org.apache.commons.math4.ga.utils.RandomGenerator; + +/** + * 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; + + /** + * @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..11c81bef50 --- /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.internal.exception.GeneticException; +import org.apache.commons.math4.ga.internal.utils.ValidationUtils; +import org.apache.commons.math4.ga.utils.RandomGenerator; + +/** + * 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/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java new file mode 100644 index 0000000000..555465fff2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates mutation rate using linear function of relative rank of input + * chromosome in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearMutationRateGenerator

implements MutationRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum mutation rate + * @param maximumRate maximum mutation rate + */ + public AdaptiveLinearMutationRateGenerator(double minimumRate, double maximumRate) { + this.minimumRate = minimumRate; + this.maximumRate = maximumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

chromosome, PopulationStatisticalSummary

populationStats, int generation) { + return minimumRate + (maximumRate - minimumRate) * + (1.0 - (double) populationStats.findRank(chromosome) / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java new file mode 100644 index 0000000000..097e98df8c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents a constant mutation rate generator. + * @param

phenotype of chromosome + */ +public class ConstantMutationRateGenerator

implements MutationRateGenerator

{ + + /** the constant mutationRate. **/ + private final double mutationRate; + + /** + * @param mutationRate mutation rate + */ + public ConstantMutationRateGenerator(double mutationRate) { + this.mutationRate = mutationRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

chromosome, PopulationStatisticalSummary

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

phenotype of chromosome + */ +public interface MutationRateGenerator

{ + + /** + * Generates mutation rate based on input params. + * @param chromosome chromosome + * @param populationStats population statictics summary + * @param generation generation count + * @return mutation rate + */ + double generate(Chromosome

chromosome, PopulationStatisticalSummary

populationStats, int generation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java new file mode 100644 index 0000000000..c7e56cefeb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.mutation.rategenerator; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java new file mode 100644 index 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..bcf8729b79 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * Population of chromosomes represented by a {@link List}. + * + * @param

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

implements Population

{ + + /** new line constant. **/ + public static final String NEW_LINE = System.getProperty("line.separator"); + + /** 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) { + + Objects.requireNonNull(chromosomes); + + if (populationLimit <= 0) { + throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, populationLimit); + } + if (chromosomes.size() > populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.populationLimit = populationLimit; + this.chromosomes = new ArrayList<>(populationLimit); + this.chromosomes.addAll(chromosomes); + } + + /** + * Add a {@link Collection} of chromosomes to this {@link Population}. + * @param chromosomeColl a {@link Collection} of chromosomes + * @since 3.1 + */ + public void addChromosomes(final Collection> 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(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..3e2c8081fe --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.selection; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.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/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..ee50bfa24b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.internal.exception.GeneticException; +import org.apache.commons.math4.ga.internal.utils.ValidationUtils; +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/RandomGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomGenerator.java new file mode 100644 index 0000000000..99d0450d11 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomGenerator.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; + +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 { + + /** + * constructs the singleton instance. + */ + private RandomGenerator() { + } + + /** + * Returns the (static) random generator. + * @return the static random generator shared by GA implementation classes + */ + public static UniformRandomProvider getRandomGenerator() { + return ThreadLocalRandomSource.current(RandomSource.XO_RO_SHI_RO_128_PP); + } + +} 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..810ecce7d7 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinary.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.convergence.FixedGenerationCount; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.TransparentListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.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() { + + // 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..245b07b7ac --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.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..a157599bdf --- /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.internal.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..a613621708 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.junit.Assert; +import org.junit.Test; + +public class ChromosomePairTest { + + @Test + public void testChromosomePair() { + Chromosome chromosome1 = new AbstractChromosome(c -> 0, c -> "0") { + }; + Chromosome chromosome2 = new AbstractChromosome(c -> 1, c -> "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..6eaab64714 --- /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.internal.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..100f600280 --- /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.internal.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..625c1f53b8 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.convergence.FixedElapsedTime; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.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..571c889168 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.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.convergencecond; + +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.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..4bf6e23219 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.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.convergencecond; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.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 AbstractChromosome(c -> fitness, c -> "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..f5d3faddef --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.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.convergencecond; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedMeanFitness; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.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 AbstractChromosome(c -> fitness, c -> "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..008d51c9ad --- /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.internal.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..fe422f6a91 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.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 IntegralValuedChromosome p1c = new IntegralValuedChromosome(p1, c -> 0, + new DummyListChromosomeDecoder("0"), 0, 2); + final IntegralValuedChromosome p2c = new IntegralValuedChromosome(p2, c -> 0, + new DummyListChromosomeDecoder("0"), 0, 2); + + 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 IntegralValuedChromosome p1c = new IntegralValuedChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + 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 IntegralValuedChromosome p2c = new IntegralValuedChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + 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}; + + IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + 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, (IntegralValuedChromosome) pair.getFirst())); + Assert.assertEquals(order, + detectCrossoverPoints(p2c, p1c, (IntegralValuedChromosome) pair.getSecond())); + } + } + + private int detectCrossoverPoints(IntegralValuedChromosome p1, + IntegralValuedChromosome p2, + IntegralValuedChromosome 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..658c073fbc --- /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.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +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}; + + IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + 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 = ((IntegralValuedChromosome) pair.getFirst()).getRepresentation().toArray(c1); + c2 = ((IntegralValuedChromosome) 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..681ab01de3 --- /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.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.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 IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + final IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + 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 = ((IntegralValuedChromosome) pair.getFirst()).getRepresentation(); + final List c2 = ((IntegralValuedChromosome) 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/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java new file mode 100644 index 0000000000..d903ffadf9 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.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.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest { + + @Test + public void testGenerate() { + final double minCrossoverRate = .5; + final double maxCrossoverRate = 1.0; + + IntegralValuedChromosome chromosome1 = IntegralValuedChromosome.randomChromosome(10, c -> 1, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome2 = IntegralValuedChromosome.randomChromosome(10, c -> 2, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome3 = IntegralValuedChromosome.randomChromosome(10, c -> 3, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome4 = IntegralValuedChromosome.randomChromosome(10, c -> 4, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome5 = IntegralValuedChromosome.randomChromosome(10, c -> 5, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + + Population population = new ListPopulation<>(5); + population.addChromosome(chromosome1); + population.addChromosome(chromosome2); + population.addChromosome(chromosome3); + population.addChromosome(chromosome4); + population.addChromosome(chromosome5); + PopulationStatisticalSummary stats = new PopulationStatisticalSummaryImpl<>(population); + + CrossoverRateGenerator crossoverRateGenerator = new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>( + minCrossoverRate, maxCrossoverRate); + + Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome1, chromosome5, stats, 1), + .00000001); + Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome5, chromosome2, stats, 1), + .00000001); + Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome3, chromosome5, stats, 1), + .00000001); + Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome4, chromosome5, stats, 1), + .00000001); + + } + +} 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..478d50d986 --- /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.internal.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..6df8ed8342 --- /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.internal.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..9d7aa1d677 --- /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.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +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 IntegralValuedChromosome<>(rp, c -> 0, + new TransparentListChromosomeDecoder<>(), 0, 2); + 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..a93ad9a7bb --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.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.exception; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +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..45e07f7310 --- /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.internal.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..0b95314083 --- /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<>(); + 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..063155fd4e --- /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.internal.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..976363a26d --- /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.internal.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..03a922d93f --- /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.internal.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/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java new file mode 100644 index 0000000000..9780057414 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class AdaptiveLinearMutationRateGeneratorTest { + + @Test + public void testGenerate() { + final double minMutationRate = .01; + final double maxMutationRate = .1; + + IntegralValuedChromosome chromosome1 = IntegralValuedChromosome.randomChromosome(10, c -> 1, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome2 = IntegralValuedChromosome.randomChromosome(10, c -> 2, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome3 = IntegralValuedChromosome.randomChromosome(10, c -> 3, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome4 = IntegralValuedChromosome.randomChromosome(10, c -> 4, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome5 = IntegralValuedChromosome.randomChromosome(10, c -> 5, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + + Population population = new ListPopulation<>(5); + population.addChromosome(chromosome1); + population.addChromosome(chromosome2); + population.addChromosome(chromosome3); + population.addChromosome(chromosome4); + population.addChromosome(chromosome5); + PopulationStatisticalSummary stats = new PopulationStatisticalSummaryImpl<>(population); + + MutationRateGenerator mutationRateGenerator = new AdaptiveLinearMutationRateGenerator<>(minMutationRate, + maxMutationRate); + + Assert.assertEquals(maxMutationRate, mutationRateGenerator.generate(chromosome1, stats, 1), .00000001); + Assert.assertEquals(minMutationRate, mutationRateGenerator.generate(chromosome5, stats, 1), .00000001); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java new file mode 100644 index 0000000000..5962ae476e --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.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.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.junit.Assert; +import org.junit.Test; + +public class ConstantMutationRateGeneratorTest { + + @Test + public void testGenerate() { + for (int i = 0; i < 100; i++) { + double mutationRate = RandomGenerator.getRandomGenerator().nextDouble(); + MutationRateGenerator mutationRateGenerator = new ConstantMutationRateGenerator<>(mutationRate); + IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, + new DummyListChromosomeDecoder<>("Fixed"), 0, 2); + Population population = new ListPopulation<>(1); + population.addChromosome(chromosome); + Assert.assertEquals(mutationRate, + mutationRateGenerator.generate(chromosome, new PopulationStatisticalSummaryImpl<>(population), i), + .0000001); + } + } + +} 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..ac1c5adcbb --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.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.population; + +import java.util.ArrayList; +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.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.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(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + 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(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + new ListPopulation(chromosomes, -10); + } + + @Test(expected = GeneticException.class) + public void testConstructorListOfChromosomesBiggerThanPopulationSize() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + new ListPopulation(chromosomes, 1); + } + + @Test(expected = GeneticException.class) + public void testAddTooManyChromosomes() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + 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(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void testIterator() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + 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(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(chromosomes, 3); + + population.setPopulationLimit(2); + } + + @Test + public void testNextGeneration() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + 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..15bce4c987 --- /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.internal.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..4cc1aec683 --- /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.internal.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/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..55fc293750 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ValidationUtilsTest.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.utils; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.internal.utils.ValidationUtils; +import org.junit.Test; + +public class ValidationUtilsTest { + + @Test(expected = GeneticException.class) + public void testMinMaxDouble() { + ValidationUtils.checkForMinMax(10.0, 1.0); + } + + @Test(expected = GeneticException.class) + public void testMinMaxInt() { + ValidationUtils.checkForMinMax(10, 1); + } + +} diff --git a/pom.xml b/pom.xml index c618f112cb..818fbb30ae 100644 --- a/pom.xml +++ b/pom.xml @@ -127,6 +127,7 @@ commons-math-examples + commons-math-ga diff --git a/src/main/resources/spotbugs/spotbugs-exclude-filter.xml b/src/main/resources/spotbugs/spotbugs-exclude-filter.xml index 3de6902894..8183da2988 100644 --- a/src/main/resources/spotbugs/spotbugs-exclude-filter.xml +++ b/src/main/resources/spotbugs/spotbugs-exclude-filter.xml @@ -57,4 +57,10 @@ + + + + + + From 8b324fafe863b654badd550e692735212b4d3518 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Sun, 7 Nov 2021 15:05:33 +0530 Subject: [PATCH 2/9] Modifications as per review comments --- .../examples-ga-math-functions/pom.xml | 34 ++++---- .../examples-ga/examples-ga-tsp/pom.xml | 32 ++++---- .../spotbugs/spotbugs-exclude-filter.xml | 66 +++++++++++++++ commons-math-ga/pom.xml | 31 +++---- .../chromosome/IntegralValuedChromosome.java | 5 +- .../ga/chromosome/RealValuedChromosome.java | 5 +- .../AbstractChromosomeCrossoverPolicy.java | 4 +- .../math4/ga/crossover/CycleCrossover.java | 6 +- .../math4/ga/crossover/NPointCrossover.java | 4 +- .../math4/ga/crossover/OnePointCrossover.java | 4 +- .../math4/ga/crossover/OrderedCrossover.java | 6 +- .../math4/ga/crossover/UniformCrossover.java | 5 +- .../ga/internal/utils/ValidationUtils.java | 38 --------- .../math4/ga/internal/utils/package-info.java | 20 ----- .../AbstractListChromosomeMutationPolicy.java | 8 +- .../ga/mutation/IntegralValuedMutation.java | 9 +- .../math4/ga/mutation/RealValuedMutation.java | 9 +- .../ga/selection/TournamentSelection.java | 4 +- .../utils/ChromosomeRepresentationUtils.java | 13 +-- ...erator.java => RandomNumberGenerator.java} | 17 +++- .../math4/ga/GeneticAlgorithmTestBinary.java | 67 ++++++++------- .../ga/GeneticAlgorithmTestPermutations.java | 6 +- .../ga/chromosome/AbstractChromosomeTest.java | 20 ++--- .../ga/chromosome/BinaryChromosomeTest.java | 13 +-- .../ga/chromosome/ChromosomePairTest.java | 8 +- .../IntegralValuedChromosomeTest.java | 50 ++++++----- .../chromosome/RealValuedChromosomeTest.java | 22 +++-- .../convergencecond/FixedElapsedTimeTest.java | 12 +-- .../FixedGenerationCountTest.java | 13 +-- .../UnchangedBestFitnessTest.java | 9 +- .../UnchangedMeanFitnessTest.java | 9 +- ...AbstractChromosomeCrossoverPolicyTest.java | 8 +- ...ractListChromosomeCrossoverPolicyTest.java | 17 ++-- .../ga/crossover/CycleCrossoverTest.java | 20 ++--- .../ga/crossover/NPointCrossoverTest.java | 32 +++++--- .../ga/crossover/OnePointCrossoverTest.java | 26 +++--- .../ga/crossover/OrderedCrossoverTest.java | 12 +-- .../ga/crossover/UniformCrossoverTest.java | 30 ++++--- ...umRankBasedCrossoverRateGeneratorTest.java | 14 ++-- .../AbstractListChromosomeDecoderTest.java | 10 ++- .../ga/decoder/RandomKeyDecoderTest.java | 20 +++-- .../TransparentListChromosomeDecoderTest.java | 6 +- .../math4/ga/dummy/DummyListChromosome.java | 1 - .../DummyListChromosomeDecoder.java | 2 +- .../ga/exception/GeneticExceptionTest.java | 15 ++-- .../ConvergenceListenerRegistryTest.java | 14 ++-- .../PopulationStatisticsLoggerTest.java | 8 +- ...tractListChromosomeMutationPolicyTest.java | 10 +-- .../math4/ga/mutation/BinaryMutationTest.java | 22 ++--- .../mutation/IntegralValuedMutationTest.java | 34 ++++---- .../ga/mutation/RealValuedMutationTest.java | 34 ++++---- ...aptiveLinearMutationRateGeneratorTest.java | 10 +-- .../ConstantMutationRateGeneratorTest.java | 12 +-- .../ga/population/ListPopulationTest.java | 82 ++++++++++++------- .../ga/selection/TournamentSelectionTest.java | 23 ++++-- .../ChromosomeRepresentationUtilsTest.java | 64 +++++++-------- .../math4/ga/utils/ValidationUtilsTest.java | 35 -------- 57 files changed, 596 insertions(+), 514 deletions(-) create mode 100644 commons-math-examples/examples-ga/src/main/resources/spotbugs/spotbugs-exclude-filter.xml delete mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.java delete mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/package-info.java rename commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/{RandomGenerator.java => RandomNumberGenerator.java} (71%) rename commons-math-ga/src/test/java/org/apache/commons/math4/ga/{utils => dummy}/DummyListChromosomeDecoder.java (97%) delete mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ValidationUtilsTest.java 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 index a387377b80..d09ca3ff10 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml @@ -1,20 +1,14 @@ - + @@ -40,6 +34,14 @@ examples-ga-mathfunctions org.apache.commons.math4.examples.ga.mathfunctions.Dimension2FunctionOptimizer + 1.7.32 + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + \ No newline at end of file diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml index db88cd0756..b4a7811307 100644 --- a/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml +++ b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml @@ -1,20 +1,14 @@ - + @@ -40,6 +34,7 @@ examples-ga-mathfunctions org.apache.commons.math4.examples.ga.tsp.TSPOptimizer + 1.7.32 @@ -48,5 +43,10 @@ commons-csv 1.9.0 + + org.slf4j + slf4j-simple + ${slf4jVersion} + \ 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..8183da2988 --- /dev/null +++ b/commons-math-examples/examples-ga/src/main/resources/spotbugs/spotbugs-exclude-filter.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/commons-math-ga/pom.xml b/commons-math-ga/pom.xml index 6afdd5f572..a2a3689ea1 100644 --- a/commons-math-ga/pom.xml +++ b/commons-math-ga/pom.xml @@ -1,20 +1,14 @@ - + @@ -55,11 +49,6 @@ slf4j-api ${slf4jVersion} - - org.slf4j - slf4j-simple - ${slf4jVersion} - \ No newline at end of file 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 index e5669cfb18..91fcfd25c3 100644 --- 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 @@ -21,7 +21,6 @@ import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; import org.apache.commons.math4.ga.fitness.FitnessFunction; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.internal.utils.ValidationUtils; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; /** @@ -94,7 +93,9 @@ public int getMax() { * Asserts that representation can represent a valid chromosome. */ private void checkValidity() { - ValidationUtils.checkForMinMax(min, max); + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } for (int i : getRepresentation()) { if (i < min || i >= max) { throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); 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 index 9732872b11..603a1e2130 100644 --- 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 @@ -22,7 +22,6 @@ import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; import org.apache.commons.math4.ga.fitness.FitnessFunction; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.internal.utils.ValidationUtils; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; /** @@ -119,7 +118,9 @@ public double getMax() { * Asserts that representation can represent a valid chromosome. */ private void checkValidity() { - ValidationUtils.checkForMinMax(min, max); + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } for (double i : getRepresentation()) { if (i < min || i >= max) { throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); 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 index eee06caec4..6947ca5f45 100644 --- 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 @@ -19,7 +19,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; /** * An abstraction to represent the base crossover policy. @@ -35,7 +35,7 @@ public abstract class AbstractChromosomeCrossoverPolicy

implements CrossoverP public ChromosomePair

crossover(final Chromosome

first, final Chromosome

second, final double crossoverRate) { - if (RandomGenerator.getRandomGenerator().nextDouble() < crossoverRate) { + if (RandomNumberGenerator.getRandomGenerator().nextDouble() < crossoverRate) { return crossover(first, second); } else { return new ChromosomePair<>(first, second); 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 index c0a1678e8b..6b05cfd817 100644 --- 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 @@ -17,14 +17,14 @@ 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.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; /** * Cycle Crossover [CX] builds offspring from ordered chromosomes by @@ -124,7 +124,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List indices = new ArrayList<>(length); // determine the starting index - int idx = randomStart ? RandomGenerator.getRandomGenerator().nextInt(length) : 0; + int idx = randomStart ? RandomNumberGenerator.getRandomGenerator().nextInt(length) : 0; int cycle = 1; while (visitedIndices.size() < length) { 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 index 1a567a07ad..e5538b88bb 100644 --- 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 @@ -22,7 +22,7 @@ import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; import org.apache.commons.rng.UniformRandomProvider; /** @@ -118,7 +118,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List child1Rep = new ArrayList<>(length); final List child2Rep = new ArrayList<>(length); - final UniformRandomProvider random = RandomGenerator.getRandomGenerator(); + final UniformRandomProvider random = RandomNumberGenerator.getRandomGenerator(); List c1 = child1Rep; List c2 = 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 index 5beefa01fe..d90c869cec 100644 --- 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 @@ -21,7 +21,7 @@ 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.math4.ga.utils.RandomNumberGenerator; /** * One point crossover policy. A random crossover point is selected and the @@ -83,7 +83,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, 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)); + final int crossoverIndex = 1 + (RandomNumberGenerator.getRandomGenerator().nextInt(length - 2)); // copy the first part for (int i = 0; i < crossoverIndex; i++) { 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 index 3cbdd2ed4d..58eef774db 100644 --- 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 @@ -17,15 +17,15 @@ 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.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; import org.apache.commons.rng.UniformRandomProvider; /** @@ -84,7 +84,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final Set child1Set = new HashSet<>(length); final Set child2Set = new HashSet<>(length); - final UniformRandomProvider random = RandomGenerator.getRandomGenerator(); + final UniformRandomProvider random = RandomNumberGenerator.getRandomGenerator(); // choose random points, making sure that lb < ub. final int a = random.nextInt(length); int b; 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 index 8ad9d9aa35..c624d94439 100644 --- 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 @@ -20,9 +20,10 @@ import java.util.List; import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; import org.apache.commons.rng.UniformRandomProvider; /** @@ -103,7 +104,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List child1Rep = new ArrayList<>(length); final List child2Rep = new ArrayList<>(length); - final UniformRandomProvider random = RandomGenerator.getRandomGenerator(); + final UniformRandomProvider random = RandomNumberGenerator.getRandomGenerator(); for (int index = 0; index < length; index++) { diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.java deleted file mode 100644 index af92b3170a..0000000000 --- a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/ValidationUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.math4.ga.internal.utils; - -import org.apache.commons.math4.ga.internal.exception.GeneticException; - -/** - * This class contains common validation methods. - * @since 4.0 - */ -public interface ValidationUtils { - - /** - * 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/internal/utils/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/package-info.java deleted file mode 100644 index c0e5560400..0000000000 --- a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/utils/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * This package provides Genetic Algorithms components and implementations. - */ -package org.apache.commons.math4.ga.internal.utils; 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 index a38c348ec5..de757965ca 100644 --- 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 @@ -24,7 +24,7 @@ import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; import org.apache.commons.math4.ga.chromosome.Chromosome; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; /** * This abstraction represents an abstract mutation policy for ListChromosomes. @@ -80,10 +80,10 @@ protected Set getMutableGeneIndexes(int length, double mutationRate) { if (chromosomeMutationRate >= 1) { final int noOfMutation = (int) Math.round(chromosomeMutationRate); while (indexSet.size() < noOfMutation) { - indexSet.add(RandomGenerator.getRandomGenerator().nextInt(length)); + indexSet.add(RandomNumberGenerator.getRandomGenerator().nextInt(length)); } - } else if (RandomGenerator.getRandomGenerator().nextDouble() < chromosomeMutationRate) { - indexSet.add(RandomGenerator.getRandomGenerator().nextInt(length)); + } else if (RandomNumberGenerator.getRandomGenerator().nextDouble() < chromosomeMutationRate) { + indexSet.add(RandomNumberGenerator.getRandomGenerator().nextInt(length)); } return indexSet; 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 index 5147d25067..a587a762e7 100644 --- 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 @@ -19,8 +19,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.internal.utils.ValidationUtils; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; /** * Mutation for {@link IntegralValuedChromosome}. Randomly changes few genes. @@ -42,7 +41,9 @@ public class IntegralValuedMutation

extends AbstractListChromosomeMutationPol public IntegralValuedMutation(final int min, final int max) { this.min = min; this.max = max; - ValidationUtils.checkForMinMax(min, max); + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } } /** @@ -81,7 +82,7 @@ protected void checkValidity(Chromosome

original) { */ @Override protected Integer mutateGene(Integer originalValue) { - return min + RandomGenerator.getRandomGenerator().nextInt(max - min); + return min + RandomNumberGenerator.getRandomGenerator().nextInt(max - min); } } 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 index 11c81bef50..a24f46bba2 100644 --- 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 @@ -19,8 +19,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.internal.utils.ValidationUtils; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; /** * This class mutates real-valued chromosome. @@ -51,7 +50,9 @@ public RealValuedMutation() { public RealValuedMutation(double min, double max) { this.min = min; this.max = max; - ValidationUtils.checkForMinMax(min, max); + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } } /** @@ -90,7 +91,7 @@ protected void checkValidity(Chromosome

original) { */ @Override protected Double mutateGene(Double originalValue) { - return min + RandomGenerator.getRandomGenerator().nextDouble() * (max - min); + return min + RandomNumberGenerator.getRandomGenerator().nextDouble() * (max - min); } } 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 index 3e2c8081fe..f59ac1780a 100644 --- 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 @@ -25,7 +25,7 @@ import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.apache.commons.math4.ga.utils.RandomGenerator; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; /** * Tournament selection scheme. Each of the two selected chromosomes is selected @@ -86,7 +86,7 @@ private Chromosome

tournament(final ListPopulation

population) { 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()); + final int rind = RandomNumberGenerator.getRandomGenerator().nextInt(chromosomes.size()); selectedChromosomes.add(chromosomes.get(rind)); // do not select it again chromosomes.remove(rind); 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 index ee50bfa24b..e7dc0c2e64 100644 --- 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 @@ -23,7 +23,6 @@ import java.util.List; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.internal.utils.ValidationUtils; import org.apache.commons.rng.UniformRandomProvider; /** @@ -40,7 +39,7 @@ public interface ChromosomeRepresentationUtils { * @return representation of a random permutation */ static List randomPermutation(final int l) { - final UniformRandomProvider randomProvider = RandomGenerator.getRandomGenerator(); + final UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); final List repr = new ArrayList<>(l); for (int i = 0; i < l; i++) { repr.add(randomProvider.nextDouble()); @@ -127,7 +126,7 @@ static List inducedPermutation(final List originalData, final Lis * @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 UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); final List rList = new ArrayList<>(length); for (int j = 0; j < length; j++) { rList.add(min + randomProvider.nextInt(max - min)); @@ -142,7 +141,7 @@ static List randomIntegralRepresentation(final int length, final int mi * @return a random binary array of length length */ static List randomBinaryRepresentation(final int length) { - final UniformRandomProvider randomProvider = RandomGenerator.getRandomGenerator(); + final UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); // random binary list final List rList = new ArrayList<>(length); for (int j = 0; j < length; j++) { @@ -170,9 +169,11 @@ static List randomNormalizedDoubleRepresentation(final int l) { * @return representation as List of Double */ static List randomDoubleRepresentation(final int l, double min, double max) { - ValidationUtils.checkForMinMax(min, max); + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } final double range = max - min; - final UniformRandomProvider randomProvider = RandomGenerator.getRandomGenerator(); + final UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); final List repr = new ArrayList<>(l); for (int i = 0; i < l; i++) { repr.add(min + randomProvider.nextDouble() * range); 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/RandomNumberGenerator.java similarity index 71% rename from commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomGenerator.java rename to commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomNumberGenerator.java index 99d0450d11..710b6c18f0 100644 --- 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/RandomNumberGenerator.java @@ -25,12 +25,23 @@ * An utility to generate per thread {@link UniformRandomProvider} instance. * @since 4.0 */ -public final class RandomGenerator { +public final class RandomNumberGenerator { + + /** The default RandomSource for random number generation. **/ + private static RandomSource randomSource = RandomSource.XO_RO_SHI_RO_128_PP; + + /** + * Sets the random source for this random generator. + * @param randomSource + */ + public static void configure(RandomSource randomSource) { + RandomNumberGenerator.randomSource = randomSource; + } /** * constructs the singleton instance. */ - private RandomGenerator() { + private RandomNumberGenerator() { } /** @@ -38,7 +49,7 @@ private RandomGenerator() { * @return the static random generator shared by GA implementation classes */ public static UniformRandomProvider getRandomGenerator() { - return ThreadLocalRandomSource.current(RandomSource.XO_RO_SHI_RO_128_PP); + return ThreadLocalRandomSource.current(RandomNumberGenerator.randomSource); } } 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 index 810ecce7d7..f72d2afa9a 100644 --- 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 @@ -34,9 +34,9 @@ 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; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** * This is also an example of usage. @@ -51,31 +51,16 @@ public class GeneticAlgorithmTestBinary { 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() { + removeListeners(); // 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()); + Assertions.assertEquals(0, ga.getGenerationsEvolved()); // initial population Population> initial = randomPopulation(); @@ -95,9 +80,31 @@ public void test() { // 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()); + Assertions.assertTrue(bestFinal.compareTo(bestInitial) > 0); + Assertions.assertEquals(NUM_GENERATIONS, ga.getGenerationsEvolved()); + + } + private void removeListeners() { + try { + 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); + } catch (NoSuchFieldException | SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } } /** @@ -132,16 +139,20 @@ private static class FindOnes extends BinaryChromosome> { } } - @Test(expected = GeneticException.class) + @Test public void testCrossoverRate() { - new GeneticAlgorithm<>(new OnePointCrossover<>(), 1.5, new BinaryMutation<>(), .01, - new TournamentSelection<>(10)); + Assertions.assertThrows(GeneticException.class, () -> { + new GeneticAlgorithm<>(new OnePointCrossover<>(), 1.5, new BinaryMutation<>(), .01, + new TournamentSelection<>(10)); + }); } - @Test(expected = GeneticException.class) + @Test public void testMutationRate() { - new GeneticAlgorithm<>(new OnePointCrossover<>(), .5, new BinaryMutation<>(), 1.5, - new TournamentSelection<>(10)); + Assertions.assertThrows(GeneticException.class, () -> { + new GeneticAlgorithm<>(new OnePointCrossover<>(), .5, new BinaryMutation<>(), 1.5, + new TournamentSelection<>(10)); + }); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java index 245b07b7ac..4bfb2ec047 100644 --- 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 @@ -31,8 +31,8 @@ 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; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * This is also an example of usage. @@ -87,7 +87,7 @@ public void test() { // the initial one // however, for some implementations of GA, this need not be true :) - Assert.assertTrue(bestFinal.compareTo(bestInitial) > 0); + Assertions.assertTrue(bestFinal.compareTo(bestInitial) > 0); } 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 index 047c17939a..a682024bab 100644 --- 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 @@ -16,8 +16,8 @@ */ package org.apache.commons.math4.ga.chromosome; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class AbstractChromosomeTest { @@ -25,14 +25,14 @@ public class AbstractChromosomeTest { public void testGetFitness() { Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { }; - Assert.assertEquals(1, c1.evaluate(), .001); + Assertions.assertEquals(1, c1.evaluate(), .001); } @Test public void testDecode() { Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { }; - Assert.assertEquals("1", c1.decode()); + Assertions.assertEquals("1", c1.decode()); } @Test @@ -44,10 +44,10 @@ public void testCompareTo() { 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)); + Assertions.assertTrue(c1.compareTo(c2) < 0); + Assertions.assertTrue(c2.compareTo(c1) > 0); + Assertions.assertEquals(0, c3.compareTo(c2)); + Assertions.assertEquals(0, c2.compareTo(c3)); } @Test @@ -58,8 +58,8 @@ public void testIsSame() { }; AbstractChromosome c3 = new AbstractChromosome(chromosome -> 3, chromosome -> "1") { }; - Assert.assertTrue(c1.isSame(c3)); - Assert.assertFalse(c1.isSame(c2)); + Assertions.assertTrue(c1.isSame(c3)); + Assertions.assertFalse(c1.isSame(c2)); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java index a157599bdf..2547e01ee8 100644 --- 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 @@ -16,20 +16,21 @@ */ package org.apache.commons.math4.ga.chromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class BinaryChromosomeTest { - @Test(expected = GeneticException.class) + @Test 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"); + Assertions.assertThrows(GeneticException.class, () -> { + new BinaryChromosome<>(repr, c -> 0, new DummyListChromosomeDecoder<>("0")); + }); } } 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 index a613621708..f43a27ccb8 100644 --- 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 @@ -16,8 +16,8 @@ */ package org.apache.commons.math4.ga.chromosome; -import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.api.Assertions; public class ChromosomePairTest { @@ -29,10 +29,10 @@ public void testChromosomePair() { }; ChromosomePair chromosomePair = new ChromosomePair<>(chromosome1, chromosome2); - Assert.assertEquals(chromosomePair.getFirst(), chromosome1); - Assert.assertEquals(chromosomePair.getSecond(), chromosome2); + Assertions.assertEquals(chromosomePair.getFirst(), chromosome1); + Assertions.assertEquals(chromosomePair.getSecond(), chromosome2); - Assert.assertNotNull(chromosomePair.toString()); + Assertions.assertNotNull(chromosomePair.toString()); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java index 6eaab64714..b8326bb501 100644 --- 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 @@ -16,11 +16,12 @@ */ package org.apache.commons.math4.ga.chromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; + import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class IntegralValuedChromosomeTest { @@ -31,31 +32,36 @@ public void testIntegralValuedChromosome() { 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()); + Assertions.assertEquals(min, chromosome.getMin()); + Assertions.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()); + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max).toArray(new Integer[10]), + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + Assertions.assertEquals(min, chromosome1.getMin()); + Assertions.assertEquals(max, chromosome1.getMax()); } - @Test(expected = GeneticException.class) + @Test public void testCheckValidity() { int min = 0; int max = 10; - new IntegralValuedChromosome<>(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, - new DummyListChromosomeDecoder<>("0"), max, min); + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), + c -> 0, new DummyListChromosomeDecoder<>("0"), max, min); + }); } - @Test(expected = GeneticException.class) + @Test 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); + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min - 10, max + 10), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + }); + } @Test @@ -67,10 +73,10 @@ public void testNewChromosome() { 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()); + Assertions.assertEquals(chromosome.getMin(), newChromosome.getMin()); + Assertions.assertEquals(chromosome.getMax(), newChromosome.getMax()); + Assertions.assertEquals(chromosome.getDecoder(), newChromosome.getDecoder()); + Assertions.assertEquals(chromosome.getFitnessFunction(), newChromosome.getFitnessFunction()); } @@ -80,8 +86,8 @@ public void testRandomChromosome() { 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()); + Assertions.assertEquals(min, chromosome.getMin()); + Assertions.assertEquals(max, chromosome.getMax()); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java index 100f600280..381ca388ed 100644 --- 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 @@ -16,10 +16,11 @@ */ package org.apache.commons.math4.ga.chromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class RealValuedChromosomeTest { @@ -51,21 +52,24 @@ public void testRandomChromosome() { } } - @Test(expected = GeneticException.class) + @Test public void testCheckValidity() { int min = 0; int max = 10; - new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, - new DummyListChromosomeDecoder<>("0"), max, min); + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), max, min); + }); } - @Test(expected = GeneticException.class) + @Test 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); + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min - 10, max + 10), + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + }); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java index 625c1f53b8..e2681ab8a9 100644 --- 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 @@ -22,8 +22,8 @@ import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class FixedElapsedTimeTest { @@ -47,11 +47,13 @@ public void testIsSatisfied() { final long elapsedTime = end - start; final long diff = Math.abs(elapsedTime - TimeUnit.SECONDS.toNanos(duration)); - Assert.assertTrue(diff < TimeUnit.MILLISECONDS.toNanos(100)); + Assertions.assertTrue(diff < TimeUnit.MILLISECONDS.toNanos(100)); } - @Test(expected = GeneticException.class) + @Test public void testNegativeTime() { - new FixedElapsedTime<>(-10); + Assertions.assertThrows(GeneticException.class, () -> { + new FixedElapsedTime<>(-10); + }); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java index 571c889168..5bf1567545 100644 --- 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 @@ -17,11 +17,12 @@ package org.apache.commons.math4.ga.convergencecond; import org.apache.commons.math4.ga.convergence.FixedGenerationCount; + import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class FixedGenerationCountTest { @@ -35,11 +36,13 @@ public void testIsSatisfied() { while (!fgc.isSatisfied(pop)) { cnt++; } - Assert.assertEquals(cnt, fgc.getNumGenerations()); + Assertions.assertEquals(cnt, fgc.getNumGenerations()); } - @Test(expected = GeneticException.class) + @Test public void testNegativeGenerationCount() { - new FixedGenerationCount(-1); + Assertions.assertThrows(GeneticException.class, () -> { + 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 index 4bf6e23219..5fffd0650a 100644 --- 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 @@ -17,6 +17,7 @@ package org.apache.commons.math4.ga.convergencecond; import java.util.ArrayList; + import java.util.List; import org.apache.commons.math4.ga.chromosome.AbstractChromosome; @@ -26,8 +27,8 @@ import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class UnchangedBestFitnessTest { @@ -60,8 +61,8 @@ public void testIsSatisfied() { double maxFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMaxFitness(); - Assert.assertEquals(initialMaxFitness, maxFitnessAfterConvergence, .001); - Assert.assertEquals(noOfGenerationsWithUnchangedBestFitness, counter); + Assertions.assertEquals(initialMaxFitness, maxFitnessAfterConvergence, .001); + Assertions.assertEquals(noOfGenerationsWithUnchangedBestFitness, counter); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java index f5d3faddef..6e4042cbfb 100644 --- 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 @@ -17,6 +17,7 @@ package org.apache.commons.math4.ga.convergencecond; import java.util.ArrayList; + import java.util.List; import org.apache.commons.math4.ga.chromosome.AbstractChromosome; @@ -26,8 +27,8 @@ import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class UnchangedMeanFitnessTest { @@ -60,8 +61,8 @@ public void testIsSatisfied() { double averageFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMeanFitness(); - Assert.assertEquals(initialAverageFitness, averageFitnessAfterConvergence, .001); - Assert.assertEquals(noOfGenerationsWithUnchangedMeanFitness, counter); + Assertions.assertEquals(initialAverageFitness, averageFitnessAfterConvergence, .001); + Assertions.assertEquals(noOfGenerationsWithUnchangedMeanFitness, counter); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java index 5ea57aacb9..c1249b52a5 100644 --- 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 @@ -19,8 +19,8 @@ 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; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class AbstractChromosomeCrossoverPolicyTest { @@ -38,8 +38,8 @@ protected ChromosomePair crossover(Chromosome first, Chromosome< Chromosome ch2 = new DummyChromosome(); - Assert.assertNull(crossoverPolicy.crossover(ch1, ch2, 1.0)); - Assert.assertNotNull(crossoverPolicy.crossover(ch1, ch2, 0.0)); + Assertions.assertNull(crossoverPolicy.crossover(ch1, ch2, 1.0)); + Assertions.assertNotNull(crossoverPolicy.crossover(ch1, ch2, 0.0)); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java index 008d51c9ad..a8f6b13e55 100644 --- 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 @@ -23,11 +23,12 @@ import org.apache.commons.math4.ga.dummy.DummyListChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class AbstractListChromosomeCrossoverPolicyTest { - @Test(expected = GeneticException.class) + @Test public void testCrossoverWithNonListChromosome() { CrossoverPolicy crossoverPolicy = new AbstractListChromosomeCrossoverPolicy() { @@ -44,10 +45,13 @@ protected ChromosomePair mate(AbstractListChromosome fi Chromosome ch2 = new AbstractChromosome(c -> 1, c -> "1") { }; - crossoverPolicy.crossover(ch1, ch2, 1.0); + Assertions.assertThrows(GeneticException.class, () -> { + crossoverPolicy.crossover(ch1, ch2, 1.0); + }); + } - @Test(expected = GeneticException.class) + @Test public void testCrossoverWithUnEqualLengthChromosome() { CrossoverPolicy crossoverPolicy = new AbstractListChromosomeCrossoverPolicy() { @@ -62,7 +66,10 @@ protected ChromosomePair mate(AbstractListChromosome fi Chromosome ch2 = new DummyListChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(20)); - crossoverPolicy.crossover(ch1, ch2, 1.0); + Assertions.assertThrows(GeneticException.class, () -> { + crossoverPolicy.crossover(ch1, ch2, 1.0); + }); + } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java index 1f85a31a4d..e8ac82019a 100644 --- 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 @@ -18,8 +18,8 @@ import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.dummy.DummyListChromosome; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class CycleCrossoverTest { @@ -43,8 +43,8 @@ public void testCrossoverExample() { 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); + Assertions.assertArrayEquals(c1e, c1); + Assertions.assertArrayEquals(c2e, c2); } @Test @@ -66,8 +66,8 @@ public void testCrossoverExample2() { 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); + Assertions.assertArrayEquals(c1e, c1); + Assertions.assertArrayEquals(c2e, c2); } @Test @@ -92,9 +92,9 @@ public void testCrossover() { // not it comes from the second parent. for (final Integer j : c1) { if (!p1[index].equals(j)) { - Assert.assertEquals(j, p2[index]); + Assertions.assertEquals(j, p2[index]); } else { - Assert.assertEquals(j, p1[index]); + Assertions.assertEquals(j, p1[index]); } index++; } @@ -103,9 +103,9 @@ public void testCrossover() { index = 0; for (final Integer k : c2) { if (p2[index] != k) { - Assert.assertEquals(k, p1[index]); + Assertions.assertEquals(k, p1[index]); } else { - Assert.assertEquals(k, p2[index]); + Assertions.assertEquals(k, p2[index]); } index++; } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java index fe422f6a91..35a55b025b 100644 --- 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 @@ -21,14 +21,14 @@ import org.apache.commons.math4.ga.chromosome.AbstractChromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class NPointCrossoverTest { - @Test(expected = GeneticException.class) + @Test public void testNumberIsTooLargeException() { final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; final Integer[] p2 = new Integer[] {0, 1, 1, 0, 1, 0, 1, 1, 1}; @@ -39,10 +39,12 @@ public void testNumberIsTooLargeException() { new DummyListChromosomeDecoder("0"), 0, 2); final CrossoverPolicy cp = new NPointCrossover(15); - cp.crossover(p1c, p2c, 1.0); + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); } - @Test(expected = GeneticException.class) + @Test public void testCrossoverInvalidFixedLengthChromosomeFirst() { final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; final IntegralValuedChromosome p1c = new IntegralValuedChromosome(p1, chromosome -> 0, @@ -52,10 +54,14 @@ public void testCrossoverInvalidFixedLengthChromosomeFirst() { }; final CrossoverPolicy cp = new NPointCrossover(1); - cp.crossover(p1c, p2c, 1.0); + + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); + } - @Test(expected = GeneticException.class) + @Test public void testCrossoverInvalidFixedLengthChromosomeSecond() { final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; final IntegralValuedChromosome p2c = new IntegralValuedChromosome(p1, chromosome -> 0, @@ -65,7 +71,11 @@ public void testCrossoverInvalidFixedLengthChromosomeSecond() { }; final CrossoverPolicy cp = new NPointCrossover(1); - cp.crossover(p1c, p2c, 1.0); + + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); + } @Test @@ -86,9 +96,9 @@ public void testCrossover() { // 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, + Assertions.assertEquals(order, detectCrossoverPoints(p1c, p2c, (IntegralValuedChromosome) pair.getFirst())); - Assert.assertEquals(order, + Assertions.assertEquals(order, detectCrossoverPoints(p2c, p1c, (IntegralValuedChromosome) pair.getSecond())); } } 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 index 658c073fbc..a97c74286a 100644 --- 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 @@ -18,9 +18,9 @@ import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class OnePointCrossoverTest { @@ -49,17 +49,17 @@ public void testCrossover() { c2 = ((IntegralValuedChromosome) 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]); + Assertions.assertEquals(p1[0], c1[0]); + Assertions.assertEquals(p2[0], c2[0]); + Assertions.assertEquals(p1[p1.length - 1], c1[c1.length - 1]); + Assertions.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]); + Assertions.assertEquals(p1[2], c1[2]); + Assertions.assertEquals(p2[2], c2[2]); + Assertions.assertEquals(p1[3], c1[3]); + Assertions.assertEquals(p2[3], c2[3]); + Assertions.assertEquals(p1[7], c1[7]); + Assertions.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 index 437cfbd01c..383f1d8764 100644 --- 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 @@ -22,8 +22,8 @@ import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.dummy.DummyListChromosome; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class OrderedCrossoverTest { @@ -47,14 +47,14 @@ public void testCrossover() { final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() .toArray(new Integer[p2.length]); - Assert.assertNotSame(p1c, pair.getFirst()); - Assert.assertNotSame(p2c, pair.getSecond()); + Assertions.assertNotSame(p1c, pair.getFirst()); + Assertions.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])); + Assertions.assertTrue(parentSet1.contains(c1[j])); parentSet1.remove(c1[j]); - Assert.assertTrue(parentSet2.contains(c2[j])); + Assertions.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 index 681ab01de3..6bd2f8d818 100644 --- 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 @@ -21,11 +21,11 @@ import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class UniformCrossoverTest { private static final int LEN = 10000; @@ -33,7 +33,7 @@ public class UniformCrossoverTest { private static final List p2 = new ArrayList<>(LEN); @SuppressWarnings("boxing") - @BeforeClass + @BeforeAll public static void setUpBeforeClass() { for (int i = 0; i < LEN; i++) { p1.add(0); @@ -41,14 +41,18 @@ public static void setUpBeforeClass() { } } - @Test(expected = GeneticException.class) + @Test public void testRatioTooLow() { - new UniformCrossover(-0.5d); + Assertions.assertThrows(GeneticException.class, () -> { + new UniformCrossover(-0.5d); + }); } - @Test(expected = GeneticException.class) + @Test public void testRatioTooHigh() { - new UniformCrossover(1.5d); + Assertions.assertThrows(GeneticException.class, () -> { + new UniformCrossover(1.5d); + }); } @Test @@ -86,8 +90,8 @@ private void performCrossover(double ratio) { } } - Assert.assertEquals(1.0 - ratio, (double) from1 / LEN, 0.1); - Assert.assertEquals(ratio, (double) from2 / LEN, 0.1); + Assertions.assertEquals(1.0 - ratio, (double) from1 / LEN, 0.1); + Assertions.assertEquals(ratio, (double) from2 / LEN, 0.1); from1 = 0; from2 = 0; @@ -101,8 +105,8 @@ private void performCrossover(double ratio) { } } - Assert.assertEquals(ratio, (double) from1 / LEN, 0.1); - Assert.assertEquals(1.0 - ratio, (double) from2 / LEN, 0.1); + Assertions.assertEquals(ratio, (double) from1 / LEN, 0.1); + Assertions.assertEquals(1.0 - ratio, (double) from2 / LEN, 0.1); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java index d903ffadf9..48099244d6 100644 --- a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java @@ -17,13 +17,13 @@ package org.apache.commons.math4.ga.crossover.rategenerator; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest { @@ -54,13 +54,13 @@ public void testGenerate() { CrossoverRateGenerator crossoverRateGenerator = new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>( minCrossoverRate, maxCrossoverRate); - Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome1, chromosome5, stats, 1), + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome1, chromosome5, stats, 1), .00000001); - Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome5, chromosome2, stats, 1), + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome5, chromosome2, stats, 1), .00000001); - Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome3, chromosome5, stats, 1), + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome3, chromosome5, stats, 1), .00000001); - Assert.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome4, chromosome5, stats, 1), + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome4, chromosome5, stats, 1), .00000001); } 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 index 478d50d986..32b2f05d2d 100644 --- 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 @@ -20,11 +20,12 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.dummy.DummyChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class AbstractListChromosomeDecoderTest { - @Test(expected = GeneticException.class) + @Test public void testDecodeWithInvalidChromosomeInstance() { Decoder decoder = new AbstractListChromosomeDecoder() { @@ -34,7 +35,10 @@ protected String decode(AbstractListChromosome chromosome) { } }; Chromosome ch = new DummyChromosome(); - decoder.decode(ch); + Assertions.assertThrows(GeneticException.class, () -> { + 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 index 6df8ed8342..4a2f751f8b 100644 --- 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 @@ -21,8 +21,8 @@ import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class RandomKeyDecoderTest { @@ -36,21 +36,23 @@ public void testDecodeChromosome() { 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)); + Assertions.assertEquals("b", decodedSequence.get(0)); + Assertions.assertEquals("e", decodedSequence.get(1)); + Assertions.assertEquals("a", decodedSequence.get(2)); + Assertions.assertEquals("c", decodedSequence.get(3)); + Assertions.assertEquals("d", decodedSequence.get(4)); } - @Test(expected = GeneticException.class) + @Test 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(); + Assertions.assertThrows(GeneticException.class, () -> { + 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 index 9d7aa1d677..95d444ce92 100644 --- 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 @@ -22,8 +22,8 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class TransparentListChromosomeDecoderTest { @@ -33,7 +33,7 @@ public void testDecode() { Chromosome> chromosome = new IntegralValuedChromosome<>(rp, c -> 0, new TransparentListChromosomeDecoder<>(), 0, 2); List decodedRp = chromosome.decode(); - Assert.assertTrue(Objects.equals(rp, decodedRp)); + Assertions.assertTrue(Objects.equals(rp, decodedRp)); } } 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 index eaddb90207..d3e3472048 100644 --- 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 @@ -20,7 +20,6 @@ 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 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/dummy/DummyListChromosomeDecoder.java similarity index 97% rename from commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/DummyListChromosomeDecoder.java rename to commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosomeDecoder.java index 28601220f6..541cd261f0 100644 --- 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/dummy/DummyListChromosomeDecoder.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.math4.ga.utils; +package org.apache.commons.math4.ga.dummy; import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; import org.apache.commons.math4.ga.chromosome.Chromosome; 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 index a93ad9a7bb..551331a043 100644 --- 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 @@ -17,18 +17,23 @@ package org.apache.commons.math4.ga.exception; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class GeneticExceptionTest { - @Test(expected = GeneticException.class) + @Test public void testGeneticExceptionThrowable() { - throw new GeneticException(new NullPointerException()); + Assertions.assertThrows(GeneticException.class, () -> { + throw new GeneticException(new NullPointerException()); + }); } - @Test(expected = GeneticException.class) + @Test public void testGeneticExceptionStringThrowableObjectArray() { - throw new GeneticException("Nullpointer Exception", new NullPointerException()); + Assertions.assertThrows(GeneticException.class, () -> { + 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 index 45e07f7310..2b1425849a 100644 --- 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 @@ -23,8 +23,8 @@ import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class ConvergenceListenerRegistryTest { @@ -60,7 +60,7 @@ public void notify(int generation, Population population) { @SuppressWarnings("unchecked") List> listeners1 = (List>) listenersField .get(registry); - Assert.assertSame(listeners1.get(0), convergenceListener); + Assertions.assertSame(listeners1.get(0), convergenceListener); listenersField.setAccessible(accessible); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { // No op @@ -82,7 +82,7 @@ private void reset() listenersField.setAccessible(accessible); } - @Test(expected = GeneticException.class) + @Test public void testNotifyAll() { try { reset(); @@ -95,8 +95,10 @@ public void notify(int generation, Population population) { } }; registry.addConvergenceListener(convergenceListener); - registry.notifyAll(0, new ListPopulation<>(10)); - Assert.assertTrue(true); + Assertions.assertThrows(GeneticException.class, () -> { + registry.notifyAll(0, new ListPopulation<>(10)); + }); + Assertions.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 index 0b95314083..d59a514810 100644 --- 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 @@ -17,12 +17,12 @@ package org.apache.commons.math4.ga.listener; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; 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; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class PopulationStatisticsLoggerTest { @@ -39,7 +39,7 @@ public void testPopulationStatisticsLogger() { 0, 10)); PopulationStatisticsLogger logger = new PopulationStatisticsLogger<>(); logger.notify(1, population); - Assert.assertTrue(true); + Assertions.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 index 05016e5b8b..96630588a4 100644 --- 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 @@ -17,9 +17,9 @@ 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; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class AbstractListChromosomeMutationPolicyTest { @@ -29,7 +29,7 @@ public void testGetMutableGeneIndexes() { @Override protected Integer mutateGene(Integer originalValue) { - return RandomGenerator.getRandomGenerator().nextInt(2); + return RandomNumberGenerator.getRandomGenerator().nextInt(2); } @Override @@ -37,7 +37,7 @@ protected void checkValidity(Chromosome original) { // No Op } }; - Assert.assertEquals(1, chromosomeMutationPolicy.getMutableGeneIndexes(10, .1).size()); + Assertions.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 index 063155fd4e..22b7b7b1e1 100644 --- 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 @@ -18,20 +18,22 @@ import org.apache.commons.math4.ga.chromosome.BinaryChromosome; import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class BinaryMutationTest { - @Test(expected = GeneticException.class) + @Test public void testCheckValidity() { BinaryMutation mutation = new BinaryMutation<>(); - mutation.checkValidity( - new RealValuedChromosome<>(ChromosomeRepresentationUtils - .randomNormalizedDoubleRepresentation(0), c -> 0, new DummyListChromosomeDecoder<>("0"))); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity( + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(0), + c -> 0, new DummyListChromosomeDecoder<>("0"))); + }); } @Test @@ -51,7 +53,7 @@ public void testMutate() { numDifferent++; } } - Assert.assertEquals(1, numDifferent); + Assertions.assertEquals(1, numDifferent); } // stochastic testing for two gene mutation :) @@ -67,7 +69,7 @@ public void testMutate() { numDifferent++; } } - Assert.assertEquals(2, numDifferent); + Assertions.assertEquals(2, numDifferent); } // stochastic testing for three gene mutation :) @@ -83,7 +85,7 @@ public void testMutate() { numDifferent++; } } - Assert.assertEquals(3, numDifferent); + Assertions.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 index 976363a26d..2d09f9b956 100644 --- 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 @@ -19,16 +19,16 @@ 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.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.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; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class IntegralValuedMutationTest { - @Test(expected = GeneticException.class) + @Test public void testCheckValidity() { int min = 0; int max = 10; @@ -36,10 +36,12 @@ public void testCheckValidity() { ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(10), c -> 0, new DummyListChromosomeDecoder<>("0")); IntegralValuedMutation mutation = new IntegralValuedMutation<>(min - 10, max); - mutation.checkValidity(chromosome); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); } - @Test(expected = GeneticException.class) + @Test public void testCheckValidity1() { int min = 0; int max = 10; @@ -47,12 +49,16 @@ public void testCheckValidity1() { ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); IntegralValuedMutation mutation = new IntegralValuedMutation<>(min - 10, max); - mutation.checkValidity(chromosome); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); } - @Test(expected = GeneticException.class) + @Test public void testIntegralValuedMutation() { - new IntegralValuedMutation<>(10, 5); + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedMutation<>(10, 5); + }); } @Test @@ -60,8 +66,8 @@ 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()); + Assertions.assertEquals(min, mutation.getMin()); + Assertions.assertEquals(max, mutation.getMax()); } @Test @@ -70,9 +76,9 @@ public void testMutateGene() { 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 origValue = min + RandomNumberGenerator.getRandomGenerator().nextInt(max - min); int mutatedValued = mutation.mutateGene(origValue); - Assert.assertTrue(min <= mutatedValued && mutatedValued < max); + Assertions.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 index 03a922d93f..c4beecfd39 100644 --- 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 @@ -19,16 +19,16 @@ 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.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.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; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class RealValuedMutationTest { - @Test(expected = GeneticException.class) + @Test public void testCheckValidity() { int min = 0; int max = 10; @@ -36,10 +36,12 @@ public void testCheckValidity() { ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); RealValuedMutation mutation = new RealValuedMutation<>(min - 10, max); - mutation.checkValidity(chromosome); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); } - @Test(expected = GeneticException.class) + @Test public void testCheckValidity1() { double min = 0; double max = 10; @@ -47,12 +49,16 @@ public void testCheckValidity1() { ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); RealValuedMutation mutation = new RealValuedMutation<>(min - 10, max); - mutation.checkValidity(chromosome); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); } - @Test(expected = GeneticException.class) + @Test public void testIntegralValuedMutation() { - new RealValuedMutation<>(10, 5); + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedMutation<>(10, 5); + }); } @Test @@ -60,8 +66,8 @@ 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); + Assertions.assertEquals(min, mutation.getMin(), .001); + Assertions.assertEquals(max, mutation.getMax(), .001); } @Test @@ -70,9 +76,9 @@ public void testMutateGene() { 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 origValue = min + (max - min) * RandomNumberGenerator.getRandomGenerator().nextDouble(); double mutatedValue = mutation.mutateGene(origValue); - Assert.assertTrue(min <= mutatedValue && mutatedValue < max); + Assertions.assertTrue(min <= mutatedValue && mutatedValue < max); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java index 9780057414..604e08db90 100644 --- a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java @@ -17,13 +17,13 @@ package org.apache.commons.math4.ga.mutation.rategenerator; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class AdaptiveLinearMutationRateGeneratorTest { @@ -54,8 +54,8 @@ public void testGenerate() { MutationRateGenerator mutationRateGenerator = new AdaptiveLinearMutationRateGenerator<>(minMutationRate, maxMutationRate); - Assert.assertEquals(maxMutationRate, mutationRateGenerator.generate(chromosome1, stats, 1), .00000001); - Assert.assertEquals(minMutationRate, mutationRateGenerator.generate(chromosome5, stats, 1), .00000001); + Assertions.assertEquals(maxMutationRate, mutationRateGenerator.generate(chromosome1, stats, 1), .00000001); + Assertions.assertEquals(minMutationRate, mutationRateGenerator.generate(chromosome5, stats, 1), .00000001); } } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java index 5962ae476e..e11f10d1e5 100644 --- a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java @@ -17,26 +17,26 @@ package org.apache.commons.math4.ga.mutation.rategenerator; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.apache.commons.math4.ga.utils.RandomGenerator; -import org.junit.Assert; -import org.junit.Test; +import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class ConstantMutationRateGeneratorTest { @Test public void testGenerate() { for (int i = 0; i < 100; i++) { - double mutationRate = RandomGenerator.getRandomGenerator().nextDouble(); + double mutationRate = RandomNumberGenerator.getRandomGenerator().nextDouble(); MutationRateGenerator mutationRateGenerator = new ConstantMutationRateGenerator<>(mutationRate); IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, new DummyListChromosomeDecoder<>("Fixed"), 0, 2); Population population = new ListPopulation<>(1); population.addChromosome(chromosome); - Assert.assertEquals(mutationRate, + Assertions.assertEquals(mutationRate, mutationRateGenerator.generate(chromosome, new PopulationStatisticalSummaryImpl<>(population), i), .0000001); } 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 index ac1c5adcbb..3f4cbf17d3 100644 --- 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 @@ -22,10 +22,10 @@ import org.apache.commons.math4.ga.chromosome.AbstractChromosome; import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.DummyListChromosomeDecoder; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class ListPopulationTest { @@ -45,8 +45,8 @@ public void testGetFittestChromosome() { ListPopulation population = new ListPopulation(chromosomes, 10); - Assert.assertEquals(c3, population.getFittestChromosome()); - Assert.assertNotNull(population.toString()); + Assertions.assertEquals(c3, population.getFittestChromosome()); + Assertions.assertNotNull(population.toString()); } @Test @@ -63,33 +63,40 @@ public void testChromosomes() { population.addChromosomes(chromosomes); - Assert.assertEquals(chromosomes, population.getChromosomes()); + Assertions.assertEquals(chromosomes, population.getChromosomes()); population.setPopulationLimit(50); - Assert.assertEquals(50, population.getPopulationLimit()); + Assertions.assertEquals(50, population.getPopulationLimit()); } - @Test(expected = GeneticException.class) + @Test public void testSetPopulationLimit() { final ListPopulation population = new ListPopulation<>(10); - population.setPopulationLimit(-50); + Assertions.assertThrows(GeneticException.class, () -> { + population.setPopulationLimit(-50); + }); + } - @Test(expected = GeneticException.class) + @Test public void testConstructorPopulationLimitNotPositive() { - new ListPopulation(-10); + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(-10); + }); } - @Test(expected = GeneticException.class) + @Test public void testChromosomeListConstructorPopulationLimitNotPositive() { final ArrayList> chromosomes = new ArrayList<>(); chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"), 0, 2)); - new ListPopulation(chromosomes, -10); + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(chromosomes, -10); + }); } - @Test(expected = GeneticException.class) + @Test public void testConstructorListOfChromosomesBiggerThanPopulationSize() { final ArrayList> chromosomes = new ArrayList<>(); chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, @@ -99,10 +106,13 @@ public void testConstructorListOfChromosomesBiggerThanPopulationSize() { chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, new DummyListChromosomeDecoder<>("0"), 0, 2)); - new ListPopulation(chromosomes, 1); + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(chromosomes, 1); + }); + } - @Test(expected = GeneticException.class) + @Test public void testAddTooManyChromosomes() { final ArrayList> chromosomes = new ArrayList<>(); chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, @@ -114,21 +124,26 @@ public void testAddTooManyChromosomes() { final ListPopulation population = new ListPopulation<>(2); - population.addChromosomes(chromosomes); + Assertions.assertThrows(GeneticException.class, () -> { + population.addChromosomes(chromosomes); + }); + } - @Test(expected = GeneticException.class) + @Test public void testAddTooManyChromosomesSingleCall() { final ListPopulation population = new ListPopulation<>(2); - for (int i = 0; i <= population.getPopulationLimit(); i++) { - population.addChromosome(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, - new DummyListChromosomeDecoder<>("0"), 0, 2)); - } + Assertions.assertThrows(GeneticException.class, () -> { + for (int i = 0; i <= population.getPopulationLimit(); i++) { + population.addChromosome(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + } + }); } - @Test(expected = UnsupportedOperationException.class) + @Test public void testIterator() { final ArrayList> chromosomes = new ArrayList<>(); chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, @@ -143,13 +158,15 @@ public void testIterator() { population.addChromosomes(chromosomes); final Iterator> iter = population.iterator(); - while (iter.hasNext()) { - iter.next(); - iter.remove(); - } + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + while (iter.hasNext()) { + iter.next(); + iter.remove(); + } + }); } - @Test(expected = GeneticException.class) + @Test public void testSetPopulationLimitTooSmall() { final ArrayList> chromosomes = new ArrayList<>(); chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, @@ -161,7 +178,10 @@ public void testSetPopulationLimitTooSmall() { final ListPopulation population = new ListPopulation<>(chromosomes, 3); - population.setPopulationLimit(2); + Assertions.assertThrows(GeneticException.class, () -> { + population.setPopulationLimit(2); + }); + } @Test @@ -176,7 +196,7 @@ public void testNextGeneration() { final ListPopulation population = new ListPopulation<>(chromosomes, 3); - Assert.assertEquals(1, population.nextGeneration(.4).getPopulationSize()); - Assert.assertEquals(0, population.nextGeneration(.1).getPopulationSize()); + Assertions.assertEquals(1, population.nextGeneration(.4).getPopulationSize()); + Assertions.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 index 15bce4c987..c855eb85c2 100644 --- 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 @@ -24,8 +24,8 @@ import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class TournamentSelectionTest { @@ -35,7 +35,7 @@ public class TournamentSelectionTest { public void testSelect() { TournamentSelection ts = new TournamentSelection<>(2); - Assert.assertEquals(2, ts.getArity()); + Assertions.assertEquals(2, ts.getArity()); ListPopulation pop = new ListPopulation<>(100); @@ -46,8 +46,8 @@ public void testSelect() { 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); + Assertions.assertTrue(pair.getFirst().evaluate() > 0); + Assertions.assertTrue(pair.getSecond().evaluate() > 0); } } @@ -58,7 +58,7 @@ private static class DummyChromosome extends AbstractChromosome { } - @Test(expected = GeneticException.class) + @Test public void testNonListPopulation() { Population population = new Population() { @@ -92,13 +92,18 @@ public Chromosome getFittestChromosome() { return null; } }; - new TournamentSelection(5).select(population); + Assertions.assertThrows(GeneticException.class, () -> { + new TournamentSelection(5).select(population); + }); + } - @Test(expected = GeneticException.class) + @Test public void testInvalidArity() { Population population = new ListPopulation<>(2); - new TournamentSelection(2).select(population); + Assertions.assertThrows(GeneticException.class, () -> { + 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 index 4cc1aec683..0b595c58e6 100644 --- 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 @@ -23,8 +23,8 @@ import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class ChromosomeRepresentationUtilsTest { @@ -33,7 +33,7 @@ public void testRandomPermutation() { // never generate an invalid one for (int i = 0; i < 10; i++) { List representation = ChromosomeRepresentationUtils.randomPermutation(10); - Assert.assertNotNull(representation); + Assertions.assertNotNull(representation); } } @@ -46,11 +46,11 @@ public void testIdentityPermutation() { 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)); + Assertions.assertEquals("a", decoded.get(0)); + Assertions.assertEquals("b", decoded.get(1)); + Assertions.assertEquals("c", decoded.get(2)); + Assertions.assertEquals("d", decoded.get(3)); + Assertions.assertEquals("e", decoded.get(4)); } @Test @@ -67,16 +67,16 @@ public int compare(String o1, String 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); + Assertions.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)); + Assertions.assertEquals("b", decodedData.get(0)); + Assertions.assertEquals("b", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); + Assertions.assertEquals("x", decodedData.get(3)); + Assertions.assertEquals("z", decodedData.get(4)); permutation = ChromosomeRepresentationUtils.comparatorPermutation(sequence, new Comparator() { @Override @@ -87,15 +87,15 @@ public int compare(String o1, String o2) { permArr = new Double[sequence.size()]; permArr = permutation.toArray(permArr); - Assert.assertArrayEquals(new Double[] {0.2, 0.6, 0.4, 0.0, 0.8}, permArr); + Assertions.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)); + Assertions.assertEquals("z", decodedData.get(0)); + Assertions.assertEquals("x", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); + Assertions.assertEquals("b", decodedData.get(3)); + Assertions.assertEquals("b", decodedData.get(4)); } @Test @@ -108,23 +108,23 @@ public void testInducedPermutation() { 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)); + Assertions.assertEquals("d", decoded.get(0)); + Assertions.assertEquals("b", decoded.get(1)); + Assertions.assertEquals("c", decoded.get(2)); + Assertions.assertEquals("a", decoded.get(3)); + Assertions.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"); + Assertions.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"); + Assertions.fail("Uncaught exception"); } catch (GeneticException e) { // no-op } @@ -137,9 +137,9 @@ public void testEqualRepr() { decoder); List decodedData = decoder.decode(chromosome); - Assert.assertEquals("a", decodedData.get(0)); - Assert.assertEquals("b", decodedData.get(1)); - Assert.assertEquals("c", decodedData.get(2)); + Assertions.assertEquals("a", decodedData.get(0)); + Assertions.assertEquals("b", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); } @Test @@ -148,7 +148,7 @@ public void testIntegralRepresentation() { int max = 10; List values = ChromosomeRepresentationUtils.randomIntegralRepresentation(100, min, max); for (Integer value : values) { - Assert.assertTrue(min <= value && value < max); + Assertions.assertTrue(min <= value && value < max); } } @@ -156,7 +156,7 @@ public void testIntegralRepresentation() { public void testNormalizedDoubleRepresentation() { List values = ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(100); for (Double value : values) { - Assert.assertTrue(0 <= value && value < 1); + Assertions.assertTrue(0 <= value && value < 1); } } 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 deleted file mode 100644 index 55fc293750..0000000000 --- a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ValidationUtilsTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.math4.ga.utils; - -import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.internal.utils.ValidationUtils; -import org.junit.Test; - -public class ValidationUtilsTest { - - @Test(expected = GeneticException.class) - public void testMinMaxDouble() { - ValidationUtils.checkForMinMax(10.0, 1.0); - } - - @Test(expected = GeneticException.class) - public void testMinMaxInt() { - ValidationUtils.checkForMinMax(10, 1); - } - -} From 8f7975301834eb22256c17f67b64cbcadf109924 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Sun, 14 Nov 2021 11:22:21 +0530 Subject: [PATCH 3/9] minor modifications --- .../AbstractChromosomeCrossoverPolicy.java | 4 ++-- .../math4/ga/crossover/CycleCrossover.java | 6 +++--- .../math4/ga/crossover/NPointCrossover.java | 4 ++-- .../math4/ga/crossover/OnePointCrossover.java | 4 ++-- .../math4/ga/crossover/OrderedCrossover.java | 6 +++--- .../math4/ga/crossover/UniformCrossover.java | 6 +++--- .../AbstractListChromosomeMutationPolicy.java | 8 ++++---- .../ga/mutation/IntegralValuedMutation.java | 4 ++-- .../math4/ga/mutation/RealValuedMutation.java | 4 ++-- .../math4/ga/selection/TournamentSelection.java | 4 ++-- .../ga/utils/ChromosomeRepresentationUtils.java | 8 ++++---- ...Generator.java => RandomProviderManager.java} | 16 ++++------------ .../math4/ga/GeneticAlgorithmTestBinary.java | 2 +- .../chromosome/IntegralValuedChromosomeTest.java | 4 ++-- .../ga/chromosome/RealValuedChromosomeTest.java | 2 +- ...AbstractListChromosomeMutationPolicyTest.java | 4 ++-- .../math4/ga/mutation/BinaryMutationTest.java | 2 +- .../ga/mutation/IntegralValuedMutationTest.java | 4 ++-- .../ga/mutation/RealValuedMutationTest.java | 4 ++-- .../ConstantMutationRateGeneratorTest.java | 4 ++-- 20 files changed, 46 insertions(+), 54 deletions(-) rename commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/{RandomNumberGenerator.java => RandomProviderManager.java} (76%) 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 index 6947ca5f45..c56434d71b 100644 --- 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 @@ -19,7 +19,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * An abstraction to represent the base crossover policy. @@ -35,7 +35,7 @@ public abstract class AbstractChromosomeCrossoverPolicy

implements CrossoverP public ChromosomePair

crossover(final Chromosome

first, final Chromosome

second, final double crossoverRate) { - if (RandomNumberGenerator.getRandomGenerator().nextDouble() < crossoverRate) { + if (RandomProviderManager.getRandomProvider().nextDouble() < crossoverRate) { return crossover(first, second); } else { return new ChromosomePair<>(first, second); 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 index 6b05cfd817..1d0da031e8 100644 --- 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 @@ -17,14 +17,14 @@ 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.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * Cycle Crossover [CX] builds offspring from ordered chromosomes by @@ -124,7 +124,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List indices = new ArrayList<>(length); // determine the starting index - int idx = randomStart ? RandomNumberGenerator.getRandomGenerator().nextInt(length) : 0; + int idx = randomStart ? RandomProviderManager.getRandomProvider().nextInt(length) : 0; int cycle = 1; while (visitedIndices.size() < length) { 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 index e5538b88bb..f0313c13b0 100644 --- 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 @@ -22,7 +22,7 @@ import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; import org.apache.commons.rng.UniformRandomProvider; /** @@ -118,7 +118,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List child1Rep = new ArrayList<>(length); final List child2Rep = new ArrayList<>(length); - final UniformRandomProvider random = RandomNumberGenerator.getRandomGenerator(); + final UniformRandomProvider random = RandomProviderManager.getRandomProvider(); List c1 = child1Rep; List c2 = 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 index d90c869cec..99372377dc 100644 --- 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 @@ -21,7 +21,7 @@ import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * One point crossover policy. A random crossover point is selected and the @@ -83,7 +83,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List child2Rep = new ArrayList<>(length); // select a crossover point at random (0 and length makes no sense) - final int crossoverIndex = 1 + (RandomNumberGenerator.getRandomGenerator().nextInt(length - 2)); + final int crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextInt(length - 2)); // copy the first part for (int i = 0; i < crossoverIndex; i++) { 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 index 58eef774db..24608f9da7 100644 --- 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 @@ -17,15 +17,15 @@ 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.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; import org.apache.commons.rng.UniformRandomProvider; /** @@ -84,7 +84,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final Set child1Set = new HashSet<>(length); final Set child2Set = new HashSet<>(length); - final UniformRandomProvider random = RandomNumberGenerator.getRandomGenerator(); + final UniformRandomProvider random = RandomProviderManager.getRandomProvider(); // choose random points, making sure that lb < ub. final int a = random.nextInt(length); int b; 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 index c624d94439..23292f712a 100644 --- 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 @@ -17,13 +17,13 @@ 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.Chromosome; import org.apache.commons.math4.ga.chromosome.ChromosomePair; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; import org.apache.commons.rng.UniformRandomProvider; /** @@ -104,7 +104,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List child1Rep = new ArrayList<>(length); final List child2Rep = new ArrayList<>(length); - final UniformRandomProvider random = RandomNumberGenerator.getRandomGenerator(); + final UniformRandomProvider random = RandomProviderManager.getRandomProvider(); for (int index = 0; index < length; index++) { 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 index de757965ca..70879e313b 100644 --- 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 @@ -24,7 +24,7 @@ import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; import org.apache.commons.math4.ga.chromosome.Chromosome; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * This abstraction represents an abstract mutation policy for ListChromosomes. @@ -80,10 +80,10 @@ protected Set getMutableGeneIndexes(int length, double mutationRate) { if (chromosomeMutationRate >= 1) { final int noOfMutation = (int) Math.round(chromosomeMutationRate); while (indexSet.size() < noOfMutation) { - indexSet.add(RandomNumberGenerator.getRandomGenerator().nextInt(length)); + indexSet.add(RandomProviderManager.getRandomProvider().nextInt(length)); } - } else if (RandomNumberGenerator.getRandomGenerator().nextDouble() < chromosomeMutationRate) { - indexSet.add(RandomNumberGenerator.getRandomGenerator().nextInt(length)); + } else if (RandomProviderManager.getRandomProvider().nextDouble() < chromosomeMutationRate) { + indexSet.add(RandomProviderManager.getRandomProvider().nextInt(length)); } return indexSet; 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 index a587a762e7..6d661ac32f 100644 --- 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 @@ -19,7 +19,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * Mutation for {@link IntegralValuedChromosome}. Randomly changes few genes. @@ -82,7 +82,7 @@ protected void checkValidity(Chromosome

original) { */ @Override protected Integer mutateGene(Integer originalValue) { - return min + RandomNumberGenerator.getRandomGenerator().nextInt(max - min); + return min + RandomProviderManager.getRandomProvider().nextInt(max - min); } } 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 index a24f46bba2..f586757313 100644 --- 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 @@ -19,7 +19,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * This class mutates real-valued chromosome. @@ -91,7 +91,7 @@ protected void checkValidity(Chromosome

original) { */ @Override protected Double mutateGene(Double originalValue) { - return min + RandomNumberGenerator.getRandomGenerator().nextDouble() * (max - min); + return min + RandomProviderManager.getRandomProvider().nextDouble() * (max - min); } } 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 index f59ac1780a..f0f60b916b 100644 --- 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 @@ -25,7 +25,7 @@ import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * Tournament selection scheme. Each of the two selected chromosomes is selected @@ -86,7 +86,7 @@ private Chromosome

tournament(final ListPopulation

population) { for (int i = 0; i < this.arity; i++) { // select a random individual and add it to the tournament - final int rind = RandomNumberGenerator.getRandomGenerator().nextInt(chromosomes.size()); + final int rind = RandomProviderManager.getRandomProvider().nextInt(chromosomes.size()); selectedChromosomes.add(chromosomes.get(rind)); // do not select it again chromosomes.remove(rind); 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 index e7dc0c2e64..4261b147f4 100644 --- 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 @@ -39,7 +39,7 @@ public interface ChromosomeRepresentationUtils { * @return representation of a random permutation */ static List randomPermutation(final int l) { - final UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); final List repr = new ArrayList<>(l); for (int i = 0; i < l; i++) { repr.add(randomProvider.nextDouble()); @@ -126,7 +126,7 @@ static List inducedPermutation(final List originalData, final Lis * @return a random binary array of length length */ static List randomIntegralRepresentation(final int length, final int min, final int max) { - final UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); final List rList = new ArrayList<>(length); for (int j = 0; j < length; j++) { rList.add(min + randomProvider.nextInt(max - min)); @@ -141,7 +141,7 @@ static List randomIntegralRepresentation(final int length, final int mi * @return a random binary array of length length */ static List randomBinaryRepresentation(final int length) { - final UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); // random binary list final List rList = new ArrayList<>(length); for (int j = 0; j < length; j++) { @@ -173,7 +173,7 @@ static List randomDoubleRepresentation(final int l, double min, double m throw new GeneticException(GeneticException.TOO_LARGE, min, max); } final double range = max - min; - final UniformRandomProvider randomProvider = RandomNumberGenerator.getRandomGenerator(); + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); final List repr = new ArrayList<>(l); for (int i = 0; i < l; i++) { repr.add(min + randomProvider.nextDouble() * range); diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomNumberGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java similarity index 76% rename from commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomNumberGenerator.java rename to commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java index 710b6c18f0..0c01896524 100644 --- a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomNumberGenerator.java +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java @@ -25,31 +25,23 @@ * An utility to generate per thread {@link UniformRandomProvider} instance. * @since 4.0 */ -public final class RandomNumberGenerator { +public final class RandomProviderManager { /** The default RandomSource for random number generation. **/ private static RandomSource randomSource = RandomSource.XO_RO_SHI_RO_128_PP; - /** - * Sets the random source for this random generator. - * @param randomSource - */ - public static void configure(RandomSource randomSource) { - RandomNumberGenerator.randomSource = randomSource; - } - /** * constructs the singleton instance. */ - private RandomNumberGenerator() { + private RandomProviderManager() { } /** * Returns the (static) random generator. * @return the static random generator shared by GA implementation classes */ - public static UniformRandomProvider getRandomGenerator() { - return ThreadLocalRandomSource.current(RandomNumberGenerator.randomSource); + public static UniformRandomProvider getRandomProvider() { + return ThreadLocalRandomSource.current(RandomProviderManager.randomSource); } } 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 index f72d2afa9a..203d3b34ee 100644 --- 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 @@ -17,6 +17,7 @@ package org.apache.commons.math4.ga; import java.lang.reflect.Field; + import java.util.LinkedList; import java.util.List; @@ -35,7 +36,6 @@ import org.apache.commons.math4.ga.selection.TournamentSelection; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** 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 index b8326bb501..e2a89fe4db 100644 --- 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 @@ -37,7 +37,7 @@ public void testIntegralValuedChromosome() { IntegralValuedChromosome chromosome1 = new IntegralValuedChromosome<>( ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max).toArray(new Integer[10]), - c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); Assertions.assertEquals(min, chromosome1.getMin()); Assertions.assertEquals(max, chromosome1.getMax()); } @@ -48,7 +48,7 @@ public void testCheckValidity() { int max = 10; Assertions.assertThrows(GeneticException.class, () -> { new IntegralValuedChromosome<>(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), - c -> 0, new DummyListChromosomeDecoder<>("0"), max, min); + c -> 0, new DummyListChromosomeDecoder<>("0"), max, min); }); } 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 index 381ca388ed..333e2caa32 100644 --- 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 @@ -68,7 +68,7 @@ public void testCheckValidity1() { int max = 10; Assertions.assertThrows(GeneticException.class, () -> { new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min - 10, max + 10), - c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); }); } 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 index 96630588a4..17c481a200 100644 --- 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 @@ -17,7 +17,7 @@ package org.apache.commons.math4.ga.mutation; import org.apache.commons.math4.ga.chromosome.Chromosome; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -29,7 +29,7 @@ public void testGetMutableGeneIndexes() { @Override protected Integer mutateGene(Integer originalValue) { - return RandomNumberGenerator.getRandomGenerator().nextInt(2); + return RandomProviderManager.getRandomProvider().nextInt(2); } @Override 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 index 22b7b7b1e1..d06d2e0674 100644 --- 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 @@ -32,7 +32,7 @@ public void testCheckValidity() { Assertions.assertThrows(GeneticException.class, () -> { mutation.checkValidity( new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(0), - c -> 0, new DummyListChromosomeDecoder<>("0"))); + c -> 0, new DummyListChromosomeDecoder<>("0"))); }); } 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 index 2d09f9b956..0d6135fdcf 100644 --- 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 @@ -22,7 +22,7 @@ import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -76,7 +76,7 @@ public void testMutateGene() { int max = 10; IntegralValuedMutation mutation = new IntegralValuedMutation<>(min, max); for (int i = 0; i < 100; i++) { - int origValue = min + RandomNumberGenerator.getRandomGenerator().nextInt(max - min); + int origValue = min + RandomProviderManager.getRandomProvider().nextInt(max - min); int mutatedValued = mutation.mutateGene(origValue); Assertions.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 index c4beecfd39..015801b377 100644 --- 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 @@ -22,7 +22,7 @@ import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -76,7 +76,7 @@ public void testMutateGene() { double max = 10; RealValuedMutation mutation = new RealValuedMutation<>(min, max); for (int i = 0; i < 100; i++) { - double origValue = min + (max - min) * RandomNumberGenerator.getRandomGenerator().nextDouble(); + double origValue = min + (max - min) * RandomProviderManager.getRandomProvider().nextDouble(); double mutatedValue = mutation.mutateGene(origValue); Assertions.assertTrue(min <= mutatedValue && mutatedValue < max); } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java index e11f10d1e5..0c7793fdd9 100644 --- a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java @@ -21,7 +21,7 @@ import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; import org.apache.commons.math4.ga.population.ListPopulation; import org.apache.commons.math4.ga.population.Population; -import org.apache.commons.math4.ga.utils.RandomNumberGenerator; +import org.apache.commons.math4.ga.utils.RandomProviderManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -30,7 +30,7 @@ public class ConstantMutationRateGeneratorTest { @Test public void testGenerate() { for (int i = 0; i < 100; i++) { - double mutationRate = RandomNumberGenerator.getRandomGenerator().nextDouble(); + double mutationRate = RandomProviderManager.getRandomProvider().nextDouble(); MutationRateGenerator mutationRateGenerator = new ConstantMutationRateGenerator<>(mutationRate); IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, new DummyListChromosomeDecoder<>("Fixed"), 0, 2); From f2d840b6a86f5c5cd6fb7d03a052b8131617bf58 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Tue, 21 Dec 2021 12:50:29 +0530 Subject: [PATCH 4/9] An optimized data structure implementation for binary chromosome --- .../dimension2/Dimension2Decoder.java | 29 +- .../Dimension2FunctionAdaptiveOptimizer.java | 8 +- .../Dimension2FunctionOptimizer.java | 4 +- .../dimensionN/DimensionNDecoder.java | 34 +-- .../DimensionNFunctionAdaptiveOptimizer.java | 8 +- .../DimensionNFunctionOptimizer.java | 4 +- .../ga/chromosome/AbstractChromosome.java | 2 +- .../ga/chromosome/AbstractListChromosome.java | 2 +- .../math4/ga/chromosome/BinaryChromosome.java | 262 ++++++++++++++++-- .../ga/crossover/OnePointBinaryCrossover.java | 115 ++++++++ .../math4/ga/crossover/OnePointCrossover.java | 2 +- .../internal/exception/GeneticException.java | 6 + .../math4/ga/mutation/BinaryMutation.java | 102 ++++++- .../ga/mutation/IntegralValuedMutation.java | 2 +- .../math4/ga/mutation/MutationPolicy.java | 1 + .../math4/ga/mutation/RealValuedMutation.java | 2 +- .../math4/ga/population/ListPopulation.java | 2 +- .../ga/selection/TournamentSelection.java | 2 +- .../utils/ChromosomeRepresentationUtils.java | 37 ++- ... => GeneticAlgorithmTestBinaryOneMax.java} | 57 +++- .../ga/chromosome/BinaryChromosomeTest.java | 62 ++++- ...ractListChromosomeCrossoverPolicyTest.java | 6 +- .../OnePointBinaryCrossoverTest.java | 73 +++++ .../TransparentListChromosomeDecoderTest.java | 2 +- .../math4/ga/dummy/DummyListChromosome.java | 2 +- .../math4/ga/mutation/BinaryMutationTest.java | 48 ++-- 26 files changed, 736 insertions(+), 138 deletions(-) create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java rename commons-math-ga/src/test/java/org/apache/commons/math4/ga/{GeneticAlgorithmTestBinary.java => GeneticAlgorithmTestBinaryOneMax.java} (75%) create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java index a8ef79e17b..c3b9adc439 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java @@ -16,36 +16,31 @@ */ package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; -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; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.Decoder; /** * Decoder to convert chromosome's binary genotype to phenotype * {@link Dimension2Coordinate}. */ -public class Dimension2Decoder extends AbstractListChromosomeDecoder { +public class Dimension2Decoder implements Decoder { /** - * decode the binary representation of chromosome to + * Decode the binary representation of chromosome to * {@link Dimension2Coordinate}. - * @param chromosome The {@link AbstractListChromosome} + * @param chromosome The {@link Chromosome} */ @Override - protected Dimension2Coordinate decode(AbstractListChromosome chromosome) { - final BinaryChromosome binaryChromosome = - (BinaryChromosome) chromosome; - final List alleles = binaryChromosome.getRepresentation(); + public Dimension2Coordinate decode(Chromosome chromosome) { + final BinaryChromosome binaryChromosome = (BinaryChromosome) chromosome; + final long alleles = binaryChromosome.getRepresentation()[0]; - final StringBuilder allelesStr = new StringBuilder(); - for (Integer allele : alleles) { - allelesStr.append(Integer.toBinaryString(allele)); - } + long mask1 = ~(Long.MAX_VALUE << 12); + long mask2 = ~(Long.MAX_VALUE << 24) ^ mask1; - final double x = Integer.parseInt(allelesStr.substring(0, 12), 2) / 100.0; - final double y = Integer.parseInt(allelesStr.substring(12, 24), 2) / 100.0; + double x = (alleles & mask1) / 100d; + double y = ((alleles & mask2) >> 12) / 100d; return new Dimension2Coordinate(x, y); } diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java index 6f5de86d29..66f07e5f8d 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java @@ -23,7 +23,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.convergence.StoppingCondition; import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; -import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; import org.apache.commons.math4.ga.crossover.rategenerator.ConstantCrossoverRateGenerator; import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; @@ -59,8 +59,8 @@ public static void main(String[] args) { final Dimension2FunctionAdaptiveOptimizer optimizer = new Dimension2FunctionAdaptiveOptimizer(); - final ConvergenceListenerRegistry convergenceListenerRegistry = - ConvergenceListenerRegistry.getInstance(); + final ConvergenceListenerRegistry convergenceListenerRegistry = ConvergenceListenerRegistry + .getInstance(); convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); convergenceListenerRegistry .addConvergenceListener(new Dim2GraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); @@ -72,7 +72,7 @@ private void optimize(Population initial) { // initialize a new genetic algorithm final AdaptiveGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm<>( - new OnePointCrossover(), new ConstantCrossoverRateGenerator<>(1), + new OnePointBinaryCrossover(), new ConstantCrossoverRateGenerator<>(1), new BinaryMutation(), new AdaptiveLinearMutationRateGenerator<>(Constants.AVERAGE_MUTATION_RATE / 2, Constants.AVERAGE_MUTATION_RATE * 2), diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java index c96751afd4..1d6a3fa6b5 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java @@ -23,7 +23,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.convergence.StoppingCondition; import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; -import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; import org.apache.commons.math4.ga.mutation.BinaryMutation; @@ -74,7 +74,7 @@ public void optimize(Population initial) { // initialize a new genetic algorithm final GeneticAlgorithm ga = new GeneticAlgorithm<>( - new OnePointCrossover(), Constants.CROSSOVER_RATE, + new OnePointBinaryCrossover(), Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, new TournamentSelection(TOURNAMENT_SIZE), Constants.ELITISM_RATE); diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java index 7146be6f54..a8a33972dd 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java @@ -19,37 +19,33 @@ import java.util.ArrayList; 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; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.Decoder; /** * Decoder to convert chromosome's binary genotype to phenotype * {@link DimensionNCoordinate}. */ -public class DimensionNDecoder extends AbstractListChromosomeDecoder { +public class DimensionNDecoder implements Decoder { /** - * decode the binary representation of chromosome to {@link DimensionNCoordinate}. - * @param chromosome The {@link AbstractListChromosome} + * decode the binary representation of chromosome to + * {@link DimensionNCoordinate}. + * @param chromosome The {@link Chromosome} */ @Override - protected DimensionNCoordinate 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)); - } - - List values = new ArrayList<>(); - for (int i = 0; i < allelesStr.length(); i += 12) { - values.add(Integer.parseInt(allelesStr.substring(i, i + 12), 2) / 100.0); + public DimensionNCoordinate decode(Chromosome chromosome) { + final BinaryChromosome binaryChromosome = (BinaryChromosome) chromosome; + final long length = binaryChromosome.getLength(); + List coordinates = new ArrayList<>(); + + for (int i = 0; i < length; i += 12) { + final String dimensionStrValue = binaryChromosome.getStringRepresentation(i, i + 12); + coordinates.add(Integer.parseUnsignedInt(dimensionStrValue, 2) / 100d); } - return new DimensionNCoordinate(values); + return new DimensionNCoordinate(coordinates); } } diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java index a7e88ff5ed..14fc7bcbf1 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java @@ -24,7 +24,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.convergence.StoppingCondition; import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; -import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; import org.apache.commons.math4.ga.crossover.rategenerator.AdaptiveLinearMaximumRankBasedCrossoverRateGenerator; import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; @@ -60,8 +60,8 @@ public static void main(String[] args) { final DimensionNFunctionAdaptiveOptimizer optimizer = new DimensionNFunctionAdaptiveOptimizer(); - final ConvergenceListenerRegistry convergenceListenerRegistry = - ConvergenceListenerRegistry.getInstance(); + final ConvergenceListenerRegistry convergenceListenerRegistry = ConvergenceListenerRegistry + .getInstance(); convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); convergenceListenerRegistry .addConvergenceListener(new DimNGraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); @@ -73,7 +73,7 @@ private void optimize(Population initial) { // initialize a new genetic algorithm final AbstractGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm( - new OnePointCrossover(), + new OnePointBinaryCrossover(), new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>(Constants.CROSSOVER_RATE / 2, Constants.CROSSOVER_RATE), new BinaryMutation<>(), new AdaptiveLinearMutationRateGenerator<>(0, Constants.AVERAGE_MUTATION_RATE), diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java index e039e6e436..5791b05769 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java @@ -23,7 +23,7 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.convergence.StoppingCondition; import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; -import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; import org.apache.commons.math4.ga.mutation.BinaryMutation; @@ -74,7 +74,7 @@ public void optimize(Population initial) { // initialize a new genetic algorithm final GeneticAlgorithm ga = new GeneticAlgorithm<>( - new OnePointCrossover(), Constants.CROSSOVER_RATE, + new OnePointBinaryCrossover(), Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, new TournamentSelection<>(TOURNAMENT_SIZE), Constants.ELITISM_RATE); 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 index a34bc32e84..4f61ace342 100644 --- 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 @@ -71,7 +71,7 @@ protected FitnessFunction

getFitnessFunction() { * Returns the decoder instance. * @return decoder */ - protected Decoder

getDecoder() { + public Decoder

getDecoder() { return decoder; } 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 index ac85e553a2..9e1676d4f7 100644 --- 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 @@ -103,7 +103,7 @@ public int getLength() { */ @SuppressWarnings("unchecked") @Override - protected AbstractListChromosomeDecoder getDecoder() { + public AbstractListChromosomeDecoder getDecoder() { return (AbstractListChromosomeDecoder) super.getDecoder(); } 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 index 84975c7a39..a9ef839a70 100644 --- 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 @@ -18,45 +18,271 @@ import java.util.List; -import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import java.util.Objects; + +import org.apache.commons.math4.ga.decoder.Decoder; import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; /** - * Chromosome represented by a vector of 0s and 1s. + * BinaryChromosome represented by a vector of 0s and 1s. * @param

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

extends IntegralValuedChromosome

{ +public class BinaryChromosome

extends AbstractChromosome

{ + + /** + * maximum allowed length of binary chromosome. + */ + public static final long MAX_LENGTH = Integer.MAX_VALUE; + + /** + * length of binary chromosome. + */ + private final long length; + + /** + * binary representation of chromosome. + */ + private final long[] representation; /** * @param representation Internal representation of chromosome. + * @param length length of chromosome * @param fitnessFunction The {@link FitnessFunction} - * @param decoder The {@link AbstractListChromosomeDecoder} + * @param decoder The {@link Decoder} */ - public BinaryChromosome(List representation, + public BinaryChromosome(List representation, + long length, FitnessFunction

fitnessFunction, - AbstractListChromosomeDecoder decoder) { - super(representation, fitnessFunction, decoder, 0, 2); + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + checkMaximumLength(length); + this.length = length; + this.representation = new long[representation.size()]; + for (int i = 0; i < representation.size(); i++) { + this.representation[i] = representation.get(i); + } } /** * @param representation Internal representation of chromosome. + * @param length length of chromosome * @param fitnessFunction The {@link FitnessFunction} - * @param decoder The {@link AbstractListChromosomeDecoder} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(Long[] representation, + long length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + checkMaximumLength(length); + this.length = length; + this.representation = new long[representation.length]; + for (int i = 0; i < representation.length; i++) { + this.representation[i] = representation[i]; + } + } + + /** + * @param inputRepresentation Internal representation of chromosome. + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} */ - public BinaryChromosome(Integer[] representation, + public BinaryChromosome(long[] inputRepresentation, + long length, FitnessFunction

fitnessFunction, - AbstractListChromosomeDecoder decoder) { - super(representation, fitnessFunction, decoder, 0, 2); + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(inputRepresentation); + checkMaximumLength(length); + if (length <= (inputRepresentation.length - 1) * Long.SIZE || length > inputRepresentation.length * Long.SIZE) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "provided length does not match expected representation"); + } + this.length = length; + this.representation = new long[inputRepresentation.length]; + System.arraycopy(inputRepresentation, 0, representation, 0, inputRepresentation.length); + } + + /** + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(String representation, FitnessFunction

fitnessFunction, Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + this.length = representation.length(); + this.representation = encode(representation); + } + + /** + * Checks the input chromosome length against predefined maximum length. + * @param chromosomeLength input chromsome length + */ + protected void checkMaximumLength(long chromosomeLength) { + if (chromosomeLength > MAX_LENGTH) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "length exceeded the max length " + MAX_LENGTH); + } + } + + /** + * Validates the string representation. + * @param stringRepresentation binary string representation of chromosome + */ + private void validateStringRepresentation(String stringRepresentation) { + char allele = '\0'; + final int chromosomeLength = stringRepresentation.length(); + for (int i = 0; i < chromosomeLength; i++) { + if ((allele = stringRepresentation.charAt(i)) != '0' && allele != '1') { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "Only 0 or 1 are acceptable as characters."); + } + } + } + + /** + * encodes the binary string representation as an array of long. + * @param stringRepresentation binary string + * @return encoded representation + */ + private long[] encode(String stringRepresentation) { + validateStringRepresentation(stringRepresentation); + final int chromosomeLength = stringRepresentation.length(); + final int arraySize = (int) Math.ceil(chromosomeLength / (double) Long.SIZE); + final long[] encodedRepresentation = new long[arraySize]; + final int offset = (int) (chromosomeLength % Long.SIZE == 0 ? 0 : Long.SIZE - chromosomeLength % Long.SIZE); + encodedRepresentation[0] = Long.parseUnsignedLong(stringRepresentation.substring(0, Long.SIZE - offset), 2); + for (int i = Long.SIZE - offset, j = 1; i < chromosomeLength; i += Long.SIZE, j++) { + encodedRepresentation[j] = Long.parseUnsignedLong(stringRepresentation.substring(i, i + Long.SIZE), 2); + } + return encodedRepresentation; + } + + /** + * Returns the chromosome length. + * @return length + */ + public long getLength() { + return length; + } + + /** + * Returns the binary representation. + * @return representation + */ + public long[] getRepresentation() { + final long[] clonedRepresentation = new long[representation.length]; + System.arraycopy(representation, 0, clonedRepresentation, 0, representation.length); + return clonedRepresentation; + } + + /** + * Returns the binary string representation of the chromosome. + * @return the string representation + */ + public String getStringRepresentation() { + if (length > Integer.MAX_VALUE) { + throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, length); + } + return getStringRepresentation(0, length); + } + + /** + * Returns the binary string representation of the chromosome alleles from + * start(inclusive) to end(exclusive) index. + * @param start start allele/gene index(inclusive) + * @param end end allele/gene index(exclusive) + * @return the string representation + */ + public String getStringRepresentation(long start, long end) { + if (start >= end) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "start " + start + " is greater than end " + end); + } + if (end - start > Integer.MAX_VALUE) { + throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, end - start); + } + final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE); + final long offsettedStart = offset + start; + final long offsettedEnd = offset + end; + final int startAlleleBlockIndex = (int) (offsettedStart / Long.SIZE); + final int endAlleleBlockIndex = (int) (offsettedEnd / Long.SIZE); + final int startAlleleElementIndex = (int) (offsettedStart % Long.SIZE); + final int endAlleleElementIndex = (int) (offsettedEnd % Long.SIZE); + + if (startAlleleBlockIndex == endAlleleBlockIndex) { + return getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex, + endAlleleElementIndex); + } else { + final StringBuilder allelesStrRepresentation = new StringBuilder(); + + // extract string representation of first allele block. + allelesStrRepresentation + .append(getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex)); + + // extract string representation of all allele blocks except first and last. + for (int i = startAlleleBlockIndex + 1; i < endAlleleBlockIndex; i++) { + allelesStrRepresentation.append(getAlleleBlockString(i)); + } + + // extract string representation from last allele block if end allele index != + // 0. + if (endAlleleElementIndex != 0) { + allelesStrRepresentation + .append(getAlleleBlockString(endAlleleBlockIndex).substring(0, endAlleleElementIndex)); + } + + return allelesStrRepresentation.toString(); + } + } + + /** + * Returns the allele block as binary string representation. + * @param alleleBlockIndex index of allele block i.e. array element + * @return the string representation + */ + private String getAlleleBlockString(final int alleleBlockIndex) { + return prepareZeroPrefix(representation[alleleBlockIndex] == 0 ? Long.SIZE - 1 : + Long.numberOfLeadingZeros(representation[alleleBlockIndex])) + + Long.toUnsignedString(representation[alleleBlockIndex], 2); + } + + /** + * Prepares zero prefix for binary chromosome. + * @param count number of zeros + * @return prefix + */ + private String prepareZeroPrefix(int count) { + final StringBuilder zeroPrefix = new StringBuilder(); + for (int i = 0; i < count; i++) { + zeroPrefix.append('0'); + } + return zeroPrefix.toString(); } /** * {@inheritDoc} */ @Override - public BinaryChromosome

newChromosome(List chromosomeRepresentation) { - return new BinaryChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder()); + public FitnessFunction

getFitnessFunction() { + return super.getFitnessFunction(); + } + + /** + * Creates a new chromosome with provided parameters. + * @param chromosomeRepresentation the representation + * @param chromosomeLength length of chromosome + * @return chromosome + */ + public BinaryChromosome

newChromosome(long[] chromosomeRepresentation, long chromosomeLength) { + return new BinaryChromosome

(chromosomeRepresentation, chromosomeLength, getFitnessFunction(), getDecoder()); } /** @@ -64,14 +290,14 @@ public BinaryChromosome

newChromosome(List chromosomeRepresentation) * @param

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

BinaryChromosome

randomChromosome(int length, FitnessFunction

fitnessFunction, - AbstractListChromosomeDecoder decoder) { - return new BinaryChromosome<>(ChromosomeRepresentationUtils.randomBinaryRepresentation(length), fitnessFunction, - decoder); + Decoder

decoder) { + return new BinaryChromosome

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

the phenotype + */ +public class OnePointBinaryCrossover

extends AbstractChromosomeCrossoverPolicy

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

crossover(Chromosome

first, Chromosome

second) { + + if (!(first instanceof BinaryChromosome && second instanceof BinaryChromosome)) { + throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME); + } + final BinaryChromosome

firstChromosome = (BinaryChromosome

) first; + final BinaryChromosome

secondChromosome = (BinaryChromosome

) second; + + final long alleleCount = firstChromosome.getLength(); + + // array representations of the parents + final long[] parent1Rep = firstChromosome.getRepresentation(); + final long[] parent2Rep = secondChromosome.getRepresentation(); + + // and of the children + final long[] child1Rep = new long[parent1Rep.length]; + final long[] child2Rep = new long[parent2Rep.length]; + + // select a crossover point at random (0 and length makes no sense) + final long crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextLong(alleleCount - 1)); + + final int offset = (int) (alleleCount % Long.SIZE == 0 ? 0 : Long.SIZE - alleleCount % Long.SIZE); + final long offsettedCrossoverIndex = crossoverIndex + offset; + + final int crossoverBlockIndex = (int) (offsettedCrossoverIndex / Long.SIZE); + final int crossoverBlockAlleleIndex = (int) offsettedCrossoverIndex % Long.SIZE; + + if (crossoverBlockAlleleIndex == 0) { + // if the offsetted-crossover index is divisible by + // Long.SIZE then first copy all + // elements of previous array elements. + for (int i = 0; i < crossoverBlockIndex; i++) { + child1Rep[i] = parent1Rep[i]; + child2Rep[i] = parent2Rep[i]; + } + // copy all elements from crossover block index. + for (int i = crossoverBlockIndex; i < parent1Rep.length; i++) { + child1Rep[i] = parent2Rep[i]; + child2Rep[i] = parent1Rep[i]; + } + } else { + // copy all parent array elements to child till crossover block index - 1. + for (int i = 0; i < crossoverBlockIndex; i++) { + child1Rep[i] = parent1Rep[i]; + child2Rep[i] = parent2Rep[i]; + } + // do exchange of alleles of the array element indexed at crossover block index. + final long parent1CrossoverBlockRep = parent1Rep[crossoverBlockIndex]; + final long parent2CrossoverBlockRep = parent2Rep[crossoverBlockIndex]; + final long leftMask = Long.MIN_VALUE >> crossoverBlockAlleleIndex - 1; + final long rightMask = crossoverBlockAlleleIndex != 1 ? + (long) Math.pow(2, Long.SIZE - crossoverBlockAlleleIndex) - 1 : + Long.MAX_VALUE; + + final long child1CrossoverBlockRep = (parent1CrossoverBlockRep & leftMask) | + (parent2CrossoverBlockRep & rightMask); + final long child2CrossoverBlockRep = (parent2CrossoverBlockRep & leftMask) | + (parent1CrossoverBlockRep & rightMask); + + child1Rep[crossoverBlockIndex] = child1CrossoverBlockRep; + child2Rep[crossoverBlockIndex] = child2CrossoverBlockRep; + + // Copy all the alleles which belong to array elements having index > + // crossover block index. + if (crossoverBlockIndex < parent1Rep.length - 1) { + for (int i = crossoverBlockIndex + 1; i < parent1Rep.length; i++) { + child1Rep[i] = parent2Rep[i]; + child2Rep[i] = parent1Rep[i]; + } + } + } + + final BinaryChromosome

childChromosome1 = new BinaryChromosome<>(child1Rep, alleleCount, + firstChromosome.getFitnessFunction(), firstChromosome.getDecoder()); + final BinaryChromosome

childChromosome2 = new BinaryChromosome<>(child2Rep, alleleCount, + secondChromosome.getFitnessFunction(), secondChromosome.getDecoder()); + + return new ChromosomePair<>(childChromosome1, childChromosome2); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java index 99372377dc..2c6da54ccb 100644 --- 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 @@ -83,7 +83,7 @@ protected ChromosomePair

mate(final AbstractListChromosome first, final List child2Rep = new ArrayList<>(length); // select a crossover point at random (0 and length makes no sense) - final int crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextInt(length - 2)); + final int crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextInt(length - 1)); // copy the first part for (int i = 0; i < crossoverIndex; i++) { diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java index bb0666460e..7209472c37 100644 --- a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java @@ -79,6 +79,12 @@ public class GeneticException extends RuntimeException { */ public static final String DIFFERENT_ORIG_AND_PERMUTED_DATA = "Different original and permuted data"; + /** + * Error message for chromosome length larger than expected. + */ + public static final String LENGTH_TOO_LARGE = "Chromosome length larger than {0} not supported for" + + " string representation."; + /** Serializable version identifier. */ private static final long serialVersionUID = 20210516L; 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 index 138a90f323..2470f38022 100644 --- 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 @@ -16,38 +16,120 @@ */ package org.apache.commons.math4.ga.mutation; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + import org.apache.commons.math4.ga.chromosome.BinaryChromosome; import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; /** * Mutation for {@link BinaryChromosome}s. Randomly changes few genes. * @param

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

extends IntegralValuedMutation

{ - - public BinaryMutation() { - super(0, 2); - } +public class BinaryMutation

implements MutationPolicy

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

mutate(Chromosome

original, double mutationRate) { + // check for validity. + checkValidity(original); + final BinaryChromosome

chromosome = (BinaryChromosome

) original; + final long[] representation = chromosome.getRepresentation(); + final long[] newRep = new long[representation.length]; + System.arraycopy(representation, 0, newRep, 0, representation.length); + + final Map> mutableGeneIndexMap = getMutableGeneIndexes(chromosome.getLength(), + mutationRate); + for (Entry> entry : mutableGeneIndexMap.entrySet()) { + final int alleleBlockIndex = entry.getKey(); + long mask = 0; + final Set alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex); + for (int index : alleleElementIndexes) { + mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index); + } + newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask; + } +// for (int alleleBlockIndex : mutableGeneIndexMap.keySet()) { +// long mask = 0; +// final Set alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex); +// for (int index : alleleElementIndexes) { +// mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index); +// } +// newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask; +// } + + return chromosome.newChromosome(newRep, chromosome.getLength()); + } + + /** + * Checks input chromosome validity. + * @param original chromosome to be mutated + */ protected void checkValidity(Chromosome

original) { - super.checkValidity(original); if (!BinaryChromosome.class.isAssignableFrom(original.getClass())) { throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); } } /** - * {@inheritDoc} + * 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 */ - @Override - protected Integer mutateGene(Integer originalValue) { - return originalValue == 0 ? 1 : 0; + protected Map> getMutableGeneIndexes(long length, double mutationRate) { + + // calculate the total mutation rate of all the alleles i.e. chromosome. + final double chromosomeMutationRate = mutationRate * length; + final Map> indexMap = new HashMap<>(); + + // if chromosomeMutationRate >= 1 then more than one allele will be mutated. + if (chromosomeMutationRate >= 1) { + final int noOfMutation = (int) Math.round(chromosomeMutationRate); + final Set mutationIndexes = new HashSet<>(); + for (int i = 0; i < noOfMutation; i++) { + final long mutationIndex = generateMutationIndex(length, mutationIndexes); + mutationIndexes.add(mutationIndex); + updateIndexMap(indexMap, length, mutationIndex); + } + } else if (RandomProviderManager.getRandomProvider().nextDouble() < chromosomeMutationRate) { + updateIndexMap(indexMap, length); + } + return indexMap; + } + + private long generateMutationIndex(long length, Set mutationIndexes) { + long mutationIndex = 0; + do { + mutationIndex = RandomProviderManager.getRandomProvider().nextLong(length); + } while (mutationIndexes.contains(mutationIndex)); + return mutationIndex; + } + + private void updateIndexMap(Map> indexMap, long length, long mutationIndex) { + final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE); + final long offsettedMutableAlleleIndex = offset + mutationIndex; + + final int alleleBlockIndex = (int) (offsettedMutableAlleleIndex / Long.SIZE); + + if (!indexMap.containsKey(alleleBlockIndex)) { + indexMap.put(alleleBlockIndex, new HashSet<>()); + } + final int alleleElementIndex = (int) (offsettedMutableAlleleIndex % Long.SIZE); + + indexMap.get(alleleBlockIndex).add(alleleElementIndex); + } + + private void updateIndexMap(Map> indexMap, long length) { + updateIndexMap(indexMap, length, RandomProviderManager.getRandomProvider().nextLong(length)); } } diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java index 6d661ac32f..2ff7f0f893 100644 --- 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 @@ -70,7 +70,7 @@ protected void checkValidity(Chromosome

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

chromosome = (IntegralValuedChromosome

) original; + final 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()); 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 index 66bdbc3516..096e419a58 100644 --- 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 @@ -32,4 +32,5 @@ public interface MutationPolicy

{ * @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 index f586757313..3dcbdcf69e 100644 --- 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 @@ -79,7 +79,7 @@ protected void checkValidity(Chromosome

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

chromosome = (RealValuedChromosome

) original; + final 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()); 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 index bcf8729b79..1622d9c09b 100644 --- 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 @@ -168,7 +168,7 @@ public int getPopulationSize() { */ @Override public String toString() { - StringBuilder populationStrRepr = new StringBuilder(); + final StringBuilder populationStrRepr = new StringBuilder(); for (Chromosome

chromosome : chromosomes) { populationStrRepr.append(chromosome.toString()); populationStrRepr.append(NEW_LINE); 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 index f0f60b916b..8240f5da26 100644 --- 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 @@ -38,7 +38,7 @@ public class TournamentSelection

implements SelectionPolicy

{ /** number of chromosomes included in the tournament selections. */ - private int arity; + private final int arity; /** * Creates a new TournamentSelection instance. 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 index 4261b147f4..f4b236c87c 100644 --- 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 @@ -21,7 +21,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Objects; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.rng.UniformRandomProvider; @@ -140,14 +142,39 @@ static List randomIntegralRepresentation(final int length, final int mi * @param length length of the array * @return a random binary array of length length */ - static List randomBinaryRepresentation(final int length) { + static long[] randomBinaryRepresentation(final long length) { + if (length > BinaryChromosome.MAX_LENGTH) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "length exceeded the max length " + BinaryChromosome.MAX_LENGTH); + } final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + int elementCount = (int) Math.ceil(length / 64d); // random binary list - final List rList = new ArrayList<>(length); - for (int j = 0; j < length; j++) { - rList.add(randomProvider.nextInt(2)); + final long[] representation = new long[elementCount]; + int remainder = (int) (length % 64); + representation[0] = remainder == 0 ? randomProvider.nextLong() : + randomProvider.nextLong((long) Math.pow(2, remainder)); + for (int i = 1; i < elementCount; i++) { + representation[i] = randomProvider.nextLong(); } - return rList; + return representation; + } + + /** + * Generates a random string representation of chromosome with specified + * characters. + * @param alleles characters representing alleles + * @param length length of chromosome + * @return returns chromosome representation as string + */ + static String randomStringRepresentation(char[] alleles, final long length) { + Objects.requireNonNull(alleles); + final StringBuilder representationStr = new StringBuilder(); + for (int i = 0; i < length; i++) { + representationStr + .append(alleles[(int) (RandomProviderManager.getRandomProvider().nextInt(alleles.length))]); + } + return representationStr.toString(); } /** 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/GeneticAlgorithmTestBinaryOneMax.java similarity index 75% rename from commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinary.java rename to commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java index 203d3b34ee..99a378542a 100644 --- 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/GeneticAlgorithmTestBinaryOneMax.java @@ -17,7 +17,7 @@ package org.apache.commons.math4.ga; import java.lang.reflect.Field; - +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -25,8 +25,10 @@ import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.convergence.FixedGenerationCount; import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; import org.apache.commons.math4.ga.crossover.OnePointCrossover; -import org.apache.commons.math4.ga.decoder.TransparentListChromosomeDecoder; +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.listener.ConvergenceListener; import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; @@ -41,7 +43,7 @@ /** * This is also an example of usage. */ -public class GeneticAlgorithmTestBinary { +public class GeneticAlgorithmTestBinaryOneMax { // parameters for the GA private static final int DIMENSION = 50; @@ -56,7 +58,7 @@ public void test() { removeListeners(); // initialize a new genetic algorithm - GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointCrossover>(), + GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointBinaryCrossover>(), CROSSOVER_RATE, new BinaryMutation>(), MUTATION_RATE, new TournamentSelection>(TOURNAMENT_ARITY)); @@ -110,12 +112,12 @@ private void removeListeners() { /** * Initializes a random population. */ - private static ListPopulation> randomPopulation() { + private ListPopulation> randomPopulation() { List>> popList = new LinkedList<>(); for (int i = 0; i < POPULATION_SIZE; i++) { BinaryChromosome> randChrom = new FindOnes( - ChromosomeRepresentationUtils.randomBinaryRepresentation(DIMENSION)); + ChromosomeRepresentationUtils.randomBinaryRepresentation(DIMENSION), DIMENSION); popList.add(randChrom); } return new ListPopulation<>(popList, popList.size()); @@ -126,16 +128,43 @@ private static ListPopulation> randomPopulation() { * * The goal is to set all bits (genes) to 1. */ - private static class FindOnes extends BinaryChromosome> { + private class FindOnes extends BinaryChromosome> { + + FindOnes(long[] representation, long length) { + super(representation, length, new OneMaxFitnessFunction(), new OneMaxDecoder()); + } + } + + private class OneMaxFitnessFunction implements FitnessFunction> { + + @Override + public double compute(List decodedChromosome) { + double value = 0; + for (Integer allele : decodedChromosome) { + value += allele; + } + return value; + } + + } - FindOnes(List representation) { - super(representation, phenotype -> { - Integer val = 0; - for (Integer num : phenotype) { - val += num; + private class OneMaxDecoder implements Decoder> { + + @Override + public List decode(Chromosome> chromosome) { + BinaryChromosome> binaryChromosome = (BinaryChromosome>) chromosome; + List phenotype = new ArrayList<>(); + long[] representation = binaryChromosome.getRepresentation(); + for (int i = 0; i < representation.length; i++) { + String value = Long.toUnsignedString(representation[i], 2); + for (int j = 64 - value.length(); j > 0; j--) { + phenotype.add(Integer.valueOf(0)); + } + for (int j = 0; j < value.length(); j++) { + phenotype.add(Integer.parseInt("" + value.charAt(j))); } - return val; - }, new TransparentListChromosomeDecoder<>()); + } + return phenotype; } } 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 index 2547e01ee8..ef6669ae25 100644 --- 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 @@ -18,6 +18,7 @@ import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -25,13 +26,14 @@ public class BinaryChromosomeTest { @Test 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) { - Assertions.assertThrows(GeneticException.class, () -> { - new BinaryChromosome<>(repr, c -> 0, new DummyListChromosomeDecoder<>("0")); - }); - } + Assertions.assertThrows(GeneticException.class, () -> { + new BinaryChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), Long.MAX_VALUE, + c -> 0, c -> "0"); + }); + Assertions.assertThrows(GeneticException.class, () -> { + new BinaryChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), 100, c -> 0, + c -> "0"); + }); } @Test @@ -41,4 +43,50 @@ public void testRandomConstructor() { } } + @Test + public void testGetStringRepresentation() { + int length = 10; + int startToEndGap = 1; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 64; + startToEndGap = 10; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 100; + startToEndGap = 50; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 128; + startToEndGap = 70; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 250; + startToEndGap = 128; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 350; + startToEndGap = 228; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 450; + startToEndGap = 108; + testStringRepresentationWithRanges(length, startToEndGap); + + } + + private void testStringRepresentationWithRanges(int length, int startToEndGap) { + for (int i = 0; i < 50; i++) { + String representationStr = ChromosomeRepresentationUtils.randomStringRepresentation(new char[] {'0', '1'}, + length); + BinaryChromosome chromosome = new BinaryChromosome<>(representationStr, c -> 0, c -> "0"); + Assertions.assertEquals(representationStr, chromosome.getStringRepresentation()); + for (int j = 0; j < length - startToEndGap; j++) { + int index = (int) ((length + 1 - startToEndGap) * Math.random()); + Assertions.assertEquals(representationStr.substring(index, index + startToEndGap), + chromosome.getStringRepresentation(index, index + startToEndGap)); + } + } + } + } diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java index a8f6b13e55..0739f0d8db 100644 --- 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 @@ -62,9 +62,11 @@ protected ChromosomePair mate(AbstractListChromosome fi return new ChromosomePair<>(first, second); } }; - Chromosome ch1 = new DummyListChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10)); + Chromosome ch1 = new DummyListChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2)); - Chromosome ch2 = new DummyListChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(20)); + Chromosome ch2 = new DummyListChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(12, 0, 2)); Assertions.assertThrows(GeneticException.class, () -> { crossoverPolicy.crossover(ch1, ch2, 1.0); diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java new file mode 100644 index 0000000000..21f26a6cba --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.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.crossover; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class OnePointBinaryCrossoverTest { + + @Test + public void testCrossover() { + + OnePointBinaryCrossover opc = new OnePointBinaryCrossover<>(); + + // test a stochastic method. + for (int i = 0; i < 1000; i++) { + long length = 32 + (long) (200 * Math.random()); + BinaryChromosome p1c = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length, c -> 0, c -> "0"); + BinaryChromosome p2c = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length, c -> 0, c -> "0"); + + ChromosomePair pair = opc.crossover(p1c, p2c, 1.0); + + long[] p1 = p1c.getRepresentation(); + long[] p2 = p2c.getRepresentation(); + + long[] c1 = ((BinaryChromosome) pair.getFirst()).getRepresentation(); + long[] c2 = ((BinaryChromosome) pair.getSecond()).getRepresentation(); + + final int offset = (int) (length % 64 == 0 ? 0 : 64 - (length % 64)); + + // first and last values will be the same + Assertions.assertEquals(prependZero(Long.toBinaryString(p1[0])).charAt(offset), + prependZero(Long.toBinaryString(c1[0])).charAt(offset)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p2[0])).charAt(offset), + prependZero(Long.toBinaryString(c2[0])).charAt(offset)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p1[p1.length - 1])).charAt(63), + prependZero(Long.toBinaryString(c2[c2.length - 1])).charAt(63)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p2[p2.length - 1])).charAt(63), + prependZero(Long.toBinaryString(c1[c1.length - 1])).charAt(63)); + + } + } + + private String prependZero(String value) { + StringBuilder modValue = new StringBuilder(); + for (int i = 64 - value.length(); i > 0; i--) { + modValue.append('0'); + } + modValue.append(value); + + return modValue.toString(); + } + +} 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 index 95d444ce92..39b6b9eb63 100644 --- 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 @@ -29,7 +29,7 @@ public class TransparentListChromosomeDecoderTest { @Test public void testDecode() { - List rp = ChromosomeRepresentationUtils.randomBinaryRepresentation(10); + List rp = ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2); Chromosome> chromosome = new IntegralValuedChromosome<>(rp, c -> 0, new TransparentListChromosomeDecoder<>(), 0, 2); List decodedRp = chromosome.decode(); 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 index d3e3472048..908bb43e78 100644 --- 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 @@ -31,7 +31,7 @@ public DummyListChromosome(final Integer[] representation) { } public DummyListChromosome() { - super(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), chromosome -> 0, + super(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2), chromosome -> 0, new DummyListChromosomeDecoder<>("0")); } 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 index d06d2e0674..d2cc2bb7eb 100644 --- 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 @@ -17,7 +17,7 @@ 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.chromosome.IntegralValuedChromosome; import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; import org.apache.commons.math4.ga.internal.exception.GeneticException; import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; @@ -30,9 +30,9 @@ public class BinaryMutationTest { public void testCheckValidity() { BinaryMutation mutation = new BinaryMutation<>(); Assertions.assertThrows(GeneticException.class, () -> { - mutation.checkValidity( - new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(0), - c -> 0, new DummyListChromosomeDecoder<>("0"))); + mutation.checkValidity(new IntegralValuedChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2), c -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); }); } @@ -47,46 +47,44 @@ public void testMutate() { 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++; - } - } - Assertions.assertEquals(1, numDifferent); + Assertions.assertEquals(1, calculateNoOfMutatedBits(original, mutated)); } // stochastic testing for two gene mutation :) for (int i = 0; i < 20; i++) { BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, - new DummyListChromosomeDecoder<>("0")); + c -> "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++; - } - } - Assertions.assertEquals(2, numDifferent); + Assertions.assertEquals(2, calculateNoOfMutatedBits(original, mutated)); } // stochastic testing for three gene mutation :) for (int i = 0; i < 20; i++) { BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, - new DummyListChromosomeDecoder<>("0")); + c -> "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)) { + // three genes should be different + Assertions.assertEquals(3, calculateNoOfMutatedBits(original, mutated)); + } + } + + private int calculateNoOfMutatedBits(BinaryChromosome original, BinaryChromosome mutated) { + int numDifferent = 0; + long[] originalReps = original.getRepresentation(); + long[] mutatedReps = mutated.getRepresentation(); + for (int j = 0; j < originalReps.length; j++) { + long xORValue = originalReps[j] ^ mutatedReps[j]; + String xORValueStr = Long.toBinaryString(xORValue); + for (int k = 0; k < xORValueStr.length(); k++) { + if (xORValueStr.charAt(k) == '1') { numDifferent++; } } - Assertions.assertEquals(3, numDifferent); } + return numDifferent; } } From 63f0562d0a82cfab6a817e6cabe0f8bb627bf5c7 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Tue, 21 Dec 2021 18:42:12 +0530 Subject: [PATCH 5/9] updated for checkstyle formatting --- .../ga/mathfunctions/dimension2/Dimension2Decoder.java | 3 ++- .../dimension2/Dimension2FunctionAdaptiveOptimizer.java | 4 ++-- .../ga/mathfunctions/dimensionN/DimensionNDecoder.java | 3 ++- .../dimensionN/DimensionNFunctionAdaptiveOptimizer.java | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java index c3b9adc439..6e87848fe0 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java @@ -33,7 +33,8 @@ public class Dimension2Decoder implements Decoder { */ @Override public Dimension2Coordinate decode(Chromosome chromosome) { - final BinaryChromosome binaryChromosome = (BinaryChromosome) chromosome; + final BinaryChromosome binaryChromosome = + (BinaryChromosome) chromosome; final long alleles = binaryChromosome.getRepresentation()[0]; long mask1 = ~(Long.MAX_VALUE << 12); diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java index 66f07e5f8d..6579dda896 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java @@ -59,8 +59,8 @@ public static void main(String[] args) { final Dimension2FunctionAdaptiveOptimizer optimizer = new Dimension2FunctionAdaptiveOptimizer(); - final ConvergenceListenerRegistry convergenceListenerRegistry = ConvergenceListenerRegistry - .getInstance(); + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); convergenceListenerRegistry .addConvergenceListener(new Dim2GraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java index a8a33972dd..88a1eeb4f9 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java @@ -36,7 +36,8 @@ public class DimensionNDecoder implements Decoder { */ @Override public DimensionNCoordinate decode(Chromosome chromosome) { - final BinaryChromosome binaryChromosome = (BinaryChromosome) chromosome; + final BinaryChromosome binaryChromosome = + (BinaryChromosome) chromosome; final long length = binaryChromosome.getLength(); List coordinates = new ArrayList<>(); diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java index 14fc7bcbf1..dee317a3dd 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java @@ -60,8 +60,8 @@ public static void main(String[] args) { final DimensionNFunctionAdaptiveOptimizer optimizer = new DimensionNFunctionAdaptiveOptimizer(); - final ConvergenceListenerRegistry convergenceListenerRegistry = ConvergenceListenerRegistry - .getInstance(); + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); convergenceListenerRegistry .addConvergenceListener(new DimNGraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); From 5eccbaab4c1ec530390276f5b044914a9ed745f7 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Tue, 21 Dec 2021 19:48:40 +0530 Subject: [PATCH 6/9] modified as per PMD recommendations --- .../ga/mathfunctions/dimension2/Dimension2Decoder.java | 4 ++-- .../ga/mathfunctions/dimensionN/DimensionNDecoder.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java index 6e87848fe0..e8394d12c6 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java @@ -40,8 +40,8 @@ public Dimension2Coordinate decode(Chromosome chromosome) long mask1 = ~(Long.MAX_VALUE << 12); long mask2 = ~(Long.MAX_VALUE << 24) ^ mask1; - double x = (alleles & mask1) / 100d; - double y = ((alleles & mask2) >> 12) / 100d; + final double x = (alleles & mask1) / 100d; + final double y = ((alleles & mask2) >> 12) / 100d; return new Dimension2Coordinate(x, y); } diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java index 88a1eeb4f9..957981500f 100644 --- a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java @@ -39,7 +39,7 @@ public DimensionNCoordinate decode(Chromosome chromosome) final BinaryChromosome binaryChromosome = (BinaryChromosome) chromosome; final long length = binaryChromosome.getLength(); - List coordinates = new ArrayList<>(); + final List coordinates = new ArrayList<>(); for (int i = 0; i < length; i += 12) { final String dimensionStrValue = binaryChromosome.getStringRepresentation(i, i + 12); From 201f9ff65596092bfb75fb571cda86610ba795f7 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Fri, 24 Dec 2021 12:08:05 +0530 Subject: [PATCH 7/9] Minor change for UniformRandomProvider --- .../ga/mutation/AbstractListChromosomeMutationPolicy.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 index 70879e313b..3e41fc20c9 100644 --- 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 @@ -25,6 +25,7 @@ import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; import org.apache.commons.math4.ga.chromosome.Chromosome; import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.apache.commons.rng.UniformRandomProvider; /** * This abstraction represents an abstract mutation policy for ListChromosomes. @@ -75,15 +76,16 @@ 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<>(); + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); // if chromosomeMutationRate >= 1 then more than one allele will be mutated. if (chromosomeMutationRate >= 1) { final int noOfMutation = (int) Math.round(chromosomeMutationRate); while (indexSet.size() < noOfMutation) { - indexSet.add(RandomProviderManager.getRandomProvider().nextInt(length)); + indexSet.add(randomProvider.nextInt(length)); } - } else if (RandomProviderManager.getRandomProvider().nextDouble() < chromosomeMutationRate) { - indexSet.add(RandomProviderManager.getRandomProvider().nextInt(length)); + } else if (randomProvider.nextDouble() < chromosomeMutationRate) { + indexSet.add(randomProvider.nextInt(length)); } return indexSet; From f51ff75b6c232e8a6d5431c4712bf7afbb9b5b05 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Mon, 27 Dec 2021 17:49:20 +0530 Subject: [PATCH 8/9] removed 64 by Long.SIZE --- .../commons/math4/ga/utils/ChromosomeRepresentationUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index f4b236c87c..47503354fb 100644 --- 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 @@ -148,10 +148,10 @@ static long[] randomBinaryRepresentation(final long length) { "length exceeded the max length " + BinaryChromosome.MAX_LENGTH); } final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); - int elementCount = (int) Math.ceil(length / 64d); + int elementCount = (int) Math.ceil(length / (double)Long.SIZE); // random binary list final long[] representation = new long[elementCount]; - int remainder = (int) (length % 64); + int remainder = (int) (length % Long.SIZE); representation[0] = remainder == 0 ? randomProvider.nextLong() : randomProvider.nextLong((long) Math.pow(2, remainder)); for (int i = 1; i < elementCount; i++) { From fa90c7f5f3e709212b8dd0e735d571e1b958da23 Mon Sep 17 00:00:00 2001 From: avbasak1 Date: Sun, 8 Aug 2021 21:38:28 +0530 Subject: [PATCH 9/9] MATH-1563. Refractoring of GA functionality. Modified design and data structure of the library to improve robustness. --- .../examples-ga-math-functions/pom.xml | 47 +++ .../dimension2/Dim2GraphPlotter.java | 145 +++++++++ .../dimension2/Dimension2Coordinate.java | 64 ++++ .../dimension2/Dimension2Decoder.java | 49 +++ .../dimension2/Dimension2FitnessFunction.java | 40 +++ .../Dimension2FunctionAdaptiveOptimizer.java | 114 +++++++ .../Dimension2FunctionOptimizer.java | 114 +++++++ .../Dimension2FunctionOptimizerLegacy.java | 103 ++++++ .../legacy/LegacyBinaryChromosome.java | 65 ++++ .../legacy/UnchangedBestFitness.java | 67 ++++ .../dimension2/legacy/package-info.java | 20 ++ .../dimension2/package-info.java | 20 ++ .../dimensionN/DimNGraphPlotter.java | 145 +++++++++ .../dimensionN/DimensionNCoordinate.java | 54 ++++ .../dimensionN/DimensionNDecoder.java | 52 +++ .../dimensionN/DimensionNFitnessFunction.java | 42 +++ .../DimensionNFunctionAdaptiveOptimizer.java | 114 +++++++ .../DimensionNFunctionOptimizer.java | 113 +++++++ .../dimensionN/package-info.java | 20 ++ .../ga/mathfunctions/utils/Constants.java | 55 ++++ .../ga/mathfunctions/utils/package-info.java | 20 ++ .../examples-ga/examples-ga-tsp/pom.xml | 52 +++ .../examples/ga/tsp/TSPFitnessFunction.java | 52 +++ .../math4/examples/ga/tsp/TSPOptimizer.java | 116 +++++++ .../math4/examples/ga/tsp/commons/City.java | 77 +++++ .../ga/tsp/commons/DistanceMatrix.java | 71 ++++ .../examples/ga/tsp/commons/package-info.java | 20 ++ .../examples/ga/tsp/legacy/TSPChromosome.java | 76 +++++ .../ga/tsp/legacy/TSPOptimizerLegacy.java | 99 ++++++ .../ga/tsp/legacy/UnchangedBestFitness.java | 67 ++++ .../examples/ga/tsp/legacy/package-info.java | 20 ++ .../math4/examples/ga/tsp/package-info.java | 20 ++ .../examples/ga/tsp/utils/Constants.java | 65 ++++ .../examples/ga/tsp/utils/GraphPlotter.java | 146 +++++++++ .../examples/ga/tsp/utils/package-info.java | 20 ++ commons-math-examples/examples-ga/pom.xml | 56 ++++ .../spotbugs/spotbugs-exclude-filter.xml | 66 ++++ commons-math-examples/pom.xml | 1 + commons-math-ga/pom.xml | 54 ++++ .../math4/ga/AbstractGeneticAlgorithm.java | 180 +++++++++++ .../math4/ga/AdaptiveGeneticAlgorithm.java | 164 ++++++++++ .../commons/math4/ga/GeneticAlgorithm.java | 171 ++++++++++ .../ga/chromosome/AbstractChromosome.java | 148 +++++++++ .../ga/chromosome/AbstractListChromosome.java | 125 ++++++++ .../math4/ga/chromosome/BinaryChromosome.java | 303 ++++++++++++++++++ .../math4/ga/chromosome/Chromosome.java | 48 +++ .../math4/ga/chromosome/ChromosomePair.java | 66 ++++ .../chromosome/IntegralValuedChromosome.java | 136 ++++++++ .../ga/chromosome/RealValuedChromosome.java | 160 +++++++++ .../math4/ga/chromosome/package-info.java | 20 ++ .../ga/convergence/FixedElapsedTime.java | 80 +++++ .../ga/convergence/FixedGenerationCount.java | 76 +++++ .../ga/convergence/StoppingCondition.java | 38 +++ .../ga/convergence/UnchangedBestFitness.java | 72 +++++ .../ga/convergence/UnchangedMeanFitness.java | 85 +++++ .../math4/ga/convergence/package-info.java | 20 ++ .../AbstractChromosomeCrossoverPolicy.java | 53 +++ ...AbstractListChromosomeCrossoverPolicy.java | 76 +++++ .../math4/ga/crossover/CrossoverPolicy.java | 39 +++ .../math4/ga/crossover/CycleCrossover.java | 168 ++++++++++ .../math4/ga/crossover/NPointCrossover.java | 154 +++++++++ .../ga/crossover/OnePointBinaryCrossover.java | 115 +++++++ .../math4/ga/crossover/OnePointCrossover.java | 102 ++++++ .../math4/ga/crossover/OrderedCrossover.java | 131 ++++++++ .../math4/ga/crossover/UniformCrossover.java | 123 +++++++ .../math4/ga/crossover/package-info.java | 20 ++ ...verageRankBasedCrossoverRateGenerator.java | 58 ++++ ...aximumRankBasedCrossoverRateGenerator.java | 58 ++++ .../ConstantCrossoverRateGenerator.java | 50 +++ .../rategenerator/CrossoverRateGenerator.java | 42 +++ .../crossover/rategenerator/package-info.java | 20 ++ .../AbstractListChromosomeDecoder.java | 59 ++++ .../commons/math4/ga/decoder/Decoder.java | 35 ++ .../math4/ga/decoder/RandomKeyDecoder.java | 75 +++++ .../TransparentListChromosomeDecoder.java | 39 +++ .../math4/ga/decoder/package-info.java | 20 ++ .../math4/ga/fitness/FitnessFunction.java | 34 ++ .../math4/ga/fitness/package-info.java | 20 ++ .../internal/exception/GeneticException.java | 120 +++++++ .../ga/internal/exception/package-info.java | 20 ++ .../PopulationStatisticalSummaryImpl.java | 177 ++++++++++ .../math4/ga/internal/stats/package-info.java | 20 ++ .../ga/listener/ConvergenceListener.java | 37 +++ .../listener/ConvergenceListenerRegistry.java | 91 ++++++ .../listener/PopulationStatisticsLogger.java | 49 +++ .../math4/ga/listener/package-info.java | 20 ++ .../AbstractListChromosomeMutationPolicy.java | 101 ++++++ .../math4/ga/mutation/BinaryMutation.java | 135 ++++++++ .../ga/mutation/IntegralValuedMutation.java | 88 +++++ .../math4/ga/mutation/MutationPolicy.java | 36 +++ .../math4/ga/mutation/RealValuedMutation.java | 97 ++++++ .../math4/ga/mutation/package-info.java | 20 ++ .../AdaptiveLinearMutationRateGenerator.java | 54 ++++ .../ConstantMutationRateGenerator.java | 47 +++ .../rategenerator/MutationRateGenerator.java | 38 +++ .../mutation/rategenerator/package-info.java | 20 ++ .../apache/commons/math4/ga/package-info.java | 20 ++ .../math4/ga/population/ListPopulation.java | 220 +++++++++++++ .../math4/ga/population/Population.java | 59 ++++ .../math4/ga/population/package-info.java | 20 ++ .../math4/ga/selection/SelectionPolicy.java | 35 ++ .../ga/selection/TournamentSelection.java | 108 +++++++ .../math4/ga/selection/package-info.java | 20 ++ .../stats/PopulationStatisticalSummary.java | 67 ++++ .../commons/math4/ga/stats/package-info.java | 20 ++ .../utils/ChromosomeRepresentationUtils.java | 211 ++++++++++++ .../math4/ga/utils/RandomProviderManager.java | 47 +++ .../commons/math4/ga/utils/package-info.java | 20 ++ .../ga/GeneticAlgorithmTestBinaryOneMax.java | 187 +++++++++++ .../ga/GeneticAlgorithmTestPermutations.java | 144 +++++++++ .../ga/chromosome/AbstractChromosomeTest.java | 65 ++++ .../ga/chromosome/BinaryChromosomeTest.java | 92 ++++++ .../ga/chromosome/ChromosomePairTest.java | 38 +++ .../IntegralValuedChromosomeTest.java | 93 ++++++ .../chromosome/RealValuedChromosomeTest.java | 75 +++++ .../convergencecond/FixedElapsedTimeTest.java | 59 ++++ .../FixedGenerationCountTest.java | 48 +++ .../UnchangedBestFitnessTest.java | 68 ++++ .../UnchangedMeanFitnessTest.java | 68 ++++ ...AbstractChromosomeCrossoverPolicyTest.java | 45 +++ ...ractListChromosomeCrossoverPolicyTest.java | 77 +++++ .../ga/crossover/CycleCrossoverTest.java | 115 +++++++ .../ga/crossover/NPointCrossoverTest.java | 127 ++++++++ .../OnePointBinaryCrossoverTest.java | 73 +++++ .../ga/crossover/OnePointCrossoverTest.java | 66 ++++ .../ga/crossover/OrderedCrossoverTest.java | 63 ++++ .../ga/crossover/UniformCrossoverTest.java | 113 +++++++ ...umRankBasedCrossoverRateGeneratorTest.java | 68 ++++ .../AbstractListChromosomeDecoderTest.java | 44 +++ .../ga/decoder/RandomKeyDecoderTest.java | 58 ++++ .../TransparentListChromosomeDecoderTest.java | 39 +++ .../math4/ga/dummy/DummyChromosome.java | 27 ++ .../math4/ga/dummy/DummyListChromosome.java | 47 +++ .../ga/dummy/DummyListChromosomeDecoder.java | 41 +++ .../ga/exception/GeneticExceptionTest.java | 39 +++ .../ConvergenceListenerRegistryTest.java | 107 +++++++ .../PopulationStatisticsLoggerTest.java | 45 +++ ...tractListChromosomeMutationPolicyTest.java | 44 +++ .../math4/ga/mutation/BinaryMutationTest.java | 90 ++++++ .../mutation/IntegralValuedMutationTest.java | 85 +++++ .../ga/mutation/RealValuedMutationTest.java | 85 +++++ ...aptiveLinearMutationRateGeneratorTest.java | 61 ++++ .../ConstantMutationRateGeneratorTest.java | 45 +++ .../ga/population/ListPopulationTest.java | 202 ++++++++++++ .../ga/selection/TournamentSelectionTest.java | 109 +++++++ .../ChromosomeRepresentationUtilsTest.java | 163 ++++++++++ pom.xml | 1 + .../spotbugs/spotbugs-exclude-filter.xml | 6 + 148 files changed, 10963 insertions(+) create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java create mode 100644 commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/pom.xml create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java create mode 100644 commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java create mode 100644 commons-math-examples/examples-ga/pom.xml create mode 100644 commons-math-examples/examples-ga/src/main/resources/spotbugs/spotbugs-exclude-filter.xml create mode 100644 commons-math-ga/pom.xml create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java create mode 100644 commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosomeDecoder.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java create mode 100644 commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java 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..d09ca3ff10 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml @@ -0,0 +1,47 @@ + + + + 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 + 1.7.32 + + + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + + \ No newline at end of file diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java new file mode 100644 index 0000000000..eca37e8798 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import java.awt.BorderLayout; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class Dim2GraphPlotter extends JFrame implements ConvergenceListener { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public Dim2GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List 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/dimension2/Dimension2Coordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java new file mode 100644 index 0000000000..307f1efff2 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +/** + * This class represents the coordinate of the problem domain i.e. the phenotype of chromosome. + */ +public class Dimension2Coordinate { + + /** coordinate of first dimension. **/ + private final double x; + + /** coordinate of second dimension. **/ + private final double y; + + /** + * constructor. + * @param x coordinate of first dimension + * @param y coordinate of second dimension + */ + public Dimension2Coordinate(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * returns the coordinate of first dimension. + * @return coordinate of first dimension + */ + public double getX() { + return x; + } + + /** + * returns the coordinate of second dimension. + * @return coordinate of second dimension + */ + public double getY() { + return y; + } + + /** + * Returns a string representation of coordinate. + */ + @Override + public String toString() { + return "Coordinate [x=" + x + ", y=" + y + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java new file mode 100644 index 0000000000..e8394d12c6 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.Decoder; + +/** + * Decoder to convert chromosome's binary genotype to phenotype + * {@link Dimension2Coordinate}. + */ +public class Dimension2Decoder implements Decoder { + + /** + * Decode the binary representation of chromosome to + * {@link Dimension2Coordinate}. + * @param chromosome The {@link Chromosome} + */ + @Override + public Dimension2Coordinate decode(Chromosome chromosome) { + final BinaryChromosome binaryChromosome = + (BinaryChromosome) chromosome; + final long alleles = binaryChromosome.getRepresentation()[0]; + + long mask1 = ~(Long.MAX_VALUE << 12); + long mask2 = ~(Long.MAX_VALUE << 24) ^ mask1; + + final double x = (alleles & mask1) / 100d; + final double y = ((alleles & mask2) >> 12) / 100d; + + return new Dimension2Coordinate(x, y); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java new file mode 100644 index 0000000000..33e8f73018 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the mathematical fitness function for optimizing a 2 + * dimension mathematical function. + */ +public class Dimension2FitnessFunction implements FitnessFunction { + + /** + * Computes the fitness value based on the decoded chromosome. + * @param coordinate The {@link Dimension2Coordinate} + * @return the fitness value + */ + @Override + public double compute(Dimension2Coordinate coordinate) { + return -Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .25) * + (Math.pow(Math.sin(50 * Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .1)), + 2) + 1); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java new file mode 100644 index 0000000000..6579dda896 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.crossover.rategenerator.ConstantCrossoverRateGenerator; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class Dimension2FunctionAdaptiveOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final Dimension2FunctionAdaptiveOptimizer optimizer = new Dimension2FunctionAdaptiveOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new Dim2GraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + private void optimize(Population initial) { + + // initialize a new genetic algorithm + final AdaptiveGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm<>( + new OnePointBinaryCrossover(), new ConstantCrossoverRateGenerator<>(1), + new BinaryMutation(), + new AdaptiveLinearMutationRateGenerator<>(Constants.AVERAGE_MUTATION_RATE / 2, + Constants.AVERAGE_MUTATION_RATE * 2), + new TournamentSelection<>(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 Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction(); + final Dimension2Decoder decoder = new Dimension2Decoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java new file mode 100644 index 0000000000..1d6a3fa6b5 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class Dimension2FunctionOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final Dimension2FunctionOptimizer optimizer = new Dimension2FunctionOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new Dim2GraphPlotter("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 OnePointBinaryCrossover(), Constants.CROSSOVER_RATE, + new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction(); + final Dimension2Decoder decoder = new Dimension2Decoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java new file mode 100644 index 0000000000..b523d41f74 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import org.apache.commons.math3.genetics.BinaryChromosome; +import org.apache.commons.math3.genetics.BinaryMutation; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * the legacy genetic algorithm. + */ +public class Dimension2FunctionOptimizerLegacy { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** + * Optimizes the 2-dimensional fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + final Dimension2FunctionOptimizerLegacy simulation = new Dimension2FunctionOptimizerLegacy(); + + simulation.optimize(initPopulation); + } + + /** + * Optimizes the initial population using legacy genetic algorithm. + * @param initial initial {@link Population} + */ + public void optimize(Population initial) { + + // initialize a new genetic algorithm + final GeneticAlgorithm geneticAlgorithm = new GeneticAlgorithm(new OnePointCrossover<>(), + Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = geneticAlgorithm.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) { + writer.write("*********************************************"); + writer.newLine(); + writer.write("***********Optimization Result***************"); + writer.write(bestFinal.toString()); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + /** + * Generates the initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ElitisticListPopulation(Constants.POPULATION_SIZE_PER_DIMENSION, + Constants.ELITISM_RATE); + for (int i = 0; i < Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(new LegacyBinaryChromosome(BinaryChromosome + .randomBinaryRepresentation(DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION))); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java new file mode 100644 index 0000000000..66c4615abd --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import java.util.List; + +import org.apache.commons.math3.genetics.AbstractListChromosome; +import org.apache.commons.math3.genetics.BinaryChromosome; + +/** + * A representation of concrete binary chromosome. + */ +public class LegacyBinaryChromosome extends BinaryChromosome { + + /** + * constructor. + * @param representation the internal representation + */ + public LegacyBinaryChromosome(List 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/dimension2/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java new file mode 100644 index 0000000000..8800f3978b --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; + +/** + * This class represents the stopping condition based on unchanged best fitness. + */ +public class UnchangedBestFitness implements StoppingCondition { + + /** last best fitness. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** maximum number of generations evolved with unchanged best fitness. **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** generations having unchanged best fitness. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations evolved with + * unchanged best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population population) { + final double currentBestFitness = population.getFittestChromosome().getFitness(); + + if (lastBestFitness == currentBestFitness) { + this.generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java new file mode 100644 index 0000000000..7965791c5c --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java new file mode 100644 index 0000000000..432651f230 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java new file mode 100644 index 0000000000..52571e3825 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.awt.BorderLayout; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class DimNGraphPlotter extends JFrame implements ConvergenceListener { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public DimNGraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List 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/dimensionN/DimensionNCoordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java new file mode 100644 index 0000000000..98a31bcffc --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.util.List; + +/** + * This class represents the coordinate of the problem domain i.e. the phenotype + * of chromosome. + */ +public class DimensionNCoordinate { + + /** coordinate of all dimensions. **/ + private final List values; + + /** + * constructor. + * @param values coordinates of all dimensions. + */ + public DimensionNCoordinate(List values) { + this.values = values; + } + + /** + * Returns the values of all coordinates. + * @return values of coordinates + */ + public List getValues() { + return values; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Coordinate [values=" + values + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java new file mode 100644 index 0000000000..957981500f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.Decoder; + +/** + * Decoder to convert chromosome's binary genotype to phenotype + * {@link DimensionNCoordinate}. + */ +public class DimensionNDecoder implements Decoder { + + /** + * decode the binary representation of chromosome to + * {@link DimensionNCoordinate}. + * @param chromosome The {@link Chromosome} + */ + @Override + public DimensionNCoordinate decode(Chromosome chromosome) { + final BinaryChromosome binaryChromosome = + (BinaryChromosome) chromosome; + final long length = binaryChromosome.getLength(); + final List coordinates = new ArrayList<>(); + + for (int i = 0; i < length; i += 12) { + final String dimensionStrValue = binaryChromosome.getStringRepresentation(i, i + 12); + coordinates.add(Integer.parseUnsignedInt(dimensionStrValue, 2) / 100d); + } + + return new DimensionNCoordinate(coordinates); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java new file mode 100644 index 0000000000..1c0d7db94f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the mathematical fitness function for optimizing a 2 + * dimension mathematical function. + */ +public class DimensionNFitnessFunction implements FitnessFunction { + + /** + * Computes the fitness value based on the decoded chromosome. + * @param coordinate The {@link DimensionNCoordinate} + * @return the fitness value + */ + @Override + public double compute(DimensionNCoordinate coordinate) { + double sumOfSquare = 0.0; + for (Double value : coordinate.getValues()) { + sumOfSquare += Math.pow(value, 2); + } + return -Math.pow(sumOfSquare, .25) * (Math.pow(Math.sin(50 * Math.pow(sumOfSquare, .1)), 2) + 1); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java new file mode 100644 index 0000000000..dee317a3dd --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.AbstractGeneticAlgorithm; +import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.crossover.rategenerator.AdaptiveLinearMaximumRankBasedCrossoverRateGenerator; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class DimensionNFunctionAdaptiveOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 10; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 4; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final DimensionNFunctionAdaptiveOptimizer optimizer = new DimensionNFunctionAdaptiveOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new DimNGraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + private void optimize(Population initial) { + + // initialize a new genetic algorithm + final AbstractGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm( + new OnePointBinaryCrossover(), + new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>(Constants.CROSSOVER_RATE / 2, + Constants.CROSSOVER_RATE), + new BinaryMutation<>(), new AdaptiveLinearMutationRateGenerator<>(0, Constants.AVERAGE_MUTATION_RATE), + new TournamentSelection<>(TOURNAMENT_SIZE), Constants.ELITISM_RATE); + + // stopping condition + final StoppingCondition 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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction(); + final DimensionNDecoder decoder = new DimensionNDecoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java new file mode 100644 index 0000000000..5791b05769 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class DimensionNFunctionOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 10; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 4; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final DimensionNFunctionOptimizer optimizer = new DimensionNFunctionOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new DimNGraphPlotter("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 OnePointBinaryCrossover(), Constants.CROSSOVER_RATE, + new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection<>(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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction(); + final DimensionNDecoder decoder = new DimensionNDecoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java new file mode 100644 index 0000000000..6a15fed226 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java new file mode 100644 index 0000000000..1389c75418 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.utils; + +/** + * This abstraction maintains constants used by this module. + */ +public final class Constants { + + /** size of population. **/ + public static final int POPULATION_SIZE_PER_DIMENSION = 10; + + /** size of tournament. **/ + public static final int TOURNAMENT_SIZE_PER_DIMENSION = 2; + + /** length of chromosome. **/ + public static final int CHROMOSOME_LENGTH_PER_DIMENSION = 12; + + /** rate of crossover. **/ + public static final double CROSSOVER_RATE = 1.0; + + /** rate of elitism. **/ + public static final double ELITISM_RATE = 0.25; + + /** rate of mutation. **/ + public static final double AVERAGE_MUTATION_RATE = 0.05; + + /** number of generations with unchanged best fitness. **/ + public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50; + + /** encoding for console logger. **/ + public static final String ENCODING = "UTF-8"; + + /** + * constructor. + */ + private Constants() { + + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java new file mode 100644 index 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..b4a7811307 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml @@ -0,0 +1,52 @@ + + + + 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 + 1.7.32 + + + + + org.apache.commons + commons-csv + 1.9.0 + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + + \ 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..21833ef3de --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp; + +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.examples.ga.tsp.utils.GraphPlotter; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents the optimizer for traveling salesman problem. + */ +public class TSPOptimizer { + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(TSPOptimizer.class); + + /** + * Main method to initiate the optimization process. + * @param args arguments + */ + public static void main(String[] args) { + try { + final Population> initPopulation = getInitialPopulation(Constants.CITIES); + + final TSPOptimizer optimizer = new TSPOptimizer(); + + final ConvergenceListenerRegistry> convergenceListenerRegistry = ConvergenceListenerRegistry + .getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<>()); + convergenceListenerRegistry + .addConvergenceListener(new GraphPlotter("Convergence", "generation", "total-distance")); + + optimizer.optimizeSGA(initPopulation, Constants.CITIES); + + Thread.sleep(5000); + + } catch (InterruptedException e) { + throw new GeneticException(e); + } + } + + /** + * Optimizes the tsp problem. + * @param initial initial population + * @param cities cities + */ + public void optimizeSGA(Population> 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(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.decode().toString()); + + } + + 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..81ae597601 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import java.io.BufferedWriter; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; + +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.RandomKey; +import org.apache.commons.math3.genetics.RandomKeyMutation; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * This class represents the tsp optimizer based on legacy implementation of + * Genetic Algorithm. + */ +public class TSPOptimizerLegacy { + + /** + * MainThread.sleep(5000) method to initiate optimization. + * @param args arguments + */ + public static void main(String[] args) { + + final Population initPopulation = getInitialPopulation(Constants.CITIES); + final TSPOptimizerLegacy optimizer = new TSPOptimizerLegacy(); + + optimizer.optimize(initPopulation, Constants.CITIES); + + } + + /** + * Optimizes the tsp problem using legacy GA. + * @param initial initial population + * @param cities cities + */ + public void optimize(Population initial, List 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..701380bcea --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.utils; + +import java.awt.BorderLayout; + +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class GraphPlotter extends JFrame implements ConvergenceListener> { + + /** + * 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..84324837ad --- /dev/null +++ b/commons-math-examples/examples-ga/pom.xml @@ -0,0 +1,56 @@ + + + + 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..8183da2988 --- /dev/null +++ b/commons-math-examples/examples-ga/src/main/resources/spotbugs/spotbugs-exclude-filter.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..a2a3689ea1 --- /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}/.. + 1.7.32 + + + + + org.apache.commons + commons-numbers-core + + + org.apache.commons + commons-rng-simple + + + org.slf4j + slf4j-api + ${slf4jVersion} + + + + \ 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..7f9359f8ec --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an abstraction for all Genetic algorithm implementation + * comprising the basic properties and operations. + * @param

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

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** the crossover policy used by the algorithm. */ + private final CrossoverPolicy

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

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

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

crossoverPolicy, + final MutationPolicy

mutationPolicy, + final SelectionPolicy

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

crossoverPolicy, + final MutationPolicy

mutationPolicy, + final SelectionPolicy

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

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

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

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

evolve(final Population

initial, final StoppingCondition

condition) { + Population

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

getInstance().notifyAll(generationsEvolved, current); + + current = nextGeneration(current); + this.generationsEvolved++; + } + LOGGER.info("Population convergence achieved after generations: " + generationsEvolved); + + return current; + } + + /** + * Evolve the given population into the next generation. + *

    + *
  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/AdaptiveGeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java new file mode 100644 index 0000000000..07e891316f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.crossover.rategenerator.CrossoverRateGenerator; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.mutation.rategenerator.MutationRateGenerator; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An implementation of Genetic Algorithm. The probability of crossover and + * mutation is generated in an adaptive way. This implementation allows + * configuration of dynamic crossover and mutation rate generator along with + * crossover policy, mutation policy, selection policy and optionally elitism + * rate. + * @param

phenotype of chromosome + */ +public class AdaptiveGeneticAlgorithm

extends AbstractGeneticAlgorithm

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** The crossover rate generator. **/ + private final CrossoverRateGenerator

crossoverRateGenerator; + + /** The mutation rate generator. **/ + private final MutationRateGenerator

mutationRateGenerator; + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy

crossoverPolicy, + CrossoverRateGenerator

crossoverProbabilityGenerator, + MutationPolicy

mutationPolicy, + MutationRateGenerator

mutationProbabilityGenerator, + SelectionPolicy

selectionPolicy) { + super(crossoverPolicy, mutationPolicy, selectionPolicy); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + * @param elitismRate elitism rate + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy

crossoverPolicy, + CrossoverRateGenerator

crossoverProbabilityGenerator, + MutationPolicy

mutationPolicy, + MutationRateGenerator

mutationProbabilityGenerator, + SelectionPolicy

selectionPolicy, + double elitismRate) { + super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * {@inheritDoc} + */ + @Override + protected Population

nextGeneration(Population

current) { + + LOGGER.debug("Reproducing next generation."); + + // compute statistics of current generation chromosomes. + PopulationStatisticalSummary

populationStats = new PopulationStatisticalSummaryImpl<>(current); + + // Initialize the next generation with elit chromosomes from previous + // generation. + final Population

nextGeneration = current.nextGeneration(getElitismRate()); + + LOGGER.debug( + "No of Elite chromosomes selected from previous generation: " + nextGeneration.getPopulationSize()); + + final int maxOffspringCount = nextGeneration.getPopulationLimit() - nextGeneration.getPopulationSize(); + + // Initialize an empty population for offsprings. + final Population

offspringPopulation = current.nextGeneration(0); + + // perform crossover and generate new offsprings + while (offspringPopulation.getPopulationSize() < maxOffspringCount) { + + // select parent chromosomes + ChromosomePair

pair = getSelectionPolicy().select(current); + LOGGER.debug("Selected Chromosomes: \r\n" + pair.toString()); + + final double crossoverRate = crossoverRateGenerator.generate(pair.getFirst(), pair.getSecond(), + populationStats, getGenerationsEvolved()); + // apply crossover policy to create two offspring + pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond(), crossoverRate); + LOGGER.debug("Offsprings after Crossover: \r\n" + pair.toString()); + + // add the first chromosome to the population + offspringPopulation.addChromosome(pair.getFirst()); + // is there still a place for the second chromosome? + if (offspringPopulation.getPopulationSize() < maxOffspringCount) { + // add the second chromosome to the population + offspringPopulation.addChromosome(pair.getSecond()); + } + } + LOGGER.debug("Performing adaptive mutation of offsprings."); + + // recompute the statistics of the offspring population. + populationStats = new PopulationStatisticalSummaryImpl<>(offspringPopulation); + + // apply mutation policy to the offspring chromosomes and add the mutated + // chromosomes to next generation. + for (Chromosome

chromosome : offspringPopulation) { + nextGeneration.addChromosome(getMutationPolicy().mutate(chromosome, + mutationRateGenerator.generate(chromosome, populationStats, getGenerationsEvolved()))); + } + LOGGER.debug("New Generation: \r\n" + nextGeneration.toString()); + + return nextGeneration; + } + + /** + * Returns crossover probability generator. + * @return crossover probability generator + */ + public CrossoverRateGenerator

getCrossoverProbabilityGenerator() { + return crossoverRateGenerator; + } + + /** + * Returns mutation probability generator. + * @return mutation probability generator + */ + public MutationRateGenerator

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

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

extends AbstractGeneticAlgorithm

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** crossover rate string. **/ + private static final String CROSSOVER_RATE = "CROSSOVER_RATE"; + + /** mutation rate string. **/ + private static final String MUTATION_RATE = "MUTATION_RATE"; + + /** the rate of crossover for the algorithm. */ + private final double crossoverRate; + + /** the rate of mutation for the algorithm. */ + private final double mutationRate; + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + */ + public GeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, + final double crossoverRate, + final MutationPolicy

mutationPolicy, + final double mutationRate, + final SelectionPolicy

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

crossoverPolicy, + final double crossoverRate, + final MutationPolicy

mutationPolicy, + final double mutationRate, + final SelectionPolicy

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

    + *
  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) { + + LOGGER.debug("Reproducing next generation."); + final Population

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

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

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

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

implements Chromosome

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

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

decoder; + + /** Id of chromosome. **/ + private final String id; + + /** + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + protected AbstractChromosome(final FitnessFunction

fitnessFunction, final Decoder

decoder) { + this.fitnessFunction = Objects.requireNonNull(fitnessFunction); + this.decoder = Objects.requireNonNull(decoder); + this.id = UUID.randomUUID().toString(); + } + + /** + * returns fitness function. + * @return fitnessFunction + */ + protected FitnessFunction

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

getDecoder() { + return decoder; + } + + /** + * Returns id of chromosome. + * @return id + */ + @Override + public String getId() { + return id; + } + + /** + * Access the fitness of this chromosome. The bigger the fitness, the better the + * chromosome. + *

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

    + *
  • -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..9e1676d4f7 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents an abstract chromosome containing an immutable list of + * allele/genes. + * @param 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; + + /** + * @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); + } + + /** + * @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); + } + + /** + * @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); + Objects.requireNonNull(representation); + this.representation = Collections.unmodifiableList(copyList ? new ArrayList<>(representation) : representation); + } + + /** + * Returns the (immutable) inner representation of the chromosome. + * @return the representation of the chromosome + */ + public List getRepresentation() { + return representation; + } + + /** + * Returns the length of the chromosome. + * @return the length of the chromosome + */ + public int getLength() { + return getRepresentation().size(); + } + + /** + * returns the decoder. + * @return decoder + */ + @SuppressWarnings("unchecked") + @Override + public AbstractListChromosomeDecoder 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..a9ef839a70 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java @@ -0,0 +1,303 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.List; + +import java.util.Objects; + +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; + +/** + * BinaryChromosome represented by a vector of 0s and 1s. + * @param

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

extends AbstractChromosome

{ + + /** + * maximum allowed length of binary chromosome. + */ + public static final long MAX_LENGTH = Integer.MAX_VALUE; + + /** + * length of binary chromosome. + */ + private final long length; + + /** + * binary representation of chromosome. + */ + private final long[] representation; + + /** + * @param representation Internal representation of chromosome. + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(List representation, + long length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + checkMaximumLength(length); + this.length = length; + this.representation = new long[representation.size()]; + for (int i = 0; i < representation.size(); i++) { + this.representation[i] = representation.get(i); + } + } + + /** + * @param representation Internal representation of chromosome. + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(Long[] representation, + long length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + checkMaximumLength(length); + this.length = length; + this.representation = new long[representation.length]; + for (int i = 0; i < representation.length; i++) { + this.representation[i] = representation[i]; + } + } + + /** + * @param inputRepresentation Internal representation of chromosome. + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(long[] inputRepresentation, + long length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(inputRepresentation); + checkMaximumLength(length); + if (length <= (inputRepresentation.length - 1) * Long.SIZE || length > inputRepresentation.length * Long.SIZE) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "provided length does not match expected representation"); + } + this.length = length; + this.representation = new long[inputRepresentation.length]; + System.arraycopy(inputRepresentation, 0, representation, 0, inputRepresentation.length); + } + + /** + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(String representation, FitnessFunction

fitnessFunction, Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + this.length = representation.length(); + this.representation = encode(representation); + } + + /** + * Checks the input chromosome length against predefined maximum length. + * @param chromosomeLength input chromsome length + */ + protected void checkMaximumLength(long chromosomeLength) { + if (chromosomeLength > MAX_LENGTH) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "length exceeded the max length " + MAX_LENGTH); + } + } + + /** + * Validates the string representation. + * @param stringRepresentation binary string representation of chromosome + */ + private void validateStringRepresentation(String stringRepresentation) { + char allele = '\0'; + final int chromosomeLength = stringRepresentation.length(); + for (int i = 0; i < chromosomeLength; i++) { + if ((allele = stringRepresentation.charAt(i)) != '0' && allele != '1') { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "Only 0 or 1 are acceptable as characters."); + } + } + } + + /** + * encodes the binary string representation as an array of long. + * @param stringRepresentation binary string + * @return encoded representation + */ + private long[] encode(String stringRepresentation) { + validateStringRepresentation(stringRepresentation); + final int chromosomeLength = stringRepresentation.length(); + final int arraySize = (int) Math.ceil(chromosomeLength / (double) Long.SIZE); + final long[] encodedRepresentation = new long[arraySize]; + final int offset = (int) (chromosomeLength % Long.SIZE == 0 ? 0 : Long.SIZE - chromosomeLength % Long.SIZE); + encodedRepresentation[0] = Long.parseUnsignedLong(stringRepresentation.substring(0, Long.SIZE - offset), 2); + for (int i = Long.SIZE - offset, j = 1; i < chromosomeLength; i += Long.SIZE, j++) { + encodedRepresentation[j] = Long.parseUnsignedLong(stringRepresentation.substring(i, i + Long.SIZE), 2); + } + return encodedRepresentation; + } + + /** + * Returns the chromosome length. + * @return length + */ + public long getLength() { + return length; + } + + /** + * Returns the binary representation. + * @return representation + */ + public long[] getRepresentation() { + final long[] clonedRepresentation = new long[representation.length]; + System.arraycopy(representation, 0, clonedRepresentation, 0, representation.length); + return clonedRepresentation; + } + + /** + * Returns the binary string representation of the chromosome. + * @return the string representation + */ + public String getStringRepresentation() { + if (length > Integer.MAX_VALUE) { + throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, length); + } + return getStringRepresentation(0, length); + } + + /** + * Returns the binary string representation of the chromosome alleles from + * start(inclusive) to end(exclusive) index. + * @param start start allele/gene index(inclusive) + * @param end end allele/gene index(exclusive) + * @return the string representation + */ + public String getStringRepresentation(long start, long end) { + if (start >= end) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "start " + start + " is greater than end " + end); + } + if (end - start > Integer.MAX_VALUE) { + throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, end - start); + } + final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE); + final long offsettedStart = offset + start; + final long offsettedEnd = offset + end; + final int startAlleleBlockIndex = (int) (offsettedStart / Long.SIZE); + final int endAlleleBlockIndex = (int) (offsettedEnd / Long.SIZE); + final int startAlleleElementIndex = (int) (offsettedStart % Long.SIZE); + final int endAlleleElementIndex = (int) (offsettedEnd % Long.SIZE); + + if (startAlleleBlockIndex == endAlleleBlockIndex) { + return getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex, + endAlleleElementIndex); + } else { + final StringBuilder allelesStrRepresentation = new StringBuilder(); + + // extract string representation of first allele block. + allelesStrRepresentation + .append(getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex)); + + // extract string representation of all allele blocks except first and last. + for (int i = startAlleleBlockIndex + 1; i < endAlleleBlockIndex; i++) { + allelesStrRepresentation.append(getAlleleBlockString(i)); + } + + // extract string representation from last allele block if end allele index != + // 0. + if (endAlleleElementIndex != 0) { + allelesStrRepresentation + .append(getAlleleBlockString(endAlleleBlockIndex).substring(0, endAlleleElementIndex)); + } + + return allelesStrRepresentation.toString(); + } + } + + /** + * Returns the allele block as binary string representation. + * @param alleleBlockIndex index of allele block i.e. array element + * @return the string representation + */ + private String getAlleleBlockString(final int alleleBlockIndex) { + return prepareZeroPrefix(representation[alleleBlockIndex] == 0 ? Long.SIZE - 1 : + Long.numberOfLeadingZeros(representation[alleleBlockIndex])) + + Long.toUnsignedString(representation[alleleBlockIndex], 2); + } + + /** + * Prepares zero prefix for binary chromosome. + * @param count number of zeros + * @return prefix + */ + private String prepareZeroPrefix(int count) { + final StringBuilder zeroPrefix = new StringBuilder(); + for (int i = 0; i < count; i++) { + zeroPrefix.append('0'); + } + return zeroPrefix.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public FitnessFunction

getFitnessFunction() { + return super.getFitnessFunction(); + } + + /** + * Creates a new chromosome with provided parameters. + * @param chromosomeRepresentation the representation + * @param chromosomeLength length of chromosome + * @return chromosome + */ + public BinaryChromosome

newChromosome(long[] chromosomeRepresentation, long chromosomeLength) { + return new BinaryChromosome

(chromosomeRepresentation, chromosomeLength, getFitnessFunction(), getDecoder()); + } + + /** + * Creates an instance of Binary Chromosome with random binary representation. + * @param

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

BinaryChromosome

randomChromosome(int length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + return new BinaryChromosome

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

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

extends Comparable> { + + /** + * 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(); + + /** + * Returns unique Id of chromosome. + * @return id + */ + String getId(); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java new file mode 100644 index 0000000000..1157a00822 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +/** + * A pair of {@link Chromosome} objects. + * @param

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

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

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

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

c1, final Chromosome

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

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

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

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

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

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

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() { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + for (int i : getRepresentation()) { + if (i < min || i >= max) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public IntegralValuedChromosome

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

+ * @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; + + /** + * @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(); + } + + /** + * @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(); + } + + /** + * @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); + } + + /** + * @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() { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + for (double i : getRepresentation()) { + if (i < min || i >= max) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public RealValuedChromosome

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/convergence/FixedElapsedTime.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java new file mode 100644 index 0000000000..8fda02bd0c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergence; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.Population; + +/** + * Stops after a fixed amount of time has elapsed. + *

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

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

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

implements StoppingCondition

{ + /** Number of generations that have passed. */ + private int numGenerations; + + /** Maximum number of generations (stopping criteria). */ + private final int maxGenerations; + + /** + * Create a new FixedGenerationCount instance. + * + * @param maxGenerations number of generations to evolve + */ + public FixedGenerationCount(final int maxGenerations) { + if (maxGenerations <= 0) { + throw new GeneticException(GeneticException.TOO_SMALL, maxGenerations, 1); + } + this.maxGenerations = maxGenerations; + } + + /** + * Determine whether or not the given number of generations have passed. + * Increments the number of generations counter if the maximum has not been + * reached. + * + * @return 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/convergence/StoppingCondition.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java new file mode 100644 index 0000000000..601c098711 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergence; + +import org.apache.commons.math4.ga.population.Population; + +/** + * Algorithm used to determine when to stop evolution. + * + * @param

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

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

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

implements StoppingCondition

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

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

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

implements StoppingCondition

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

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

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

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

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

implements CrossoverPolicy

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

crossover(final Chromosome

first, + final Chromosome

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

crossover(Chromosome

first, Chromosome

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

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

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

+ * -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 = RandomProviderManager.getRandomProvider(); + + 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/OnePointBinaryCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java new file mode 100644 index 0000000000..f860798c82 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * OnePoint Crossover Policy for Binary chromosomes. + * @param

the phenotype + */ +public class OnePointBinaryCrossover

extends AbstractChromosomeCrossoverPolicy

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

crossover(Chromosome

first, Chromosome

second) { + + if (!(first instanceof BinaryChromosome && second instanceof BinaryChromosome)) { + throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME); + } + final BinaryChromosome

firstChromosome = (BinaryChromosome

) first; + final BinaryChromosome

secondChromosome = (BinaryChromosome

) second; + + final long alleleCount = firstChromosome.getLength(); + + // array representations of the parents + final long[] parent1Rep = firstChromosome.getRepresentation(); + final long[] parent2Rep = secondChromosome.getRepresentation(); + + // and of the children + final long[] child1Rep = new long[parent1Rep.length]; + final long[] child2Rep = new long[parent2Rep.length]; + + // select a crossover point at random (0 and length makes no sense) + final long crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextLong(alleleCount - 1)); + + final int offset = (int) (alleleCount % Long.SIZE == 0 ? 0 : Long.SIZE - alleleCount % Long.SIZE); + final long offsettedCrossoverIndex = crossoverIndex + offset; + + final int crossoverBlockIndex = (int) (offsettedCrossoverIndex / Long.SIZE); + final int crossoverBlockAlleleIndex = (int) offsettedCrossoverIndex % Long.SIZE; + + if (crossoverBlockAlleleIndex == 0) { + // if the offsetted-crossover index is divisible by + // Long.SIZE then first copy all + // elements of previous array elements. + for (int i = 0; i < crossoverBlockIndex; i++) { + child1Rep[i] = parent1Rep[i]; + child2Rep[i] = parent2Rep[i]; + } + // copy all elements from crossover block index. + for (int i = crossoverBlockIndex; i < parent1Rep.length; i++) { + child1Rep[i] = parent2Rep[i]; + child2Rep[i] = parent1Rep[i]; + } + } else { + // copy all parent array elements to child till crossover block index - 1. + for (int i = 0; i < crossoverBlockIndex; i++) { + child1Rep[i] = parent1Rep[i]; + child2Rep[i] = parent2Rep[i]; + } + // do exchange of alleles of the array element indexed at crossover block index. + final long parent1CrossoverBlockRep = parent1Rep[crossoverBlockIndex]; + final long parent2CrossoverBlockRep = parent2Rep[crossoverBlockIndex]; + final long leftMask = Long.MIN_VALUE >> crossoverBlockAlleleIndex - 1; + final long rightMask = crossoverBlockAlleleIndex != 1 ? + (long) Math.pow(2, Long.SIZE - crossoverBlockAlleleIndex) - 1 : + Long.MAX_VALUE; + + final long child1CrossoverBlockRep = (parent1CrossoverBlockRep & leftMask) | + (parent2CrossoverBlockRep & rightMask); + final long child2CrossoverBlockRep = (parent2CrossoverBlockRep & leftMask) | + (parent1CrossoverBlockRep & rightMask); + + child1Rep[crossoverBlockIndex] = child1CrossoverBlockRep; + child2Rep[crossoverBlockIndex] = child2CrossoverBlockRep; + + // Copy all the alleles which belong to array elements having index > + // crossover block index. + if (crossoverBlockIndex < parent1Rep.length - 1) { + for (int i = crossoverBlockIndex + 1; i < parent1Rep.length; i++) { + child1Rep[i] = parent2Rep[i]; + child2Rep[i] = parent1Rep[i]; + } + } + } + + final BinaryChromosome

childChromosome1 = new BinaryChromosome<>(child1Rep, alleleCount, + firstChromosome.getFitnessFunction(), firstChromosome.getDecoder()); + final BinaryChromosome

childChromosome2 = new BinaryChromosome<>(child2Rep, alleleCount, + secondChromosome.getFitnessFunction(), secondChromosome.getDecoder()); + + return new ChromosomePair<>(childChromosome1, childChromosome2); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java new file mode 100644 index 0000000000..2c6da54ccb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * One point crossover policy. A random crossover point is selected and the + * first part from each parent is copied to the corresponding child, and the + * second parts are copied crosswise. + * + * Example: + *

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

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

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 = RandomProviderManager.getRandomProvider(); + + for (int index = 0; index < length; index++) { + + if (random.nextDouble() < ratio) { + // swap the bits -> take other parent + child1Rep.add(parent2Rep.get(index)); + child2Rep.add(parent1Rep.get(index)); + } else { + child1Rep.add(parent1Rep.get(index)); + child2Rep.add(parent2Rep.get(index)); + } + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java new file mode 100644 index 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/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java new file mode 100644 index 0000000000..1b1057de0f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates crossover rate based on linear function of relative average rank of + * input chromosomes in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearAverageRankBasedCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum crossover rate + * @param maximumRate maximum crossover rate + */ + public AdaptiveLinearAverageRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) { + this.maximumRate = maximumRate; + this.minimumRate = minimumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation) { + final int averageRank = (populationStats.findRank(first) + populationStats.findRank(second)) / 2; + return minimumRate + + (maximumRate - minimumRate) * (1.0 - (double) averageRank / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java new file mode 100644 index 0000000000..c03f488f5f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates crossover rate based on linear function of relative maximum rank of + * input chromosomes in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearMaximumRankBasedCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum crossover rate + * @param maximumRate maximum crossover rate + */ + public AdaptiveLinearMaximumRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) { + this.maximumRate = maximumRate; + this.minimumRate = minimumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation) { + final int maximumRank = Math.max(populationStats.findRank(first), populationStats.findRank(second)); + return minimumRate + + (maximumRate - minimumRate) * (1.0 - (double) maximumRank / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java new file mode 100644 index 0000000000..2f77ce12f0 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents a constant crossover rate generator. + * @param

phenotype of chromosome + */ +public class ConstantCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** the fixed value of crossover rate. **/ + private final double crossoverRate; + + /** + * @param crossoverRate crossover rate + */ + public ConstantCrossoverRateGenerator(double crossoverRate) { + this.crossoverRate = crossoverRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

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

phenotype of chromosome + */ +public interface CrossoverRateGenerator

{ + + /** + * Generates crossover rate. + * @param first first chromosome + * @param second second chromosome + * @param populationStats population statistics summary + * @param generation generation number + * @return crossover rate rate of crossover + */ + double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java new file mode 100644 index 0000000000..886eab7704 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.crossover.rategenerator; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java new file mode 100644 index 0000000000..96a1920533 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * An abstract Decoder of ListChromosome. + * @param 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..50f2ba935d --- /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..037ccfde03 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * A concrete implementation of RandomKey decoder. This class is responsible for + * decoding permutation chromosome encoded with random key. + * @param type of the permutation element + * @since 4.0 + */ +public final class RandomKeyDecoder extends AbstractListChromosomeDecoder> { + + /** base sequence for decoding chromosome. **/ + private final List baseSequence; + + /** + * @param baseSequence the unpermuted sequence + */ + public RandomKeyDecoder(List baseSequence) { + this.baseSequence = Collections.unmodifiableList(Objects.requireNonNull(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/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/internal/exception/GeneticException.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java new file mode 100644 index 0000000000..7209472c37 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.internal.exception; + +import java.text.MessageFormat; + +/** + * This class represents the Exception encountered during GA optimization. + * @since 4.0 + */ +public class GeneticException extends RuntimeException { + + /** Error message for "out of range" condition. */ + public static final String OUT_OF_RANGE = "Value {0} of {1} is out of range [{2}, {3}]"; + + /** Error message for "not strictly positive" condition. */ + public static final String NOT_STRICTLY_POSITIVE = "Number {0} is not strictly positive"; + + /** Error message for "too large" condition. */ + public static final String TOO_LARGE = "Number {0} is larger than {1}"; + + /** Error message for "too small" condition. */ + public static final String TOO_SMALL = "Number {0} is smaller than {1}"; + + /** Error message for "out of range" condition. */ + public static final String NO_DATA = "No data"; + + /** Error message for "size mismatch" condition. */ + public static final String SIZE_MISMATCH = "Size mismatch: {0} != {1}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_ARGUMENT = "Illegal Argument Exception: {0}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_RANGE = "Illegal Range of Value Exception: " + + "[Expected min-{0}, max-{1}], [Passed min-{2}, max-{3}]"; + + /** Error message for "generic illegal argument" condition. */ + public static final String INVALID_FIXED_LENGTH_CHROMOSOME = "Invalid Fixed Length Chromosome."; + + /** Error message for "NULL ARGUMENT" condition. */ + public static final String NULL_ARGUMENT = "Null Argument Exception: {0}"; + + /** + * Error message for "List of Chromosome bigger than population size" condition. + */ + public static final String LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE = "List of chromosome bigger than " + + "population size: {0} > {1}"; + + /** + * Error message for "population limit not positive" condition. + */ + public static final String POPULATION_LIMIT_NOT_POSITIVE = "Population limit not positive :{0}"; + + /** + * Error message for " population limit less than list of chromosomes size" + * condition. + */ + public static final String POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE = "Population limit is " + + " lesser than list of chromosomes size : {0} < {1}"; + + /** + * Error message for different origin and permuted data. + */ + public static final String DIFFERENT_ORIG_AND_PERMUTED_DATA = "Different original and permuted data"; + + /** + * Error message for chromosome length larger than expected. + */ + public static final String LENGTH_TOO_LARGE = "Chromosome length larger than {0} not supported for" + + " string representation."; + + /** Serializable version identifier. */ + private static final long serialVersionUID = 20210516L; + + /** + * Create an exception where the message is constructed by applying the + * {@code format()} method from {@code java.text.MessageFormat}. + * + * @param message Message format (with replaceable parameters). + * @param formatArguments Actual arguments to be displayed in the message. + */ + public GeneticException(String message, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments)); + } + + /** + * Create an exception. + * @param t instance of {@link Throwable} + */ + public GeneticException(Throwable t) { + super(t); + } + + /** + * Create an exception having both stacktrace and message. + * @param message the exception message + * @param t the instance of {@link Throwable} + * @param formatArguments arguments to format the exception message + */ + public GeneticException(String message, Throwable t, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments), t); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java new file mode 100644 index 0000000000..b55e918a60 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.internal.exception; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java new file mode 100644 index 0000000000..b95b60f1a6 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.internal.stats; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents an implementation of population statistical summary. + * @param

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

implements PopulationStatisticalSummary

{ + + /** maximum fitness of the population. **/ + private final double maxFitness; + + /** minimum fitness of the population. **/ + private final double minFitness; + + /** mean fitness of the population. **/ + private double meanFitness; + + /** variance of population fitness. **/ + private final double variance; + + /** population size. **/ + private final int populationSize; + + /** a map of chromosome Id and corresponding rank in population. **/ + private final Map chromosomeIdRankMap = new HashMap<>(); + + /** + * @param population current population {@link Population} of chromosomes + */ + public PopulationStatisticalSummaryImpl(Population

population) { + + // Fetch all chromosomes. + List> chromosomes = getChromosomes(Objects.requireNonNull(population)); + + // Sort all chromosomes. + Collections.sort(chromosomes); + + this.populationSize = chromosomes.size(); + this.maxFitness = chromosomes.get(chromosomes.size() - 1).evaluate(); + this.minFitness = chromosomes.get(0).evaluate(); + this.meanFitness = calculateMeanFitness(chromosomes); + this.variance = calculateVariance(chromosomes); + + updateChromosomeIdRankMap(chromosomes); + + } + + /** + * Updates chromosome Id and rank. + * @param chromosomes list of chromosomes + */ + private void updateChromosomeIdRankMap(List> chromosomes) { + for (int rank = 0; rank < chromosomes.size(); rank++) { + this.chromosomeIdRankMap.put(chromosomes.get(rank).getId(), rank); + } + } + + /** + * Fetches chromosomes. + * @param population + * @return list of chromosomes + */ + private List> getChromosomes(Population

population) { + List> chromosomes = new ArrayList<>(population.getPopulationSize()); + for (Chromosome

chromosome : population) { + chromosomes.add(chromosome); + } + return chromosomes; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMeanFitness() { + return this.meanFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getFitnessVariance() { + return this.variance; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMaxFitness() { + return this.maxFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMinFitness() { + return this.minFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public long getPopulationSize() { + return this.populationSize; + } + + /** + * Calculate mean fitness. + * @param chromosomes list of chromosomes + * @return returns mean fitness + */ + private double calculateMeanFitness(List> chromosomes) { + double sum = 0.0; + for (Chromosome

chromosome : chromosomes) { + sum += chromosome.evaluate(); + } + return sum / chromosomes.size(); + } + + /** + * Calculate variance of population fitness. + * @param chromosomes List of chromosomes + * @return fitness variance + */ + private double calculateVariance(List> chromosomes) { + if (this.meanFitness == 0) { + this.meanFitness = calculateMeanFitness(chromosomes); + } + double sumOfSquare = 0.0; + for (Chromosome

chromosome : chromosomes) { + sumOfSquare += Math.pow(chromosome.evaluate(), 2); + } + + return (sumOfSquare / chromosomes.size()) - Math.pow(this.meanFitness, 2); + } + + /** + * {@inheritDoc} + */ + @Override + public int findRank(Chromosome

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

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<>(); + + /** + * private constructor to construct the singleton instance. + */ + private ConvergenceListenerRegistry() { + } + + /** + * Registers the interested ConvergenceListener passed as an argument. + * @param convergenceListener The {@link ConvergenceListener} + */ + public void addConvergenceListener(ConvergenceListener

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

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) { + for (ConvergenceListener

convergenceListener : Objects.requireNonNull(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..b0f7276f8a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Logs population statistics during the convergence process. + * @param

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

implements ConvergenceListener

{ + + /** instance of log4j logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(PopulationStatisticsLogger.class); + + /** + * Logs the population statistics during the process of convergence. + */ + @Override + public void notify(int generation, Population

population) { + final PopulationStatisticalSummary

populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>( + population); + LOGGER.info( + "Population statistics for generation %d ::: Mean Fitness: %f, Max Fitness: %f, Fitness Variance: %f", + generation, populationStatisticalSummary.getMeanFitness(), populationStatisticalSummary.getMaxFitness(), + populationStatisticalSummary.getFitnessVariance()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java new file mode 100644 index 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..3e41fc20c9 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * This abstraction represents an abstract mutation policy for ListChromosomes. + * @param 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<>(); + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + + // if chromosomeMutationRate >= 1 then more than one allele will be mutated. + if (chromosomeMutationRate >= 1) { + final int noOfMutation = (int) Math.round(chromosomeMutationRate); + while (indexSet.size() < noOfMutation) { + indexSet.add(randomProvider.nextInt(length)); + } + } else if (randomProvider.nextDouble() < chromosomeMutationRate) { + indexSet.add(randomProvider.nextInt(length)); + } + + return indexSet; + } + + /** + * Mutates an individual gene/allele. + * @param originalValue the original value of gene + * @return mutated value of gene + */ + protected abstract T mutateGene(T originalValue); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java new file mode 100644 index 0000000000..2470f38022 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * Mutation for {@link BinaryChromosome}s. Randomly changes few genes. + * @param

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

implements MutationPolicy

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

mutate(Chromosome

original, double mutationRate) { + // check for validity. + checkValidity(original); + final BinaryChromosome

chromosome = (BinaryChromosome

) original; + final long[] representation = chromosome.getRepresentation(); + final long[] newRep = new long[representation.length]; + System.arraycopy(representation, 0, newRep, 0, representation.length); + + final Map> mutableGeneIndexMap = getMutableGeneIndexes(chromosome.getLength(), + mutationRate); + for (Entry> entry : mutableGeneIndexMap.entrySet()) { + final int alleleBlockIndex = entry.getKey(); + long mask = 0; + final Set alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex); + for (int index : alleleElementIndexes) { + mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index); + } + newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask; + } +// for (int alleleBlockIndex : mutableGeneIndexMap.keySet()) { +// long mask = 0; +// final Set alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex); +// for (int index : alleleElementIndexes) { +// mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index); +// } +// newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask; +// } + + return chromosome.newChromosome(newRep, chromosome.getLength()); + } + + /** + * Checks input chromosome validity. + * @param original chromosome to be mutated + */ + protected void checkValidity(Chromosome

original) { + if (!BinaryChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + } + + /** + * Selects and returns mutable gene indexes based on mutation rate. + * @param length no of alleles/genes in chromosome + * @param mutationRate mutation rate of the allele/gene + * @return mutable gene indexes + */ + protected Map> getMutableGeneIndexes(long length, double mutationRate) { + + // calculate the total mutation rate of all the alleles i.e. chromosome. + final double chromosomeMutationRate = mutationRate * length; + final Map> indexMap = new HashMap<>(); + + // if chromosomeMutationRate >= 1 then more than one allele will be mutated. + if (chromosomeMutationRate >= 1) { + final int noOfMutation = (int) Math.round(chromosomeMutationRate); + final Set mutationIndexes = new HashSet<>(); + for (int i = 0; i < noOfMutation; i++) { + final long mutationIndex = generateMutationIndex(length, mutationIndexes); + mutationIndexes.add(mutationIndex); + updateIndexMap(indexMap, length, mutationIndex); + } + } else if (RandomProviderManager.getRandomProvider().nextDouble() < chromosomeMutationRate) { + updateIndexMap(indexMap, length); + } + return indexMap; + } + + private long generateMutationIndex(long length, Set mutationIndexes) { + long mutationIndex = 0; + do { + mutationIndex = RandomProviderManager.getRandomProvider().nextLong(length); + } while (mutationIndexes.contains(mutationIndex)); + return mutationIndex; + } + + private void updateIndexMap(Map> indexMap, long length, long mutationIndex) { + final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE); + final long offsettedMutableAlleleIndex = offset + mutationIndex; + + final int alleleBlockIndex = (int) (offsettedMutableAlleleIndex / Long.SIZE); + + if (!indexMap.containsKey(alleleBlockIndex)) { + indexMap.put(alleleBlockIndex, new HashSet<>()); + } + final int alleleElementIndex = (int) (offsettedMutableAlleleIndex % Long.SIZE); + + indexMap.get(alleleBlockIndex).add(alleleElementIndex); + } + + private void updateIndexMap(Map> indexMap, long length) { + updateIndexMap(indexMap, length, RandomProviderManager.getRandomProvider().nextLong(length)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java new file mode 100644 index 0000000000..2ff7f0f893 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * Mutation for {@link IntegralValuedChromosome}. Randomly changes few genes. + * @param

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; + + /** + * @param min minimum value of allele + * @param max maximum value of allele + */ + public IntegralValuedMutation(final int min, final int max) { + this.min = min; + this.max = max; + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + } + + /** + * Returns the minimum acceptable value. + * @return minimum + */ + public int getMin() { + return min; + } + + /** + * Returns the maximum acceptable value. + * @return maximum + */ + public int getMax() { + return max; + } + + /** + * {@inheritDoc} + */ + @Override + protected void checkValidity(Chromosome

original) { + if (!IntegralValuedChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + final 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 + RandomProviderManager.getRandomProvider().nextInt(max - min); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java new file mode 100644 index 0000000000..096e419a58 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * Algorithm used to mutate a chromosome. + * @param

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

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; + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + } + + /** + * Returns the minimum acceptable value. + * @return minimum + */ + public double getMin() { + return min; + } + + /** + * Returns the maximum acceptable value. + * @return maximum + */ + public double getMax() { + return max; + } + + /** + * {@inheritDoc} + */ + @Override + protected void checkValidity(Chromosome

original) { + if (!RealValuedChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + final 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 + RandomProviderManager.getRandomProvider().nextDouble() * (max - min); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java new file mode 100644 index 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/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java new file mode 100644 index 0000000000..555465fff2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates mutation rate using linear function of relative rank of input + * chromosome in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearMutationRateGenerator

implements MutationRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum mutation rate + * @param maximumRate maximum mutation rate + */ + public AdaptiveLinearMutationRateGenerator(double minimumRate, double maximumRate) { + this.minimumRate = minimumRate; + this.maximumRate = maximumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

chromosome, PopulationStatisticalSummary

populationStats, int generation) { + return minimumRate + (maximumRate - minimumRate) * + (1.0 - (double) populationStats.findRank(chromosome) / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java new file mode 100644 index 0000000000..097e98df8c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents a constant mutation rate generator. + * @param

phenotype of chromosome + */ +public class ConstantMutationRateGenerator

implements MutationRateGenerator

{ + + /** the constant mutationRate. **/ + private final double mutationRate; + + /** + * @param mutationRate mutation rate + */ + public ConstantMutationRateGenerator(double mutationRate) { + this.mutationRate = mutationRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

chromosome, PopulationStatisticalSummary

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

phenotype of chromosome + */ +public interface MutationRateGenerator

{ + + /** + * Generates mutation rate based on input params. + * @param chromosome chromosome + * @param populationStats population statictics summary + * @param generation generation count + * @return mutation rate + */ + double generate(Chromosome

chromosome, PopulationStatisticalSummary

populationStats, int generation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java new file mode 100644 index 0000000000..c7e56cefeb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.mutation.rategenerator; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java new file mode 100644 index 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..1622d9c09b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * Population of chromosomes represented by a {@link List}. + * + * @param

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

implements Population

{ + + /** new line constant. **/ + public static final String NEW_LINE = System.getProperty("line.separator"); + + /** 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) { + + Objects.requireNonNull(chromosomes); + + if (populationLimit <= 0) { + throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, populationLimit); + } + if (chromosomes.size() > populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.populationLimit = populationLimit; + this.chromosomes = new ArrayList<>(populationLimit); + this.chromosomes.addAll(chromosomes); + } + + /** + * Add a {@link Collection} of chromosomes to this {@link Population}. + * @param chromosomeColl a {@link Collection} of chromosomes + * @since 3.1 + */ + public void addChromosomes(final Collection> 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() { + final StringBuilder populationStrRepr = new StringBuilder(); + for (Chromosome

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

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

implements SelectionPolicy

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

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 = RandomProviderManager.getRandomProvider().nextInt(chromosomes.size()); + selectedChromosomes.add(chromosomes.get(rind)); + // do not select it again + chromosomes.remove(rind); + } + + // the winner takes it all + return Collections.max(selectedChromosomes); + } + + /** + * Gets the arity (number of chromosomes drawn to the tournament). + * + * @return arity of the tournament + */ + public int getArity() { + return arity; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java new file mode 100644 index 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/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..47503354fb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * This interface generates all random representations for chromosomes. + * @since 4.0 + */ +public interface ChromosomeRepresentationUtils { + + /** + * Generates a representation corresponding to a random permutation of length l + * which can be passed to the RandomKey constructor. + * + * @param l length of the permutation + * @return representation of a random permutation + */ + static List randomPermutation(final int l) { + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + 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 = RandomProviderManager.getRandomProvider(); + 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 long[] randomBinaryRepresentation(final long length) { + if (length > BinaryChromosome.MAX_LENGTH) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "length exceeded the max length " + BinaryChromosome.MAX_LENGTH); + } + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + int elementCount = (int) Math.ceil(length / (double)Long.SIZE); + // random binary list + final long[] representation = new long[elementCount]; + int remainder = (int) (length % Long.SIZE); + representation[0] = remainder == 0 ? randomProvider.nextLong() : + randomProvider.nextLong((long) Math.pow(2, remainder)); + for (int i = 1; i < elementCount; i++) { + representation[i] = randomProvider.nextLong(); + } + return representation; + } + + /** + * Generates a random string representation of chromosome with specified + * characters. + * @param alleles characters representing alleles + * @param length length of chromosome + * @return returns chromosome representation as string + */ + static String randomStringRepresentation(char[] alleles, final long length) { + Objects.requireNonNull(alleles); + final StringBuilder representationStr = new StringBuilder(); + for (int i = 0; i < length; i++) { + representationStr + .append(alleles[(int) (RandomProviderManager.getRandomProvider().nextInt(alleles.length))]); + } + return representationStr.toString(); + } + + /** + * Generates a representation corresponding to a random double values[0..1] of + * length l. + * @param l length of the permutation + * @return representation of a random permutation + */ + static List 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) { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + final double range = max - min; + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + 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/RandomProviderManager.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java new file mode 100644 index 0000000000..0c01896524 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.utils; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.simple.RandomSource; +import org.apache.commons.rng.simple.ThreadLocalRandomSource; + +/** + * An utility to generate per thread {@link UniformRandomProvider} instance. + * @since 4.0 + */ +public final class RandomProviderManager { + + /** The default RandomSource for random number generation. **/ + private static RandomSource randomSource = RandomSource.XO_RO_SHI_RO_128_PP; + + /** + * constructs the singleton instance. + */ + private RandomProviderManager() { + } + + /** + * Returns the (static) random generator. + * @return the static random generator shared by GA implementation classes + */ + public static UniformRandomProvider getRandomProvider() { + return ThreadLocalRandomSource.current(RandomProviderManager.randomSource); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java new file mode 100644 index 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/GeneticAlgorithmTestBinaryOneMax.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java new file mode 100644 index 0000000000..99a378542a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * This is also an example of usage. + */ +public class GeneticAlgorithmTestBinaryOneMax { + + // parameters for the GA + private static final int DIMENSION = 50; + private static final int POPULATION_SIZE = 50; + private static final int NUM_GENERATIONS = 50; + private static final double CROSSOVER_RATE = 1; + private static final double MUTATION_RATE = 0.1; + private static final int TOURNAMENT_ARITY = 2; + + @Test + public void test() { + removeListeners(); + + // initialize a new genetic algorithm + GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointBinaryCrossover>(), + CROSSOVER_RATE, new BinaryMutation>(), MUTATION_RATE, + new TournamentSelection>(TOURNAMENT_ARITY)); + + Assertions.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 :) + + Assertions.assertTrue(bestFinal.compareTo(bestInitial) > 0); + Assertions.assertEquals(NUM_GENERATIONS, ga.getGenerationsEvolved()); + + } + + private void removeListeners() { + try { + 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); + } catch (NoSuchFieldException | SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + /** + * Initializes a random population. + */ + private ListPopulation> randomPopulation() { + List>> popList = new LinkedList<>(); + + for (int i = 0; i < POPULATION_SIZE; i++) { + BinaryChromosome> randChrom = new FindOnes( + ChromosomeRepresentationUtils.randomBinaryRepresentation(DIMENSION), DIMENSION); + popList.add(randChrom); + } + return new ListPopulation<>(popList, popList.size()); + } + + /** + * Chromosomes represented by a binary chromosome. + * + * The goal is to set all bits (genes) to 1. + */ + private class FindOnes extends BinaryChromosome> { + + FindOnes(long[] representation, long length) { + super(representation, length, new OneMaxFitnessFunction(), new OneMaxDecoder()); + } + } + + private class OneMaxFitnessFunction implements FitnessFunction> { + + @Override + public double compute(List decodedChromosome) { + double value = 0; + for (Integer allele : decodedChromosome) { + value += allele; + } + return value; + } + + } + + private class OneMaxDecoder implements Decoder> { + + @Override + public List decode(Chromosome> chromosome) { + BinaryChromosome> binaryChromosome = (BinaryChromosome>) chromosome; + List phenotype = new ArrayList<>(); + long[] representation = binaryChromosome.getRepresentation(); + for (int i = 0; i < representation.length; i++) { + String value = Long.toUnsignedString(representation[i], 2); + for (int j = 64 - value.length(); j > 0; j--) { + phenotype.add(Integer.valueOf(0)); + } + for (int j = 0; j < value.length(); j++) { + phenotype.add(Integer.parseInt("" + value.charAt(j))); + } + } + return phenotype; + } + } + + @Test + public void testCrossoverRate() { + Assertions.assertThrows(GeneticException.class, () -> { + new GeneticAlgorithm<>(new OnePointCrossover<>(), 1.5, new BinaryMutation<>(), .01, + new TournamentSelection<>(10)); + }); + } + + @Test + public void testMutationRate() { + Assertions.assertThrows(GeneticException.class, () -> { + new GeneticAlgorithm<>(new OnePointCrossover<>(), .5, new BinaryMutation<>(), 1.5, + new TournamentSelection<>(10)); + }); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java new file mode 100644 index 0000000000..4bfb2ec047 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * This is also an example of usage. + * + * This algorithm does "stochastic sorting" of a sequence 0,...,N. + * + */ +public class GeneticAlgorithmTestPermutations { + + // parameters for the GA + private static final int DIMENSION = 20; + private static final int POPULATION_SIZE = 80; + private static final int NUM_GENERATIONS = 200; + private static final double ELITISM_RATE = 0.2; + private static final double CROSSOVER_RATE = 1; + private static final double MUTATION_RATE = 0.08; + private static final int TOURNAMENT_ARITY = 2; + + // numbers from 0 to N-1 + private static final List 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 :) + + Assertions.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..a682024bab --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractChromosomeTest { + + @Test + public void testGetFitness() { + Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + Assertions.assertEquals(1, c1.evaluate(), .001); + } + + @Test + public void testDecode() { + Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + Assertions.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") { + }; + + Assertions.assertTrue(c1.compareTo(c2) < 0); + Assertions.assertTrue(c2.compareTo(c1) > 0); + Assertions.assertEquals(0, c3.compareTo(c2)); + Assertions.assertEquals(0, c2.compareTo(c3)); + } + + @Test + public void testIsSame() { + AbstractChromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + AbstractChromosome c2 = new AbstractChromosome(chromosome -> 2, chromosome -> "2") { + }; + AbstractChromosome c3 = new AbstractChromosome(chromosome -> 3, chromosome -> "1") { + }; + Assertions.assertTrue(c1.isSame(c3)); + Assertions.assertFalse(c1.isSame(c2)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java new file mode 100644 index 0000000000..ef6669ae25 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class BinaryChromosomeTest { + + @Test + public void testInvalidConstructor() { + Assertions.assertThrows(GeneticException.class, () -> { + new BinaryChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), Long.MAX_VALUE, + c -> 0, c -> "0"); + }); + Assertions.assertThrows(GeneticException.class, () -> { + new BinaryChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), 100, c -> 0, + c -> "0"); + }); + } + + @Test + public void testRandomConstructor() { + for (int i = 0; i < 20; i++) { + BinaryChromosome.randomChromosome(10, c -> 1, new DummyListChromosomeDecoder<>("1")); + } + } + + @Test + public void testGetStringRepresentation() { + int length = 10; + int startToEndGap = 1; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 64; + startToEndGap = 10; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 100; + startToEndGap = 50; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 128; + startToEndGap = 70; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 250; + startToEndGap = 128; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 350; + startToEndGap = 228; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 450; + startToEndGap = 108; + testStringRepresentationWithRanges(length, startToEndGap); + + } + + private void testStringRepresentationWithRanges(int length, int startToEndGap) { + for (int i = 0; i < 50; i++) { + String representationStr = ChromosomeRepresentationUtils.randomStringRepresentation(new char[] {'0', '1'}, + length); + BinaryChromosome chromosome = new BinaryChromosome<>(representationStr, c -> 0, c -> "0"); + Assertions.assertEquals(representationStr, chromosome.getStringRepresentation()); + for (int j = 0; j < length - startToEndGap; j++) { + int index = (int) ((length + 1 - startToEndGap) * Math.random()); + Assertions.assertEquals(representationStr.substring(index, index + startToEndGap), + chromosome.getStringRepresentation(index, index + startToEndGap)); + } + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java new file mode 100644 index 0000000000..f43a27ccb8 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class ChromosomePairTest { + + @Test + public void testChromosomePair() { + Chromosome chromosome1 = new AbstractChromosome(c -> 0, c -> "0") { + }; + Chromosome chromosome2 = new AbstractChromosome(c -> 1, c -> "1") { + }; + ChromosomePair chromosomePair = new ChromosomePair<>(chromosome1, chromosome2); + + Assertions.assertEquals(chromosomePair.getFirst(), chromosome1); + Assertions.assertEquals(chromosomePair.getSecond(), chromosome2); + + Assertions.assertNotNull(chromosomePair.toString()); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java new file mode 100644 index 0000000000..e2a89fe4db --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IntegralValuedChromosomeTest { + + @Test + public void testIntegralValuedChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + Assertions.assertEquals(min, chromosome.getMin()); + Assertions.assertEquals(max, chromosome.getMax()); + + IntegralValuedChromosome chromosome1 = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max).toArray(new Integer[10]), + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + Assertions.assertEquals(min, chromosome1.getMin()); + Assertions.assertEquals(max, chromosome1.getMax()); + } + + @Test + public void testCheckValidity() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), + c -> 0, new DummyListChromosomeDecoder<>("0"), max, min); + }); + } + + @Test + public void testCheckValidity1() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min - 10, max + 10), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + }); + + } + + @Test + public void testNewChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + IntegralValuedChromosome newChromosome = chromosome + .newChromosome(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max)); + Assertions.assertEquals(chromosome.getMin(), newChromosome.getMin()); + Assertions.assertEquals(chromosome.getMax(), newChromosome.getMax()); + Assertions.assertEquals(chromosome.getDecoder(), newChromosome.getDecoder()); + Assertions.assertEquals(chromosome.getFitnessFunction(), newChromosome.getFitnessFunction()); + + } + + @Test + public void testRandomChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + Assertions.assertEquals(min, chromosome.getMin()); + Assertions.assertEquals(max, chromosome.getMax()); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java new file mode 100644 index 0000000000..333e2caa32 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RealValuedChromosomeTest { + + @Test + public void test() { + for (int i = 0; i < 10; i++) { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1, + new DummyListChromosomeDecoder<>("1")); + new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1).toArray(new Double[10]), c -> 0, + new DummyListChromosomeDecoder<>("0")); + } + } + + @Test + public void testNewChromosome() { + for (int i = 0; i < 10; i++) { + RealValuedChromosome chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1, + new DummyListChromosomeDecoder<>("1")); + chromosome.newChromosome(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1)); + } + } + + @Test + public void testRandomChromosome() { + for (int i = 0; i < 10; i++) { + RealValuedChromosome.randomChromosome(5, c -> 0, new DummyListChromosomeDecoder<>("0"), 0, 2); + } + } + + @Test + public void testCheckValidity() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), max, min); + }); + } + + @Test + public void testCheckValidity1() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min - 10, max + 10), + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + }); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java new file mode 100644 index 0000000000..e2681ab8a9 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.convergence.FixedElapsedTime; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class FixedElapsedTimeTest { + + @Test + public void testIsSatisfied() { + final Population 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)); + + Assertions.assertTrue(diff < TimeUnit.MILLISECONDS.toNanos(100)); + } + + @Test + public void testNegativeTime() { + Assertions.assertThrows(GeneticException.class, () -> { + new FixedElapsedTime<>(-10); + }); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java new file mode 100644 index 0000000000..5bf1567545 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class FixedGenerationCountTest { + + @Test + public void testIsSatisfied() { + FixedGenerationCount fgc = new FixedGenerationCount(20); + + int cnt = 0; + Population pop = new ListPopulation<>(10); + + while (!fgc.isSatisfied(pop)) { + cnt++; + } + Assertions.assertEquals(cnt, fgc.getNumGenerations()); + } + + @Test + public void testNegativeGenerationCount() { + Assertions.assertThrows(GeneticException.class, () -> { + 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..5fffd0650a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.ArrayList; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UnchangedBestFitnessTest { + + @Test + public void testIsSatisfied() { + + final int noOfGenerationsWithUnchangedBestFitness = 5; + StoppingCondition 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 AbstractChromosome(c -> fitness, c -> "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(); + + Assertions.assertEquals(initialMaxFitness, maxFitnessAfterConvergence, .001); + Assertions.assertEquals(noOfGenerationsWithUnchangedBestFitness, counter); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java new file mode 100644 index 0000000000..6e4042cbfb --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.ArrayList; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedMeanFitness; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UnchangedMeanFitnessTest { + + @Test + public void testIsSatisfied() { + + final int noOfGenerationsWithUnchangedMeanFitness = 5; + StoppingCondition 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 AbstractChromosome(c -> fitness, c -> "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(); + + Assertions.assertEquals(initialAverageFitness, averageFitnessAfterConvergence, .001); + Assertions.assertEquals(noOfGenerationsWithUnchangedMeanFitness, counter); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java new file mode 100644 index 0000000000..c1249b52a5 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyChromosome; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractChromosomeCrossoverPolicyTest { + + @Test + public void testCrossoverProbability() { + + CrossoverPolicy crossoverPolicy = new AbstractChromosomeCrossoverPolicy() { + @Override + protected ChromosomePair crossover(Chromosome first, Chromosome second) { + return null; + } + }; + + Chromosome ch1 = new DummyChromosome(); + + Chromosome ch2 = new DummyChromosome(); + + Assertions.assertNull(crossoverPolicy.crossover(ch1, ch2, 1.0)); + Assertions.assertNotNull(crossoverPolicy.crossover(ch1, ch2, 0.0)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java new file mode 100644 index 0000000000..0739f0d8db --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractListChromosomeCrossoverPolicyTest { + + @Test + public void testCrossoverWithNonListChromosome() { + + CrossoverPolicy 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") { + }; + + Assertions.assertThrows(GeneticException.class, () -> { + crossoverPolicy.crossover(ch1, ch2, 1.0); + }); + + } + + @Test + 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.randomIntegralRepresentation(10, 0, 2)); + + Chromosome ch2 = new DummyListChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(12, 0, 2)); + + Assertions.assertThrows(GeneticException.class, () -> { + crossoverPolicy.crossover(ch1, ch2, 1.0); + }); + + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java new file mode 100644 index 0000000000..e8ac82019a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CycleCrossoverTest { + + @Test + public void testCrossoverExample() { + // taken from + // http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx + final Integer[] p1 = new Integer[] {8, 4, 7, 3, 6, 2, 5, 1, 9, 0}; + final Integer[] p2 = new Integer[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy 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}; + + Assertions.assertArrayEquals(c1e, c1); + Assertions.assertArrayEquals(c2e, c2); + } + + @Test + public void testCrossoverExample2() { + // taken from http://www.scribd.com/doc/54206412/32/Cycle-crossover + final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9}; + final Integer[] p2 = new Integer[] {9, 3, 7, 8, 2, 6, 5, 1, 4}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy 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}; + + Assertions.assertArrayEquals(c1e, c1); + Assertions.assertArrayEquals(c2e, c2); + } + + @Test + public void testCrossover() { + final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + final Integer[] p2 = new Integer[] {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy 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)) { + Assertions.assertEquals(j, p2[index]); + } else { + Assertions.assertEquals(j, p1[index]); + } + index++; + } + + // Same as above only for the second parent. + index = 0; + for (final Integer k : c2) { + if (p2[index] != k) { + Assertions.assertEquals(k, p1[index]); + } else { + Assertions.assertEquals(k, p2[index]); + } + index++; + } + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java new file mode 100644 index 0000000000..35a55b025b --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NPointCrossoverTest { + + @Test + public void testNumberIsTooLargeException() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final Integer[] p2 = new Integer[] {0, 1, 1, 0, 1, 0, 1, 1, 1}; + + final IntegralValuedChromosome p1c = new IntegralValuedChromosome(p1, c -> 0, + new DummyListChromosomeDecoder("0"), 0, 2); + final IntegralValuedChromosome p2c = new IntegralValuedChromosome(p2, c -> 0, + new DummyListChromosomeDecoder("0"), 0, 2); + + final CrossoverPolicy cp = new NPointCrossover(15); + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); + } + + @Test + public void testCrossoverInvalidFixedLengthChromosomeFirst() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final IntegralValuedChromosome p1c = new IntegralValuedChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + final AbstractChromosome p2c = new AbstractChromosome(chromosome -> 0, + new DummyListChromosomeDecoder<>("0")) { + }; + + final CrossoverPolicy cp = new NPointCrossover(1); + + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); + + } + + @Test + public void testCrossoverInvalidFixedLengthChromosomeSecond() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final IntegralValuedChromosome p2c = new IntegralValuedChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + final AbstractChromosome p1c = new AbstractChromosome(chromosome -> 0, + new DummyListChromosomeDecoder<>("0")) { + }; + + final CrossoverPolicy cp = new NPointCrossover(1); + + Assertions.assertThrows(GeneticException.class, () -> { + 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}; + + IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + 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); + Assertions.assertEquals(order, + detectCrossoverPoints(p1c, p2c, (IntegralValuedChromosome) pair.getFirst())); + Assertions.assertEquals(order, + detectCrossoverPoints(p2c, p1c, (IntegralValuedChromosome) pair.getSecond())); + } + } + + private int detectCrossoverPoints(IntegralValuedChromosome p1, + IntegralValuedChromosome p2, + IntegralValuedChromosome 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/OnePointBinaryCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java new file mode 100644 index 0000000000..21f26a6cba --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.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.crossover; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class OnePointBinaryCrossoverTest { + + @Test + public void testCrossover() { + + OnePointBinaryCrossover opc = new OnePointBinaryCrossover<>(); + + // test a stochastic method. + for (int i = 0; i < 1000; i++) { + long length = 32 + (long) (200 * Math.random()); + BinaryChromosome p1c = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length, c -> 0, c -> "0"); + BinaryChromosome p2c = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length, c -> 0, c -> "0"); + + ChromosomePair pair = opc.crossover(p1c, p2c, 1.0); + + long[] p1 = p1c.getRepresentation(); + long[] p2 = p2c.getRepresentation(); + + long[] c1 = ((BinaryChromosome) pair.getFirst()).getRepresentation(); + long[] c2 = ((BinaryChromosome) pair.getSecond()).getRepresentation(); + + final int offset = (int) (length % 64 == 0 ? 0 : 64 - (length % 64)); + + // first and last values will be the same + Assertions.assertEquals(prependZero(Long.toBinaryString(p1[0])).charAt(offset), + prependZero(Long.toBinaryString(c1[0])).charAt(offset)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p2[0])).charAt(offset), + prependZero(Long.toBinaryString(c2[0])).charAt(offset)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p1[p1.length - 1])).charAt(63), + prependZero(Long.toBinaryString(c2[c2.length - 1])).charAt(63)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p2[p2.length - 1])).charAt(63), + prependZero(Long.toBinaryString(c1[c1.length - 1])).charAt(63)); + + } + } + + private String prependZero(String value) { + StringBuilder modValue = new StringBuilder(); + for (int i = 64 - value.length(); i > 0; i--) { + modValue.append('0'); + } + modValue.append(value); + + return modValue.toString(); + } + +} 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..a97c74286a --- /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.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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}; + + IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + 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 = ((IntegralValuedChromosome) pair.getFirst()).getRepresentation().toArray(c1); + c2 = ((IntegralValuedChromosome) pair.getSecond()).getRepresentation().toArray(c2); + + // first and last values will be the same + Assertions.assertEquals(p1[0], c1[0]); + Assertions.assertEquals(p2[0], c2[0]); + Assertions.assertEquals(p1[p1.length - 1], c1[c1.length - 1]); + Assertions.assertEquals(p2[p2.length - 1], c2[c2.length - 1]); + // moreover, in the above setting, the 2nd, 3rd and 7th values will be the same + Assertions.assertEquals(p1[2], c1[2]); + Assertions.assertEquals(p2[2], c2[2]); + Assertions.assertEquals(p1[3], c1[3]); + Assertions.assertEquals(p2[3], c2[3]); + Assertions.assertEquals(p1[7], c1[7]); + Assertions.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..383f1d8764 --- /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.jupiter.api.Assertions; +import org.junit.jupiter.api.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]); + + Assertions.assertNotSame(p1c, pair.getFirst()); + Assertions.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++) { + Assertions.assertTrue(parentSet1.contains(c1[j])); + parentSet1.remove(c1[j]); + Assertions.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..6bd2f8d818 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.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") + @BeforeAll + public static void setUpBeforeClass() { + for (int i = 0; i < LEN; i++) { + p1.add(0); + p2.add(1); + } + } + + @Test + public void testRatioTooLow() { + Assertions.assertThrows(GeneticException.class, () -> { + new UniformCrossover(-0.5d); + }); + } + + @Test + public void testRatioTooHigh() { + Assertions.assertThrows(GeneticException.class, () -> { + 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 IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + final IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + 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 = ((IntegralValuedChromosome) pair.getFirst()).getRepresentation(); + final List c2 = ((IntegralValuedChromosome) pair.getSecond()).getRepresentation(); + + int from1 = 0; + int from2 = 0; + + // check first child + for (int val : c1) { + if (val == 0) { + from1++; + } else { + from2++; + } + } + + Assertions.assertEquals(1.0 - ratio, (double) from1 / LEN, 0.1); + Assertions.assertEquals(ratio, (double) from2 / LEN, 0.1); + + from1 = 0; + from2 = 0; + + // check second child + for (int val : c2) { + if (val == 0) { + from1++; + } else { + from2++; + } + } + + Assertions.assertEquals(ratio, (double) from1 / LEN, 0.1); + Assertions.assertEquals(1.0 - ratio, (double) from2 / LEN, 0.1); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java new file mode 100644 index 0000000000..48099244d6 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.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.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest { + + @Test + public void testGenerate() { + final double minCrossoverRate = .5; + final double maxCrossoverRate = 1.0; + + IntegralValuedChromosome chromosome1 = IntegralValuedChromosome.randomChromosome(10, c -> 1, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome2 = IntegralValuedChromosome.randomChromosome(10, c -> 2, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome3 = IntegralValuedChromosome.randomChromosome(10, c -> 3, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome4 = IntegralValuedChromosome.randomChromosome(10, c -> 4, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome5 = IntegralValuedChromosome.randomChromosome(10, c -> 5, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + + Population population = new ListPopulation<>(5); + population.addChromosome(chromosome1); + population.addChromosome(chromosome2); + population.addChromosome(chromosome3); + population.addChromosome(chromosome4); + population.addChromosome(chromosome5); + PopulationStatisticalSummary stats = new PopulationStatisticalSummaryImpl<>(population); + + CrossoverRateGenerator crossoverRateGenerator = new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>( + minCrossoverRate, maxCrossoverRate); + + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome1, chromosome5, stats, 1), + .00000001); + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome5, chromosome2, stats, 1), + .00000001); + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome3, chromosome5, stats, 1), + .00000001); + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome4, chromosome5, stats, 1), + .00000001); + + } + +} 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..32b2f05d2d --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.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.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.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractListChromosomeDecoderTest { + + @Test + public void testDecodeWithInvalidChromosomeInstance() { + Decoder decoder = new AbstractListChromosomeDecoder() { + + @Override + protected String decode(AbstractListChromosome chromosome) { + return null; + } + }; + Chromosome ch = new DummyChromosome(); + Assertions.assertThrows(GeneticException.class, () -> { + 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..4a2f751f8b --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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(); + + Assertions.assertEquals("b", decodedSequence.get(0)); + Assertions.assertEquals("e", decodedSequence.get(1)); + Assertions.assertEquals("a", decodedSequence.get(2)); + Assertions.assertEquals("c", decodedSequence.get(3)); + Assertions.assertEquals("d", decodedSequence.get(4)); + + } + + @Test + 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); + Assertions.assertThrows(GeneticException.class, () -> { + 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..39b6b9eb63 --- /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.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TransparentListChromosomeDecoderTest { + + @Test + public void testDecode() { + List rp = ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2); + Chromosome> chromosome = new IntegralValuedChromosome<>(rp, c -> 0, + new TransparentListChromosomeDecoder<>(), 0, 2); + List decodedRp = chromosome.decode(); + Assertions.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..908bb43e78 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.dummy; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; + +/** + * 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.randomIntegralRepresentation(10, 0, 2), 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/dummy/DummyListChromosomeDecoder.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosomeDecoder.java new file mode 100644 index 0000000000..541cd261f0 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/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.dummy; + +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/exception/GeneticExceptionTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java new file mode 100644 index 0000000000..551331a043 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.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.exception; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class GeneticExceptionTest { + + @Test + public void testGeneticExceptionThrowable() { + Assertions.assertThrows(GeneticException.class, () -> { + throw new GeneticException(new NullPointerException()); + }); + } + + @Test + public void testGeneticExceptionStringThrowableObjectArray() { + Assertions.assertThrows(GeneticException.class, () -> { + 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..2b1425849a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class 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); + Assertions.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 + 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); + Assertions.assertThrows(GeneticException.class, () -> { + registry.notifyAll(0, new ListPopulation<>(10)); + }); + Assertions.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..d59a514810 --- /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.dummy.DummyListChromosomeDecoder; +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.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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<>(); + logger.notify(1, population); + Assertions.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..17c481a200 --- /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.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractListChromosomeMutationPolicyTest { + + @Test + public void testGetMutableGeneIndexes() { + AbstractListChromosomeMutationPolicy chromosomeMutationPolicy = new AbstractListChromosomeMutationPolicy() { + + @Override + protected Integer mutateGene(Integer originalValue) { + return RandomProviderManager.getRandomProvider().nextInt(2); + } + + @Override + protected void checkValidity(Chromosome original) { + // No Op + } + }; + Assertions.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..d2cc2bb7eb --- /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.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class BinaryMutationTest { + + @Test + public void testCheckValidity() { + BinaryMutation mutation = new BinaryMutation<>(); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(new IntegralValuedChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2), c -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + }); + } + + @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 + Assertions.assertEquals(1, calculateNoOfMutatedBits(original, mutated)); + } + + // stochastic testing for two gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + c -> "0"); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .2); + + // one gene should be different + Assertions.assertEquals(2, calculateNoOfMutatedBits(original, mutated)); + } + + // stochastic testing for three gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + c -> "0"); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .3); + + // three genes should be different + Assertions.assertEquals(3, calculateNoOfMutatedBits(original, mutated)); + } + } + + private int calculateNoOfMutatedBits(BinaryChromosome original, BinaryChromosome mutated) { + int numDifferent = 0; + long[] originalReps = original.getRepresentation(); + long[] mutatedReps = mutated.getRepresentation(); + for (int j = 0; j < originalReps.length; j++) { + long xORValue = originalReps[j] ^ mutatedReps[j]; + String xORValueStr = Long.toBinaryString(xORValue); + for (int k = 0; k < xORValueStr.length(); k++) { + if (xORValueStr.charAt(k) == '1') { + numDifferent++; + } + } + } + return 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..0d6135fdcf --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.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.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IntegralValuedMutationTest { + + @Test + 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); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + 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); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + public void testIntegralValuedMutation() { + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedMutation<>(10, 5); + }); + } + + @Test + public void testGetMinMax() { + int min = 0; + int max = 10; + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min, max); + Assertions.assertEquals(min, mutation.getMin()); + Assertions.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 + RandomProviderManager.getRandomProvider().nextInt(max - min); + int mutatedValued = mutation.mutateGene(origValue); + Assertions.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..015801b377 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.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.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RealValuedMutationTest { + + @Test + 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); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + 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); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + public void testIntegralValuedMutation() { + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedMutation<>(10, 5); + }); + } + + @Test + public void testGetMinMax() { + double min = 0; + double max = 10; + RealValuedMutation mutation = new RealValuedMutation<>(min, max); + Assertions.assertEquals(min, mutation.getMin(), .001); + Assertions.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) * RandomProviderManager.getRandomProvider().nextDouble(); + double mutatedValue = mutation.mutateGene(origValue); + Assertions.assertTrue(min <= mutatedValue && mutatedValue < max); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java new file mode 100644 index 0000000000..604e08db90 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AdaptiveLinearMutationRateGeneratorTest { + + @Test + public void testGenerate() { + final double minMutationRate = .01; + final double maxMutationRate = .1; + + IntegralValuedChromosome chromosome1 = IntegralValuedChromosome.randomChromosome(10, c -> 1, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome2 = IntegralValuedChromosome.randomChromosome(10, c -> 2, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome3 = IntegralValuedChromosome.randomChromosome(10, c -> 3, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome4 = IntegralValuedChromosome.randomChromosome(10, c -> 4, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome5 = IntegralValuedChromosome.randomChromosome(10, c -> 5, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + + Population population = new ListPopulation<>(5); + population.addChromosome(chromosome1); + population.addChromosome(chromosome2); + population.addChromosome(chromosome3); + population.addChromosome(chromosome4); + population.addChromosome(chromosome5); + PopulationStatisticalSummary stats = new PopulationStatisticalSummaryImpl<>(population); + + MutationRateGenerator mutationRateGenerator = new AdaptiveLinearMutationRateGenerator<>(minMutationRate, + maxMutationRate); + + Assertions.assertEquals(maxMutationRate, mutationRateGenerator.generate(chromosome1, stats, 1), .00000001); + Assertions.assertEquals(minMutationRate, mutationRateGenerator.generate(chromosome5, stats, 1), .00000001); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java new file mode 100644 index 0000000000..0c7793fdd9 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.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.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ConstantMutationRateGeneratorTest { + + @Test + public void testGenerate() { + for (int i = 0; i < 100; i++) { + double mutationRate = RandomProviderManager.getRandomProvider().nextDouble(); + MutationRateGenerator mutationRateGenerator = new ConstantMutationRateGenerator<>(mutationRate); + IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, + new DummyListChromosomeDecoder<>("Fixed"), 0, 2); + Population population = new ListPopulation<>(1); + population.addChromosome(chromosome); + Assertions.assertEquals(mutationRate, + mutationRateGenerator.generate(chromosome, new PopulationStatisticalSummaryImpl<>(population), i), + .0000001); + } + } + +} 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..3f4cbf17d3 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class 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); + + Assertions.assertEquals(c3, population.getFittestChromosome()); + Assertions.assertNotNull(population.toString()); + } + + @Test + public void testChromosomes() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(10); + + population.addChromosomes(chromosomes); + + Assertions.assertEquals(chromosomes, population.getChromosomes()); + + population.setPopulationLimit(50); + Assertions.assertEquals(50, population.getPopulationLimit()); + } + + @Test + public void testSetPopulationLimit() { + final ListPopulation population = new ListPopulation<>(10); + + Assertions.assertThrows(GeneticException.class, () -> { + population.setPopulationLimit(-50); + }); + + } + + @Test + public void testConstructorPopulationLimitNotPositive() { + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(-10); + }); + } + + @Test + public void testChromosomeListConstructorPopulationLimitNotPositive() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(chromosomes, -10); + }); + } + + @Test + public void testConstructorListOfChromosomesBiggerThanPopulationSize() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(chromosomes, 1); + }); + + } + + @Test + public void testAddTooManyChromosomes() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(2); + + Assertions.assertThrows(GeneticException.class, () -> { + population.addChromosomes(chromosomes); + }); + + } + + @Test + public void testAddTooManyChromosomesSingleCall() { + + final ListPopulation population = new ListPopulation<>(2); + + Assertions.assertThrows(GeneticException.class, () -> { + for (int i = 0; i <= population.getPopulationLimit(); i++) { + population.addChromosome(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + } + }); + } + + @Test + public void testIterator() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(10); + + population.addChromosomes(chromosomes); + + final Iterator> iter = population.iterator(); + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + while (iter.hasNext()) { + iter.next(); + iter.remove(); + } + }); + } + + @Test + public void testSetPopulationLimitTooSmall() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(chromosomes, 3); + + Assertions.assertThrows(GeneticException.class, () -> { + population.setPopulationLimit(2); + }); + + } + + @Test + public void testNextGeneration() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(chromosomes, 3); + + Assertions.assertEquals(1, population.nextGeneration(.4).getPopulationSize()); + Assertions.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..c855eb85c2 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.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.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.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TournamentSelectionTest { + + private static int counter; + + @Test + public void testSelect() { + TournamentSelection ts = new TournamentSelection<>(2); + + Assertions.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 + Assertions.assertTrue(pair.getFirst().evaluate() > 0); + Assertions.assertTrue(pair.getSecond().evaluate() > 0); + } + } + + private static class DummyChromosome extends AbstractChromosome { + DummyChromosome() { + super(c -> counter++, c -> "0"); + } + + } + + @Test + 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; + } + }; + Assertions.assertThrows(GeneticException.class, () -> { + new TournamentSelection(5).select(population); + }); + + } + + @Test + public void testInvalidArity() { + Population population = new ListPopulation<>(2); + Assertions.assertThrows(GeneticException.class, () -> { + 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..0b595c58e6 --- /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.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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); + Assertions.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); + + Assertions.assertEquals("a", decoded.get(0)); + Assertions.assertEquals("b", decoded.get(1)); + Assertions.assertEquals("c", decoded.get(2)); + Assertions.assertEquals("d", decoded.get(3)); + Assertions.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); + + Assertions.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)); + + Assertions.assertEquals("b", decodedData.get(0)); + Assertions.assertEquals("b", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); + Assertions.assertEquals("x", decodedData.get(3)); + Assertions.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); + + Assertions.assertArrayEquals(new Double[] {0.2, 0.6, 0.4, 0.0, 0.8}, permArr); + + decodedData = decoder.decode(new RealValuedChromosome<>(permutation, c -> 0, decoder)); + + Assertions.assertEquals("z", decodedData.get(0)); + Assertions.assertEquals("x", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); + Assertions.assertEquals("b", decodedData.get(3)); + Assertions.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); + + Assertions.assertEquals("d", decoded.get(0)); + Assertions.assertEquals("b", decoded.get(1)); + Assertions.assertEquals("c", decoded.get(2)); + Assertions.assertEquals("a", decoded.get(3)); + Assertions.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"})); + Assertions.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"})); + Assertions.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); + Assertions.assertEquals("a", decodedData.get(0)); + Assertions.assertEquals("b", decodedData.get(1)); + Assertions.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) { + Assertions.assertTrue(min <= value && value < max); + } + } + + @Test + public void testNormalizedDoubleRepresentation() { + List values = ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(100); + for (Double value : values) { + Assertions.assertTrue(0 <= value && value < 1); + } + } + +} diff --git a/pom.xml b/pom.xml index c618f112cb..818fbb30ae 100644 --- a/pom.xml +++ b/pom.xml @@ -127,6 +127,7 @@ commons-math-examples + commons-math-ga diff --git a/src/main/resources/spotbugs/spotbugs-exclude-filter.xml b/src/main/resources/spotbugs/spotbugs-exclude-filter.xml index 3de6902894..8183da2988 100644 --- a/src/main/resources/spotbugs/spotbugs-exclude-filter.xml +++ b/src/main/resources/spotbugs/spotbugs-exclude-filter.xml @@ -57,4 +57,10 @@ + + + + + +