/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ Franz Wilhelmstötter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) */ package org.jenetics; import static java.lang.String.format; import static org.jenetics.util.RandomRegistry.using; import java.io.PrintStream; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import org.jenetics.stat.Histogram; import org.jenetics.util.Factory; import org.jenetics.util.LCG64ShiftRandom; import org.jenetics.util.ObjectTester; /** * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> */ public abstract class SelectorTester<S extends Selector<DoubleGene, Double>> extends ObjectTester<S> { public static final int CLASS_COUNT = 25; public static final double MIN = 0.0; public static final double MAX = 1_000.0; public static final double SELECTION_FRACTION = 2.0; public static final int POPULATION_COUNT = (int)(CLASS_COUNT*10*SELECTION_FRACTION); protected S selector() { return factory().newInstance(); } @Test(expectedExceptions = IllegalArgumentException.class) public void selectNegativeCountArgument() { final Factory<Genotype<DoubleGene>> gtf = Genotype.of(new DoubleChromosome(0.0, 1.0)); final Population<DoubleGene, Double> population = new Population<>(2); for (int i = 0, n = 2; i < n; ++i) { population.add(Phenotype.of(gtf.newInstance(), 12, TestUtils.FF)); } selector().select(population, -1, Optimize.MAXIMUM); } @Test(expectedExceptions = NullPointerException.class) public void selectNullPopulationArgument() { selector().select(null, 23, Optimize.MAXIMUM); } @Test(expectedExceptions = NullPointerException.class) public void selectNullOptimizeArgument() { final Factory<Genotype<DoubleGene>> gtf = Genotype.of(new DoubleChromosome(0.0, 1.0)); final Population<DoubleGene, Double> population = new Population<>(2); for (int i = 0, n = 2; i < n; ++i) { population.add(Phenotype.of(gtf.newInstance(), 12, TestUtils.FF)); } selector().select(population, 1, null); } @Test(dataProvider = "selectionPerformanceParameters") public void selectionPerformance( final Integer size, final Integer count, final Optimize opt ) { final Function<Genotype<DoubleGene>, Double> ff = g -> g.getGene().getAllele(); final Factory<Phenotype<DoubleGene, Double>> ptf = () -> Phenotype.of(Genotype.of(DoubleChromosome.of(0.0, 100.0)), 1, ff); using(new LCG64ShiftRandom(543455), r -> { final Population<DoubleGene, Double> population = IntStream.range(0, size) .mapToObj(i -> ptf.newInstance()) .collect(Population.toPopulation()); final Selector<DoubleGene, Double> selector = selector(); if (!(selector instanceof MonteCarloSelector<?, ?>)) { final double monteCarloSelectionSum = new MonteCarloSelector<DoubleGene, Double>() .select(population, count, opt).stream() .mapToDouble(Phenotype::getFitness) .sum(); final double selectionSum = selector .select(population, count, opt).stream() .mapToDouble(Phenotype::getFitness) .sum(); if (opt == Optimize.MAXIMUM) { Assert.assertTrue( selectionSum > monteCarloSelectionSum, format("%f <= %f", selectionSum, monteCarloSelectionSum) ); } else { Assert.assertTrue( selectionSum < monteCarloSelectionSum, format("%f >= %f", selectionSum, monteCarloSelectionSum) ); } } }); } @DataProvider(name = "selectionPerformanceParameters") public Object[][] selectionPerformanceParameters() { return new Object[][] { {200, 100, Optimize.MAXIMUM}, {2000, 1000, Optimize.MAXIMUM}, {200, 100, Optimize.MINIMUM}, {2000, 1000, Optimize.MINIMUM} }; } @Test(dataProvider = "selectParameters") public void select(final Integer size, final Integer count, final Optimize opt) { final Function<Genotype<DoubleGene>, Double> ff = gt -> gt.getGene().getAllele(); final Factory<Phenotype<DoubleGene, Double>> ptf = () -> Phenotype.of(Genotype.of(DoubleChromosome.of(0.0, 1_000.0)), 1, ff); final Population<DoubleGene, Double> population = IntStream.range(0, size) .mapToObj(i -> ptf.newInstance()) .collect(Population.toPopulation()); final Population<DoubleGene, Double> selection = selector().select(population, count, opt); if (size == 0) { Assert.assertEquals( selection.size(), 0 ); } else { Assert.assertEquals(selection.size(), count.intValue()); } for (Phenotype<DoubleGene, Double> pt : selection) { Assert.assertTrue( population.contains(pt), format("Population doesn't contain %s.", pt) ); } } @DataProvider(name = "selectParameters") public Object[][] selectParameters() { final List<Integer> sizes = Arrays.asList(0, 1, 2, 3, 5, 11, 50, 100, 10_000); final List<Integer> counts = Arrays.asList(0, 1, 2, 3, 5, 11, 50, 100, 10_000); final List<Object[]> result = new ArrayList<>(); for (Integer size : sizes) { for (Integer count : counts) { result.add(new Object[]{size, count, Optimize.MINIMUM}); result.add(new Object[]{size, count, Optimize.MAXIMUM}); } } return result.toArray(new Object[0][]); } /* ************************************************************************* * Distribution creation code. ***************************************************************************/ /** * Print the distributions, each for every parameter. * * @param writer the print writer where the histograms are printed * @param parameters the selector creation parameters * @param selector the selector factory * @param opt the optimization strategy * @param populationCount the number of individuals for the test population * @param loops the number of selections performed for one population * @param <P> the parameter type */ public static <P> void printDistributions( final PrintStream writer, final List<P> parameters, final Function<P, Selector<DoubleGene, Double>> selector, final Optimize opt, final int populationCount, final int loops ) { final List<Histogram<Double>> histograms = distributions( parameters, selector, opt, populationCount, loops ); final List<Selector<?, ?>> selectors = parameters.stream() .map(selector) .collect(Collectors.toList()); print(writer, opt, selectors, parameters, histograms, populationCount, loops); } /** * Create a list of distribution, each for every parameter. * * @param parameters the selector creation parameters * @param selector the selector factory * @param opt the optimization strategy * @param populationCount the number of individuals for the test population * @param loops the number of selections performed for one population * @param <P> the parameter type * @return the selector distributions */ public static <P> List<Histogram<Double>> distributions( final List<P> parameters, final Function<P, Selector<DoubleGene, Double>> selector, final Optimize opt, final int populationCount, final int loops ) { return parameters.stream() .map(p -> distribution(selector.apply(p), opt, populationCount, loops)) .collect(Collectors.toList()); } /** * Create a selection histogram (distribution) for the given selector and * with the given parameters. * * @param selector the selector for which to determine the distribution * @param opt the selectors optimization strategy * @param populationCount the number of in used for determining the * selector distribution. * @param loops the number of selections performed for one population * @return the selectors selection distribution */ public static Histogram<Double> distribution( final Selector<DoubleGene, Double> selector, final Optimize opt, final int populationCount, final int loops ) { final Function<Genotype<DoubleGene>, Double> ff = gt -> gt.getGene().getAllele(); final Factory<Phenotype<DoubleGene, Double>> ptf = () -> Phenotype.of(Genotype.of(DoubleChromosome.of(MIN, MAX)), 1, ff); return IntStream.range(0, loops).parallel().mapToObj(j -> { final Histogram<Double> hist = Histogram.ofDouble(MIN, MAX, CLASS_COUNT); final Population<DoubleGene, Double> population = IntStream.range(0, populationCount) .mapToObj(i -> ptf.newInstance()) .collect(Population.toPopulation()); final int selectionCount = (int)(populationCount/SELECTION_FRACTION); selector.select(population, selectionCount, opt).stream() .map(pt -> pt.getGenotype().getGene().getAllele()) .forEach(hist::accept); return hist; }).collect(Histogram.toDoubleHistogram(MIN, MAX, CLASS_COUNT)); } /** * Print the list of histogram as CSV to the given writer. * * @param writer the print writer where the histograms are printed * @param parameters the selector creation parameters * @param histograms the histograms to print */ private static void print( final PrintStream writer, final Optimize opt, final List<Selector<?, ?>> selectors, final List<?> parameters, final List<Histogram<Double>> histograms, final int populationCount, final int loops ) { final NumberFormat format = NumberFormat.getNumberInstance(Locale.ENGLISH); format.setMinimumFractionDigits(15); format.setMaximumFractionDigits(15); printt(writer, "Creating selector distribution: %s", new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date()) ); printv(writer); println(writer, "# %-76s#", format("Selector distributions (opt=%s, npop=%d, loops=%d):", opt, populationCount, loops)); for (Selector<?, ?> selector : selectors) { println(writer, "# %-76s#", format(" - %s", selector)); } println(writer, "#=============================================================================#"); final String header = parameters.stream() .map(Objects::toString) .collect(Collectors.joining(",", "", "")); writer.println(); writer.println(header); final double[][] array = histograms.stream() .map(Histogram::getNormalizedHistogram) .toArray(double[][]::new); for (int i = 0; i < array[0].length; ++i) { for (int j = 0; j < array.length; ++j) { writer.print(format.format(array[j][i])); if (j < array.length - 1) { writer.print(','); } } writer.println(); } } private static void printt(final PrintStream writer, final String title, final Object... args) { println(writer, "#=============================================================================#"); println(writer, "# %-76s#", format(title, args)); println(writer, "#=============================================================================#"); } private static void printv(final PrintStream writer) { println(writer, "#=============================================================================#"); println(writer, "# %-76s#", format("%s %s (%s) ", p("os.name"), p("os.version"), p("os.arch")) ); println(writer, "# %-76s#", format("java version \"%s\"", p("java.version")) ); println(writer, "# %-76s#", format("%s (build %s)", p("java.runtime.name"), p("java.runtime.version")) ); println(writer, "# %-76s#", format("%s (build %s)", p("java.vm.name"), p("java.vm.version")) ); println(writer,"#=============================================================================#"); } private static String p(final String name) { return System.getProperty(name); } private static void println(final PrintStream writer, final String pattern, final Object... args) { writer.println(format(pattern, args)); } }