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.statistics.distribution;
018
019import org.apache.commons.rng.UniformRandomProvider;
020
021/**
022 * Implementation of the chi-squared distribution.
023 *
024 * <p>The probability density function of \( X \) is:
025 *
026 * <p>\[ f(x; k) = \frac{1}{2^{k/2} \Gamma(k/2)} x^{k/2 -1} e^{-x/2} \]
027 *
028 * <p>for \( k &gt; 0 \) the degrees of freedom,
029 * \( \Gamma(k/2) \) is the gamma function, and
030 * \( x \in [0, \infty) \).
031 *
032 * @see <a href="https://en.wikipedia.org/wiki/Chi-squared_distribution">Chi-squared distribution (Wikipedia)</a>
033 * @see <a href="https://mathworld.wolfram.com/Chi-SquaredDistribution.html">Chi-squared distribution (MathWorld)</a>
034 */
035public final class ChiSquaredDistribution extends AbstractContinuousDistribution {
036    /** Internal Gamma distribution. */
037    private final GammaDistribution gamma;
038
039    /**
040     * @param degreesOfFreedom Degrees of freedom.
041     */
042    private ChiSquaredDistribution(double degreesOfFreedom) {
043        gamma = GammaDistribution.of(degreesOfFreedom / 2, 2);
044    }
045
046    /**
047     * Creates a chi-squared distribution.
048     *
049     * @param degreesOfFreedom Degrees of freedom.
050     * @return the distribution
051     * @throws IllegalArgumentException if {@code degreesOfFreedom <= 0}.
052     */
053    public static ChiSquaredDistribution of(double degreesOfFreedom) {
054        return new ChiSquaredDistribution(degreesOfFreedom);
055    }
056
057    /**
058     * Gets the degrees of freedom parameter of this distribution.
059     *
060     * @return the degrees of freedom.
061     */
062    public double getDegreesOfFreedom() {
063        return gamma.getShape() * 2;
064    }
065
066    /** {@inheritDoc}
067     *
068     * <p>Returns the limit when {@code x = 0}:
069     * <ul>
070     * <li>{@code df < 2}: Infinity
071     * <li>{@code df == 2}: 1 / 2
072     * <li>{@code df > 2}: 0
073     * </ul>
074     */
075    @Override
076    public double density(double x) {
077        return gamma.density(x);
078    }
079
080    /** {@inheritDoc}
081     *
082     * <p>Returns the limit when {@code x = 0}:
083     * <ul>
084     * <li>{@code df < 2}: Infinity
085     * <li>{@code df == 2}: log(1 / 2)
086     * <li>{@code df > 2}: -Infinity
087     * </ul>
088     */
089    @Override
090    public double logDensity(double x) {
091        return gamma.logDensity(x);
092    }
093
094    /** {@inheritDoc} */
095    @Override
096    public double cumulativeProbability(double x)  {
097        return gamma.cumulativeProbability(x);
098    }
099
100    /** {@inheritDoc} */
101    @Override
102    public double survivalProbability(double x) {
103        return gamma.survivalProbability(x);
104    }
105
106    /** {@inheritDoc} */
107    @Override
108    public double inverseCumulativeProbability(double p) {
109        return gamma.inverseCumulativeProbability(p);
110    }
111
112    /** {@inheritDoc} */
113    @Override
114    public double inverseSurvivalProbability(double p) {
115        return gamma.inverseSurvivalProbability(p);
116    }
117
118    /**
119     * {@inheritDoc}
120     *
121     * <p>For \( k \) degrees of freedom, the mean is \( k \).
122     */
123    @Override
124    public double getMean() {
125        return getDegreesOfFreedom();
126    }
127
128    /**
129     * {@inheritDoc}
130     *
131     * <p>For \( k \) degrees of freedom, the variance is \( 2k \).
132     */
133    @Override
134    public double getVariance() {
135        return 2 * getDegreesOfFreedom();
136    }
137
138    /**
139     * {@inheritDoc}
140     *
141     * <p>The lower bound of the support is always 0.
142     *
143     * @return 0.
144     */
145    @Override
146    public double getSupportLowerBound() {
147        return 0;
148    }
149
150    /**
151     * {@inheritDoc}
152     *
153     * <p>The upper bound of the support is always positive infinity.
154     *
155     * @return {@linkplain Double#POSITIVE_INFINITY positive infinity}.
156     */
157    @Override
158    public double getSupportUpperBound() {
159        return Double.POSITIVE_INFINITY;
160    }
161
162    /** {@inheritDoc} */
163    @Override
164    public ContinuousDistribution.Sampler createSampler(final UniformRandomProvider rng) {
165        return gamma.createSampler(rng);
166    }
167}