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 */ 017package org.apache.commons.math3.distribution; 018 019import org.apache.commons.math3.exception.NotStrictlyPositiveException; 020import org.apache.commons.math3.exception.util.LocalizedFormats; 021import org.apache.commons.math3.random.RandomGenerator; 022import org.apache.commons.math3.random.Well19937c; 023import org.apache.commons.math3.special.Gamma; 024import org.apache.commons.math3.util.FastMath; 025 026/** 027 * Implementation of the Gamma distribution. 028 * 029 * @see <a href="http://en.wikipedia.org/wiki/Gamma_distribution">Gamma distribution (Wikipedia)</a> 030 * @see <a href="http://mathworld.wolfram.com/GammaDistribution.html">Gamma distribution (MathWorld)</a> 031 */ 032public class GammaDistribution extends AbstractRealDistribution { 033 /** 034 * Default inverse cumulative probability accuracy. 035 * @since 2.1 036 */ 037 public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9; 038 /** Serializable version identifier. */ 039 private static final long serialVersionUID = 20120524L; 040 /** The shape parameter. */ 041 private final double shape; 042 /** The scale parameter. */ 043 private final double scale; 044 /** 045 * The constant value of {@code shape + g + 0.5}, where {@code g} is the 046 * Lanczos constant {@link Gamma#LANCZOS_G}. 047 */ 048 private final double shiftedShape; 049 /** 050 * The constant value of 051 * {@code shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape)}, 052 * where {@code L(shape)} is the Lanczos approximation returned by 053 * {@link Gamma#lanczos(double)}. This prefactor is used in 054 * {@link #density(double)}, when no overflow occurs with the natural 055 * calculation. 056 */ 057 private final double densityPrefactor1; 058 /** 059 * The constant value of 060 * {@code log(shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape))}, 061 * where {@code L(shape)} is the Lanczos approximation returned by 062 * {@link Gamma#lanczos(double)}. This prefactor is used in 063 * {@link #logDensity(double)}, when no overflow occurs with the natural 064 * calculation. 065 */ 066 private final double logDensityPrefactor1; 067 /** 068 * The constant value of 069 * {@code shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape)}, 070 * where {@code L(shape)} is the Lanczos approximation returned by 071 * {@link Gamma#lanczos(double)}. This prefactor is used in 072 * {@link #density(double)}, when overflow occurs with the natural 073 * calculation. 074 */ 075 private final double densityPrefactor2; 076 /** 077 * The constant value of 078 * {@code log(shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape))}, 079 * where {@code L(shape)} is the Lanczos approximation returned by 080 * {@link Gamma#lanczos(double)}. This prefactor is used in 081 * {@link #logDensity(double)}, when overflow occurs with the natural 082 * calculation. 083 */ 084 private final double logDensityPrefactor2; 085 /** 086 * Lower bound on {@code y = x / scale} for the selection of the computation 087 * method in {@link #density(double)}. For {@code y <= minY}, the natural 088 * calculation overflows. 089 */ 090 private final double minY; 091 /** 092 * Upper bound on {@code log(y)} ({@code y = x / scale}) for the selection 093 * of the computation method in {@link #density(double)}. For 094 * {@code log(y) >= maxLogY}, the natural calculation overflows. 095 */ 096 private final double maxLogY; 097 /** Inverse cumulative probability accuracy. */ 098 private final double solverAbsoluteAccuracy; 099 100 /** 101 * Creates a new gamma distribution with specified values of the shape and 102 * scale parameters. 103 * <p> 104 * <b>Note:</b> this constructor will implicitly create an instance of 105 * {@link Well19937c} as random generator to be used for sampling only (see 106 * {@link #sample()} and {@link #sample(int)}). In case no sampling is 107 * needed for the created distribution, it is advised to pass {@code null} 108 * as random generator via the appropriate constructors to avoid the 109 * additional initialisation overhead. 110 * 111 * @param shape the shape parameter 112 * @param scale the scale parameter 113 * @throws NotStrictlyPositiveException if {@code shape <= 0} or 114 * {@code scale <= 0}. 115 */ 116 public GammaDistribution(double shape, double scale) throws NotStrictlyPositiveException { 117 this(shape, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY); 118 } 119 120 /** 121 * Creates a new gamma distribution with specified values of the shape and 122 * scale parameters. 123 * <p> 124 * <b>Note:</b> this constructor will implicitly create an instance of 125 * {@link Well19937c} as random generator to be used for sampling only (see 126 * {@link #sample()} and {@link #sample(int)}). In case no sampling is 127 * needed for the created distribution, it is advised to pass {@code null} 128 * as random generator via the appropriate constructors to avoid the 129 * additional initialisation overhead. 130 * 131 * @param shape the shape parameter 132 * @param scale the scale parameter 133 * @param inverseCumAccuracy the maximum absolute error in inverse 134 * cumulative probability estimates (defaults to 135 * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}). 136 * @throws NotStrictlyPositiveException if {@code shape <= 0} or 137 * {@code scale <= 0}. 138 * @since 2.1 139 */ 140 public GammaDistribution(double shape, double scale, double inverseCumAccuracy) 141 throws NotStrictlyPositiveException { 142 this(new Well19937c(), shape, scale, inverseCumAccuracy); 143 } 144 145 /** 146 * Creates a Gamma distribution. 147 * 148 * @param rng Random number generator. 149 * @param shape the shape parameter 150 * @param scale the scale parameter 151 * @throws NotStrictlyPositiveException if {@code shape <= 0} or 152 * {@code scale <= 0}. 153 * @since 3.3 154 */ 155 public GammaDistribution(RandomGenerator rng, double shape, double scale) 156 throws NotStrictlyPositiveException { 157 this(rng, shape, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY); 158 } 159 160 /** 161 * Creates a Gamma distribution. 162 * 163 * @param rng Random number generator. 164 * @param shape the shape parameter 165 * @param scale the scale parameter 166 * @param inverseCumAccuracy the maximum absolute error in inverse 167 * cumulative probability estimates (defaults to 168 * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}). 169 * @throws NotStrictlyPositiveException if {@code shape <= 0} or 170 * {@code scale <= 0}. 171 * @since 3.1 172 */ 173 public GammaDistribution(RandomGenerator rng, 174 double shape, 175 double scale, 176 double inverseCumAccuracy) 177 throws NotStrictlyPositiveException { 178 super(rng); 179 180 if (shape <= 0) { 181 throw new NotStrictlyPositiveException(LocalizedFormats.SHAPE, shape); 182 } 183 if (scale <= 0) { 184 throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, scale); 185 } 186 187 this.shape = shape; 188 this.scale = scale; 189 this.solverAbsoluteAccuracy = inverseCumAccuracy; 190 this.shiftedShape = shape + Gamma.LANCZOS_G + 0.5; 191 final double aux = FastMath.E / (2.0 * FastMath.PI * shiftedShape); 192 this.densityPrefactor2 = shape * FastMath.sqrt(aux) / Gamma.lanczos(shape); 193 this.logDensityPrefactor2 = FastMath.log(shape) + 0.5 * FastMath.log(aux) - 194 FastMath.log(Gamma.lanczos(shape)); 195 this.densityPrefactor1 = this.densityPrefactor2 / scale * 196 FastMath.pow(shiftedShape, -shape) * 197 FastMath.exp(shape + Gamma.LANCZOS_G); 198 this.logDensityPrefactor1 = this.logDensityPrefactor2 - FastMath.log(scale) - 199 FastMath.log(shiftedShape) * shape + 200 shape + Gamma.LANCZOS_G; 201 this.minY = shape + Gamma.LANCZOS_G - FastMath.log(Double.MAX_VALUE); 202 this.maxLogY = FastMath.log(Double.MAX_VALUE) / (shape - 1.0); 203 } 204 205 /** 206 * Returns the shape parameter of {@code this} distribution. 207 * 208 * @return the shape parameter 209 * @deprecated as of version 3.1, {@link #getShape()} should be preferred. 210 * This method will be removed in version 4.0. 211 */ 212 @Deprecated 213 public double getAlpha() { 214 return shape; 215 } 216 217 /** 218 * Returns the shape parameter of {@code this} distribution. 219 * 220 * @return the shape parameter 221 * @since 3.1 222 */ 223 public double getShape() { 224 return shape; 225 } 226 227 /** 228 * Returns the scale parameter of {@code this} distribution. 229 * 230 * @return the scale parameter 231 * @deprecated as of version 3.1, {@link #getScale()} should be preferred. 232 * This method will be removed in version 4.0. 233 */ 234 @Deprecated 235 public double getBeta() { 236 return scale; 237 } 238 239 /** 240 * Returns the scale parameter of {@code this} distribution. 241 * 242 * @return the scale parameter 243 * @since 3.1 244 */ 245 public double getScale() { 246 return scale; 247 } 248 249 /** {@inheritDoc} */ 250 public double density(double x) { 251 /* The present method must return the value of 252 * 253 * 1 x a - x 254 * ---------- (-) exp(---) 255 * x Gamma(a) b b 256 * 257 * where a is the shape parameter, and b the scale parameter. 258 * Substituting the Lanczos approximation of Gamma(a) leads to the 259 * following expression of the density 260 * 261 * a e 1 y a 262 * - sqrt(------------------) ---- (-----------) exp(a - y + g), 263 * x 2 pi (a + g + 0.5) L(a) a + g + 0.5 264 * 265 * where y = x / b. The above formula is the "natural" computation, which 266 * is implemented when no overflow is likely to occur. If overflow occurs 267 * with the natural computation, the following identity is used. It is 268 * based on the BOOST library 269 * http://www.boost.org/doc/libs/1_35_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_gamma/igamma.html 270 * Formula (15) needs adaptations, which are detailed below. 271 * 272 * y a 273 * (-----------) exp(a - y + g) 274 * a + g + 0.5 275 * y - a - g - 0.5 y (g + 0.5) 276 * = exp(a log1pm(---------------) - ----------- + g), 277 * a + g + 0.5 a + g + 0.5 278 * 279 * where log1pm(z) = log(1 + z) - z. Therefore, the value to be 280 * returned is 281 * 282 * a e 1 283 * - sqrt(------------------) ---- 284 * x 2 pi (a + g + 0.5) L(a) 285 * y - a - g - 0.5 y (g + 0.5) 286 * * exp(a log1pm(---------------) - ----------- + g). 287 * a + g + 0.5 a + g + 0.5 288 */ 289 if (x < 0) { 290 return 0; 291 } 292 final double y = x / scale; 293 if ((y <= minY) || (FastMath.log(y) >= maxLogY)) { 294 /* 295 * Overflow. 296 */ 297 final double aux1 = (y - shiftedShape) / shiftedShape; 298 final double aux2 = shape * (FastMath.log1p(aux1) - aux1); 299 final double aux3 = -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape + 300 Gamma.LANCZOS_G + aux2; 301 return densityPrefactor2 / x * FastMath.exp(aux3); 302 } 303 /* 304 * Natural calculation. 305 */ 306 return densityPrefactor1 * FastMath.exp(-y) * FastMath.pow(y, shape - 1); 307 } 308 309 /** {@inheritDoc} **/ 310 @Override 311 public double logDensity(double x) { 312 /* 313 * see the comment in {@link #density(double)} for computation details 314 */ 315 if (x < 0) { 316 return Double.NEGATIVE_INFINITY; 317 } 318 final double y = x / scale; 319 if ((y <= minY) || (FastMath.log(y) >= maxLogY)) { 320 /* 321 * Overflow. 322 */ 323 final double aux1 = (y - shiftedShape) / shiftedShape; 324 final double aux2 = shape * (FastMath.log1p(aux1) - aux1); 325 final double aux3 = -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape + 326 Gamma.LANCZOS_G + aux2; 327 return logDensityPrefactor2 - FastMath.log(x) + aux3; 328 } 329 /* 330 * Natural calculation. 331 */ 332 return logDensityPrefactor1 - y + FastMath.log(y) * (shape - 1); 333 } 334 335 /** 336 * {@inheritDoc} 337 * 338 * The implementation of this method is based on: 339 * <ul> 340 * <li> 341 * <a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html"> 342 * Chi-Squared Distribution</a>, equation (9). 343 * </li> 344 * <li>Casella, G., & Berger, R. (1990). <i>Statistical Inference</i>. 345 * Belmont, CA: Duxbury Press. 346 * </li> 347 * </ul> 348 */ 349 public double cumulativeProbability(double x) { 350 double ret; 351 352 if (x <= 0) { 353 ret = 0; 354 } else { 355 ret = Gamma.regularizedGammaP(shape, x / scale); 356 } 357 358 return ret; 359 } 360 361 /** {@inheritDoc} */ 362 @Override 363 protected double getSolverAbsoluteAccuracy() { 364 return solverAbsoluteAccuracy; 365 } 366 367 /** 368 * {@inheritDoc} 369 * 370 * For shape parameter {@code alpha} and scale parameter {@code beta}, the 371 * mean is {@code alpha * beta}. 372 */ 373 public double getNumericalMean() { 374 return shape * scale; 375 } 376 377 /** 378 * {@inheritDoc} 379 * 380 * For shape parameter {@code alpha} and scale parameter {@code beta}, the 381 * variance is {@code alpha * beta^2}. 382 * 383 * @return {@inheritDoc} 384 */ 385 public double getNumericalVariance() { 386 return shape * scale * scale; 387 } 388 389 /** 390 * {@inheritDoc} 391 * 392 * The lower bound of the support is always 0 no matter the parameters. 393 * 394 * @return lower bound of the support (always 0) 395 */ 396 public double getSupportLowerBound() { 397 return 0; 398 } 399 400 /** 401 * {@inheritDoc} 402 * 403 * The upper bound of the support is always positive infinity 404 * no matter the parameters. 405 * 406 * @return upper bound of the support (always Double.POSITIVE_INFINITY) 407 */ 408 public double getSupportUpperBound() { 409 return Double.POSITIVE_INFINITY; 410 } 411 412 /** {@inheritDoc} */ 413 public boolean isSupportLowerBoundInclusive() { 414 return true; 415 } 416 417 /** {@inheritDoc} */ 418 public boolean isSupportUpperBoundInclusive() { 419 return false; 420 } 421 422 /** 423 * {@inheritDoc} 424 * 425 * The support of this distribution is connected. 426 * 427 * @return {@code true} 428 */ 429 public boolean isSupportConnected() { 430 return true; 431 } 432 433 /** 434 * <p>This implementation uses the following algorithms: </p> 435 * 436 * <p>For 0 < shape < 1: <br/> 437 * Ahrens, J. H. and Dieter, U., <i>Computer methods for 438 * sampling from gamma, beta, Poisson and binomial distributions.</i> 439 * Computing, 12, 223-246, 1974.</p> 440 * 441 * <p>For shape >= 1: <br/> 442 * Marsaglia and Tsang, <i>A Simple Method for Generating 443 * Gamma Variables.</i> ACM Transactions on Mathematical Software, 444 * Volume 26 Issue 3, September, 2000.</p> 445 * 446 * @return random value sampled from the Gamma(shape, scale) distribution 447 */ 448 @Override 449 public double sample() { 450 if (shape < 1) { 451 // [1]: p. 228, Algorithm GS 452 453 while (true) { 454 // Step 1: 455 final double u = random.nextDouble(); 456 final double bGS = 1 + shape / FastMath.E; 457 final double p = bGS * u; 458 459 if (p <= 1) { 460 // Step 2: 461 462 final double x = FastMath.pow(p, 1 / shape); 463 final double u2 = random.nextDouble(); 464 465 if (u2 > FastMath.exp(-x)) { 466 // Reject 467 continue; 468 } else { 469 return scale * x; 470 } 471 } else { 472 // Step 3: 473 474 final double x = -1 * FastMath.log((bGS - p) / shape); 475 final double u2 = random.nextDouble(); 476 477 if (u2 > FastMath.pow(x, shape - 1)) { 478 // Reject 479 continue; 480 } else { 481 return scale * x; 482 } 483 } 484 } 485 } 486 487 // Now shape >= 1 488 489 final double d = shape - 0.333333333333333333; 490 final double c = 1 / (3 * FastMath.sqrt(d)); 491 492 while (true) { 493 final double x = random.nextGaussian(); 494 final double v = (1 + c * x) * (1 + c * x) * (1 + c * x); 495 496 if (v <= 0) { 497 continue; 498 } 499 500 final double x2 = x * x; 501 final double u = random.nextDouble(); 502 503 // Squeeze 504 if (u < 1 - 0.0331 * x2 * x2) { 505 return scale * d * v; 506 } 507 508 if (FastMath.log(u) < 0.5 * x2 + d * (1 - v + FastMath.log(v))) { 509 return scale * d * v; 510 } 511 } 512 } 513}