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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.distribution.discrete;
23  
24  import org.hipparchus.distribution.IntegerDistribution;
25  import org.hipparchus.exception.MathIllegalArgumentException;
26  import org.hipparchus.util.FastMath;
27  import org.junit.jupiter.api.AfterEach;
28  import org.junit.jupiter.api.BeforeEach;
29  import org.junit.jupiter.api.Test;
30  
31  import static org.junit.jupiter.api.Assertions.assertEquals;
32  import static org.junit.jupiter.api.Assertions.fail;
33  
34  /**
35   * Abstract base class for {@link IntegerDistribution} tests.
36   * <p>
37   * To create a concrete test class for an integer distribution implementation,
38   * implement makeDistribution() to return a distribution instance to use in
39   * tests and each of the test data generation methods below.  In each case, the
40   * test points and test values arrays returned represent parallel arrays of
41   * inputs and expected values for the distribution returned by makeDistribution().
42   * <p>
43   * makeDensityTestPoints() -- arguments used to test probability density calculation
44   * makeDensityTestValues() -- expected probability densities
45   * makeCumulativeTestPoints() -- arguments used to test cumulative probabilities
46   * makeCumulativeTestValues() -- expected cumulative probabilities
47   * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf evaluation
48   * makeInverseCumulativeTestValues() -- expected inverse cdf values
49   * <p>
50   * To implement additional test cases with different distribution instances and test data,
51   * use the setXxx methods for the instance data in test cases and call the verifyXxx methods
52   * to verify results.
53   */
54  public abstract class IntegerDistributionAbstractTest {
55  
56  //-------------------- Private test instance data -------------------------
57      /** Discrete distribution instance used to perform tests */
58      private IntegerDistribution distribution;
59  
60      /** Tolerance used in comparing expected and returned values */
61      private double tolerance = 1E-12;
62  
63      /** Arguments used to test probability density calculations */
64      private int[] densityTestPoints;
65  
66      /** Values used to test probability density calculations */
67      private double[] densityTestValues;
68  
69      /** Values used to test logarithmic probability density calculations */
70      private double[] logDensityTestValues;
71  
72      /** Arguments used to test cumulative probability density calculations */
73      private int[] cumulativeTestPoints;
74  
75      /** Values used to test cumulative probability density calculations */
76      private double[] cumulativeTestValues;
77  
78      /** Arguments used to test inverse cumulative probability density calculations */
79      private double[] inverseCumulativeTestPoints;
80  
81      /** Values used to test inverse cumulative probability density calculations */
82      private int[] inverseCumulativeTestValues;
83  
84      //-------------------- Abstract methods -----------------------------------
85  
86      /** Creates the default discrete distribution instance to use in tests. */
87      public abstract IntegerDistribution makeDistribution();
88  
89      /** Creates the default probability density test input values */
90      public abstract int[] makeDensityTestPoints();
91  
92      /** Creates the default probability density test expected values */
93      public abstract double[] makeDensityTestValues();
94  
95      /** Creates the default logarithmic probability density test expected values.
96       *
97       * The default implementation simply computes the logarithm of all the values in
98       * {@link #makeDensityTestValues()}.
99       *
100      * @return double[] the default logarithmic probability density test expected values.
101      */
102     public double[] makeLogDensityTestValues() {
103         final double[] densityTestValues = makeDensityTestValues();
104         final double[] logDensityTestValues = new double[densityTestValues.length];
105         for (int i = 0; i < densityTestValues.length; i++) {
106             logDensityTestValues[i] = FastMath.log(densityTestValues[i]);
107         }
108         return logDensityTestValues;
109     }
110 
111     /** Creates the default cumulative probability density test input values */
112     public abstract int[] makeCumulativeTestPoints();
113 
114     /** Creates the default cumulative probability density test expected values */
115     public abstract double[] makeCumulativeTestValues();
116 
117     /** Creates the default inverse cumulative probability test input values */
118     public abstract double[] makeInverseCumulativeTestPoints();
119 
120     /** Creates the default inverse cumulative probability density test expected values */
121     public abstract int[] makeInverseCumulativeTestValues();
122 
123     //-------------------- Setup / tear down ----------------------------------
124 
125     /**
126      * Setup sets all test instance data to default values
127      */
128     @BeforeEach
129     public void setUp() {
130         distribution = makeDistribution();
131         densityTestPoints = makeDensityTestPoints();
132         densityTestValues = makeDensityTestValues();
133         logDensityTestValues = makeLogDensityTestValues();
134         cumulativeTestPoints = makeCumulativeTestPoints();
135         cumulativeTestValues = makeCumulativeTestValues();
136         inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
137         inverseCumulativeTestValues = makeInverseCumulativeTestValues();
138     }
139 
140     /**
141      * Cleans up test instance data
142      */
143     @AfterEach
144     public void tearDown() {
145         distribution = null;
146         densityTestPoints = null;
147         densityTestValues = null;
148         logDensityTestValues = null;
149         cumulativeTestPoints = null;
150         cumulativeTestValues = null;
151         inverseCumulativeTestPoints = null;
152         inverseCumulativeTestValues = null;
153     }
154 
155     //-------------------- Verification methods -------------------------------
156 
157     /**
158      * Verifies that probability density calculations match expected values
159      * using current test instance data
160      */
161     protected void verifyDensities() {
162         for (int i = 0; i < densityTestPoints.length; i++) {
163             assertEquals(densityTestValues[i],
164                     distribution.probability(densityTestPoints[i]), getTolerance(), "Incorrect density value returned for " + densityTestPoints[i]);
165         }
166     }
167 
168     /**
169      * Verifies that logarithmic probability density calculations match expected values
170      * using current test instance data.
171      */
172     protected void verifyLogDensities() {
173         for (int i = 0; i < densityTestPoints.length; i++) {
174             // FIXME: when logProbability methods are added to IntegerDistribution in 4.0, remove cast below
175             assertEquals(logDensityTestValues[i],
176                     ((AbstractIntegerDistribution) distribution).logProbability(densityTestPoints[i]), tolerance, "Incorrect log density value returned for " + densityTestPoints[i]);
177         }
178     }
179 
180     /**
181      * Verifies that cumulative probability density calculations match expected values
182      * using current test instance data
183      */
184     protected void verifyCumulativeProbabilities() {
185         for (int i = 0; i < cumulativeTestPoints.length; i++) {
186             assertEquals(cumulativeTestValues[i],
187                     distribution.cumulativeProbability(cumulativeTestPoints[i]), getTolerance(), "Incorrect cumulative probability value returned for " + cumulativeTestPoints[i]);
188         }
189     }
190 
191 
192     /**
193      * Verifies that inverse cumulative probability density calculations match expected values
194      * using current test instance data
195      */
196     protected void verifyInverseCumulativeProbabilities() {
197         for (int i = 0; i < inverseCumulativeTestPoints.length; i++) {
198             assertEquals(inverseCumulativeTestValues[i],
199                     distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i]),
200                     "Incorrect inverse cumulative probability value returned for "
201                     + inverseCumulativeTestPoints[i]);
202         }
203     }
204 
205     //------------------------ Default test cases -----------------------------
206 
207     /**
208      * Verifies that probability density calculations match expected values
209      * using default test instance data
210      */
211     @Test
212     public void testDensities() {
213         verifyDensities();
214     }
215 
216     /**
217      * Verifies that logarithmic probability density calculations match expected values
218      * using default test instance data
219      */
220     @Test
221     public void testLogDensities() {
222         verifyLogDensities();
223     }
224 
225     /**
226      * Verifies that cumulative probability density calculations match expected values
227      * using default test instance data
228      */
229     @Test
230     public void testCumulativeProbabilities() {
231         verifyCumulativeProbabilities();
232     }
233 
234     /**
235      * Verifies that inverse cumulative probability density calculations match expected values
236      * using default test instance data
237      */
238     @Test
239     public void testInverseCumulativeProbabilities() {
240         verifyInverseCumulativeProbabilities();
241     }
242 
243     @Test
244     public void testConsistencyAtSupportBounds() {
245         final int lower = distribution.getSupportLowerBound();
246         assertEquals(0.0, distribution.cumulativeProbability(lower - 1), 0.0, "Cumulative probability mmust be 0 below support lower bound.");
247         assertEquals(distribution.probability(lower), distribution.cumulativeProbability(lower), getTolerance(), "Cumulative probability of support lower bound must be equal to probability mass at this point.");
248         assertEquals(lower, distribution.inverseCumulativeProbability(0.0), "Inverse cumulative probability of 0 must be equal to support lower bound.");
249 
250         final int upper = distribution.getSupportUpperBound();
251         if (upper != Integer.MAX_VALUE)
252             assertEquals(1.0, distribution.cumulativeProbability(upper), 0.0, "Cumulative probability of support upper bound must be equal to 1.");
253         assertEquals(upper, distribution.inverseCumulativeProbability(1.0), "Inverse cumulative probability of 1 must be equal to support upper bound.");
254     }
255 
256     /**
257      * Verifies that illegal arguments are correctly handled
258      */
259     @Test
260     public void testIllegalArguments() {
261         try {
262             distribution.probability(1, 0);
263             fail("Expecting MathIllegalArgumentException for bad cumulativeProbability interval");
264         } catch (MathIllegalArgumentException ex) {
265             // expected
266         }
267         try {
268             distribution.inverseCumulativeProbability(-1);
269             fail("Expecting MathIllegalArgumentException for p = -1");
270         } catch (MathIllegalArgumentException ex) {
271             // expected
272         }
273         try {
274             distribution.inverseCumulativeProbability(2);
275             fail("Expecting MathIllegalArgumentException for p = 2");
276         } catch (MathIllegalArgumentException ex) {
277             // expected
278         }
279     }
280 
281     //------------------ Getters / Setters for test instance data -----------
282     /**
283      * @return Returns the cumulativeTestPoints.
284      */
285     protected int[] getCumulativeTestPoints() {
286         return cumulativeTestPoints;
287     }
288 
289     /**
290      * @param cumulativeTestPoints The cumulativeTestPoints to set.
291      */
292     protected void setCumulativeTestPoints(int[] cumulativeTestPoints) {
293         this.cumulativeTestPoints = cumulativeTestPoints;
294     }
295 
296     /**
297      * @return Returns the cumulativeTestValues.
298      */
299     protected double[] getCumulativeTestValues() {
300         return cumulativeTestValues;
301     }
302 
303     /**
304      * @param cumulativeTestValues The cumulativeTestValues to set.
305      */
306     protected void setCumulativeTestValues(double[] cumulativeTestValues) {
307         this.cumulativeTestValues = cumulativeTestValues;
308     }
309 
310     /**
311      * @return Returns the densityTestPoints.
312      */
313     protected int[] getDensityTestPoints() {
314         return densityTestPoints;
315     }
316 
317     /**
318      * @param densityTestPoints The densityTestPoints to set.
319      */
320     protected void setDensityTestPoints(int[] densityTestPoints) {
321         this.densityTestPoints = densityTestPoints;
322     }
323 
324     /**
325      * @return Returns the densityTestValues.
326      */
327     protected double[] getDensityTestValues() {
328         return densityTestValues;
329     }
330 
331     /**
332      * @param densityTestValues The densityTestValues to set.
333      */
334     protected void setDensityTestValues(double[] densityTestValues) {
335         this.densityTestValues = densityTestValues;
336     }
337 
338     /**
339      * @return Returns the distribution.
340      */
341     protected IntegerDistribution getDistribution() {
342         return distribution;
343     }
344 
345     /**
346      * @param distribution The distribution to set.
347      */
348     protected void setDistribution(IntegerDistribution distribution) {
349         this.distribution = distribution;
350     }
351 
352     /**
353      * @return Returns the inverseCumulativeTestPoints.
354      */
355     protected double[] getInverseCumulativeTestPoints() {
356         return inverseCumulativeTestPoints;
357     }
358 
359     /**
360      * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set.
361      */
362     protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) {
363         this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
364     }
365 
366     /**
367      * @return Returns the inverseCumulativeTestValues.
368      */
369     protected int[] getInverseCumulativeTestValues() {
370         return inverseCumulativeTestValues;
371     }
372 
373     /**
374      * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set.
375      */
376     protected void setInverseCumulativeTestValues(int[] inverseCumulativeTestValues) {
377         this.inverseCumulativeTestValues = inverseCumulativeTestValues;
378     }
379 
380     /**
381      * @return Returns the tolerance.
382      */
383     protected double getTolerance() {
384         return tolerance;
385     }
386 
387     /**
388      * @param tolerance The tolerance to set.
389      */
390     protected void setTolerance(double tolerance) {
391         this.tolerance = tolerance;
392     }
393 
394 }