| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.math4.distribution; |
| |
| import org.apache.commons.statistics.distribution.DiscreteDistribution; |
| import org.apache.commons.math4.TestUtils; |
| import org.apache.commons.math4.exception.MathIllegalArgumentException; |
| import org.apache.commons.rng.simple.RandomSource; |
| import org.apache.commons.math4.util.FastMath; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Abstract base class for {@link DiscreteDistribution} tests. |
| * <p> |
| * To create a concrete test class for an integer distribution implementation, |
| * implement makeDistribution() to return a distribution instance to use in |
| * tests and each of the test data generation methods below. In each case, the |
| * test points and test values arrays returned represent parallel arrays of |
| * inputs and expected values for the distribution returned by makeDistribution(). |
| * <p> |
| * makeDensityTestPoints() -- arguments used to test probability density calculation |
| * makeDensityTestValues() -- expected probability densities |
| * makeCumulativeTestPoints() -- arguments used to test cumulative probabilities |
| * makeCumulativeTestValues() -- expected cumulative probabilities |
| * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf evaluation |
| * makeInverseCumulativeTestValues() -- expected inverse cdf values |
| * <p> |
| * To implement additional test cases with different distribution instances and test data, |
| * use the setXxx methods for the instance data in test cases and call the verifyXxx methods |
| * to verify results. |
| * |
| */ |
| public abstract class IntegerDistributionAbstractTest { |
| |
| //-------------------- Private test instance data ------------------------- |
| /** Discrete distribution instance used to perform tests */ |
| private DiscreteDistribution distribution; |
| |
| /** Tolerance used in comparing expected and returned values */ |
| private double tolerance = 1E-12; |
| |
| /** Arguments used to test probability density calculations */ |
| private int[] densityTestPoints; |
| |
| /** Values used to test probability density calculations */ |
| private double[] densityTestValues; |
| |
| /** Values used to test logarithmic probability density calculations */ |
| private double[] logDensityTestValues; |
| |
| /** Arguments used to test cumulative probability density calculations */ |
| private int[] cumulativeTestPoints; |
| |
| /** Values used to test cumulative probability density calculations */ |
| private double[] cumulativeTestValues; |
| |
| /** Arguments used to test inverse cumulative probability density calculations */ |
| private double[] inverseCumulativeTestPoints; |
| |
| /** Values used to test inverse cumulative probability density calculations */ |
| private int[] inverseCumulativeTestValues; |
| |
| //-------------------- Abstract methods ----------------------------------- |
| |
| /** Creates the default discrete distribution instance to use in tests. */ |
| public abstract DiscreteDistribution makeDistribution(); |
| |
| /** Creates the default probability density test input values */ |
| public abstract int[] makeDensityTestPoints(); |
| |
| /** Creates the default probability density test expected values */ |
| public abstract double[] makeDensityTestValues(); |
| |
| /** Creates the default logarithmic probability density test expected values. |
| * |
| * The default implementation simply computes the logarithm of all the values in |
| * {@link #makeDensityTestValues()}. |
| * |
| * @return double[] the default logarithmic probability density test expected values. |
| */ |
| public double[] makeLogDensityTestValues() { |
| final double[] densityTestValues = makeDensityTestValues(); |
| final double[] logDensityTestValues = new double[densityTestValues.length]; |
| for (int i = 0; i < densityTestValues.length; i++) { |
| logDensityTestValues[i] = FastMath.log(densityTestValues[i]); |
| } |
| return logDensityTestValues; |
| } |
| |
| /** Creates the default cumulative probability density test input values */ |
| public abstract int[] makeCumulativeTestPoints(); |
| |
| /** Creates the default cumulative probability density test expected values */ |
| public abstract double[] makeCumulativeTestValues(); |
| |
| /** Creates the default inverse cumulative probability test input values */ |
| public abstract double[] makeInverseCumulativeTestPoints(); |
| |
| /** Creates the default inverse cumulative probability density test expected values */ |
| public abstract int[] makeInverseCumulativeTestValues(); |
| |
| //-------------------- Setup / tear down ---------------------------------- |
| |
| /** |
| * Setup sets all test instance data to default values |
| */ |
| @Before |
| public void setUp() { |
| distribution = makeDistribution(); |
| densityTestPoints = makeDensityTestPoints(); |
| densityTestValues = makeDensityTestValues(); |
| logDensityTestValues = makeLogDensityTestValues(); |
| cumulativeTestPoints = makeCumulativeTestPoints(); |
| cumulativeTestValues = makeCumulativeTestValues(); |
| inverseCumulativeTestPoints = makeInverseCumulativeTestPoints(); |
| inverseCumulativeTestValues = makeInverseCumulativeTestValues(); |
| } |
| |
| /** |
| * Cleans up test instance data |
| */ |
| @After |
| public void tearDown() { |
| distribution = null; |
| densityTestPoints = null; |
| densityTestValues = null; |
| logDensityTestValues = null; |
| cumulativeTestPoints = null; |
| cumulativeTestValues = null; |
| inverseCumulativeTestPoints = null; |
| inverseCumulativeTestValues = null; |
| } |
| |
| //-------------------- Verification methods ------------------------------- |
| |
| /** |
| * Verifies that probability density calculations match expected values |
| * using current test instance data |
| */ |
| protected void verifyDensities() { |
| for (int i = 0; i < densityTestPoints.length; i++) { |
| Assert.assertEquals("Incorrect density value returned for " + densityTestPoints[i], |
| densityTestValues[i], |
| distribution.probability(densityTestPoints[i]), getTolerance()); |
| } |
| } |
| |
| /** |
| * Verifies that logarithmic probability density calculations match expected values |
| * using current test instance data. |
| */ |
| protected void verifyLogDensities() { |
| for (int i = 0; i < densityTestPoints.length; i++) { |
| Assert.assertEquals("Incorrect log density value returned for " + densityTestPoints[i], |
| logDensityTestValues[i], |
| distribution.logProbability(densityTestPoints[i]), tolerance); |
| } |
| } |
| |
| /** |
| * Verifies that cumulative probability density calculations match expected values |
| * using current test instance data |
| */ |
| protected void verifyCumulativeProbabilities() { |
| for (int i = 0; i < cumulativeTestPoints.length; i++) { |
| Assert.assertEquals("Incorrect cumulative probability value returned for " + cumulativeTestPoints[i], |
| cumulativeTestValues[i], |
| distribution.cumulativeProbability(cumulativeTestPoints[i]), getTolerance()); |
| } |
| } |
| |
| |
| /** |
| * Verifies that inverse cumulative probability density calculations match expected values |
| * using current test instance data |
| */ |
| protected void verifyInverseCumulativeProbabilities() { |
| for (int i = 0; i < inverseCumulativeTestPoints.length; i++) { |
| Assert.assertEquals("Incorrect inverse cumulative probability value returned for " |
| + inverseCumulativeTestPoints[i], inverseCumulativeTestValues[i], |
| distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i])); |
| } |
| } |
| |
| //------------------------ Default test cases ----------------------------- |
| |
| /** |
| * Verifies that probability density calculations match expected values |
| * using default test instance data |
| */ |
| @Test |
| public void testDensities() { |
| verifyDensities(); |
| } |
| |
| /** |
| * Verifies that logarithmic probability density calculations match expected values |
| * using default test instance data |
| */ |
| @Test |
| public void testLogDensities() { |
| verifyLogDensities(); |
| } |
| |
| /** |
| * Verifies that cumulative probability density calculations match expected values |
| * using default test instance data |
| */ |
| @Test |
| public void testCumulativeProbabilities() { |
| verifyCumulativeProbabilities(); |
| } |
| |
| /** |
| * Verifies that inverse cumulative probability density calculations match expected values |
| * using default test instance data |
| */ |
| @Test |
| public void testInverseCumulativeProbabilities() { |
| verifyInverseCumulativeProbabilities(); |
| } |
| |
| @Test |
| public void testConsistencyAtSupportBounds() { |
| final int lower = distribution.getSupportLowerBound(); |
| Assert.assertEquals("Cumulative probability must be 0 below support lower bound.", |
| 0.0, distribution.cumulativeProbability(lower - 1), 0.0); |
| Assert.assertEquals("Cumulative probability of support lower bound must be equal to probability mass at this point.", |
| distribution.probability(lower), distribution.cumulativeProbability(lower), getTolerance()); |
| Assert.assertEquals("Inverse cumulative probability of 0 must be equal to support lower bound.", |
| lower, distribution.inverseCumulativeProbability(0.0)); |
| |
| final int upper = distribution.getSupportUpperBound(); |
| if (upper != Integer.MAX_VALUE) { |
| Assert.assertEquals("Cumulative probability of support upper bound must be equal to 1.", |
| 1.0, distribution.cumulativeProbability(upper), 0.0); |
| } |
| Assert.assertEquals("Inverse cumulative probability of 1 must be equal to support upper bound.", |
| upper, distribution.inverseCumulativeProbability(1.0)); |
| } |
| |
| /** |
| * Verifies that illegal arguments are correctly handled |
| */ |
| @Test |
| public void testIllegalArguments() { |
| try { |
| distribution.probability(1, 0); |
| Assert.fail("Expecting MathIllegalArgumentException for bad cumulativeProbability interval"); |
| } catch (MathIllegalArgumentException ex) { |
| // expected |
| } |
| try { |
| distribution.inverseCumulativeProbability(-1); |
| Assert.fail("Expecting MathIllegalArgumentException for p = -1"); |
| } catch (MathIllegalArgumentException ex) { |
| // expected |
| } |
| try { |
| distribution.inverseCumulativeProbability(2); |
| Assert.fail("Expecting MathIllegalArgumentException for p = 2"); |
| } catch (MathIllegalArgumentException ex) { |
| // expected |
| } |
| } |
| |
| /** |
| * Test sampling |
| */ |
| @Test |
| public void testSampling() { |
| int[] densityPoints = makeDensityTestPoints(); |
| double[] densityValues = makeDensityTestValues(); |
| int sampleSize = 1000; |
| int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues); |
| AbstractIntegerDistribution distribution = (AbstractIntegerDistribution) makeDistribution(); |
| double[] expectedCounts = new double[length]; |
| long[] observedCounts = new long[length]; |
| for (int i = 0; i < length; i++) { |
| expectedCounts[i] = sampleSize * densityValues[i]; |
| } |
| // Use fixed seed. |
| final DiscreteDistribution.Sampler sampler = |
| distribution.createSampler(RandomSource.create(RandomSource.WELL_512_A, |
| 1000)); |
| int[] sample = AbstractIntegerDistribution.sample(sampleSize, sampler); |
| for (int i = 0; i < sampleSize; i++) { |
| for (int j = 0; j < length; j++) { |
| if (sample[i] == densityPoints[j]) { |
| observedCounts[j]++; |
| } |
| } |
| } |
| TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001); |
| } |
| |
| //------------------ Getters / Setters for test instance data ----------- |
| /** |
| * @return Returns the cumulativeTestPoints. |
| */ |
| protected int[] getCumulativeTestPoints() { |
| return cumulativeTestPoints; |
| } |
| |
| /** |
| * @param cumulativeTestPoints The cumulativeTestPoints to set. |
| */ |
| protected void setCumulativeTestPoints(int[] cumulativeTestPoints) { |
| this.cumulativeTestPoints = cumulativeTestPoints; |
| } |
| |
| /** |
| * @return Returns the cumulativeTestValues. |
| */ |
| protected double[] getCumulativeTestValues() { |
| return cumulativeTestValues; |
| } |
| |
| /** |
| * @param cumulativeTestValues The cumulativeTestValues to set. |
| */ |
| protected void setCumulativeTestValues(double[] cumulativeTestValues) { |
| this.cumulativeTestValues = cumulativeTestValues; |
| } |
| |
| /** |
| * @return Returns the densityTestPoints. |
| */ |
| protected int[] getDensityTestPoints() { |
| return densityTestPoints; |
| } |
| |
| /** |
| * @param densityTestPoints The densityTestPoints to set. |
| */ |
| protected void setDensityTestPoints(int[] densityTestPoints) { |
| this.densityTestPoints = densityTestPoints; |
| } |
| |
| /** |
| * @return Returns the densityTestValues. |
| */ |
| protected double[] getDensityTestValues() { |
| return densityTestValues; |
| } |
| |
| /** |
| * @param densityTestValues The densityTestValues to set. |
| */ |
| protected void setDensityTestValues(double[] densityTestValues) { |
| this.densityTestValues = densityTestValues; |
| } |
| |
| /** |
| * @return Returns the distribution. |
| */ |
| protected DiscreteDistribution getDistribution() { |
| return distribution; |
| } |
| |
| /** |
| * @param distribution The distribution to set. |
| */ |
| protected void setDistribution(DiscreteDistribution distribution) { |
| this.distribution = distribution; |
| } |
| |
| /** |
| * @return Returns the inverseCumulativeTestPoints. |
| */ |
| protected double[] getInverseCumulativeTestPoints() { |
| return inverseCumulativeTestPoints; |
| } |
| |
| /** |
| * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set. |
| */ |
| protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) { |
| this.inverseCumulativeTestPoints = inverseCumulativeTestPoints; |
| } |
| |
| /** |
| * @return Returns the inverseCumulativeTestValues. |
| */ |
| protected int[] getInverseCumulativeTestValues() { |
| return inverseCumulativeTestValues; |
| } |
| |
| /** |
| * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set. |
| */ |
| protected void setInverseCumulativeTestValues(int[] inverseCumulativeTestValues) { |
| this.inverseCumulativeTestValues = inverseCumulativeTestValues; |
| } |
| |
| /** |
| * @return Returns the tolerance. |
| */ |
| protected double getTolerance() { |
| return tolerance; |
| } |
| |
| /** |
| * @param tolerance The tolerance to set. |
| */ |
| protected void setTolerance(double tolerance) { |
| this.tolerance = tolerance; |
| } |
| } |