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 }