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 018package org.apache.commons.math3.distribution; 019 020import org.apache.commons.math3.exception.NotStrictlyPositiveException; 021import org.apache.commons.math3.exception.OutOfRangeException; 022import org.apache.commons.math3.exception.util.LocalizedFormats; 023import org.apache.commons.math3.random.RandomGenerator; 024import org.apache.commons.math3.random.Well19937c; 025import org.apache.commons.math3.special.Gamma; 026import org.apache.commons.math3.util.FastMath; 027 028/** 029 * Implementation of the Weibull distribution. This implementation uses the 030 * two parameter form of the distribution defined by 031 * <a href="http://mathworld.wolfram.com/WeibullDistribution.html"> 032 * Weibull Distribution</a>, equations (1) and (2). 033 * 034 * @see <a href="http://en.wikipedia.org/wiki/Weibull_distribution">Weibull distribution (Wikipedia)</a> 035 * @see <a href="http://mathworld.wolfram.com/WeibullDistribution.html">Weibull distribution (MathWorld)</a> 036 * @since 1.1 (changed to concrete class in 3.0) 037 */ 038public class WeibullDistribution extends AbstractRealDistribution { 039 /** 040 * Default inverse cumulative probability accuracy. 041 * @since 2.1 042 */ 043 public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9; 044 /** Serializable version identifier. */ 045 private static final long serialVersionUID = 8589540077390120676L; 046 /** The shape parameter. */ 047 private final double shape; 048 /** The scale parameter. */ 049 private final double scale; 050 /** Inverse cumulative probability accuracy. */ 051 private final double solverAbsoluteAccuracy; 052 /** Cached numerical mean */ 053 private double numericalMean = Double.NaN; 054 /** Whether or not the numerical mean has been calculated */ 055 private boolean numericalMeanIsCalculated = false; 056 /** Cached numerical variance */ 057 private double numericalVariance = Double.NaN; 058 /** Whether or not the numerical variance has been calculated */ 059 private boolean numericalVarianceIsCalculated = false; 060 061 /** 062 * Create a Weibull distribution with the given shape and scale and a 063 * location equal to zero. 064 * <p> 065 * <b>Note:</b> this constructor will implicitly create an instance of 066 * {@link Well19937c} as random generator to be used for sampling only (see 067 * {@link #sample()} and {@link #sample(int)}). In case no sampling is 068 * needed for the created distribution, it is advised to pass {@code null} 069 * as random generator via the appropriate constructors to avoid the 070 * additional initialisation overhead. 071 * 072 * @param alpha Shape parameter. 073 * @param beta Scale parameter. 074 * @throws NotStrictlyPositiveException if {@code alpha <= 0} or 075 * {@code beta <= 0}. 076 */ 077 public WeibullDistribution(double alpha, double beta) 078 throws NotStrictlyPositiveException { 079 this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY); 080 } 081 082 /** 083 * Create a Weibull distribution with the given shape, scale and inverse 084 * cumulative probability accuracy and a location equal to zero. 085 * <p> 086 * <b>Note:</b> this constructor will implicitly create an instance of 087 * {@link Well19937c} as random generator to be used for sampling only (see 088 * {@link #sample()} and {@link #sample(int)}). In case no sampling is 089 * needed for the created distribution, it is advised to pass {@code null} 090 * as random generator via the appropriate constructors to avoid the 091 * additional initialisation overhead. 092 * 093 * @param alpha Shape parameter. 094 * @param beta Scale parameter. 095 * @param inverseCumAccuracy Maximum absolute error in inverse 096 * cumulative probability estimates 097 * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}). 098 * @throws NotStrictlyPositiveException if {@code alpha <= 0} or 099 * {@code beta <= 0}. 100 * @since 2.1 101 */ 102 public WeibullDistribution(double alpha, double beta, 103 double inverseCumAccuracy) { 104 this(new Well19937c(), alpha, beta, inverseCumAccuracy); 105 } 106 107 /** 108 * Creates a Weibull distribution. 109 * 110 * @param rng Random number generator. 111 * @param alpha Shape parameter. 112 * @param beta Scale parameter. 113 * @throws NotStrictlyPositiveException if {@code alpha <= 0} or {@code beta <= 0}. 114 * @since 3.3 115 */ 116 public WeibullDistribution(RandomGenerator rng, double alpha, double beta) 117 throws NotStrictlyPositiveException { 118 this(rng, alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY); 119 } 120 121 /** 122 * Creates a Weibull distribution. 123 * 124 * @param rng Random number generator. 125 * @param alpha Shape parameter. 126 * @param beta Scale parameter. 127 * @param inverseCumAccuracy Maximum absolute error in inverse 128 * cumulative probability estimates 129 * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}). 130 * @throws NotStrictlyPositiveException if {@code alpha <= 0} or {@code beta <= 0}. 131 * @since 3.1 132 */ 133 public WeibullDistribution(RandomGenerator rng, 134 double alpha, 135 double beta, 136 double inverseCumAccuracy) 137 throws NotStrictlyPositiveException { 138 super(rng); 139 140 if (alpha <= 0) { 141 throw new NotStrictlyPositiveException(LocalizedFormats.SHAPE, 142 alpha); 143 } 144 if (beta <= 0) { 145 throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, 146 beta); 147 } 148 scale = beta; 149 shape = alpha; 150 solverAbsoluteAccuracy = inverseCumAccuracy; 151 } 152 153 /** 154 * Access the shape parameter, {@code alpha}. 155 * 156 * @return the shape parameter, {@code alpha}. 157 */ 158 public double getShape() { 159 return shape; 160 } 161 162 /** 163 * Access the scale parameter, {@code beta}. 164 * 165 * @return the scale parameter, {@code beta}. 166 */ 167 public double getScale() { 168 return scale; 169 } 170 171 /** {@inheritDoc} */ 172 public double density(double x) { 173 if (x < 0) { 174 return 0; 175 } 176 177 final double xscale = x / scale; 178 final double xscalepow = FastMath.pow(xscale, shape - 1); 179 180 /* 181 * FastMath.pow(x / scale, shape) = 182 * FastMath.pow(xscale, shape) = 183 * FastMath.pow(xscale, shape - 1) * xscale 184 */ 185 final double xscalepowshape = xscalepow * xscale; 186 187 return (shape / scale) * xscalepow * FastMath.exp(-xscalepowshape); 188 } 189 190 /** {@inheritDoc} */ 191 @Override 192 public double logDensity(double x) { 193 if (x < 0) { 194 return Double.NEGATIVE_INFINITY; 195 } 196 197 final double xscale = x / scale; 198 final double logxscalepow = FastMath.log(xscale) * (shape - 1); 199 200 /* 201 * FastMath.pow(x / scale, shape) = 202 * FastMath.pow(xscale, shape) = 203 * FastMath.pow(xscale, shape - 1) * xscale 204 */ 205 final double xscalepowshape = FastMath.exp(logxscalepow) * xscale; 206 207 return FastMath.log(shape / scale) + logxscalepow - xscalepowshape; 208 } 209 210 /** {@inheritDoc} */ 211 public double cumulativeProbability(double x) { 212 double ret; 213 if (x <= 0.0) { 214 ret = 0.0; 215 } else { 216 ret = 1.0 - FastMath.exp(-FastMath.pow(x / scale, shape)); 217 } 218 return ret; 219 } 220 221 /** 222 * {@inheritDoc} 223 * 224 * Returns {@code 0} when {@code p == 0} and 225 * {@code Double.POSITIVE_INFINITY} when {@code p == 1}. 226 */ 227 @Override 228 public double inverseCumulativeProbability(double p) { 229 double ret; 230 if (p < 0.0 || p > 1.0) { 231 throw new OutOfRangeException(p, 0.0, 1.0); 232 } else if (p == 0) { 233 ret = 0.0; 234 } else if (p == 1) { 235 ret = Double.POSITIVE_INFINITY; 236 } else { 237 ret = scale * FastMath.pow(-FastMath.log1p(-p), 1.0 / shape); 238 } 239 return ret; 240 } 241 242 /** 243 * Return the absolute accuracy setting of the solver used to estimate 244 * inverse cumulative probabilities. 245 * 246 * @return the solver absolute accuracy. 247 * @since 2.1 248 */ 249 @Override 250 protected double getSolverAbsoluteAccuracy() { 251 return solverAbsoluteAccuracy; 252 } 253 254 /** 255 * {@inheritDoc} 256 * 257 * The mean is {@code scale * Gamma(1 + (1 / shape))}, where {@code Gamma()} 258 * is the Gamma-function. 259 */ 260 public double getNumericalMean() { 261 if (!numericalMeanIsCalculated) { 262 numericalMean = calculateNumericalMean(); 263 numericalMeanIsCalculated = true; 264 } 265 return numericalMean; 266 } 267 268 /** 269 * used by {@link #getNumericalMean()} 270 * 271 * @return the mean of this distribution 272 */ 273 protected double calculateNumericalMean() { 274 final double sh = getShape(); 275 final double sc = getScale(); 276 277 return sc * FastMath.exp(Gamma.logGamma(1 + (1 / sh))); 278 } 279 280 /** 281 * {@inheritDoc} 282 * 283 * The variance is {@code scale^2 * Gamma(1 + (2 / shape)) - mean^2} 284 * where {@code Gamma()} is the Gamma-function. 285 */ 286 public double getNumericalVariance() { 287 if (!numericalVarianceIsCalculated) { 288 numericalVariance = calculateNumericalVariance(); 289 numericalVarianceIsCalculated = true; 290 } 291 return numericalVariance; 292 } 293 294 /** 295 * used by {@link #getNumericalVariance()} 296 * 297 * @return the variance of this distribution 298 */ 299 protected double calculateNumericalVariance() { 300 final double sh = getShape(); 301 final double sc = getScale(); 302 final double mn = getNumericalMean(); 303 304 return (sc * sc) * FastMath.exp(Gamma.logGamma(1 + (2 / sh))) - 305 (mn * mn); 306 } 307 308 /** 309 * {@inheritDoc} 310 * 311 * The lower bound of the support is always 0 no matter the parameters. 312 * 313 * @return lower bound of the support (always 0) 314 */ 315 public double getSupportLowerBound() { 316 return 0; 317 } 318 319 /** 320 * {@inheritDoc} 321 * 322 * The upper bound of the support is always positive infinity 323 * no matter the parameters. 324 * 325 * @return upper bound of the support (always 326 * {@code Double.POSITIVE_INFINITY}) 327 */ 328 public double getSupportUpperBound() { 329 return Double.POSITIVE_INFINITY; 330 } 331 332 /** {@inheritDoc} */ 333 public boolean isSupportLowerBoundInclusive() { 334 return true; 335 } 336 337 /** {@inheritDoc} */ 338 public boolean isSupportUpperBoundInclusive() { 339 return false; 340 } 341 342 /** 343 * {@inheritDoc} 344 * 345 * The support of this distribution is connected. 346 * 347 * @return {@code true} 348 */ 349 public boolean isSupportConnected() { 350 return true; 351 } 352} 353