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 }