1 /*
2 * Copyright 2003-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.commons.math.distribution;
17
18 import java.io.Serializable;
19
20 import org.apache.commons.math.MathException;
21
22 /**
23 * The default implementation of {@link ChiSquaredDistribution}
24 *
25 * @version $Revision: 348519 $ $Date: 2005-11-23 12:12:18 -0700 (Wed, 23 Nov 2005) $
26 */
27 public class ChiSquaredDistributionImpl
28 extends AbstractContinuousDistribution
29 implements ChiSquaredDistribution, Serializable {
30
31 /** Serializable version identifier */
32 private static final long serialVersionUID = -8352658048349159782L;
33
34 /** Internal Gamma distribution. */
35 private GammaDistribution gamma;
36
37 /**
38 * Create a Chi-Squared distribution with the given degrees of freedom.
39 * @param degreesOfFreedom degrees of freedom.
40 */
41 public ChiSquaredDistributionImpl(double degreesOfFreedom) {
42 super();
43 setGamma(DistributionFactory.newInstance().createGammaDistribution(
44 degreesOfFreedom / 2.0, 2.0));
45 }
46
47 /**
48 * Modify the degrees of freedom.
49 * @param degreesOfFreedom the new degrees of freedom.
50 */
51 public void setDegreesOfFreedom(double degreesOfFreedom) {
52 getGamma().setAlpha(degreesOfFreedom / 2.0);
53 }
54
55 /**
56 * Access the degrees of freedom.
57 * @return the degrees of freedom.
58 */
59 public double getDegreesOfFreedom() {
60 return getGamma().getAlpha() * 2.0;
61 }
62
63 /**
64 * For this disbution, X, this method returns P(X < x).
65 * @param x the value at which the CDF is evaluated.
66 * @return CDF for this distribution.
67 * @throws MathException if the cumulative probability can not be
68 * computed due to convergence or other numerical errors.
69 */
70 public double cumulativeProbability(double x) throws MathException {
71 return getGamma().cumulativeProbability(x);
72 }
73
74 /**
75 * For this distribution, X, this method returns the critical point x, such
76 * that P(X < x) = <code>p</code>.
77 * <p>
78 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.
79 *
80 * @param p the desired probability
81 * @return x, such that P(X < x) = <code>p</code>
82 * @throws MathException if the inverse cumulative probability can not be
83 * computed due to convergence or other numerical errors.
84 * @throws IllegalArgumentException if <code>p</code> is not a valid
85 * probability.
86 */
87 public double inverseCumulativeProbability(final double p)
88 throws MathException {
89 if (p == 0) {
90 return 0d;
91 }
92 if (p == 1) {
93 return Double.POSITIVE_INFINITY;
94 }
95 return super.inverseCumulativeProbability(p);
96 }
97
98 /**
99 * Access the domain value lower bound, based on <code>p</code>, used to
100 * bracket a CDF root. This method is used by
101 * {@link #inverseCumulativeProbability(double)} to find critical values.
102 *
103 * @param p the desired probability for the critical value
104 * @return domain value lower bound, i.e.
105 * P(X < <i>lower bound</i>) < <code>p</code>
106 */
107 protected double getDomainLowerBound(double p) {
108 return Double.MIN_VALUE * getGamma().getBeta();
109 }
110
111 /**
112 * Access the domain value upper bound, based on <code>p</code>, used to
113 * bracket a CDF root. This method is used by
114 * {@link #inverseCumulativeProbability(double)} to find critical values.
115 *
116 * @param p the desired probability for the critical value
117 * @return domain value upper bound, i.e.
118 * P(X < <i>upper bound</i>) > <code>p</code>
119 */
120 protected double getDomainUpperBound(double p) {
121 // NOTE: chi squared is skewed to the left
122 // NOTE: therefore, P(X < μ) > .5
123
124 double ret;
125
126 if (p < .5) {
127 // use mean
128 ret = getDegreesOfFreedom();
129 } else {
130 // use max
131 ret = Double.MAX_VALUE;
132 }
133
134 return ret;
135 }
136
137 /**
138 * Access the initial domain value, based on <code>p</code>, used to
139 * bracket a CDF root. This method is used by
140 * {@link #inverseCumulativeProbability(double)} to find critical values.
141 *
142 * @param p the desired probability for the critical value
143 * @return initial domain value
144 */
145 protected double getInitialDomain(double p) {
146 // NOTE: chi squared is skewed to the left
147 // NOTE: therefore, P(X < μ) > .5
148
149 double ret;
150
151 if (p < .5) {
152 // use 1/2 mean
153 ret = getDegreesOfFreedom() * .5;
154 } else {
155 // use mean
156 ret = getDegreesOfFreedom();
157 }
158
159 return ret;
160 }
161
162 /**
163 * Modify the Gamma distribution.
164 * @param gamma the new distribution.
165 */
166 private void setGamma(GammaDistribution gamma) {
167 this.gamma = gamma;
168 }
169
170 /**
171 * Access the Gamma distribution.
172 * @return the internal Gamma distribution.
173 */
174 private GammaDistribution getGamma() {
175 return gamma;
176 }
177 }