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: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
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        @Deprecated
119        public double density(Double x) {
120            return density(x.doubleValue());
121        }
122    
123        /**
124         * Return the probability density for a particular point.
125         *
126         * @param x The point at which the density should be computed.
127         * @return The pdf at point x.
128         * @since 2.1
129         */
130        @Override
131        public double density(double x) {
132            return gamma.density(x);
133        }
134    
135        /**
136         * For this distribution, X, this method returns P(X < x).
137         * @param x the value at which the CDF is evaluated.
138         * @return CDF for this distribution.
139         * @throws MathException if the cumulative probability can not be
140         *            computed due to convergence or other numerical errors.
141         */
142        public double cumulativeProbability(double x) throws MathException {
143            return gamma.cumulativeProbability(x);
144        }
145    
146        /**
147         * For this distribution, X, this method returns the critical point x, such
148         * that P(X &lt; x) = <code>p</code>.
149         * <p>
150         * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
151         *
152         * @param p the desired probability
153         * @return x, such that P(X &lt; x) = <code>p</code>
154         * @throws MathException if the inverse cumulative probability can not be
155         *         computed due to convergence or other numerical errors.
156         * @throws IllegalArgumentException if <code>p</code> is not a valid
157         *         probability.
158         */
159        @Override
160        public double inverseCumulativeProbability(final double p)
161            throws MathException {
162            if (p == 0) {
163                return 0d;
164            }
165            if (p == 1) {
166                return Double.POSITIVE_INFINITY;
167            }
168            return super.inverseCumulativeProbability(p);
169        }
170    
171        /**
172         * Access the domain value lower bound, based on <code>p</code>, used to
173         * bracket a CDF root.  This method is used by
174         * {@link #inverseCumulativeProbability(double)} to find critical values.
175         *
176         * @param p the desired probability for the critical value
177         * @return domain value lower bound, i.e.
178         *         P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
179         */
180        @Override
181        protected double getDomainLowerBound(double p) {
182            return Double.MIN_VALUE * gamma.getBeta();
183        }
184    
185        /**
186         * Access the domain value upper bound, based on <code>p</code>, used to
187         * bracket a CDF root.  This method is used by
188         * {@link #inverseCumulativeProbability(double)} to find critical values.
189         *
190         * @param p the desired probability for the critical value
191         * @return domain value upper bound, i.e.
192         *         P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
193         */
194        @Override
195        protected double getDomainUpperBound(double p) {
196            // NOTE: chi squared is skewed to the left
197            // NOTE: therefore, P(X < &mu;) > .5
198    
199            double ret;
200    
201            if (p < .5) {
202                // use mean
203                ret = getDegreesOfFreedom();
204            } else {
205                // use max
206                ret = Double.MAX_VALUE;
207            }
208    
209            return ret;
210        }
211    
212        /**
213         * Access the initial domain value, based on <code>p</code>, used to
214         * bracket a CDF root.  This method is used by
215         * {@link #inverseCumulativeProbability(double)} to find critical values.
216         *
217         * @param p the desired probability for the critical value
218         * @return initial domain value
219         */
220        @Override
221        protected double getInitialDomain(double p) {
222            // NOTE: chi squared is skewed to the left
223            // NOTE: therefore, P(X < &mu;) > .5
224    
225            double ret;
226    
227            if (p < .5) {
228                // use 1/2 mean
229                ret = getDegreesOfFreedom() * .5;
230            } else {
231                // use mean
232                ret = getDegreesOfFreedom();
233            }
234    
235            return ret;
236        }
237    
238        /**
239         * Modify the underlying gamma distribution.  The caller is responsible for
240         * insuring the gamma distribution has the proper parameter settings.
241         * @param g the new distribution.
242         * @since 1.2 made public
243         * @deprecated as of 2.1 (class will become immutable in 3.0)
244         */
245        @Deprecated
246        public void setGamma(GammaDistribution g) {
247            setGammaInternal(g);
248        }
249        /**
250         * Modify the underlying gamma distribution.  The caller is responsible for
251         * insuring the gamma distribution has the proper parameter settings.
252         * @param g the new distribution.
253         * @since 1.2 made public
254         */
255        private void setGammaInternal(GammaDistribution g) {
256            this.gamma = g;
257    
258        }
259    
260    
261        /**
262         * Return the absolute accuracy setting of the solver used to estimate
263         * inverse cumulative probabilities.
264         *
265         * @return the solver absolute accuracy
266         * @since 2.1
267         */
268        @Override
269        protected double getSolverAbsoluteAccuracy() {
270            return solverAbsoluteAccuracy;
271        }
272    
273        /**
274         * Returns the lower bound of the support for the distribution.
275         *
276         * The lower bound of the support is always 0 no matter the
277         * degrees of freedom.
278         *
279         * @return lower bound of the support (always 0)
280         * @since 2.2
281         */
282        public double getSupportLowerBound() {
283            return 0;
284        }
285    
286        /**
287         * Returns the upper bound for the support for the distribution.
288         *
289         * The upper bound of the support is always positive infinity no matter the
290         * degrees of freedom.
291         *
292         * @return upper bound of the support (always Double.POSITIVE_INFINITY)
293         * @since 2.2
294         */
295        public double getSupportUpperBound() {
296            return Double.POSITIVE_INFINITY;
297        }
298    
299        /**
300         * Returns the mean of the distribution.
301         *
302         * For <code>k</code> degrees of freedom, the mean is
303         * <code>k</code>
304         *
305         * @return the mean
306         * @since 2.2
307         */
308        public double getNumericalMean() {
309            return getDegreesOfFreedom();
310        }
311    
312        /**
313         * Returns the variance of the distribution.
314         *
315         * For <code>k</code> degrees of freedom, the variance is
316         * <code>2 * k</code>
317         *
318         * @return the variance
319         * @since 2.2
320         */
321        public double getNumericalVariance() {
322            return 2*getDegreesOfFreedom();
323        }
324    }