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 }