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: 617953 $ $Date: 2008-02-02 22:54:00 -0700 (Sat, 02 Feb 2008) $
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 * For this disbution, X, this method returns P(X < x).
76 * @param x the value at which the CDF is evaluated.
77 * @return CDF for this distribution.
78 * @throws MathException if the cumulative probability can not be
79 * computed due to convergence or other numerical errors.
80 */
81 public double cumulativeProbability(double x) throws MathException {
82 return getGamma().cumulativeProbability(x);
83 }
84
85 /**
86 * For this distribution, X, this method returns the critical point x, such
87 * that P(X < x) = <code>p</code>.
88 * <p>
89 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
90 *
91 * @param p the desired probability
92 * @return x, such that P(X < x) = <code>p</code>
93 * @throws MathException if the inverse cumulative probability can not be
94 * computed due to convergence or other numerical errors.
95 * @throws IllegalArgumentException if <code>p</code> is not a valid
96 * probability.
97 */
98 public double inverseCumulativeProbability(final double p)
99 throws MathException {
100 if (p == 0) {
101 return 0d;
102 }
103 if (p == 1) {
104 return Double.POSITIVE_INFINITY;
105 }
106 return super.inverseCumulativeProbability(p);
107 }
108
109 /**
110 * Access the domain value lower bound, based on <code>p</code>, used to
111 * bracket a CDF root. This method is used by
112 * {@link #inverseCumulativeProbability(double)} to find critical values.
113 *
114 * @param p the desired probability for the critical value
115 * @return domain value lower bound, i.e.
116 * P(X < <i>lower bound</i>) < <code>p</code>
117 */
118 protected double getDomainLowerBound(double p) {
119 return Double.MIN_VALUE * getGamma().getBeta();
120 }
121
122 /**
123 * Access the domain value upper bound, based on <code>p</code>, used to
124 * bracket a CDF root. This method is used by
125 * {@link #inverseCumulativeProbability(double)} to find critical values.
126 *
127 * @param p the desired probability for the critical value
128 * @return domain value upper bound, i.e.
129 * P(X < <i>upper bound</i>) > <code>p</code>
130 */
131 protected double getDomainUpperBound(double p) {
132 // NOTE: chi squared is skewed to the left
133 // NOTE: therefore, P(X < μ) > .5
134
135 double ret;
136
137 if (p < .5) {
138 // use mean
139 ret = getDegreesOfFreedom();
140 } else {
141 // use max
142 ret = Double.MAX_VALUE;
143 }
144
145 return ret;
146 }
147
148 /**
149 * Access the initial domain value, based on <code>p</code>, used to
150 * bracket a CDF root. This method is used by
151 * {@link #inverseCumulativeProbability(double)} to find critical values.
152 *
153 * @param p the desired probability for the critical value
154 * @return initial domain value
155 */
156 protected double getInitialDomain(double p) {
157 // NOTE: chi squared is skewed to the left
158 // NOTE: therefore, P(X < μ) > .5
159
160 double ret;
161
162 if (p < .5) {
163 // use 1/2 mean
164 ret = getDegreesOfFreedom() * .5;
165 } else {
166 // use mean
167 ret = getDegreesOfFreedom();
168 }
169
170 return ret;
171 }
172
173 /**
174 * Modify the underlying gamma distribution. The caller is responsible for
175 * insuring the gamma distribution has the proper parameter settings.
176 * @param g the new distribution.
177 * @since 1.2 made public
178 */
179 public void setGamma(GammaDistribution g) {
180 this.gamma = g;
181
182 }
183
184 /**
185 * Access the Gamma distribution.
186 * @return the internal Gamma distribution.
187 */
188 private GammaDistribution getGamma() {
189 return gamma;
190 }
191 }