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 }