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.math.distribution;
18
19 import junit.framework.TestCase;
20
21 import org.apache.commons.math.TestUtils;
22
23 /**
24 * Abstract base class for {@link ContinuousDistribution} tests.
25 * <p>
26 * To create a concrete test class for a continuous distribution
27 * implementation, first implement makeDistribution() to return a distribution
28 * instance to use in tests. Then implement each of the test data generation
29 * methods below. In each case, the test points and test values arrays
30 * returned represent parallel arrays of inputs and expected values for the
31 * distribution returned by makeDistribution(). Default implementations
32 * are provided for the makeInverseXxx methods that just invert the mapping
33 * defined by the arrays returned by the makeCumulativeXxx methods.
34 * <p>
35 * makeCumulativeTestPoints() -- arguments used to test cumulative probabilities
36 * makeCumulativeTestValues() -- expected cumulative probabilites
37 * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf
38 * makeInverseCumulativeTestValues() -- expected inverse cdf values
39 * <p>
40 * To implement additional test cases with different distribution instances and
41 * test data, use the setXxx methods for the instance data in test cases and
42 * call the verifyXxx methods to verify results.
43 * <p>
44 * Error tolerance can be overriden by implementing getTolerance().
45 * <p>
46 * Test data should be validated against reference tables or other packages
47 * where possible, and the source of the reference data and/or validation
48 * should be documented in the test cases. A framework for validating
49 * distribution data against R is included in the /src/test/R source tree.
50 * <p>
51 * See {@link NormalDistributionTest} and {@link ChiSquareDistributionTest}
52 * for examples.
53 *
54 * @version $Revision: 762087 $ $Date: 2009-04-05 10:20:18 -0400 (Sun, 05 Apr 2009) $
55 */
56 public abstract class ContinuousDistributionAbstractTest extends TestCase {
57
58 //-------------------- Private test instance data -------------------------
59 /** Distribution instance used to perform tests */
60 private ContinuousDistribution distribution;
61
62 /** Tolerance used in comparing expected and returned values */
63 private double tolerance = 1E-4;
64
65 /** Arguments used to test cumulative probability density calculations */
66 private double[] cumulativeTestPoints;
67
68 /** Values used to test cumulative probability density calculations */
69 private double[] cumulativeTestValues;
70
71 /** Arguments used to test inverse cumulative probability density calculations */
72 private double[] inverseCumulativeTestPoints;
73
74 /** Values used to test inverse cumulative probability density calculations */
75 private double[] inverseCumulativeTestValues;
76
77 //-------------------------------------------------------------------------
78
79 /**
80 * Constructor for ContinuousDistributionAbstractTest.
81 * @param name
82 */
83 public ContinuousDistributionAbstractTest(String name) {
84 super(name);
85 }
86
87 //-------------------- Abstract methods -----------------------------------
88
89 /** Creates the default continuous distribution instance to use in tests. */
90 public abstract ContinuousDistribution makeDistribution();
91
92 /** Creates the default cumulative probability density test input values */
93 public abstract double[] makeCumulativeTestPoints();
94
95 /** Creates the default cumulative probability density test expected values */
96 public abstract double[] makeCumulativeTestValues();
97
98 //---- Default implementations of inverse test data generation methods ----
99
100 /** Creates the default inverse cumulative probability test input values */
101 public double[] makeInverseCumulativeTestPoints() {
102 return makeCumulativeTestValues();
103 }
104
105 /** Creates the default inverse cumulative probability density test expected values */
106 public double[] makeInverseCumulativeTestValues() {
107 return makeCumulativeTestPoints();
108 }
109
110 //-------------------- Setup / tear down ----------------------------------
111
112 /**
113 * Setup sets all test instance data to default values
114 */
115 @Override
116 protected void setUp() throws Exception {
117 super.setUp();
118 distribution = makeDistribution();
119 cumulativeTestPoints = makeCumulativeTestPoints();
120 cumulativeTestValues = makeCumulativeTestValues();
121 inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
122 inverseCumulativeTestValues = makeInverseCumulativeTestValues();
123 }
124
125 /**
126 * Cleans up test instance data
127 */
128 @Override
129 protected void tearDown() throws Exception {
130 super.tearDown();
131 distribution = null;
132 cumulativeTestPoints = null;
133 cumulativeTestValues = null;
134 inverseCumulativeTestPoints = null;
135 inverseCumulativeTestValues = null;
136 }
137
138 //-------------------- Verification methods -------------------------------
139
140 /**
141 * Verifies that cumulative probability density calculations match expected values
142 * using current test instance data
143 */
144 protected void verifyCumulativeProbabilities() throws Exception {
145 for (int i = 0; i < cumulativeTestPoints.length; i++) {
146 TestUtils.assertEquals("Incorrect cumulative probability value returned for "
147 + cumulativeTestPoints[i], cumulativeTestValues[i],
148 distribution.cumulativeProbability(cumulativeTestPoints[i]),
149 getTolerance());
150 }
151 }
152
153 /**
154 * Verifies that inverse cumulative probability density calculations match expected values
155 * using current test instance data
156 */
157 protected void verifyInverseCumulativeProbabilities() throws Exception {
158 for (int i = 0; i < inverseCumulativeTestPoints.length; i++) {
159 TestUtils.assertEquals("Incorrect inverse cumulative probability value returned for "
160 + inverseCumulativeTestPoints[i], inverseCumulativeTestValues[i],
161 distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i]),
162 getTolerance());
163 }
164 }
165
166 //------------------------ Default test cases -----------------------------
167
168 /**
169 * Verifies that cumulative probability density calculations match expected values
170 * using default test instance data
171 */
172 public void testCumulativeProbabilities() throws Exception {
173 verifyCumulativeProbabilities();
174 }
175
176 /**
177 * Verifies that inverse cumulative probability density calculations match expected values
178 * using default test instance data
179 */
180 public void testInverseCumulativeProbabilities() throws Exception {
181 verifyInverseCumulativeProbabilities();
182 }
183
184 /**
185 * Verifies that probability computations are consistent
186 */
187 public void testConsistency() throws Exception {
188 for (int i=1; i < cumulativeTestPoints.length; i++) {
189
190 // check that cdf(x, x) = 0
191 TestUtils.assertEquals(0d,
192 distribution.cumulativeProbability
193 (cumulativeTestPoints[i], cumulativeTestPoints[i]), tolerance);
194
195 // check that P(a < X < b) = P(X < b) - P(X < a)
196 double upper = Math.max(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
197 double lower = Math.min(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
198 double diff = distribution.cumulativeProbability(upper) -
199 distribution.cumulativeProbability(lower);
200 double direct = distribution.cumulativeProbability(lower, upper);
201 TestUtils.assertEquals("Inconsistent cumulative probabilities for ("
202 + lower + "," + upper + ")", diff, direct, tolerance);
203 }
204 }
205
206 /**
207 * Verifies that illegal arguments are correctly handled
208 */
209 public void testIllegalArguments() throws Exception {
210 try {
211 distribution.cumulativeProbability(1, 0);
212 fail("Expecting IllegalArgumentException for bad cumulativeProbability interval");
213 } catch (IllegalArgumentException ex) {
214 // expected
215 }
216 try {
217 distribution.inverseCumulativeProbability(-1);
218 fail("Expecting IllegalArgumentException for p = -1");
219 } catch (IllegalArgumentException ex) {
220 // expected
221 }
222 try {
223 distribution.inverseCumulativeProbability(2);
224 fail("Expecting IllegalArgumentException for p = 2");
225 } catch (IllegalArgumentException ex) {
226 // expected
227 }
228 }
229
230 //------------------ Getters / Setters for test instance data -----------
231 /**
232 * @return Returns the cumulativeTestPoints.
233 */
234 protected double[] getCumulativeTestPoints() {
235 return cumulativeTestPoints;
236 }
237
238 /**
239 * @param cumulativeTestPoints The cumulativeTestPoints to set.
240 */
241 protected void setCumulativeTestPoints(double[] cumulativeTestPoints) {
242 this.cumulativeTestPoints = cumulativeTestPoints;
243 }
244
245 /**
246 * @return Returns the cumulativeTestValues.
247 */
248 protected double[] getCumulativeTestValues() {
249 return cumulativeTestValues;
250 }
251
252 /**
253 * @param cumulativeTestValues The cumulativeTestValues to set.
254 */
255 protected void setCumulativeTestValues(double[] cumulativeTestValues) {
256 this.cumulativeTestValues = cumulativeTestValues;
257 }
258
259 /**
260 * @return Returns the distribution.
261 */
262 protected ContinuousDistribution getDistribution() {
263 return distribution;
264 }
265
266 /**
267 * @param distribution The distribution to set.
268 */
269 protected void setDistribution(ContinuousDistribution distribution) {
270 this.distribution = distribution;
271 }
272
273 /**
274 * @return Returns the inverseCumulativeTestPoints.
275 */
276 protected double[] getInverseCumulativeTestPoints() {
277 return inverseCumulativeTestPoints;
278 }
279
280 /**
281 * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set.
282 */
283 protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) {
284 this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
285 }
286
287 /**
288 * @return Returns the inverseCumulativeTestValues.
289 */
290 protected double[] getInverseCumulativeTestValues() {
291 return inverseCumulativeTestValues;
292 }
293
294 /**
295 * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set.
296 */
297 protected void setInverseCumulativeTestValues(double[] inverseCumulativeTestValues) {
298 this.inverseCumulativeTestValues = inverseCumulativeTestValues;
299 }
300
301 /**
302 * @return Returns the tolerance.
303 */
304 protected double getTolerance() {
305 return tolerance;
306 }
307
308 /**
309 * @param tolerance The tolerance to set.
310 */
311 protected void setTolerance(double tolerance) {
312 this.tolerance = tolerance;
313 }
314
315 }