Home | History | Annotate | Download | only in distribution
      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: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
     27  */
     28 public class ChiSquaredDistributionImpl
     29     extends AbstractContinuousDistribution
     30     implements ChiSquaredDistribution, Serializable  {
     31 
     32     /**
     33      * Default inverse cumulative probability accuracy
     34      * @since 2.1
     35      */
     36     public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
     37 
     38     /** Serializable version identifier */
     39     private static final long serialVersionUID = -8352658048349159782L;
     40 
     41     /** Internal Gamma distribution. */
     42     private GammaDistribution gamma;
     43 
     44     /** Inverse cumulative probability accuracy */
     45     private final double solverAbsoluteAccuracy;
     46 
     47     /**
     48      * Create a Chi-Squared distribution with the given degrees of freedom.
     49      * @param df degrees of freedom.
     50      */
     51     public ChiSquaredDistributionImpl(double df) {
     52         this(df, new GammaDistributionImpl(df / 2.0, 2.0));
     53     }
     54 
     55     /**
     56      * Create a Chi-Squared distribution with the given degrees of freedom.
     57      * @param df degrees of freedom.
     58      * @param g the underlying gamma distribution used to compute probabilities.
     59      * @since 1.2
     60      * @deprecated as of 2.1 (to avoid possibly inconsistent state, the
     61      * "GammaDistribution" will be instantiated internally)
     62      */
     63     @Deprecated
     64     public ChiSquaredDistributionImpl(double df, GammaDistribution g) {
     65         super();
     66         setGammaInternal(g);
     67         setDegreesOfFreedomInternal(df);
     68         solverAbsoluteAccuracy = DEFAULT_INVERSE_ABSOLUTE_ACCURACY;
     69     }
     70 
     71     /**
     72      * Create a Chi-Squared distribution with the given degrees of freedom and
     73      * inverse cumulative probability accuracy.
     74      * @param df degrees of freedom.
     75      * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
     76      * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
     77      * @since 2.1
     78      */
     79     public ChiSquaredDistributionImpl(double df, double inverseCumAccuracy) {
     80         super();
     81         gamma = new GammaDistributionImpl(df / 2.0, 2.0);
     82         setDegreesOfFreedomInternal(df);
     83         solverAbsoluteAccuracy = inverseCumAccuracy;
     84     }
     85 
     86     /**
     87      * Modify the degrees of freedom.
     88      * @param degreesOfFreedom the new degrees of freedom.
     89      * @deprecated as of 2.1 (class will become immutable in 3.0)
     90      */
     91     @Deprecated
     92     public void setDegreesOfFreedom(double degreesOfFreedom) {
     93         setDegreesOfFreedomInternal(degreesOfFreedom);
     94     }
     95     /**
     96      * Modify the degrees of freedom.
     97      * @param degreesOfFreedom the new degrees of freedom.
     98      */
     99     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 }
    325