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 &lt; 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 &lt; 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 &lt; <i>lower bound</i>) &lt; <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 &lt; <i>upper bound</i>) &gt; <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 < &mu;) > .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 < &mu;) > .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    }