001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.math.distribution; 018 019 import junit.framework.TestCase; 020 021 import org.apache.commons.math.TestUtils; 022 023 /** 024 * Abstract base class for {@link ContinuousDistribution} tests. 025 * <p> 026 * To create a concrete test class for a continuous distribution 027 * implementation, first implement makeDistribution() to return a distribution 028 * instance to use in tests. Then implement each of the test data generation 029 * methods below. In each case, the test points and test values arrays 030 * returned represent parallel arrays of inputs and expected values for the 031 * distribution returned by makeDistribution(). Default implementations 032 * are provided for the makeInverseXxx methods that just invert the mapping 033 * defined by the arrays returned by the makeCumulativeXxx methods. 034 * <p> 035 * makeCumulativeTestPoints() -- arguments used to test cumulative probabilities 036 * makeCumulativeTestValues() -- expected cumulative probabilites 037 * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf 038 * makeInverseCumulativeTestValues() -- expected inverse cdf values 039 * <p> 040 * To implement additional test cases with different distribution instances and 041 * test data, use the setXxx methods for the instance data in test cases and 042 * call the verifyXxx methods to verify results. 043 * <p> 044 * Error tolerance can be overriden by implementing getTolerance(). 045 * <p> 046 * Test data should be validated against reference tables or other packages 047 * where possible, and the source of the reference data and/or validation 048 * should be documented in the test cases. A framework for validating 049 * distribution data against R is included in the /src/test/R source tree. 050 * <p> 051 * See {@link NormalDistributionTest} and {@link ChiSquareDistributionTest} 052 * for examples. 053 * 054 * @version $Revision: 762087 $ $Date: 2009-04-05 10:20:18 -0400 (Sun, 05 Apr 2009) $ 055 */ 056 public abstract class ContinuousDistributionAbstractTest extends TestCase { 057 058 //-------------------- Private test instance data ------------------------- 059 /** Distribution instance used to perform tests */ 060 private ContinuousDistribution distribution; 061 062 /** Tolerance used in comparing expected and returned values */ 063 private double tolerance = 1E-4; 064 065 /** Arguments used to test cumulative probability density calculations */ 066 private double[] cumulativeTestPoints; 067 068 /** Values used to test cumulative probability density calculations */ 069 private double[] cumulativeTestValues; 070 071 /** Arguments used to test inverse cumulative probability density calculations */ 072 private double[] inverseCumulativeTestPoints; 073 074 /** Values used to test inverse cumulative probability density calculations */ 075 private double[] inverseCumulativeTestValues; 076 077 //------------------------------------------------------------------------- 078 079 /** 080 * Constructor for ContinuousDistributionAbstractTest. 081 * @param name 082 */ 083 public ContinuousDistributionAbstractTest(String name) { 084 super(name); 085 } 086 087 //-------------------- Abstract methods ----------------------------------- 088 089 /** Creates the default continuous distribution instance to use in tests. */ 090 public abstract ContinuousDistribution makeDistribution(); 091 092 /** Creates the default cumulative probability density test input values */ 093 public abstract double[] makeCumulativeTestPoints(); 094 095 /** Creates the default cumulative probability density test expected values */ 096 public abstract double[] makeCumulativeTestValues(); 097 098 //---- Default implementations of inverse test data generation methods ---- 099 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 }