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.statistics.distribution;
18
19 import org.apache.commons.rng.UniformRandomProvider;
20 import org.apache.commons.rng.sampling.distribution.ZigguratSampler;
21
22 /**
23 * Implementation of the exponential distribution.
24 *
25 * <p>The probability density function of \( X \) is:
26 *
27 * <p>\[ f(x; \mu) = \frac{1}{\mu} e^{-x / \mu} \]
28 *
29 * <p>for \( \mu > 0 \) the mean and
30 * \( x \in [0, \infty) \).
31 *
32 * <p>This implementation uses the scale parameter \( \mu \) which is the mean of the distribution.
33 * A common alternative parameterization uses the rate parameter \( \lambda \) which is the reciprocal
34 * of the mean. The distribution can be be created using \( \mu = \frac{1}{\lambda} \).
35 *
36 * @see <a href="https://en.wikipedia.org/wiki/Exponential_distribution">Exponential distribution (Wikipedia)</a>
37 * @see <a href="https://mathworld.wolfram.com/ExponentialDistribution.html">Exponential distribution (MathWorld)</a>
38 */
39 public final class ExponentialDistribution extends AbstractContinuousDistribution {
40 /** Support lower bound. */
41 private static final double SUPPORT_LO = 0;
42 /** Support upper bound. */
43 private static final double SUPPORT_HI = Double.POSITIVE_INFINITY;
44 /** The mean of this distribution. */
45 private final double mean;
46 /** The logarithm of the mean, stored to reduce computing time. */
47 private final double logMean;
48
49 /**
50 * @param mean Mean of this distribution.
51 */
52 private ExponentialDistribution(double mean) {
53 this.mean = mean;
54 logMean = Math.log(mean);
55 }
56
57 /**
58 * Creates an exponential distribution.
59 *
60 * @param mean Mean of this distribution. This is a scale parameter.
61 * @return the distribution
62 * @throws IllegalArgumentException if {@code mean <= 0}.
63 */
64 public static ExponentialDistribution of(double mean) {
65 if (mean <= 0) {
66 throw new DistributionException(DistributionException.NOT_STRICTLY_POSITIVE, mean);
67 }
68 return new ExponentialDistribution(mean);
69 }
70
71 /** {@inheritDoc} */
72 @Override
73 public double density(double x) {
74 if (x < SUPPORT_LO) {
75 return 0;
76 }
77 return Math.exp(-x / mean) / mean;
78 }
79
80 /** {@inheritDoc} **/
81 @Override
82 public double logDensity(double x) {
83 if (x < SUPPORT_LO) {
84 return Double.NEGATIVE_INFINITY;
85 }
86 return -x / mean - logMean;
87 }
88
89 /** {@inheritDoc} */
90 @Override
91 public double cumulativeProbability(double x) {
92 if (x <= SUPPORT_LO) {
93 return 0;
94 }
95 return -Math.expm1(-x / mean);
96 }
97
98 /** {@inheritDoc} */
99 @Override
100 public double survivalProbability(double x) {
101 if (x <= SUPPORT_LO) {
102 return 1;
103 }
104 return Math.exp(-x / mean);
105 }
106
107 /**
108 * {@inheritDoc}
109 *
110 * <p>Returns {@code 0} when {@code p == 0} and
111 * {@link Double#POSITIVE_INFINITY} when {@code p == 1}.
112 */
113 @Override
114 public double inverseCumulativeProbability(double p) {
115 ArgumentUtils.checkProbability(p);
116 if (p == 1) {
117 return Double.POSITIVE_INFINITY;
118 }
119 // Subtract from zero to prevent returning -0.0 for p=-0.0
120 return 0 - mean * Math.log1p(-p);
121 }
122
123 /**
124 * {@inheritDoc}
125 *
126 * <p>Returns {@code 0} when {@code p == 1} and
127 * {@link Double#POSITIVE_INFINITY} when {@code p == 0}.
128 */
129 @Override
130 public double inverseSurvivalProbability(double p) {
131 ArgumentUtils.checkProbability(p);
132 if (p == 0) {
133 return Double.POSITIVE_INFINITY;
134 }
135 // Subtract from zero to prevent returning -0.0 for p=1
136 return 0 - mean * Math.log(p);
137 }
138
139 /** {@inheritDoc} */
140 @Override
141 public double getMean() {
142 return mean;
143 }
144
145 /**
146 * {@inheritDoc}
147 *
148 * <p>For mean \( \mu \), the variance is \( \mu^2 \).
149 */
150 @Override
151 public double getVariance() {
152 return mean * mean;
153 }
154
155 /**
156 * {@inheritDoc}
157 *
158 * <p>The lower bound of the support is always 0.
159 *
160 * @return 0.
161 */
162 @Override
163 public double getSupportLowerBound() {
164 return SUPPORT_LO;
165 }
166
167 /**
168 * {@inheritDoc}
169 *
170 * <p>The upper bound of the support is always positive infinity.
171 *
172 * @return {@linkplain Double#POSITIVE_INFINITY positive infinity}.
173 */
174 @Override
175 public double getSupportUpperBound() {
176 return SUPPORT_HI;
177 }
178
179 /** {@inheritDoc} */
180 @Override
181 double getMedian() {
182 // Overridden for the probability(double, double) method.
183 // This is intentionally not a public method.
184 // ln(2) / rate = mean * ln(2)
185 return mean * Constants.LN_TWO;
186 }
187
188 /** {@inheritDoc} */
189 @Override
190 public ContinuousDistribution.Sampler createSampler(final UniformRandomProvider rng) {
191 // Exponential distribution sampler.
192 return ZigguratSampler.Exponential.of(rng, getMean())::sample;
193 }
194 }