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 }