1   /*
2    * Copyright 2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.math.distribution;
17  
18  import junit.framework.TestCase;
19  import org.apache.commons.math.TestUtils;
20  
21  /**
22   * Abstract base class for {@link ContinuousDistribution} tests.
23   * <p>
24   * To create a concrete test class for a continuous distribution implementation,
25   * implement makeDistribution() to return a distribution instance to use in 
26   * tests and each of the test data generation methods below.  In each case, the
27   * test points and test values arrays returned represent parallel arrays of 
28   * inputs and expected values for the distribution returned by makeDistribution().
29   *  <p>
30   * makeCumulativeTestPoints() -- arguments used to test cumulative probabilities
31   * makeCumulativeTestValues() -- expected cumulative probabilites
32   * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf evaluation
33   * makeInverseCumulativeTestValues() -- expected inverse cdf values
34   * <p>
35   * To implement additional test cases with different distribution instances and test data,
36   * use the setXxx methods for the instance data in test cases and call the verifyXxx methods
37   * to verify results. 
38   * <p>
39   * Error tolerance can be overriden by implementing getTolerance().
40   * 
41   * @version $Revision: 201916 $ $Date: 2005-06-26 15:25:41 -0700 (Sun, 26 Jun 2005) $
42   */
43  public abstract class ContinuousDistributionAbstractTest extends TestCase {
44      
45  //-------------------- Private test instance data -------------------------
46      /**  Distribution instance used to perform tests */
47      private ContinuousDistribution distribution;
48      
49      /** Tolerance used in comparing expected and returned values */
50      private double tolerance = 1E-4;
51      
52      /** Arguments used to test cumulative probability density calculations */
53      private double[] cumulativeTestPoints;
54      
55      /** Values used to test cumulative probability density calculations */
56      private double[] cumulativeTestValues;
57      
58      /** Arguments used to test inverse cumulative probability density calculations */
59      private double[] inverseCumulativeTestPoints;
60      
61      /** Values used to test inverse cumulative probability density calculations */
62      private double[] inverseCumulativeTestValues;
63      
64      //-------------------------------------------------------------------------
65      
66      /**
67       * Constructor for ContinuousDistributionAbstractTest.
68       * @param name
69       */
70      public ContinuousDistributionAbstractTest(String name) {
71          super(name);
72      }
73      
74      //-------------------- Abstract methods -----------------------------------
75      
76      /** Creates the default continuous distribution instance to use in tests. */
77      public abstract ContinuousDistribution makeDistribution();
78      
79      /** Creates the default cumulative probability density test input values */
80      public abstract double[] makeCumulativeTestPoints();
81      
82      /** Creates the default cumulative probability density test expected values */
83      public abstract double[] makeCumulativeTestValues();
84      
85      //---- Default implementations of inverse test data generation methods ----
86      
87      /** Creates the default inverse cumulative probability test input values */
88      public double[] makeInverseCumulativeTestPoints() {
89          return makeCumulativeTestValues();
90      }
91      
92      /** Creates the default inverse cumulative probability density test expected values */
93      public double[] makeInverseCumulativeTestValues() {
94          return makeCumulativeTestPoints();
95      }
96      
97      //-------------------- Setup / tear down ----------------------------------
98       
99      /**
100      * Setup sets all test instance data to default values 
101      */
102     protected void setUp() throws Exception {
103         super.setUp();
104         distribution = makeDistribution();
105         cumulativeTestPoints = makeCumulativeTestPoints();
106         cumulativeTestValues = makeCumulativeTestValues();
107         inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
108         inverseCumulativeTestValues = makeInverseCumulativeTestValues();   
109     }
110     
111     /**
112      * Cleans up test instance data
113      */
114     protected void tearDown() throws Exception {      
115         super.tearDown();
116         distribution = null;
117         cumulativeTestPoints = null;
118         cumulativeTestValues = null;
119         inverseCumulativeTestPoints = null;
120         inverseCumulativeTestValues = null;   
121     }
122     
123     //-------------------- Verification methods -------------------------------
124     
125     /**
126      * Verifies that cumulative probability density calculations match expected values
127      * using current test instance data
128      */   
129     protected void verifyCumulativeProbabilities() throws Exception {
130         for (int i = 0; i < cumulativeTestPoints.length; i++) {
131             TestUtils.assertEquals("Incorrect cumulative probability value returned for " 
132                 + cumulativeTestPoints[i], cumulativeTestValues[i], 
133                 distribution.cumulativeProbability(cumulativeTestPoints[i]), 
134                 getTolerance());
135         }           
136     }
137     
138     /**
139      * Verifies that inverse cumulative probability density calculations match expected values
140      * using current test instance data
141      */
142     protected void verifyInverseCumulativeProbabilities() throws Exception {
143         for (int i = 0; i < inverseCumulativeTestPoints.length; i++) {
144             TestUtils.assertEquals("Incorrect inverse cumulative probability value returned for " 
145                 + inverseCumulativeTestPoints[i], inverseCumulativeTestValues[i], 
146                  distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i]), 
147                  getTolerance());
148         }           
149     }
150     
151     //------------------------ Default test cases -----------------------------
152     
153     /**
154      * Verifies that cumulative probability density calculations match expected values
155      * using default test instance data
156      */
157     public void testCumulativeProbabilities() throws Exception {
158         verifyCumulativeProbabilities();      
159     }
160     
161     /**
162      * Verifies that inverse cumulative probability density calculations match expected values
163      * using default test instance data
164      */
165     public void testInverseCumulativeProbabilities() throws Exception {
166         verifyInverseCumulativeProbabilities();       
167     }
168     
169     /**
170      * Verifies that probability computations are consistent
171      */
172     public void testConsistency() throws Exception {
173         for (int i=1; i < cumulativeTestPoints.length; i++) {
174             
175             // check that cdf(x, x) = 0
176             TestUtils.assertEquals(0d, 
177                distribution.cumulativeProbability
178                  (cumulativeTestPoints[i], cumulativeTestPoints[i]), tolerance);
179             
180             // check that P(a < X < b) = P(X < b) - P(X < a)
181             double upper = Math.max(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
182             double lower = Math.min(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
183             double diff = distribution.cumulativeProbability(upper) - 
184                 distribution.cumulativeProbability(lower);
185             double direct = distribution.cumulativeProbability(lower, upper);
186             TestUtils.assertEquals("Inconsistent cumulative probabilities for (" 
187                     + lower + "," + upper + ")", diff, direct, tolerance);
188         }
189     }
190     
191     /**
192      * Verifies that illegal arguments are correctly handled
193      */
194     public void testIllegalArguments() throws Exception {
195         try {
196             distribution.cumulativeProbability(1, 0);
197             fail("Expecting IllegalArgumentException for bad cumulativeProbability interval");
198         } catch (IllegalArgumentException ex) {
199             // expected
200         }
201         try {
202             distribution.inverseCumulativeProbability(-1);
203             fail("Expecting IllegalArgumentException for p = -1");
204         } catch (IllegalArgumentException ex) {
205             // expected
206         }
207         try {
208             distribution.inverseCumulativeProbability(2);
209             fail("Expecting IllegalArgumentException for p = 2");
210         } catch (IllegalArgumentException ex) {
211             // expected
212         }       
213     }
214     
215     //------------------ Getters / Setters for test instance data -----------
216     /**
217      * @return Returns the cumulativeTestPoints.
218      */
219     protected double[] getCumulativeTestPoints() {
220         return cumulativeTestPoints;
221     }
222 
223     /**
224      * @param cumulativeTestPoints The cumulativeTestPoints to set.
225      */
226     protected void setCumulativeTestPoints(double[] cumulativeTestPoints) {
227         this.cumulativeTestPoints = cumulativeTestPoints;
228     }
229 
230     /**
231      * @return Returns the cumulativeTestValues.
232      */
233     protected double[] getCumulativeTestValues() {
234         return cumulativeTestValues;
235     }
236 
237     /**
238      * @param cumulativeTestValues The cumulativeTestValues to set.
239      */
240     protected void setCumulativeTestValues(double[] cumulativeTestValues) {
241         this.cumulativeTestValues = cumulativeTestValues;
242     }
243 
244     /**
245      * @return Returns the distribution.
246      */
247     protected ContinuousDistribution getDistribution() {
248         return distribution;
249     }
250 
251     /**
252      * @param distribution The distribution to set.
253      */
254     protected void setDistribution(ContinuousDistribution distribution) {
255         this.distribution = distribution;
256     }
257 
258     /**
259      * @return Returns the inverseCumulativeTestPoints.
260      */
261     protected double[] getInverseCumulativeTestPoints() {
262         return inverseCumulativeTestPoints;
263     }
264 
265     /**
266      * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set.
267      */
268     protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) {
269         this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
270     }
271 
272     /**
273      * @return Returns the inverseCumulativeTestValues.
274      */
275     protected double[] getInverseCumulativeTestValues() {
276         return inverseCumulativeTestValues;
277     }
278 
279     /**
280      * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set.
281      */
282     protected void setInverseCumulativeTestValues(double[] inverseCumulativeTestValues) {
283         this.inverseCumulativeTestValues = inverseCumulativeTestValues;
284     }
285 
286     /**
287      * @return Returns the tolerance.
288      */
289     protected double getTolerance() {
290         return tolerance;
291     }
292 
293     /**
294      * @param tolerance The tolerance to set.
295      */
296     protected void setTolerance(double tolerance) {
297         this.tolerance = tolerance;
298     }
299 
300 }