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.exception.NumberIsTooSmallException;
020import org.apache.commons.math3.exception.util.LocalizedFormats;
021import org.apache.commons.math3.random.RandomGenerator;
022import org.apache.commons.math3.random.Well19937c;
023import org.apache.commons.math3.special.Beta;
024import org.apache.commons.math3.special.Gamma;
025import org.apache.commons.math3.util.FastMath;
026
027/**
028 * Implements the Beta distribution.
029 *
030 * @see <a href="http://en.wikipedia.org/wiki/Beta_distribution">Beta distribution</a>
031 * @since 2.0 (changed to concrete class in 3.0)
032 */
033public class BetaDistribution extends AbstractRealDistribution {
034    /**
035     * Default inverse cumulative probability accuracy.
036     * @since 2.1
037     */
038    public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
039    /** Serializable version identifier. */
040    private static final long serialVersionUID = -1221965979403477668L;
041    /** First shape parameter. */
042    private final double alpha;
043    /** Second shape parameter. */
044    private final double beta;
045    /** Normalizing factor used in density computations.
046     * updated whenever alpha or beta are changed.
047     */
048    private double z;
049    /** Inverse cumulative probability accuracy. */
050    private final double solverAbsoluteAccuracy;
051
052    /**
053     * Build a new instance.
054     * <p>
055     * <b>Note:</b> this constructor will implicitly create an instance of
056     * {@link Well19937c} as random generator to be used for sampling only (see
057     * {@link #sample()} and {@link #sample(int)}). In case no sampling is
058     * needed for the created distribution, it is advised to pass {@code null}
059     * as random generator via the appropriate constructors to avoid the
060     * additional initialisation overhead.
061     *
062     * @param alpha First shape parameter (must be positive).
063     * @param beta Second shape parameter (must be positive).
064     */
065    public BetaDistribution(double alpha, double beta) {
066        this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
067    }
068
069    /**
070     * Build a new instance.
071     * <p>
072     * <b>Note:</b> this constructor will implicitly create an instance of
073     * {@link Well19937c} as random generator to be used for sampling only (see
074     * {@link #sample()} and {@link #sample(int)}). In case no sampling is
075     * needed for the created distribution, it is advised to pass {@code null}
076     * as random generator via the appropriate constructors to avoid the
077     * additional initialisation overhead.
078     *
079     * @param alpha First shape parameter (must be positive).
080     * @param beta Second shape parameter (must be positive).
081     * @param inverseCumAccuracy Maximum absolute error in inverse
082     * cumulative probability estimates (defaults to
083     * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
084     * @since 2.1
085     */
086    public BetaDistribution(double alpha, double beta, double inverseCumAccuracy) {
087        this(new Well19937c(), alpha, beta, inverseCumAccuracy);
088    }
089
090    /**
091     * Creates a &beta; distribution.
092     *
093     * @param rng Random number generator.
094     * @param alpha First shape parameter (must be positive).
095     * @param beta Second shape parameter (must be positive).
096     * @since 3.3
097     */
098    public BetaDistribution(RandomGenerator rng, double alpha, double beta) {
099        this(rng, alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
100    }
101
102    /**
103     * Creates a &beta; distribution.
104     *
105     * @param rng Random number generator.
106     * @param alpha First shape parameter (must be positive).
107     * @param beta Second shape parameter (must be positive).
108     * @param inverseCumAccuracy Maximum absolute error in inverse
109     * cumulative probability estimates (defaults to
110     * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
111     * @since 3.1
112     */
113    public BetaDistribution(RandomGenerator rng,
114                            double alpha,
115                            double beta,
116                            double inverseCumAccuracy) {
117        super(rng);
118
119        this.alpha = alpha;
120        this.beta = beta;
121        z = Double.NaN;
122        solverAbsoluteAccuracy = inverseCumAccuracy;
123    }
124
125    /**
126     * Access the first shape parameter, {@code alpha}.
127     *
128     * @return the first shape parameter.
129     */
130    public double getAlpha() {
131        return alpha;
132    }
133
134    /**
135     * Access the second shape parameter, {@code beta}.
136     *
137     * @return the second shape parameter.
138     */
139    public double getBeta() {
140        return beta;
141    }
142
143    /** Recompute the normalization factor. */
144    private void recomputeZ() {
145        if (Double.isNaN(z)) {
146            z = Gamma.logGamma(alpha) + Gamma.logGamma(beta) - Gamma.logGamma(alpha + beta);
147        }
148    }
149
150    /** {@inheritDoc} */
151    public double density(double x) {
152        final double logDensity = logDensity(x);
153        return logDensity == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logDensity);
154    }
155
156    /** {@inheritDoc} **/
157    @Override
158    public double logDensity(double x) {
159        recomputeZ();
160        if (x < 0 || x > 1) {
161            return Double.NEGATIVE_INFINITY;
162        } else if (x == 0) {
163            if (alpha < 1) {
164                throw new NumberIsTooSmallException(LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA, alpha, 1, false);
165            }
166            return Double.NEGATIVE_INFINITY;
167        } else if (x == 1) {
168            if (beta < 1) {
169                throw new NumberIsTooSmallException(LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA, beta, 1, false);
170            }
171            return Double.NEGATIVE_INFINITY;
172        } else {
173            double logX = FastMath.log(x);
174            double log1mX = FastMath.log1p(-x);
175            return (alpha - 1) * logX + (beta - 1) * log1mX - z;
176        }
177    }
178
179    /** {@inheritDoc} */
180    public double cumulativeProbability(double x)  {
181        if (x <= 0) {
182            return 0;
183        } else if (x >= 1) {
184            return 1;
185        } else {
186            return Beta.regularizedBeta(x, alpha, beta);
187        }
188    }
189
190    /**
191     * Return the absolute accuracy setting of the solver used to estimate
192     * inverse cumulative probabilities.
193     *
194     * @return the solver absolute accuracy.
195     * @since 2.1
196     */
197    @Override
198    protected double getSolverAbsoluteAccuracy() {
199        return solverAbsoluteAccuracy;
200    }
201
202    /**
203     * {@inheritDoc}
204     *
205     * For first shape parameter {@code alpha} and second shape parameter
206     * {@code beta}, the mean is {@code alpha / (alpha + beta)}.
207     */
208    public double getNumericalMean() {
209        final double a = getAlpha();
210        return a / (a + getBeta());
211    }
212
213    /**
214     * {@inheritDoc}
215     *
216     * For first shape parameter {@code alpha} and second shape parameter
217     * {@code beta}, the variance is
218     * {@code (alpha * beta) / [(alpha + beta)^2 * (alpha + beta + 1)]}.
219     */
220    public double getNumericalVariance() {
221        final double a = getAlpha();
222        final double b = getBeta();
223        final double alphabetasum = a + b;
224        return (a * b) / ((alphabetasum * alphabetasum) * (alphabetasum + 1));
225    }
226
227    /**
228     * {@inheritDoc}
229     *
230     * The lower bound of the support is always 0 no matter the parameters.
231     *
232     * @return lower bound of the support (always 0)
233     */
234    public double getSupportLowerBound() {
235        return 0;
236    }
237
238    /**
239     * {@inheritDoc}
240     *
241     * The upper bound of the support is always 1 no matter the parameters.
242     *
243     * @return upper bound of the support (always 1)
244     */
245    public double getSupportUpperBound() {
246        return 1;
247    }
248
249    /** {@inheritDoc} */
250    public boolean isSupportLowerBoundInclusive() {
251        return false;
252    }
253
254    /** {@inheritDoc} */
255    public boolean isSupportUpperBoundInclusive() {
256        return false;
257    }
258
259    /**
260     * {@inheritDoc}
261     *
262     * The support of this distribution is connected.
263     *
264     * @return {@code true}
265     */
266    public boolean isSupportConnected() {
267        return true;
268    }
269}