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: 545192 $ $Date: 2007-06-07 07:35:04 -0700 (Thu, 07 Jun 2007) $
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 protected void setUp() throws Exception {
116 super.setUp();
117 distribution = makeDistribution();
118 cumulativeTestPoints = makeCumulativeTestPoints();
119 cumulativeTestValues = makeCumulativeTestValues();
120 inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
121 inverseCumulativeTestValues = makeInverseCumulativeTestValues();
122 }
123
124 /**
125 * Cleans up test instance data
126 */
127 protected void tearDown() throws Exception {
128 super.tearDown();
129 distribution = null;
130 cumulativeTestPoints = null;
131 cumulativeTestValues = null;
132 inverseCumulativeTestPoints = null;
133 inverseCumulativeTestValues = null;
134 }
135
136 //-------------------- Verification methods -------------------------------
137
138 /**
139 * Verifies that cumulative probability density calculations match expected values
140 * using current test instance data
141 */
142 protected void verifyCumulativeProbabilities() throws Exception {
143 for (int i = 0; i < cumulativeTestPoints.length; i++) {
144 TestUtils.assertEquals("Incorrect cumulative probability value returned for "
145 + cumulativeTestPoints[i], cumulativeTestValues[i],
146 distribution.cumulativeProbability(cumulativeTestPoints[i]),
147 getTolerance());
148 }
149 }
150
151 /**
152 * Verifies that inverse cumulative probability density calculations match expected values
153 * using current test instance data
154 */
155 protected void verifyInverseCumulativeProbabilities() throws Exception {
156 for (int i = 0; i < inverseCumulativeTestPoints.length; i++) {
157 TestUtils.assertEquals("Incorrect inverse cumulative probability value returned for "
158 + inverseCumulativeTestPoints[i], inverseCumulativeTestValues[i],
159 distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i]),
160 getTolerance());
161 }
162 }
163
164 //------------------------ Default test cases -----------------------------
165
166 /**
167 * Verifies that cumulative probability density calculations match expected values
168 * using default test instance data
169 */
170 public void testCumulativeProbabilities() throws Exception {
171 verifyCumulativeProbabilities();
172 }
173
174 /**
175 * Verifies that inverse cumulative probability density calculations match expected values
176 * using default test instance data
177 */
178 public void testInverseCumulativeProbabilities() throws Exception {
179 verifyInverseCumulativeProbabilities();
180 }
181
182 /**
183 * Verifies that probability computations are consistent
184 */
185 public void testConsistency() throws Exception {
186 for (int i=1; i < cumulativeTestPoints.length; i++) {
187
188 // check that cdf(x, x) = 0
189 TestUtils.assertEquals(0d,
190 distribution.cumulativeProbability
191 (cumulativeTestPoints[i], cumulativeTestPoints[i]), tolerance);
192
193 // check that P(a < X < b) = P(X < b) - P(X < a)
194 double upper = Math.max(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
195 double lower = Math.min(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
196 double diff = distribution.cumulativeProbability(upper) -
197 distribution.cumulativeProbability(lower);
198 double direct = distribution.cumulativeProbability(lower, upper);
199 TestUtils.assertEquals("Inconsistent cumulative probabilities for ("
200 + lower + "," + upper + ")", diff, direct, tolerance);
201 }
202 }
203
204 /**
205 * Verifies that illegal arguments are correctly handled
206 */
207 public void testIllegalArguments() throws Exception {
208 try {
209 distribution.cumulativeProbability(1, 0);
210 fail("Expecting IllegalArgumentException for bad cumulativeProbability interval");
211 } catch (IllegalArgumentException ex) {
212 // expected
213 }
214 try {
215 distribution.inverseCumulativeProbability(-1);
216 fail("Expecting IllegalArgumentException for p = -1");
217 } catch (IllegalArgumentException ex) {
218 // expected
219 }
220 try {
221 distribution.inverseCumulativeProbability(2);
222 fail("Expecting IllegalArgumentException for p = 2");
223 } catch (IllegalArgumentException ex) {
224 // expected
225 }
226 }
227
228 //------------------ Getters / Setters for test instance data -----------
229 /**
230 * @return Returns the cumulativeTestPoints.
231 */
232 protected double[] getCumulativeTestPoints() {
233 return cumulativeTestPoints;
234 }
235
236 /**
237 * @param cumulativeTestPoints The cumulativeTestPoints to set.
238 */
239 protected void setCumulativeTestPoints(double[] cumulativeTestPoints) {
240 this.cumulativeTestPoints = cumulativeTestPoints;
241 }
242
243 /**
244 * @return Returns the cumulativeTestValues.
245 */
246 protected double[] getCumulativeTestValues() {
247 return cumulativeTestValues;
248 }
249
250 /**
251 * @param cumulativeTestValues The cumulativeTestValues to set.
252 */
253 protected void setCumulativeTestValues(double[] cumulativeTestValues) {
254 this.cumulativeTestValues = cumulativeTestValues;
255 }
256
257 /**
258 * @return Returns the distribution.
259 */
260 protected ContinuousDistribution getDistribution() {
261 return distribution;
262 }
263
264 /**
265 * @param distribution The distribution to set.
266 */
267 protected void setDistribution(ContinuousDistribution distribution) {
268 this.distribution = distribution;
269 }
270
271 /**
272 * @return Returns the inverseCumulativeTestPoints.
273 */
274 protected double[] getInverseCumulativeTestPoints() {
275 return inverseCumulativeTestPoints;
276 }
277
278 /**
279 * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set.
280 */
281 protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) {
282 this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
283 }
284
285 /**
286 * @return Returns the inverseCumulativeTestValues.
287 */
288 protected double[] getInverseCumulativeTestValues() {
289 return inverseCumulativeTestValues;
290 }
291
292 /**
293 * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set.
294 */
295 protected void setInverseCumulativeTestValues(double[] inverseCumulativeTestValues) {
296 this.inverseCumulativeTestValues = inverseCumulativeTestValues;
297 }
298
299 /**
300 * @return Returns the tolerance.
301 */
302 protected double getTolerance() {
303 return tolerance;
304 }
305
306 /**
307 * @param tolerance The tolerance to set.
308 */
309 protected void setTolerance(double tolerance) {
310 this.tolerance = tolerance;
311 }
312
313 }