View Javadoc
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 &gt; 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 }