1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.distribution.continuous; 23 24 import org.hipparchus.exception.LocalizedCoreFormats; 25 import org.hipparchus.exception.MathIllegalArgumentException; 26 import org.hipparchus.special.Gamma; 27 import org.hipparchus.util.FastMath; 28 29 /** 30 * Implementation of the Gamma distribution. 31 * 32 * @see <a href="http://en.wikipedia.org/wiki/Gamma_distribution">Gamma distribution (Wikipedia)</a> 33 * @see <a href="http://mathworld.wolfram.com/GammaDistribution.html">Gamma distribution (MathWorld)</a> 34 */ 35 public class GammaDistribution extends AbstractRealDistribution { 36 /** Serializable version identifier. */ 37 private static final long serialVersionUID = 20120524L; 38 /** The shape parameter. */ 39 private final double shape; 40 /** The scale parameter. */ 41 private final double scale; 42 /** 43 * The constant value of {@code shape + g + 0.5}, where {@code g} is the 44 * Lanczos constant {@link Gamma#LANCZOS_G}. 45 */ 46 private final double shiftedShape; 47 /** 48 * The constant value of 49 * {@code shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape)}, 50 * where {@code L(shape)} is the Lanczos approximation returned by 51 * {@link Gamma#lanczos(double)}. This prefactor is used in 52 * {@link #density(double)}, when no overflow occurs with the natural 53 * calculation. 54 */ 55 private final double densityPrefactor1; 56 /** 57 * The constant value of 58 * {@code log(shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape))}, 59 * where {@code L(shape)} is the Lanczos approximation returned by 60 * {@link Gamma#lanczos(double)}. This prefactor is used in 61 * {@link #logDensity(double)}, when no overflow occurs with the natural 62 * calculation. 63 */ 64 private final double logDensityPrefactor1; 65 /** 66 * The constant value of 67 * {@code shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape)}, 68 * where {@code L(shape)} is the Lanczos approximation returned by 69 * {@link Gamma#lanczos(double)}. This prefactor is used in 70 * {@link #density(double)}, when overflow occurs with the natural 71 * calculation. 72 */ 73 private final double densityPrefactor2; 74 /** 75 * The constant value of 76 * {@code log(shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape))}, 77 * where {@code L(shape)} is the Lanczos approximation returned by 78 * {@link Gamma#lanczos(double)}. This prefactor is used in 79 * {@link #logDensity(double)}, when overflow occurs with the natural 80 * calculation. 81 */ 82 private final double logDensityPrefactor2; 83 /** 84 * Lower bound on {@code y = x / scale} for the selection of the computation 85 * method in {@link #density(double)}. For {@code y <= minY}, the natural 86 * calculation overflows. 87 */ 88 private final double minY; 89 /** 90 * Upper bound on {@code log(y)} ({@code y = x / scale}) for the selection 91 * of the computation method in {@link #density(double)}. For 92 * {@code log(y) >= maxLogY}, the natural calculation overflows. 93 */ 94 private final double maxLogY; 95 96 /** 97 * Creates a new gamma distribution with specified values of the shape and 98 * scale parameters. 99 * 100 * @param shape the shape parameter 101 * @param scale the scale parameter 102 * @throws MathIllegalArgumentException if {@code shape <= 0} or 103 * {@code scale <= 0}. 104 */ 105 public GammaDistribution(double shape, double scale) throws MathIllegalArgumentException { 106 this(shape, scale, DEFAULT_SOLVER_ABSOLUTE_ACCURACY); 107 } 108 109 110 /** 111 * Creates a Gamma distribution. 112 * 113 * @param shape the shape parameter 114 * @param scale the scale parameter 115 * @param inverseCumAccuracy the maximum absolute error in inverse 116 * cumulative probability estimates (defaults to 117 * {@link #DEFAULT_SOLVER_ABSOLUTE_ACCURACY}). 118 * @throws MathIllegalArgumentException if {@code shape <= 0} or 119 * {@code scale <= 0}. 120 */ 121 public GammaDistribution(final double shape, 122 final double scale, 123 final double inverseCumAccuracy) 124 throws MathIllegalArgumentException { 125 super(inverseCumAccuracy); 126 127 if (shape <= 0) { 128 throw new MathIllegalArgumentException(LocalizedCoreFormats.SHAPE, shape); 129 } 130 if (scale <= 0) { 131 throw new MathIllegalArgumentException(LocalizedCoreFormats.SCALE, scale); 132 } 133 134 this.shape = shape; 135 this.scale = scale; 136 this.shiftedShape = shape + Gamma.LANCZOS_G + 0.5; 137 final double aux = FastMath.E / (2.0 * FastMath.PI * shiftedShape); 138 this.densityPrefactor2 = shape * FastMath.sqrt(aux) / Gamma.lanczos(shape); 139 this.logDensityPrefactor2 = FastMath.log(shape) + 0.5 * FastMath.log(aux) - 140 FastMath.log(Gamma.lanczos(shape)); 141 this.densityPrefactor1 = this.densityPrefactor2 / scale * 142 FastMath.pow(shiftedShape, -shape) * 143 FastMath.exp(shape + Gamma.LANCZOS_G); 144 this.logDensityPrefactor1 = this.logDensityPrefactor2 - FastMath.log(scale) - 145 FastMath.log(shiftedShape) * shape + 146 shape + Gamma.LANCZOS_G; 147 this.minY = shape + Gamma.LANCZOS_G - FastMath.log(Double.MAX_VALUE); 148 this.maxLogY = FastMath.log(Double.MAX_VALUE) / (shape - 1.0); 149 } 150 151 /** 152 * Returns the shape parameter of {@code this} distribution. 153 * 154 * @return the shape parameter 155 */ 156 public double getShape() { 157 return shape; 158 } 159 160 /** 161 * Returns the scale parameter of {@code this} distribution. 162 * 163 * @return the scale parameter 164 */ 165 public double getScale() { 166 return scale; 167 } 168 169 /** {@inheritDoc} */ 170 @Override 171 public double density(double x) { 172 /* The present method must return the value of 173 * 174 * 1 x a - x 175 * ---------- (-) exp(---) 176 * x Gamma(a) b b 177 * 178 * where a is the shape parameter, and b the scale parameter. 179 * Substituting the Lanczos approximation of Gamma(a) leads to the 180 * following expression of the density 181 * 182 * a e 1 y a 183 * - sqrt(------------------) ---- (-----------) exp(a - y + g), 184 * x 2 pi (a + g + 0.5) L(a) a + g + 0.5 185 * 186 * where y = x / b. The above formula is the "natural" computation, which 187 * is implemented when no overflow is likely to occur. If overflow occurs 188 * with the natural computation, the following identity is used. It is 189 * based on the BOOST library 190 * http://www.boost.org/doc/libs/1_35_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_gamma/igamma.html 191 * Formula (15) needs adaptations, which are detailed below. 192 * 193 * y a 194 * (-----------) exp(a - y + g) 195 * a + g + 0.5 196 * y - a - g - 0.5 y (g + 0.5) 197 * = exp(a log1pm(---------------) - ----------- + g), 198 * a + g + 0.5 a + g + 0.5 199 * 200 * where log1pm(z) = log(1 + z) - z. Therefore, the value to be 201 * returned is 202 * 203 * a e 1 204 * - sqrt(------------------) ---- 205 * x 2 pi (a + g + 0.5) L(a) 206 * y - a - g - 0.5 y (g + 0.5) 207 * * exp(a log1pm(---------------) - ----------- + g). 208 * a + g + 0.5 a + g + 0.5 209 */ 210 if (x < 0) { 211 return 0; 212 } 213 final double y = x / scale; 214 if ((y <= minY) || (FastMath.log(y) >= maxLogY)) { 215 /* 216 * Overflow. 217 */ 218 final double aux1 = (y - shiftedShape) / shiftedShape; 219 final double aux2 = shape * (FastMath.log1p(aux1) - aux1); 220 final double aux3 = -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape + 221 Gamma.LANCZOS_G + aux2; 222 return densityPrefactor2 / x * FastMath.exp(aux3); 223 } 224 /* 225 * Natural calculation. 226 */ 227 return densityPrefactor1 * FastMath.exp(-y) * FastMath.pow(y, shape - 1); 228 } 229 230 /** {@inheritDoc} **/ 231 @Override 232 public double logDensity(double x) { 233 /* 234 * see the comment in {@link #density(double)} for computation details 235 */ 236 if (x < 0) { 237 return Double.NEGATIVE_INFINITY; 238 } 239 final double y = x / scale; 240 if ((y <= minY) || (FastMath.log(y) >= maxLogY)) { 241 /* 242 * Overflow. 243 */ 244 final double aux1 = (y - shiftedShape) / shiftedShape; 245 final double aux2 = shape * (FastMath.log1p(aux1) - aux1); 246 final double aux3 = -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape + 247 Gamma.LANCZOS_G + aux2; 248 return logDensityPrefactor2 - FastMath.log(x) + aux3; 249 } 250 /* 251 * Natural calculation. 252 */ 253 return logDensityPrefactor1 - y + FastMath.log(y) * (shape - 1); 254 } 255 256 /** 257 * {@inheritDoc} 258 * 259 * The implementation of this method is based on: 260 * <ul> 261 * <li> 262 * <a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html"> 263 * Chi-Squared Distribution</a>, equation (9). 264 * </li> 265 * <li>Casella, G., & Berger, R. (1990). <i>Statistical Inference</i>. 266 * Belmont, CA: Duxbury Press. 267 * </li> 268 * </ul> 269 */ 270 @Override 271 public double cumulativeProbability(double x) { 272 double ret; 273 274 if (x <= 0) { 275 ret = 0; 276 } else { 277 ret = Gamma.regularizedGammaP(shape, x / scale); 278 } 279 280 return ret; 281 } 282 283 /** 284 * {@inheritDoc} 285 * 286 * For shape parameter {@code alpha} and scale parameter {@code beta}, the 287 * mean is {@code alpha * beta}. 288 */ 289 @Override 290 public double getNumericalMean() { 291 return shape * scale; 292 } 293 294 /** 295 * {@inheritDoc} 296 * 297 * For shape parameter {@code alpha} and scale parameter {@code beta}, the 298 * variance is {@code alpha * beta^2}. 299 * 300 * @return {@inheritDoc} 301 */ 302 @Override 303 public double getNumericalVariance() { 304 return shape * scale * scale; 305 } 306 307 /** 308 * {@inheritDoc} 309 * 310 * The lower bound of the support is always 0 no matter the parameters. 311 * 312 * @return lower bound of the support (always 0) 313 */ 314 @Override 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 Double.POSITIVE_INFINITY) 326 */ 327 @Override 328 public double getSupportUpperBound() { 329 return Double.POSITIVE_INFINITY; 330 } 331 332 /** 333 * {@inheritDoc} 334 * 335 * The support of this distribution is connected. 336 * 337 * @return {@code true} 338 */ 339 @Override 340 public boolean isSupportConnected() { 341 return true; 342 } 343 }