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 java.io.Serializable; 020 021 import org.apache.commons.math.MathException; 022 023 /** 024 * The default implementation of {@link ChiSquaredDistribution} 025 * 026 * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $ 027 */ 028 public class ChiSquaredDistributionImpl 029 extends AbstractContinuousDistribution 030 implements ChiSquaredDistribution, Serializable { 031 032 /** 033 * Default inverse cumulative probability accuracy 034 * @since 2.1 035 */ 036 public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9; 037 038 /** Serializable version identifier */ 039 private static final long serialVersionUID = -8352658048349159782L; 040 041 /** Internal Gamma distribution. */ 042 private GammaDistribution gamma; 043 044 /** Inverse cumulative probability accuracy */ 045 private final double solverAbsoluteAccuracy; 046 047 /** 048 * Create a Chi-Squared distribution with the given degrees of freedom. 049 * @param df degrees of freedom. 050 */ 051 public ChiSquaredDistributionImpl(double df) { 052 this(df, new GammaDistributionImpl(df / 2.0, 2.0)); 053 } 054 055 /** 056 * Create a Chi-Squared distribution with the given degrees of freedom. 057 * @param df degrees of freedom. 058 * @param g the underlying gamma distribution used to compute probabilities. 059 * @since 1.2 060 * @deprecated as of 2.1 (to avoid possibly inconsistent state, the 061 * "GammaDistribution" will be instantiated internally) 062 */ 063 @Deprecated 064 public ChiSquaredDistributionImpl(double df, GammaDistribution g) { 065 super(); 066 setGammaInternal(g); 067 setDegreesOfFreedomInternal(df); 068 solverAbsoluteAccuracy = DEFAULT_INVERSE_ABSOLUTE_ACCURACY; 069 } 070 071 /** 072 * Create a Chi-Squared distribution with the given degrees of freedom and 073 * inverse cumulative probability accuracy. 074 * @param df degrees of freedom. 075 * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates 076 * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}) 077 * @since 2.1 078 */ 079 public ChiSquaredDistributionImpl(double df, double inverseCumAccuracy) { 080 super(); 081 gamma = new GammaDistributionImpl(df / 2.0, 2.0); 082 setDegreesOfFreedomInternal(df); 083 solverAbsoluteAccuracy = inverseCumAccuracy; 084 } 085 086 /** 087 * Modify the degrees of freedom. 088 * @param degreesOfFreedom the new degrees of freedom. 089 * @deprecated as of 2.1 (class will become immutable in 3.0) 090 */ 091 @Deprecated 092 public void setDegreesOfFreedom(double degreesOfFreedom) { 093 setDegreesOfFreedomInternal(degreesOfFreedom); 094 } 095 /** 096 * Modify the degrees of freedom. 097 * @param degreesOfFreedom the new degrees of freedom. 098 */ 099 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 }