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: 762087 $ $Date: 2009-04-05 10:20:18 -0400 (Sun, 05 Apr 2009) $ 027 */ 028 public class ChiSquaredDistributionImpl 029 extends AbstractContinuousDistribution 030 implements ChiSquaredDistribution, Serializable { 031 032 /** Serializable version identifier */ 033 private static final long serialVersionUID = -8352658048349159782L; 034 035 /** Internal Gamma distribution. */ 036 private GammaDistribution gamma; 037 038 /** 039 * Create a Chi-Squared distribution with the given degrees of freedom. 040 * @param df degrees of freedom. 041 */ 042 public ChiSquaredDistributionImpl(double df) { 043 this(df, new GammaDistributionImpl(df / 2.0, 2.0)); 044 } 045 046 /** 047 * Create a Chi-Squared distribution with the given degrees of freedom. 048 * @param df degrees of freedom. 049 * @param g the underlying gamma distribution used to compute probabilities. 050 * @since 1.2 051 */ 052 public ChiSquaredDistributionImpl(double df, GammaDistribution g) { 053 super(); 054 setGamma(g); 055 setDegreesOfFreedom(df); 056 } 057 058 /** 059 * Modify the degrees of freedom. 060 * @param degreesOfFreedom the new degrees of freedom. 061 */ 062 public void setDegreesOfFreedom(double degreesOfFreedom) { 063 getGamma().setAlpha(degreesOfFreedom / 2.0); 064 } 065 066 /** 067 * Access the degrees of freedom. 068 * @return the degrees of freedom. 069 */ 070 public double getDegreesOfFreedom() { 071 return getGamma().getAlpha() * 2.0; 072 } 073 074 /** 075 * Return the probability density for a particular point. 076 * 077 * @param x The point at which the density should be computed. 078 * @return The pdf at point x. 079 */ 080 public double density(Double x) { 081 return gamma.density(x); 082 } 083 084 /** 085 * For this distribution, X, this method returns P(X < x). 086 * @param x the value at which the CDF is evaluated. 087 * @return CDF for this distribution. 088 * @throws MathException if the cumulative probability can not be 089 * computed due to convergence or other numerical errors. 090 */ 091 public double cumulativeProbability(double x) throws MathException { 092 return getGamma().cumulativeProbability(x); 093 } 094 095 /** 096 * For this distribution, X, this method returns the critical point x, such 097 * that P(X < x) = <code>p</code>. 098 * <p> 099 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p> 100 * 101 * @param p the desired probability 102 * @return x, such that P(X < x) = <code>p</code> 103 * @throws MathException if the inverse cumulative probability can not be 104 * computed due to convergence or other numerical errors. 105 * @throws IllegalArgumentException if <code>p</code> is not a valid 106 * probability. 107 */ 108 @Override 109 public double inverseCumulativeProbability(final double p) 110 throws MathException { 111 if (p == 0) { 112 return 0d; 113 } 114 if (p == 1) { 115 return Double.POSITIVE_INFINITY; 116 } 117 return super.inverseCumulativeProbability(p); 118 } 119 120 /** 121 * Access the domain value lower bound, based on <code>p</code>, used to 122 * bracket a CDF root. This method is used by 123 * {@link #inverseCumulativeProbability(double)} to find critical values. 124 * 125 * @param p the desired probability for the critical value 126 * @return domain value lower bound, i.e. 127 * P(X < <i>lower bound</i>) < <code>p</code> 128 */ 129 @Override 130 protected double getDomainLowerBound(double p) { 131 return Double.MIN_VALUE * getGamma().getBeta(); 132 } 133 134 /** 135 * Access the domain value upper bound, based on <code>p</code>, used to 136 * bracket a CDF root. This method is used by 137 * {@link #inverseCumulativeProbability(double)} to find critical values. 138 * 139 * @param p the desired probability for the critical value 140 * @return domain value upper bound, i.e. 141 * P(X < <i>upper bound</i>) > <code>p</code> 142 */ 143 @Override 144 protected double getDomainUpperBound(double p) { 145 // NOTE: chi squared is skewed to the left 146 // NOTE: therefore, P(X < μ) > .5 147 148 double ret; 149 150 if (p < .5) { 151 // use mean 152 ret = getDegreesOfFreedom(); 153 } else { 154 // use max 155 ret = Double.MAX_VALUE; 156 } 157 158 return ret; 159 } 160 161 /** 162 * Access the initial domain value, based on <code>p</code>, used to 163 * bracket a CDF root. This method is used by 164 * {@link #inverseCumulativeProbability(double)} to find critical values. 165 * 166 * @param p the desired probability for the critical value 167 * @return initial domain value 168 */ 169 @Override 170 protected double getInitialDomain(double p) { 171 // NOTE: chi squared is skewed to the left 172 // NOTE: therefore, P(X < μ) > .5 173 174 double ret; 175 176 if (p < .5) { 177 // use 1/2 mean 178 ret = getDegreesOfFreedom() * .5; 179 } else { 180 // use mean 181 ret = getDegreesOfFreedom(); 182 } 183 184 return ret; 185 } 186 187 /** 188 * Modify the underlying gamma distribution. The caller is responsible for 189 * insuring the gamma distribution has the proper parameter settings. 190 * @param g the new distribution. 191 * @since 1.2 made public 192 */ 193 public void setGamma(GammaDistribution g) { 194 this.gamma = g; 195 196 } 197 198 /** 199 * Access the Gamma distribution. 200 * @return the internal Gamma distribution. 201 */ 202 private GammaDistribution getGamma() { 203 return gamma; 204 } 205 }