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 */
017package org.apache.commons.math3.distribution;
018
019import org.apache.commons.math3.random.RandomGenerator;
020import org.apache.commons.math3.random.Well19937c;
021
022/**
023 * Implementation of the chi-squared distribution.
024 *
025 * @see <a href="http://en.wikipedia.org/wiki/Chi-squared_distribution">Chi-squared distribution (Wikipedia)</a>
026 * @see <a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html">Chi-squared Distribution (MathWorld)</a>
027 */
028public class ChiSquaredDistribution extends AbstractRealDistribution {
029    /**
030     * Default inverse cumulative probability accuracy
031     * @since 2.1
032     */
033    public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
034    /** Serializable version identifier */
035    private static final long serialVersionUID = -8352658048349159782L;
036    /** Internal Gamma distribution. */
037    private final GammaDistribution gamma;
038    /** Inverse cumulative probability accuracy */
039    private final double solverAbsoluteAccuracy;
040
041    /**
042     * Create a Chi-Squared distribution with the given degrees of freedom.
043     *
044     * @param degreesOfFreedom Degrees of freedom.
045     */
046    public ChiSquaredDistribution(double degreesOfFreedom) {
047        this(degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
048    }
049
050    /**
051     * Create a Chi-Squared distribution with the given degrees of freedom and
052     * inverse cumulative probability accuracy.
053     * <p>
054     * <b>Note:</b> this constructor will implicitly create an instance of
055     * {@link Well19937c} as random generator to be used for sampling only (see
056     * {@link #sample()} and {@link #sample(int)}). In case no sampling is
057     * needed for the created distribution, it is advised to pass {@code null}
058     * as random generator via the appropriate constructors to avoid the
059     * additional initialisation overhead.
060     *
061     * @param degreesOfFreedom Degrees of freedom.
062     * @param inverseCumAccuracy the maximum absolute error in inverse
063     * cumulative probability estimates (defaults to
064     * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
065     * @since 2.1
066     */
067    public ChiSquaredDistribution(double degreesOfFreedom,
068                                  double inverseCumAccuracy) {
069        this(new Well19937c(), degreesOfFreedom, inverseCumAccuracy);
070    }
071
072    /**
073     * Create a Chi-Squared distribution with the given degrees of freedom.
074     *
075     * @param rng Random number generator.
076     * @param degreesOfFreedom Degrees of freedom.
077     * @since 3.3
078     */
079    public ChiSquaredDistribution(RandomGenerator rng, double degreesOfFreedom) {
080        this(rng, degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
081    }
082
083    /**
084     * Create a Chi-Squared distribution with the given degrees of freedom and
085     * inverse cumulative probability accuracy.
086     *
087     * @param rng Random number generator.
088     * @param degreesOfFreedom Degrees of freedom.
089     * @param inverseCumAccuracy the maximum absolute error in inverse
090     * cumulative probability estimates (defaults to
091     * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
092     * @since 3.1
093     */
094    public ChiSquaredDistribution(RandomGenerator rng,
095                                  double degreesOfFreedom,
096                                  double inverseCumAccuracy) {
097        super(rng);
098
099        gamma = new GammaDistribution(degreesOfFreedom / 2, 2);
100        solverAbsoluteAccuracy = inverseCumAccuracy;
101    }
102
103    /**
104     * Access the number of degrees of freedom.
105     *
106     * @return the degrees of freedom.
107     */
108    public double getDegreesOfFreedom() {
109        return gamma.getShape() * 2.0;
110    }
111
112    /** {@inheritDoc} */
113    public double density(double x) {
114        return gamma.density(x);
115    }
116
117    /** {@inheritDoc} **/
118    @Override
119    public double logDensity(double x) {
120        return gamma.logDensity(x);
121    }
122
123    /** {@inheritDoc} */
124    public double cumulativeProbability(double x)  {
125        return gamma.cumulativeProbability(x);
126    }
127
128    /** {@inheritDoc} */
129    @Override
130    protected double getSolverAbsoluteAccuracy() {
131        return solverAbsoluteAccuracy;
132    }
133
134    /**
135     * {@inheritDoc}
136     *
137     * For {@code k} degrees of freedom, the mean is {@code k}.
138     */
139    public double getNumericalMean() {
140        return getDegreesOfFreedom();
141    }
142
143    /**
144     * {@inheritDoc}
145     *
146     * @return {@code 2 * k}, where {@code k} is the number of degrees of freedom.
147     */
148    public double getNumericalVariance() {
149        return 2 * getDegreesOfFreedom();
150    }
151
152    /**
153     * {@inheritDoc}
154     *
155     * The lower bound of the support is always 0 no matter the
156     * degrees of freedom.
157     *
158     * @return zero.
159     */
160    public double getSupportLowerBound() {
161        return 0;
162    }
163
164    /**
165     * {@inheritDoc}
166     *
167     * The upper bound of the support is always positive infinity no matter the
168     * degrees of freedom.
169     *
170     * @return {@code Double.POSITIVE_INFINITY}.
171     */
172    public double getSupportUpperBound() {
173        return Double.POSITIVE_INFINITY;
174    }
175
176    /** {@inheritDoc} */
177    public boolean isSupportLowerBoundInclusive() {
178        return true;
179    }
180
181    /** {@inheritDoc} */
182    public boolean isSupportUpperBoundInclusive() {
183        return false;
184    }
185
186    /**
187     * {@inheritDoc}
188     *
189     * The support of this distribution is connected.
190     *
191     * @return {@code true}
192     */
193    public boolean isSupportConnected() {
194        return true;
195    }
196}