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: 925900 $ $Date: 2010-03-21 17:10:07 -0400 (Sun, 21 Mar 2010) $
028 */
029 public class ExponentialDistributionImpl extends AbstractContinuousDistribution
030 implements ExponentialDistribution, Serializable {
031
032 /**
033 * Default inverse cumulative probability accuracy
034 * @since 2.1
035 */
036 public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
037
038 /** Serializable version identifier */
039 private static final long serialVersionUID = 2401296428283614780L;
040
041 /** The mean of this distribution. */
042 private double mean;
043
044 /** Inverse cumulative probability accuracy */
045 private final double solverAbsoluteAccuracy;
046
047 /**
048 * Create a exponential distribution with the given mean.
049 * @param mean mean of this distribution.
050 */
051 public ExponentialDistributionImpl(double mean) {
052 this(mean, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
053 }
054
055 /**
056 * Create a exponential distribution with the given mean.
057 * @param mean mean of this distribution.
058 * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
059 * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
060 * @since 2.1
061 */
062 public ExponentialDistributionImpl(double mean, double inverseCumAccuracy) {
063 super();
064 setMeanInternal(mean);
065 solverAbsoluteAccuracy = inverseCumAccuracy;
066 }
067
068 /**
069 * Modify the mean.
070 * @param mean the new mean.
071 * @throws IllegalArgumentException if <code>mean</code> is not positive.
072 * @deprecated as of 2.1 (class will become immutable in 3.0)
073 */
074 @Deprecated
075 public void setMean(double mean) {
076 setMeanInternal(mean);
077 }
078 /**
079 * Modify the mean.
080 * @param newMean the new mean.
081 * @throws IllegalArgumentException if <code>newMean</code> is not positive.
082 */
083 private void setMeanInternal(double newMean) {
084 if (newMean <= 0.0) {
085 throw MathRuntimeException.createIllegalArgumentException(
086 "mean must be positive ({0})", newMean);
087 }
088 this.mean = newMean;
089 }
090
091 /**
092 * Access the mean.
093 * @return the mean.
094 */
095 public double getMean() {
096 return mean;
097 }
098
099 /**
100 * Return the probability density for a particular point.
101 *
102 * @param x The point at which the density should be computed.
103 * @return The pdf at point x.
104 * @deprecated - use density(double)
105 */
106 public double density(Double x) {
107 return density(x.doubleValue());
108 }
109
110 /**
111 * Return the probability density for a particular point.
112 *
113 * @param x The point at which the density should be computed.
114 * @return The pdf at point x.
115 * @since 2.1
116 */
117 @Override
118 public double density(double x) {
119 if (x < 0) {
120 return 0;
121 }
122 return Math.exp(-x / mean) / mean;
123 }
124
125 /**
126 * For this distribution, X, this method returns P(X < x).
127 *
128 * The implementation of this method is based on:
129 * <ul>
130 * <li>
131 * <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">
132 * Exponential Distribution</a>, equation (1).</li>
133 * </ul>
134 *
135 * @param x the value at which the CDF is evaluated.
136 * @return CDF for this distribution.
137 * @throws MathException if the cumulative probability can not be
138 * computed due to convergence or other numerical errors.
139 */
140 public double cumulativeProbability(double x) throws MathException{
141 double ret;
142 if (x <= 0.0) {
143 ret = 0.0;
144 } else {
145 ret = 1.0 - Math.exp(-x / mean);
146 }
147 return ret;
148 }
149
150 /**
151 * For this distribution, X, this method returns the critical point x, such
152 * that P(X < x) = <code>p</code>.
153 * <p>
154 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
155 *
156 * @param p the desired probability
157 * @return x, such that P(X < x) = <code>p</code>
158 * @throws MathException if the inverse cumulative probability can not be
159 * computed due to convergence or other numerical errors.
160 * @throws IllegalArgumentException if p < 0 or p > 1.
161 */
162 @Override
163 public double inverseCumulativeProbability(double p) throws MathException {
164 double ret;
165
166 if (p < 0.0 || p > 1.0) {
167 throw MathRuntimeException.createIllegalArgumentException(
168 "{0} out of [{1}, {2}] range", p, 0.0, 1.0);
169 } else if (p == 1.0) {
170 ret = Double.POSITIVE_INFINITY;
171 } else {
172 ret = -mean * Math.log(1.0 - p);
173 }
174
175 return ret;
176 }
177
178 /**
179 * Access the domain value lower bound, based on <code>p</code>, used to
180 * bracket a CDF root.
181 *
182 * @param p the desired probability for the critical value
183 * @return domain value lower bound, i.e.
184 * P(X < <i>lower bound</i>) < <code>p</code>
185 */
186 @Override
187 protected double getDomainLowerBound(double p) {
188 return 0;
189 }
190
191 /**
192 * Access the domain value upper bound, based on <code>p</code>, used to
193 * bracket a CDF root.
194 *
195 * @param p the desired probability for the critical value
196 * @return domain value upper bound, i.e.
197 * P(X < <i>upper bound</i>) > <code>p</code>
198 */
199 @Override
200 protected double getDomainUpperBound(double p) {
201 // NOTE: exponential is skewed to the left
202 // NOTE: therefore, P(X < μ) > .5
203
204 if (p < .5) {
205 // use mean
206 return mean;
207 } else {
208 // use max
209 return Double.MAX_VALUE;
210 }
211 }
212
213 /**
214 * Access the initial domain value, based on <code>p</code>, used to
215 * bracket a CDF root.
216 *
217 * @param p the desired probability for the critical value
218 * @return initial domain value
219 */
220 @Override
221 protected double getInitialDomain(double p) {
222 // TODO: try to improve on this estimate
223 // TODO: what should really happen here is not derive from AbstractContinuousDistribution
224 // TODO: because the inverse cumulative distribution is simple.
225 // Exponential is skewed to the left, therefore, P(X < μ) > .5
226 if (p < .5) {
227 // use 1/2 mean
228 return mean * .5;
229 } else {
230 // use mean
231 return mean;
232 }
233 }
234
235 /**
236 * Return the absolute accuracy setting of the solver used to estimate
237 * inverse cumulative probabilities.
238 *
239 * @return the solver absolute accuracy
240 * @since 2.1
241 */
242 @Override
243 protected double getSolverAbsoluteAccuracy() {
244 return solverAbsoluteAccuracy;
245 }
246 }