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: 762087 $ $Date: 2009-04-05 10:20:18 -0400 (Sun, 05 Apr 2009) $
27 */
28 public class ChiSquaredDistributionImpl
29 extends AbstractContinuousDistribution
30 implements ChiSquaredDistribution, Serializable {
31
32 /** Serializable version identifier */
33 private static final long serialVersionUID = -8352658048349159782L;
34
35 /** Internal Gamma distribution. */
36 private GammaDistribution gamma;
37
38 /**
39 * Create a Chi-Squared distribution with the given degrees of freedom.
40 * @param df degrees of freedom.
41 */
42 public ChiSquaredDistributionImpl(double df) {
43 this(df, new GammaDistributionImpl(df / 2.0, 2.0));
44 }
45
46 /**
47 * Create a Chi-Squared distribution with the given degrees of freedom.
48 * @param df degrees of freedom.
49 * @param g the underlying gamma distribution used to compute probabilities.
50 * @since 1.2
51 */
52 public ChiSquaredDistributionImpl(double df, GammaDistribution g) {
53 super();
54 setGamma(g);
55 setDegreesOfFreedom(df);
56 }
57
58 /**
59 * Modify the degrees of freedom.
60 * @param degreesOfFreedom the new degrees of freedom.
61 */
62 public void setDegreesOfFreedom(double degreesOfFreedom) {
63 getGamma().setAlpha(degreesOfFreedom / 2.0);
64 }
65
66 /**
67 * Access the degrees of freedom.
68 * @return the degrees of freedom.
69 */
70 public double getDegreesOfFreedom() {
71 return getGamma().getAlpha() * 2.0;
72 }
73
74 /**
75 * Return the probability density for a particular point.
76 *
77 * @param x The point at which the density should be computed.
78 * @return The pdf at point x.
79 */
80 public double density(Double x) {
81 return gamma.density(x);
82 }
83
84 /**
85 * For this distribution, X, this method returns P(X < x).
86 * @param x the value at which the CDF is evaluated.
87 * @return CDF for this distribution.
88 * @throws MathException if the cumulative probability can not be
89 * computed due to convergence or other numerical errors.
90 */
91 public double cumulativeProbability(double x) throws MathException {
92 return getGamma().cumulativeProbability(x);
93 }
94
95 /**
96 * For this distribution, X, this method returns the critical point x, such
97 * that P(X < x) = <code>p</code>.
98 * <p>
99 * 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 }