View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math4.legacy.distribution;
18  
19  import org.apache.commons.statistics.distribution.DiscreteDistribution;
20  import org.apache.commons.math4.legacy.TestUtils;
21  import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
22  import org.apache.commons.rng.simple.RandomSource;
23  import org.apache.commons.math4.core.jdkmath.JdkMath;
24  import org.junit.After;
25  import org.junit.Assert;
26  import org.junit.Before;
27  import org.junit.Test;
28  
29  /**
30   * Abstract base class for {@link DiscreteDistribution} tests.
31   * <p>
32   * To create a concrete test class for an integer distribution implementation,
33   *  implement makeDistribution() to return a distribution instance to use in
34   *  tests and each of the test data generation methods below.  In each case, the
35   *  test points and test values arrays returned represent parallel arrays of
36   *  inputs and expected values for the distribution returned by makeDistribution().
37   *  <p>
38   *  makeDensityTestPoints() -- arguments used to test probability density calculation
39   *  makeDensityTestValues() -- expected probability densities
40   *  makeCumulativeTestPoints() -- arguments used to test cumulative probabilities
41   *  makeCumulativeTestValues() -- expected cumulative probabilities
42   *  makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf evaluation
43   *  makeInverseCumulativeTestValues() -- expected inverse cdf values
44   * <p>
45   *  To implement additional test cases with different distribution instances and test data,
46   *  use the setXxx methods for the instance data in test cases and call the verifyXxx methods
47   *  to verify results.
48   *
49   */
50  public abstract class IntegerDistributionAbstractTest {
51  
52  //-------------------- Private test instance data -------------------------
53      /** Discrete distribution instance used to perform tests */
54      private DiscreteDistribution distribution;
55  
56      /** Tolerance used in comparing expected and returned values */
57      private double tolerance = 1E-12;
58  
59      /** Arguments used to test probability density calculations */
60      private int[] densityTestPoints;
61  
62      /** Values used to test probability density calculations */
63      private double[] densityTestValues;
64  
65      /** Values used to test logarithmic probability density calculations */
66      private double[] logDensityTestValues;
67  
68      /** Arguments used to test cumulative probability density calculations */
69      private int[] cumulativeTestPoints;
70  
71      /** Values used to test cumulative probability density calculations */
72      private double[] cumulativeTestValues;
73  
74      /** Arguments used to test inverse cumulative probability density calculations */
75      private double[] inverseCumulativeTestPoints;
76  
77      /** Values used to test inverse cumulative probability density calculations */
78      private int[] inverseCumulativeTestValues;
79  
80      //-------------------- Abstract methods -----------------------------------
81  
82      /** Creates the default discrete distribution instance to use in tests. */
83      public abstract DiscreteDistribution makeDistribution();
84  
85      /** Creates the default probability density test input values */
86      public abstract int[] makeDensityTestPoints();
87  
88      /** Creates the default probability density test expected values */
89      public abstract double[] makeDensityTestValues();
90  
91      /** Creates the default logarithmic probability density test expected values.
92       *
93       * The default implementation simply computes the logarithm of all the values in
94       * {@link #makeDensityTestValues()}.
95       *
96       * @return double[] the default logarithmic probability density test expected values.
97       */
98      public double[] makeLogDensityTestValues() {
99          final double[] densityTestValues = makeDensityTestValues();
100         final double[] logDensityTestValues = new double[densityTestValues.length];
101         for (int i = 0; i < densityTestValues.length; i++) {
102             logDensityTestValues[i] = JdkMath.log(densityTestValues[i]);
103         }
104         return logDensityTestValues;
105     }
106 
107     /** Creates the default cumulative probability density test input values */
108     public abstract int[] makeCumulativeTestPoints();
109 
110     /** Creates the default cumulative probability density test expected values */
111     public abstract double[] makeCumulativeTestValues();
112 
113     /** Creates the default inverse cumulative probability test input values */
114     public abstract double[] makeInverseCumulativeTestPoints();
115 
116     /** Creates the default inverse cumulative probability density test expected values */
117     public abstract int[] makeInverseCumulativeTestValues();
118 
119     //-------------------- Setup / tear down ----------------------------------
120 
121     /**
122      * Setup sets all test instance data to default values
123      */
124     @Before
125     public void setUp() {
126         distribution = makeDistribution();
127         densityTestPoints = makeDensityTestPoints();
128         densityTestValues = makeDensityTestValues();
129         logDensityTestValues = makeLogDensityTestValues();
130         cumulativeTestPoints = makeCumulativeTestPoints();
131         cumulativeTestValues = makeCumulativeTestValues();
132         inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
133         inverseCumulativeTestValues = makeInverseCumulativeTestValues();
134     }
135 
136     /**
137      * Cleans up test instance data
138      */
139     @After
140     public void tearDown() {
141         distribution = null;
142         densityTestPoints = null;
143         densityTestValues = null;
144         logDensityTestValues = null;
145         cumulativeTestPoints = null;
146         cumulativeTestValues = null;
147         inverseCumulativeTestPoints = null;
148         inverseCumulativeTestValues = null;
149     }
150 
151     //-------------------- Verification methods -------------------------------
152 
153     /**
154      * Verifies that probability density calculations match expected values
155      * using current test instance data
156      */
157     protected void verifyDensities() {
158         for (int i = 0; i < densityTestPoints.length; i++) {
159             Assert.assertEquals("Incorrect density value returned for " + densityTestPoints[i],
160                     densityTestValues[i],
161                     distribution.probability(densityTestPoints[i]), getTolerance());
162         }
163     }
164 
165     /**
166      * Verifies that logarithmic probability density calculations match expected values
167      * using current test instance data.
168      */
169     protected void verifyLogDensities() {
170         for (int i = 0; i < densityTestPoints.length; i++) {
171             Assert.assertEquals("Incorrect log density value returned for " + densityTestPoints[i],
172                     logDensityTestValues[i],
173                     distribution.logProbability(densityTestPoints[i]), tolerance);
174         }
175     }
176 
177     /**
178      * Verifies that cumulative probability density calculations match expected values
179      * using current test instance data
180      */
181     protected void verifyCumulativeProbabilities() {
182         for (int i = 0; i < cumulativeTestPoints.length; i++) {
183             Assert.assertEquals("Incorrect cumulative probability value returned for " + cumulativeTestPoints[i],
184                     cumulativeTestValues[i],
185                     distribution.cumulativeProbability(cumulativeTestPoints[i]), getTolerance());
186         }
187     }
188 
189 
190     /**
191      * Verifies that inverse cumulative probability density calculations match expected values
192      * using current test instance data
193      */
194     protected void verifyInverseCumulativeProbabilities() {
195         for (int i = 0; i < inverseCumulativeTestPoints.length; i++) {
196             Assert.assertEquals("Incorrect inverse cumulative probability value returned for "
197                     + inverseCumulativeTestPoints[i], inverseCumulativeTestValues[i],
198                     distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i]));
199         }
200     }
201 
202     //------------------------ Default test cases -----------------------------
203 
204     /**
205      * Verifies that probability density calculations match expected values
206      * using default test instance data
207      */
208     @Test
209     public void testDensities() {
210         verifyDensities();
211     }
212 
213     /**
214      * Verifies that logarithmic probability density calculations match expected values
215      * using default test instance data
216      */
217     @Test
218     public void testLogDensities() {
219         verifyLogDensities();
220     }
221 
222     /**
223      * Verifies that cumulative probability density calculations match expected values
224      * using default test instance data
225      */
226     @Test
227     public void testCumulativeProbabilities() {
228         verifyCumulativeProbabilities();
229     }
230 
231     /**
232      * Verifies that inverse cumulative probability density calculations match expected values
233      * using default test instance data
234      */
235     @Test
236     public void testInverseCumulativeProbabilities() {
237         verifyInverseCumulativeProbabilities();
238     }
239 
240     @Test
241     public void testConsistencyAtSupportBounds() {
242         final int lower = distribution.getSupportLowerBound();
243         Assert.assertEquals("Cumulative probability must be 0 below support lower bound.",
244                 0.0, distribution.cumulativeProbability(lower - 1), 0.0);
245         Assert.assertEquals("Cumulative probability of support lower bound must be equal to probability mass at this point.",
246                 distribution.probability(lower), distribution.cumulativeProbability(lower), getTolerance());
247         Assert.assertEquals("Inverse cumulative probability of 0 must be equal to support lower bound.",
248                 lower, distribution.inverseCumulativeProbability(0.0));
249 
250         final int upper = distribution.getSupportUpperBound();
251         if (upper != Integer.MAX_VALUE) {
252             Assert.assertEquals("Cumulative probability of support upper bound must be equal to 1.",
253                     1.0, distribution.cumulativeProbability(upper), 0.0);
254         }
255         Assert.assertEquals("Inverse cumulative probability of 1 must be equal to support upper bound.",
256                 upper, distribution.inverseCumulativeProbability(1.0));
257     }
258 
259     /**
260      * Verifies that illegal arguments are correctly handled
261      */
262     @Test
263     public void testIllegalArguments() {
264         try {
265             distribution.probability(1, 0);
266             Assert.fail("Expecting MathIllegalArgumentException for bad cumulativeProbability interval");
267         } catch (MathIllegalArgumentException ex) {
268             // expected
269         }
270         try {
271             distribution.inverseCumulativeProbability(-1);
272             Assert.fail("Expecting MathIllegalArgumentException for p = -1");
273         } catch (MathIllegalArgumentException ex) {
274             // expected
275         }
276         try {
277             distribution.inverseCumulativeProbability(2);
278             Assert.fail("Expecting MathIllegalArgumentException for p = 2");
279         } catch (MathIllegalArgumentException ex) {
280             // expected
281         }
282     }
283 
284     /**
285      * Test sampling
286      */
287     @Test
288     public void testSampling() {
289         int[] densityPoints = makeDensityTestPoints();
290         double[] densityValues = makeDensityTestValues();
291         int sampleSize = 1000;
292         int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues);
293         AbstractIntegerDistribution distribution = (AbstractIntegerDistribution) makeDistribution();
294         double[] expectedCounts = new double[length];
295         long[] observedCounts = new long[length];
296         for (int i = 0; i < length; i++) {
297             expectedCounts[i] = sampleSize * densityValues[i];
298         }
299         // Use fixed seed.
300         final DiscreteDistribution.Sampler sampler =
301             distribution.createSampler(RandomSource.WELL_512_A.create(1000));
302         int[] sample = AbstractIntegerDistribution.sample(sampleSize, sampler);
303         for (int i = 0; i < sampleSize; i++) {
304           for (int j = 0; j < length; j++) {
305               if (sample[i] == densityPoints[j]) {
306                   observedCounts[j]++;
307               }
308           }
309         }
310         TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001);
311     }
312 
313     //------------------ Getters / Setters for test instance data -----------
314     /**
315      * @return Returns the cumulativeTestPoints.
316      */
317     protected int[] getCumulativeTestPoints() {
318         return cumulativeTestPoints;
319     }
320 
321     /**
322      * @param cumulativeTestPoints The cumulativeTestPoints to set.
323      */
324     protected void setCumulativeTestPoints(int[] cumulativeTestPoints) {
325         this.cumulativeTestPoints = cumulativeTestPoints;
326     }
327 
328     /**
329      * @return Returns the cumulativeTestValues.
330      */
331     protected double[] getCumulativeTestValues() {
332         return cumulativeTestValues;
333     }
334 
335     /**
336      * @param cumulativeTestValues The cumulativeTestValues to set.
337      */
338     protected void setCumulativeTestValues(double[] cumulativeTestValues) {
339         this.cumulativeTestValues = cumulativeTestValues;
340     }
341 
342     /**
343      * @return Returns the densityTestPoints.
344      */
345     protected int[] getDensityTestPoints() {
346         return densityTestPoints;
347     }
348 
349     /**
350      * @param densityTestPoints The densityTestPoints to set.
351      */
352     protected void setDensityTestPoints(int[] densityTestPoints) {
353         this.densityTestPoints = densityTestPoints;
354     }
355 
356     /**
357      * @return Returns the densityTestValues.
358      */
359     protected double[] getDensityTestValues() {
360         return densityTestValues;
361     }
362 
363     /**
364      * @param densityTestValues The densityTestValues to set.
365      */
366     protected void setDensityTestValues(double[] densityTestValues) {
367         this.densityTestValues = densityTestValues;
368     }
369 
370     /**
371      * @return Returns the distribution.
372      */
373     protected DiscreteDistribution getDistribution() {
374         return distribution;
375     }
376 
377     /**
378      * @param distribution The distribution to set.
379      */
380     protected void setDistribution(DiscreteDistribution distribution) {
381         this.distribution = distribution;
382     }
383 
384     /**
385      * @return Returns the inverseCumulativeTestPoints.
386      */
387     protected double[] getInverseCumulativeTestPoints() {
388         return inverseCumulativeTestPoints;
389     }
390 
391     /**
392      * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set.
393      */
394     protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) {
395         this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
396     }
397 
398     /**
399      * @return Returns the inverseCumulativeTestValues.
400      */
401     protected int[] getInverseCumulativeTestValues() {
402         return inverseCumulativeTestValues;
403     }
404 
405     /**
406      * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set.
407      */
408     protected void setInverseCumulativeTestValues(int[] inverseCumulativeTestValues) {
409         this.inverseCumulativeTestValues = inverseCumulativeTestValues;
410     }
411 
412     /**
413      * @return Returns the tolerance.
414      */
415     protected double getTolerance() {
416         return tolerance;
417     }
418 
419     /**
420      * @param tolerance The tolerance to set.
421      */
422     protected void setTolerance(double tolerance) {
423         this.tolerance = tolerance;
424     }
425 }