    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 &lt; 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    }