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.math.distribution;
018    
019    import java.io.Serializable;
020    
021    import org.apache.commons.math.MathException;
022    import org.apache.commons.math.MathRuntimeException;
023    
024    /**
025     * The default implementation of {@link ExponentialDistribution}.
026     *
027     * @version $Revision: 772119 $ $Date: 2009-05-06 05:43:28 -0400 (Wed, 06 May 2009) $
028     */
029    public class ExponentialDistributionImpl extends AbstractContinuousDistribution
030        implements ExponentialDistribution, Serializable {
031    
032        /** Serializable version identifier */
033        private static final long serialVersionUID = 2401296428283614780L;
034        
035        /** The mean of this distribution. */
036        private double mean;
037        
038        /**
039         * Create a exponential distribution with the given mean.
040         * @param mean mean of this distribution.
041         */
042        public ExponentialDistributionImpl(double mean) {
043            super();
044            setMean(mean);
045        }
046    
047        /**
048         * Modify the mean.
049         * @param mean the new mean.
050         * @throws IllegalArgumentException if <code>mean</code> is not positive.
051         */
052        public void setMean(double mean) {
053            if (mean <= 0.0) {
054                throw MathRuntimeException.createIllegalArgumentException(
055                      "mean must be positive ({0})", mean);
056            }
057            this.mean = mean;
058        }
059    
060        /**
061         * Access the mean.
062         * @return the mean.
063         */
064        public double getMean() {
065            return mean;
066        }
067    
068        /**
069         * Return the probability density for a particular point.
070         *
071         * @param x The point at which the density should be computed.
072         * @return The pdf at point x.
073         */
074        public double density(Double x) {
075            if (x < 0) {
076                return 0;
077            }
078            return Math.exp(-x / getMean()) / getMean();
079        }
080    
081        /**
082         * For this distribution, X, this method returns P(X &lt; x).
083         * 
084         * The implementation of this method is based on:
085         * <ul>
086         * <li>
087         * <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">
088         * Exponential Distribution</a>, equation (1).</li>
089         * </ul>
090         * 
091         * @param x the value at which the CDF is evaluated.
092         * @return CDF for this distribution.
093         * @throws MathException if the cumulative probability can not be
094         *            computed due to convergence or other numerical errors.
095         */
096        public double cumulativeProbability(double x) throws MathException{
097            double ret;
098            if (x <= 0.0) {
099                ret = 0.0;
100            } else {
101                ret = 1.0 - Math.exp(-x / getMean());
102            }
103            return ret;
104        }
105        
106        /**
107         * For this distribution, X, this method returns the critical point x, such
108         * that P(X &lt; x) = <code>p</code>.
109         * <p>
110         * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
111         * 
112         * @param p the desired probability
113         * @return x, such that P(X &lt; x) = <code>p</code>
114         * @throws MathException if the inverse cumulative probability can not be
115         *            computed due to convergence or other numerical errors.
116         * @throws IllegalArgumentException if p < 0 or p > 1.
117         */
118        @Override
119        public double inverseCumulativeProbability(double p) throws MathException {
120            double ret;
121            
122            if (p < 0.0 || p > 1.0) {
123                throw MathRuntimeException.createIllegalArgumentException(
124                      "{0} out of [{1}, {2}] range", p, 0.0, 1.0);
125            } else if (p == 1.0) {
126                ret = Double.POSITIVE_INFINITY;
127            } else {
128                ret = -getMean() * Math.log(1.0 - p);
129            }
130            
131            return ret;
132        }
133        
134        /**
135         * Access the domain value lower bound, based on <code>p</code>, used to
136         * bracket a CDF root.   
137         * 
138         * @param p the desired probability for the critical value
139         * @return domain value lower bound, i.e.
140         *         P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
141         */
142        @Override
143        protected double getDomainLowerBound(double p) {
144            return 0;
145        }
146        
147        /**
148         * Access the domain value upper bound, based on <code>p</code>, used to
149         * bracket a CDF root.   
150         * 
151         * @param p the desired probability for the critical value
152         * @return domain value upper bound, i.e.
153         *         P(X &lt; <i>upper bound</i>) &gt; <code>p</code> 
154         */
155        @Override
156        protected double getDomainUpperBound(double p) {
157            // NOTE: exponential is skewed to the left
158            // NOTE: therefore, P(X < &mu;) > .5
159    
160            if (p < .5) {
161                // use mean
162                return getMean();
163            } else {
164                // use max
165                return Double.MAX_VALUE;
166            }
167        }
168        
169        /**
170         * Access the initial domain value, based on <code>p</code>, used to
171         * bracket a CDF root.   
172         * 
173         * @param p the desired probability for the critical value
174         * @return initial domain value
175         */
176        @Override
177        protected double getInitialDomain(double p) {
178            // TODO: try to improve on this estimate
179            // TODO: what should really happen here is not derive from AbstractContinuousDistribution
180            // TODO: because the inverse cumulative distribution is simple.
181            // Exponential is skewed to the left, therefore, P(X < &mu;) > .5
182            if (p < .5) {
183                // use 1/2 mean
184                return getMean() * .5;
185            } else {
186                // use mean
187                return getMean();
188            }
189        }
190    }