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
018package org.apache.commons.statistics.distribution;
019
020import org.apache.commons.rng.UniformRandomProvider;
021import org.apache.commons.rng.sampling.distribution.ContinuousUniformSampler;
022
023/**
024 * Implementation of the uniform distribution.
025 *
026 * <p>The probability density function of \( X \) is:
027 *
028 * <p>\[ f(x; a, b) = \frac{1}{b-a} \]
029 *
030 * <p>for \( -\infty \lt a \lt b \lt \infty \) and
031 * \( x \in [a, b] \).
032 *
033 * @see <a href="https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)">
034 * Uniform distribution (Wikipedia)</a>
035 * @see <a href="https://mathworld.wolfram.com/UniformDistribution.html">
036 * Uniform distribution (MathWorld)</a>
037 */
038public final class UniformContinuousDistribution extends AbstractContinuousDistribution {
039    /** Lower bound of this distribution (inclusive). */
040    private final double lower;
041    /** Upper bound of this distribution (exclusive). */
042    private final double upper;
043    /** Range between the upper and lower bound of this distribution (cached for computations). */
044    private final double upperMinusLower;
045    /** Cache of the density. */
046    private final double pdf;
047    /** Cache of the log density. */
048    private final double logPdf;
049
050    /**
051     * @param lower Lower bound of this distribution (inclusive).
052     * @param upper Upper bound of this distribution (inclusive).
053     */
054    private UniformContinuousDistribution(double lower,
055                                          double upper) {
056        this.lower = lower;
057        this.upper = upper;
058        upperMinusLower = upper - lower;
059        pdf = 1.0 / upperMinusLower;
060        logPdf = -Math.log(upperMinusLower);
061    }
062
063    /**
064     * Creates a uniform continuous distribution.
065     *
066     * @param lower Lower bound of this distribution (inclusive).
067     * @param upper Upper bound of this distribution (inclusive).
068     * @return the distribution
069     * @throws IllegalArgumentException if {@code lower >= upper} or the range between the bounds
070     * is not finite
071     */
072    public static UniformContinuousDistribution of(double lower,
073                                                   double upper) {
074        if (lower >= upper) {
075            throw new DistributionException(DistributionException.INVALID_RANGE_LOW_GTE_HIGH,
076                                            lower, upper);
077        }
078        if (!Double.isFinite(upper - lower)) {
079            throw new DistributionException("Range %s is not finite", upper - lower);
080        }
081        return new UniformContinuousDistribution(lower, upper);
082    }
083
084    /** {@inheritDoc} */
085    @Override
086    public double density(double x) {
087        if (x < lower ||
088            x > upper) {
089            return 0;
090        }
091        return pdf;
092    }
093
094    /** {@inheritDoc} */
095    @Override
096    public double probability(double x0,
097                              double x1) {
098        if (x0 > x1) {
099            throw new DistributionException(DistributionException.INVALID_RANGE_LOW_GT_HIGH, x0, x1);
100        }
101        if (x0 >= upper || x1 <= lower) {
102            // (x0, x1] does not overlap [lower, upper]
103            return 0;
104        }
105
106        // x0 < upper
107        // x1 >= lower
108
109        // Find the range between x0 and x1 that is within [lower, upper].
110        final double l = Math.max(lower, x0);
111        final double u = Math.min(upper, x1);
112
113        return (u - l) / upperMinusLower;
114    }
115
116    /** {@inheritDoc} */
117    @Override
118    public double logDensity(double x) {
119        if (x < lower ||
120            x > upper) {
121            return Double.NEGATIVE_INFINITY;
122        }
123        return logPdf;
124    }
125
126    /** {@inheritDoc} */
127    @Override
128    public double cumulativeProbability(double x)  {
129        if (x <= lower) {
130            return 0;
131        }
132        if (x >= upper) {
133            return 1;
134        }
135        return (x - lower) / upperMinusLower;
136    }
137
138    /** {@inheritDoc} */
139    @Override
140    public double survivalProbability(double x) {
141        if (x <= lower) {
142            return 1;
143        }
144        if (x >= upper) {
145            return 0;
146        }
147        return (upper - x) / upperMinusLower;
148    }
149
150    /** {@inheritDoc} */
151    @Override
152    public double inverseCumulativeProbability(double p) {
153        ArgumentUtils.checkProbability(p);
154        // Avoid floating-point error for lower + p * (upper - lower) when p == 1.
155        return p == 1 ? upper : p * upperMinusLower + lower;
156    }
157
158    /** {@inheritDoc} */
159    @Override
160    public double inverseSurvivalProbability(double p) {
161        ArgumentUtils.checkProbability(p);
162        // Avoid floating-point error for upper - p * (upper - lower) when p == 1.
163        return p == 1 ? lower : upper - p * upperMinusLower;
164    }
165
166    /**
167     * {@inheritDoc}
168     *
169     * <p>For lower bound \( a \) and upper bound \( b \), the mean is \( \frac{1}{2} (a + b) \).
170     */
171    @Override
172    public double getMean() {
173        // Avoid overflow
174        return 0.5 * lower + 0.5 * upper;
175    }
176
177    /**
178     * {@inheritDoc}
179     *
180     * <p>For lower bound \( a \) and upper bound \( b \), the variance is \( \frac{1}{12} (b - a)^2 \).
181     */
182    @Override
183    public double getVariance() {
184        return upperMinusLower * upperMinusLower / 12;
185    }
186
187    /**
188     * {@inheritDoc}
189     *
190     * <p>The lower bound of the support is equal to the lower bound parameter
191     * of the distribution.
192     */
193    @Override
194    public double getSupportLowerBound() {
195        return lower;
196    }
197
198    /**
199     * {@inheritDoc}
200     *
201     * <p>The upper bound of the support is equal to the upper bound parameter
202     * of the distribution.
203     */
204    @Override
205    public double getSupportUpperBound() {
206        return upper;
207    }
208
209    /** {@inheritDoc} */
210    @Override
211    public ContinuousDistribution.Sampler createSampler(final UniformRandomProvider rng) {
212        // Uniform distribution sampler.
213        return ContinuousUniformSampler.of(rng, lower, upper)::sample;
214    }
215}