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.discrete; 23 24 import org.hipparchus.distribution.continuous.NormalDistribution; 25 import org.hipparchus.exception.LocalizedCoreFormats; 26 import org.hipparchus.exception.MathIllegalArgumentException; 27 import org.hipparchus.special.Gamma; 28 import org.hipparchus.util.FastMath; 29 import org.hipparchus.util.MathUtils; 30 31 /** 32 * Implementation of the Poisson distribution. 33 * 34 * @see <a href="http://en.wikipedia.org/wiki/Poisson_distribution">Poisson distribution (Wikipedia)</a> 35 * @see <a href="http://mathworld.wolfram.com/PoissonDistribution.html">Poisson distribution (MathWorld)</a> 36 */ 37 public class PoissonDistribution extends AbstractIntegerDistribution { 38 /** Default maximum number of iterations for cumulative probability calculations. */ 39 public static final int DEFAULT_MAX_ITERATIONS = 10000000; 40 /** Default convergence criterion. */ 41 public static final double DEFAULT_EPSILON = 1e-12; 42 /** Serializable version identifier. */ 43 private static final long serialVersionUID = 20160320L; 44 /** Distribution used to compute normal approximation. */ 45 private final NormalDistribution normal; 46 /** Mean of the distribution. */ 47 private final double mean; 48 49 /** 50 * Maximum number of iterations for cumulative probability. Cumulative 51 * probabilities are estimated using either Lanczos series approximation 52 * of {@link Gamma#regularizedGammaP(double, double, double, int)} 53 * or continued fraction approximation of 54 * {@link Gamma#regularizedGammaQ(double, double, double, int)}. 55 */ 56 private final int maxIterations; 57 58 /** Convergence criterion for cumulative probability. */ 59 private final double epsilon; 60 61 /** 62 * Creates a new Poisson distribution with specified mean. 63 * 64 * @param p the Poisson mean 65 * @throws MathIllegalArgumentException if {@code p <= 0}. 66 */ 67 public PoissonDistribution(double p) throws MathIllegalArgumentException { 68 this(p, DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS); 69 } 70 71 /** 72 * Creates a new Poisson distribution with specified mean, convergence 73 * criterion and maximum number of iterations. 74 * 75 * @param p Poisson mean. 76 * @param epsilon Convergence criterion for cumulative probabilities. 77 * @param maxIterations the maximum number of iterations for cumulative 78 * probabilities. 79 * @throws MathIllegalArgumentException if {@code p <= 0}. 80 */ 81 public PoissonDistribution(double p, double epsilon, int maxIterations) 82 throws MathIllegalArgumentException { 83 if (p <= 0) { 84 throw new MathIllegalArgumentException(LocalizedCoreFormats.MEAN, p); 85 } 86 mean = p; 87 this.epsilon = epsilon; 88 this.maxIterations = maxIterations; 89 90 // Use the same RNG instance as the parent class. 91 normal = new NormalDistribution(p, FastMath.sqrt(p)); 92 } 93 94 /** 95 * Creates a new Poisson distribution with the specified mean and 96 * convergence criterion. 97 * 98 * @param p Poisson mean. 99 * @param epsilon Convergence criterion for cumulative probabilities. 100 * @throws MathIllegalArgumentException if {@code p <= 0}. 101 */ 102 public PoissonDistribution(double p, double epsilon) 103 throws MathIllegalArgumentException { 104 this(p, epsilon, DEFAULT_MAX_ITERATIONS); 105 } 106 107 /** 108 * Creates a new Poisson distribution with the specified mean and maximum 109 * number of iterations. 110 * 111 * @param p Poisson mean. 112 * @param maxIterations Maximum number of iterations for cumulative probabilities. 113 */ 114 public PoissonDistribution(double p, int maxIterations) { 115 this(p, DEFAULT_EPSILON, maxIterations); 116 } 117 118 /** 119 * Get the mean for the distribution. 120 * 121 * @return the mean for the distribution. 122 */ 123 public double getMean() { 124 return mean; 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public double probability(int x) { 130 final double logProbability = logProbability(x); 131 return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability); 132 } 133 134 /** {@inheritDoc} */ 135 @Override 136 public double logProbability(int x) { 137 double ret; 138 if (x < 0 || x == Integer.MAX_VALUE) { 139 ret = Double.NEGATIVE_INFINITY; 140 } else if (x == 0) { 141 ret = -mean; 142 } else { 143 ret = -SaddlePointExpansion.getStirlingError(x) - 144 SaddlePointExpansion.getDeviancePart(x, mean) - 145 0.5 * FastMath.log(MathUtils.TWO_PI) - 0.5 * FastMath.log(x); 146 } 147 return ret; 148 } 149 150 /** {@inheritDoc} */ 151 @Override 152 public double cumulativeProbability(int x) { 153 if (x < 0) { 154 return 0; 155 } 156 if (x == Integer.MAX_VALUE) { 157 return 1; 158 } 159 return Gamma.regularizedGammaQ((double) x + 1, mean, epsilon, 160 maxIterations); 161 } 162 163 /** 164 * Calculates the Poisson distribution function using a normal 165 * approximation. The {@code N(mean, sqrt(mean))} distribution is used 166 * to approximate the Poisson distribution. The computation uses 167 * "half-correction" (evaluating the normal distribution function at 168 * {@code x + 0.5}). 169 * 170 * @param x Upper bound, inclusive. 171 * @return the distribution function value calculated using a normal 172 * approximation. 173 */ 174 public double normalApproximateProbability(int x) { 175 // calculate the probability using half-correction 176 return normal.cumulativeProbability(x + 0.5); 177 } 178 179 /** 180 * {@inheritDoc} 181 * 182 * For mean parameter {@code p}, the mean is {@code p}. 183 */ 184 @Override 185 public double getNumericalMean() { 186 return getMean(); 187 } 188 189 /** 190 * {@inheritDoc} 191 * 192 * For mean parameter {@code p}, the variance is {@code p}. 193 */ 194 @Override 195 public double getNumericalVariance() { 196 return getMean(); 197 } 198 199 /** 200 * {@inheritDoc} 201 * 202 * The lower bound of the support is always 0 no matter the mean parameter. 203 * 204 * @return lower bound of the support (always 0) 205 */ 206 @Override 207 public int getSupportLowerBound() { 208 return 0; 209 } 210 211 /** 212 * {@inheritDoc} 213 * 214 * The upper bound of the support is positive infinity, 215 * regardless of the parameter values. There is no integer infinity, 216 * so this method returns {@code Integer.MAX_VALUE}. 217 * 218 * @return upper bound of the support (always {@code Integer.MAX_VALUE} for 219 * positive infinity) 220 */ 221 @Override 222 public int getSupportUpperBound() { 223 return Integer.MAX_VALUE; 224 } 225 226 /** 227 * {@inheritDoc} 228 * 229 * The support of this distribution is connected. 230 * 231 * @return {@code true} 232 */ 233 @Override 234 public boolean isSupportConnected() { 235 return true; 236 } 237 }