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: 925812 $ $Date: 2010-03-21 11:49:31 -0400 (Sun, 21 Mar 2010) $ 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 public double density(Double x) { 119 return density(x.doubleValue()); 120 } 121 122 /** 123 * Return the probability density for a particular point. 124 * 125 * @param x The point at which the density should be computed. 126 * @return The pdf at point x. 127 * @since 2.1 128 */ 129 @Override 130 public double density(double x) { 131 return gamma.density(x); 132 } 133 134 /** 135 * For this distribution, X, this method returns P(X < x). 136 * @param x the value at which the CDF is evaluated. 137 * @return CDF for this distribution. 138 * @throws MathException if the cumulative probability can not be 139 * computed due to convergence or other numerical errors. 140 */ 141 public double cumulativeProbability(double x) throws MathException { 142 return gamma.cumulativeProbability(x); 143 } 144 145 /** 146 * For this distribution, X, this method returns the critical point x, such 147 * that P(X < x) = <code>p</code>. 148 * <p> 149 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p> 150 * 151 * @param p the desired probability 152 * @return x, such that P(X < x) = <code>p</code> 153 * @throws MathException if the inverse cumulative probability can not be 154 * computed due to convergence or other numerical errors. 155 * @throws IllegalArgumentException if <code>p</code> is not a valid 156 * probability. 157 */ 158 @Override 159 public double inverseCumulativeProbability(final double p) 160 throws MathException { 161 if (p == 0) { 162 return 0d; 163 } 164 if (p == 1) { 165 return Double.POSITIVE_INFINITY; 166 } 167 return super.inverseCumulativeProbability(p); 168 } 169 170 /** 171 * Access the domain value lower bound, based on <code>p</code>, used to 172 * bracket a CDF root. This method is used by 173 * {@link #inverseCumulativeProbability(double)} to find critical values. 174 * 175 * @param p the desired probability for the critical value 176 * @return domain value lower bound, i.e. 177 * P(X < <i>lower bound</i>) < <code>p</code> 178 */ 179 @Override 180 protected double getDomainLowerBound(double p) { 181 return Double.MIN_VALUE * gamma.getBeta(); 182 } 183 184 /** 185 * Access the domain value upper bound, based on <code>p</code>, used to 186 * bracket a CDF root. This method is used by 187 * {@link #inverseCumulativeProbability(double)} to find critical values. 188 * 189 * @param p the desired probability for the critical value 190 * @return domain value upper bound, i.e. 191 * P(X < <i>upper bound</i>) > <code>p</code> 192 */ 193 @Override 194 protected double getDomainUpperBound(double p) { 195 // NOTE: chi squared is skewed to the left 196 // NOTE: therefore, P(X < μ) > .5 197 198 double ret; 199 200 if (p < .5) { 201 // use mean 202 ret = getDegreesOfFreedom(); 203 } else { 204 // use max 205 ret = Double.MAX_VALUE; 206 } 207 208 return ret; 209 } 210 211 /** 212 * Access the initial domain value, based on <code>p</code>, used to 213 * bracket a CDF root. This method is used by 214 * {@link #inverseCumulativeProbability(double)} to find critical values. 215 * 216 * @param p the desired probability for the critical value 217 * @return initial domain value 218 */ 219 @Override 220 protected double getInitialDomain(double p) { 221 // NOTE: chi squared is skewed to the left 222 // NOTE: therefore, P(X < μ) > .5 223 224 double ret; 225 226 if (p < .5) { 227 // use 1/2 mean 228 ret = getDegreesOfFreedom() * .5; 229 } else { 230 // use mean 231 ret = getDegreesOfFreedom(); 232 } 233 234 return ret; 235 } 236 237 /** 238 * Modify the underlying gamma distribution. The caller is responsible for 239 * insuring the gamma distribution has the proper parameter settings. 240 * @param g the new distribution. 241 * @since 1.2 made public 242 * @deprecated as of 2.1 (class will become immutable in 3.0) 243 */ 244 @Deprecated 245 public void setGamma(GammaDistribution g) { 246 setGammaInternal(g); 247 } 248 /** 249 * Modify the underlying gamma distribution. The caller is responsible for 250 * insuring the gamma distribution has the proper parameter settings. 251 * @param g the new distribution. 252 * @since 1.2 made public 253 */ 254 private void setGammaInternal(GammaDistribution g) { 255 this.gamma = g; 256 257 } 258 259 260 /** 261 * Return the absolute accuracy setting of the solver used to estimate 262 * inverse cumulative probabilities. 263 * 264 * @return the solver absolute accuracy 265 * @since 2.1 266 */ 267 @Override 268 protected double getSolverAbsoluteAccuracy() { 269 return solverAbsoluteAccuracy; 270 } 271 }