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 java.io.Serializable; 20 21 import org.apache.commons.math.MathException; 22 23 /** 24 * The default implementation of {@link ChiSquaredDistribution} 25 * 26 * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $ 27 */ 28 public class ChiSquaredDistributionImpl 29 extends AbstractContinuousDistribution 30 implements ChiSquaredDistribution, Serializable { 31 32 /** 33 * Default inverse cumulative probability accuracy 34 * @since 2.1 35 */ 36 public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9; 37 38 /** Serializable version identifier */ 39 private static final long serialVersionUID = -8352658048349159782L; 40 41 /** Internal Gamma distribution. */ 42 private GammaDistribution gamma; 43 44 /** Inverse cumulative probability accuracy */ 45 private final double solverAbsoluteAccuracy; 46 47 /** 48 * Create a Chi-Squared distribution with the given degrees of freedom. 49 * @param df degrees of freedom. 50 */ 51 public ChiSquaredDistributionImpl(double df) { 52 this(df, new GammaDistributionImpl(df / 2.0, 2.0)); 53 } 54 55 /** 56 * Create a Chi-Squared distribution with the given degrees of freedom. 57 * @param df degrees of freedom. 58 * @param g the underlying gamma distribution used to compute probabilities. 59 * @since 1.2 60 * @deprecated as of 2.1 (to avoid possibly inconsistent state, the 61 * "GammaDistribution" will be instantiated internally) 62 */ 63 @Deprecated 64 public ChiSquaredDistributionImpl(double df, GammaDistribution g) { 65 super(); 66 setGammaInternal(g); 67 setDegreesOfFreedomInternal(df); 68 solverAbsoluteAccuracy = DEFAULT_INVERSE_ABSOLUTE_ACCURACY; 69 } 70 71 /** 72 * Create a Chi-Squared distribution with the given degrees of freedom and 73 * inverse cumulative probability accuracy. 74 * @param df degrees of freedom. 75 * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates 76 * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}) 77 * @since 2.1 78 */ 79 public ChiSquaredDistributionImpl(double df, double inverseCumAccuracy) { 80 super(); 81 gamma = new GammaDistributionImpl(df / 2.0, 2.0); 82 setDegreesOfFreedomInternal(df); 83 solverAbsoluteAccuracy = inverseCumAccuracy; 84 } 85 86 /** 87 * Modify the degrees of freedom. 88 * @param degreesOfFreedom the new degrees of freedom. 89 * @deprecated as of 2.1 (class will become immutable in 3.0) 90 */ 91 @Deprecated 92 public void setDegreesOfFreedom(double degreesOfFreedom) { 93 setDegreesOfFreedomInternal(degreesOfFreedom); 94 } 95 /** 96 * Modify the degrees of freedom. 97 * @param degreesOfFreedom the new degrees of freedom. 98 */ 99 private void setDegreesOfFreedomInternal(double degreesOfFreedom) { 100 gamma.setAlpha(degreesOfFreedom / 2.0); 101 } 102 103 /** 104 * Access the degrees of freedom. 105 * @return the degrees of freedom. 106 */ 107 public double getDegreesOfFreedom() { 108 return gamma.getAlpha() * 2.0; 109 } 110 111 /** 112 * Return the probability density for a particular point. 113 * 114 * @param x The point at which the density should be computed. 115 * @return The pdf at point x. 116 * @deprecated 117 */ 118 @Deprecated 119 public double density(Double x) { 120 return density(x.doubleValue()); 121 } 122 123 /** 124 * Return the probability density for a particular point. 125 * 126 * @param x The point at which the density should be computed. 127 * @return The pdf at point x. 128 * @since 2.1 129 */ 130 @Override 131 public double density(double x) { 132 return gamma.density(x); 133 } 134 135 /** 136 * For this distribution, X, this method returns P(X < x). 137 * @param x the value at which the CDF is evaluated. 138 * @return CDF for this distribution. 139 * @throws MathException if the cumulative probability can not be 140 * computed due to convergence or other numerical errors. 141 */ 142 public double cumulativeProbability(double x) throws MathException { 143 return gamma.cumulativeProbability(x); 144 } 145 146 /** 147 * For this distribution, X, this method returns the critical point x, such 148 * that P(X < x) = <code>p</code>. 149 * <p> 150 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p> 151 * 152 * @param p the desired probability 153 * @return x, such that P(X < x) = <code>p</code> 154 * @throws MathException if the inverse cumulative probability can not be 155 * computed due to convergence or other numerical errors. 156 * @throws IllegalArgumentException if <code>p</code> is not a valid 157 * probability. 158 */ 159 @Override 160 public double inverseCumulativeProbability(final double p) 161 throws MathException { 162 if (p == 0) { 163 return 0d; 164 } 165 if (p == 1) { 166 return Double.POSITIVE_INFINITY; 167 } 168 return super.inverseCumulativeProbability(p); 169 } 170 171 /** 172 * Access the domain value lower bound, based on <code>p</code>, used to 173 * bracket a CDF root. This method is used by 174 * {@link #inverseCumulativeProbability(double)} to find critical values. 175 * 176 * @param p the desired probability for the critical value 177 * @return domain value lower bound, i.e. 178 * P(X < <i>lower bound</i>) < <code>p</code> 179 */ 180 @Override 181 protected double getDomainLowerBound(double p) { 182 return Double.MIN_VALUE * gamma.getBeta(); 183 } 184 185 /** 186 * Access the domain value upper bound, based on <code>p</code>, used to 187 * bracket a CDF root. This method is used by 188 * {@link #inverseCumulativeProbability(double)} to find critical values. 189 * 190 * @param p the desired probability for the critical value 191 * @return domain value upper bound, i.e. 192 * P(X < <i>upper bound</i>) > <code>p</code> 193 */ 194 @Override 195 protected double getDomainUpperBound(double p) { 196 // NOTE: chi squared is skewed to the left 197 // NOTE: therefore, P(X < μ) > .5 198 199 double ret; 200 201 if (p < .5) { 202 // use mean 203 ret = getDegreesOfFreedom(); 204 } else { 205 // use max 206 ret = Double.MAX_VALUE; 207 } 208 209 return ret; 210 } 211 212 /** 213 * Access the initial domain value, based on <code>p</code>, used to 214 * bracket a CDF root. This method is used by 215 * {@link #inverseCumulativeProbability(double)} to find critical values. 216 * 217 * @param p the desired probability for the critical value 218 * @return initial domain value 219 */ 220 @Override 221 protected double getInitialDomain(double p) { 222 // NOTE: chi squared is skewed to the left 223 // NOTE: therefore, P(X < μ) > .5 224 225 double ret; 226 227 if (p < .5) { 228 // use 1/2 mean 229 ret = getDegreesOfFreedom() * .5; 230 } else { 231 // use mean 232 ret = getDegreesOfFreedom(); 233 } 234 235 return ret; 236 } 237 238 /** 239 * Modify the underlying gamma distribution. The caller is responsible for 240 * insuring the gamma distribution has the proper parameter settings. 241 * @param g the new distribution. 242 * @since 1.2 made public 243 * @deprecated as of 2.1 (class will become immutable in 3.0) 244 */ 245 @Deprecated 246 public void setGamma(GammaDistribution g) { 247 setGammaInternal(g); 248 } 249 /** 250 * Modify the underlying gamma distribution. The caller is responsible for 251 * insuring the gamma distribution has the proper parameter settings. 252 * @param g the new distribution. 253 * @since 1.2 made public 254 */ 255 private void setGammaInternal(GammaDistribution g) { 256 this.gamma = g; 257 258 } 259 260 261 /** 262 * Return the absolute accuracy setting of the solver used to estimate 263 * inverse cumulative probabilities. 264 * 265 * @return the solver absolute accuracy 266 * @since 2.1 267 */ 268 @Override 269 protected double getSolverAbsoluteAccuracy() { 270 return solverAbsoluteAccuracy; 271 } 272 273 /** 274 * Returns the lower bound of the support for the distribution. 275 * 276 * The lower bound of the support is always 0 no matter the 277 * degrees of freedom. 278 * 279 * @return lower bound of the support (always 0) 280 * @since 2.2 281 */ 282 public double getSupportLowerBound() { 283 return 0; 284 } 285 286 /** 287 * Returns the upper bound for the support for the distribution. 288 * 289 * The upper bound of the support is always positive infinity no matter the 290 * degrees of freedom. 291 * 292 * @return upper bound of the support (always Double.POSITIVE_INFINITY) 293 * @since 2.2 294 */ 295 public double getSupportUpperBound() { 296 return Double.POSITIVE_INFINITY; 297 } 298 299 /** 300 * Returns the mean of the distribution. 301 * 302 * For <code>k</code> degrees of freedom, the mean is 303 * <code>k</code> 304 * 305 * @return the mean 306 * @since 2.2 307 */ 308 public double getNumericalMean() { 309 return getDegreesOfFreedom(); 310 } 311 312 /** 313 * Returns the variance of the distribution. 314 * 315 * For <code>k</code> degrees of freedom, the variance is 316 * <code>2 * k</code> 317 * 318 * @return the variance 319 * @since 2.2 320 */ 321 public double getNumericalVariance() { 322 return 2*getDegreesOfFreedom(); 323 } 324 } 325