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    package org.apache.commons.math3.distribution;
018    
019    import org.apache.commons.math3.exception.NotStrictlyPositiveException;
020    import org.apache.commons.math3.exception.OutOfRangeException;
021    import org.apache.commons.math3.exception.util.LocalizedFormats;
022    import org.apache.commons.math3.util.FastMath;
023    import org.apache.commons.math3.util.ArithmeticUtils;
024    import org.apache.commons.math3.util.ResizableDoubleArray;
025    import org.apache.commons.math3.random.RandomGenerator;
026    import org.apache.commons.math3.random.Well19937c;
027    
028    /**
029     * Implementation of the exponential distribution.
030     *
031     * @see <a href="http://en.wikipedia.org/wiki/Exponential_distribution">Exponential distribution (Wikipedia)</a>
032     * @see <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">Exponential distribution (MathWorld)</a>
033     * @version $Id: ExponentialDistribution.java 1416643 2012-12-03 19:37:14Z tn $
034     */
035    public class ExponentialDistribution extends AbstractRealDistribution {
036        /**
037         * Default inverse cumulative probability accuracy.
038         * @since 2.1
039         */
040        public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
041        /** Serializable version identifier */
042        private static final long serialVersionUID = 2401296428283614780L;
043        /**
044         * Used when generating Exponential samples.
045         * Table containing the constants
046         * q_i = sum_{j=1}^i (ln 2)^j/j! = ln 2 + (ln 2)^2/2 + ... + (ln 2)^i/i!
047         * until the largest representable fraction below 1 is exceeded.
048         *
049         * Note that
050         * 1 = 2 - 1 = exp(ln 2) - 1 = sum_{n=1}^infty (ln 2)^n / n!
051         * thus q_i -> 1 as i -> +inf,
052         * so the higher i, the closer to one we get (the series is not alternating).
053         *
054         * By trying, n = 16 in Java is enough to reach 1.0.
055         */
056        private static final double[] EXPONENTIAL_SA_QI;
057        /** The mean of this distribution. */
058        private final double mean;
059        /** Inverse cumulative probability accuracy. */
060        private final double solverAbsoluteAccuracy;
061    
062        /**
063         * Initialize tables.
064         */
065        static {
066            /**
067             * Filling EXPONENTIAL_SA_QI table.
068             * Note that we don't want qi = 0 in the table.
069             */
070            final double LN2 = FastMath.log(2);
071            double qi = 0;
072            int i = 1;
073    
074            /**
075             * ArithmeticUtils provides factorials up to 20, so let's use that
076             * limit together with Precision.EPSILON to generate the following
077             * code (a priori, we know that there will be 16 elements, but it is
078             * better to not hardcode it).
079             */
080            final ResizableDoubleArray ra = new ResizableDoubleArray(20);
081    
082            while (qi < 1) {
083                qi += FastMath.pow(LN2, i) / ArithmeticUtils.factorial(i);
084                ra.addElement(qi);
085                ++i;
086            }
087    
088            EXPONENTIAL_SA_QI = ra.getElements();
089        }
090    
091        /**
092         * Create an exponential distribution with the given mean.
093         * @param mean mean of this distribution.
094         */
095        public ExponentialDistribution(double mean) {
096            this(mean, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
097        }
098    
099        /**
100         * Create an exponential distribution with the given mean.
101         *
102         * @param mean Mean of this distribution.
103         * @param inverseCumAccuracy Maximum absolute error in inverse
104         * cumulative probability estimates (defaults to
105         * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
106         * @throws NotStrictlyPositiveException if {@code mean <= 0}.
107         * @since 2.1
108         */
109        public ExponentialDistribution(double mean, double inverseCumAccuracy) {
110            this(new Well19937c(), mean, inverseCumAccuracy);
111        }
112    
113        /**
114         * Creates an exponential distribution.
115         *
116         * @param rng Random number generator.
117         * @param mean Mean of this distribution.
118         * @param inverseCumAccuracy Maximum absolute error in inverse
119         * cumulative probability estimates (defaults to
120         * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
121         * @throws NotStrictlyPositiveException if {@code mean <= 0}.
122         * @since 3.1
123         */
124        public ExponentialDistribution(RandomGenerator rng,
125                                       double mean,
126                                       double inverseCumAccuracy)
127            throws NotStrictlyPositiveException {
128            super(rng);
129    
130            if (mean <= 0) {
131                throw new NotStrictlyPositiveException(LocalizedFormats.MEAN, mean);
132            }
133            this.mean = mean;
134            solverAbsoluteAccuracy = inverseCumAccuracy;
135        }
136    
137        /**
138         * Access the mean.
139         *
140         * @return the mean.
141         */
142        public double getMean() {
143            return mean;
144        }
145    
146        /** {@inheritDoc} */
147        public double density(double x) {
148            if (x < 0) {
149                return 0;
150            }
151            return FastMath.exp(-x / mean) / mean;
152        }
153    
154        /**
155         * {@inheritDoc}
156         *
157         * The implementation of this method is based on:
158         * <ul>
159         * <li>
160         * <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">
161         * Exponential Distribution</a>, equation (1).</li>
162         * </ul>
163         */
164        public double cumulativeProbability(double x)  {
165            double ret;
166            if (x <= 0.0) {
167                ret = 0.0;
168            } else {
169                ret = 1.0 - FastMath.exp(-x / mean);
170            }
171            return ret;
172        }
173    
174        /**
175         * {@inheritDoc}
176         *
177         * Returns {@code 0} when {@code p= = 0} and
178         * {@code Double.POSITIVE_INFINITY} when {@code p == 1}.
179         */
180        @Override
181        public double inverseCumulativeProbability(double p) throws OutOfRangeException {
182            double ret;
183    
184            if (p < 0.0 || p > 1.0) {
185                throw new OutOfRangeException(p, 0.0, 1.0);
186            } else if (p == 1.0) {
187                ret = Double.POSITIVE_INFINITY;
188            } else {
189                ret = -mean * FastMath.log(1.0 - p);
190            }
191    
192            return ret;
193        }
194    
195        /**
196         * {@inheritDoc}
197         *
198         * <p><strong>Algorithm Description</strong>: this implementation uses the
199         * <a href="http://www.jesus.ox.ac.uk/~clifford/a5/chap1/node5.html">
200         * Inversion Method</a> to generate exponentially distributed random values
201         * from uniform deviates.</p>
202         *
203         * @return a random value.
204         * @since 2.2
205         */
206        @Override
207        public double sample() {
208            // Step 1:
209            double a = 0;
210            double u = random.nextDouble();
211    
212            // Step 2 and 3:
213            while (u < 0.5) {
214                a += EXPONENTIAL_SA_QI[0];
215                u *= 2;
216            }
217    
218            // Step 4 (now u >= 0.5):
219            u += u - 1;
220    
221            // Step 5:
222            if (u <= EXPONENTIAL_SA_QI[0]) {
223                return mean * (a + u);
224            }
225    
226            // Step 6:
227            int i = 0; // Should be 1, be we iterate before it in while using 0
228            double u2 = random.nextDouble();
229            double umin = u2;
230    
231            // Step 7 and 8:
232            do {
233                ++i;
234                u2 = random.nextDouble();
235    
236                if (u2 < umin) {
237                    umin = u2;
238                }
239    
240                // Step 8:
241            } while (u > EXPONENTIAL_SA_QI[i]); // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1
242    
243            return mean * (a + umin * EXPONENTIAL_SA_QI[0]);
244        }
245    
246        /** {@inheritDoc} */
247        @Override
248        protected double getSolverAbsoluteAccuracy() {
249            return solverAbsoluteAccuracy;
250        }
251    
252        /**
253         * {@inheritDoc}
254         *
255         * For mean parameter {@code k}, the mean is {@code k}.
256         */
257        public double getNumericalMean() {
258            return getMean();
259        }
260    
261        /**
262         * {@inheritDoc}
263         *
264         * For mean parameter {@code k}, the variance is {@code k^2}.
265         */
266        public double getNumericalVariance() {
267            final double m = getMean();
268            return m * m;
269        }
270    
271        /**
272         * {@inheritDoc}
273         *
274         * The lower bound of the support is always 0 no matter the mean parameter.
275         *
276         * @return lower bound of the support (always 0)
277         */
278        public double getSupportLowerBound() {
279            return 0;
280        }
281    
282        /**
283         * {@inheritDoc}
284         *
285         * The upper bound of the support is always positive infinity
286         * no matter the mean parameter.
287         *
288         * @return upper bound of the support (always Double.POSITIVE_INFINITY)
289         */
290        public double getSupportUpperBound() {
291            return Double.POSITIVE_INFINITY;
292        }
293    
294        /** {@inheritDoc} */
295        public boolean isSupportLowerBoundInclusive() {
296            return true;
297        }
298    
299        /** {@inheritDoc} */
300        public boolean isSupportUpperBoundInclusive() {
301            return false;
302        }
303    
304        /**
305         * {@inheritDoc}
306         *
307         * The support of this distribution is connected.
308         *
309         * @return {@code true}
310         */
311        public boolean isSupportConnected() {
312            return true;
313        }
314    }