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.CombinatoricsUtils;
025import org.apache.commons.math3.util.FastMath;
026import org.apache.commons.math3.util.MathUtils;
027
028/**
029 * Implementation of the Poisson distribution.
030 *
031 * @see <a href="http://en.wikipedia.org/wiki/Poisson_distribution">Poisson distribution (Wikipedia)</a>
032 * @see <a href="http://mathworld.wolfram.com/PoissonDistribution.html">Poisson distribution (MathWorld)</a>
033 */
034public class PoissonDistribution extends AbstractIntegerDistribution {
035    /**
036     * Default maximum number of iterations for cumulative probability calculations.
037     * @since 2.1
038     */
039    public static final int DEFAULT_MAX_ITERATIONS = 10000000;
040    /**
041     * Default convergence criterion.
042     * @since 2.1
043     */
044    public static final double DEFAULT_EPSILON = 1e-12;
045    /** Serializable version identifier. */
046    private static final long serialVersionUID = -3349935121172596109L;
047    /** Distribution used to compute normal approximation. */
048    private final NormalDistribution normal;
049    /** Distribution needed for the {@link #sample()} method. */
050    private final ExponentialDistribution exponential;
051    /** Mean of the distribution. */
052    private final double mean;
053
054    /**
055     * Maximum number of iterations for cumulative probability. Cumulative
056     * probabilities are estimated using either Lanczos series approximation
057     * of {@link Gamma#regularizedGammaP(double, double, double, int)}
058     * or continued fraction approximation of
059     * {@link Gamma#regularizedGammaQ(double, double, double, int)}.
060     */
061    private final int maxIterations;
062
063    /** Convergence criterion for cumulative probability. */
064    private final double epsilon;
065
066    /**
067     * Creates a new Poisson distribution with specified mean.
068     * <p>
069     * <b>Note:</b> this constructor will implicitly create an instance of
070     * {@link Well19937c} as random generator to be used for sampling only (see
071     * {@link #sample()} and {@link #sample(int)}). In case no sampling is
072     * needed for the created distribution, it is advised to pass {@code null}
073     * as random generator via the appropriate constructors to avoid the
074     * additional initialisation overhead.
075     *
076     * @param p the Poisson mean
077     * @throws NotStrictlyPositiveException if {@code p <= 0}.
078     */
079    public PoissonDistribution(double p) throws NotStrictlyPositiveException {
080        this(p, DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS);
081    }
082
083    /**
084     * Creates a new Poisson distribution with specified mean, convergence
085     * criterion and maximum number of iterations.
086     * <p>
087     * <b>Note:</b> this constructor will implicitly create an instance of
088     * {@link Well19937c} as random generator to be used for sampling only (see
089     * {@link #sample()} and {@link #sample(int)}). In case no sampling is
090     * needed for the created distribution, it is advised to pass {@code null}
091     * as random generator via the appropriate constructors to avoid the
092     * additional initialisation overhead.
093     *
094     * @param p Poisson mean.
095     * @param epsilon Convergence criterion for cumulative probabilities.
096     * @param maxIterations the maximum number of iterations for cumulative
097     * probabilities.
098     * @throws NotStrictlyPositiveException if {@code p <= 0}.
099     * @since 2.1
100     */
101    public PoissonDistribution(double p, double epsilon, int maxIterations)
102    throws NotStrictlyPositiveException {
103        this(new Well19937c(), p, epsilon, maxIterations);
104    }
105
106    /**
107     * Creates a new Poisson distribution with specified mean, convergence
108     * criterion and maximum number of iterations.
109     *
110     * @param rng Random number generator.
111     * @param p Poisson mean.
112     * @param epsilon Convergence criterion for cumulative probabilities.
113     * @param maxIterations the maximum number of iterations for cumulative
114     * probabilities.
115     * @throws NotStrictlyPositiveException if {@code p <= 0}.
116     * @since 3.1
117     */
118    public PoissonDistribution(RandomGenerator rng,
119                               double p,
120                               double epsilon,
121                               int maxIterations)
122    throws NotStrictlyPositiveException {
123        super(rng);
124
125        if (p <= 0) {
126            throw new NotStrictlyPositiveException(LocalizedFormats.MEAN, p);
127        }
128        mean = p;
129        this.epsilon = epsilon;
130        this.maxIterations = maxIterations;
131
132        // Use the same RNG instance as the parent class.
133        normal = new NormalDistribution(rng, p, FastMath.sqrt(p),
134                                        NormalDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
135        exponential = new ExponentialDistribution(rng, 1,
136                                                  ExponentialDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
137    }
138
139    /**
140     * Creates a new Poisson distribution with the specified mean and
141     * convergence criterion.
142     *
143     * @param p Poisson mean.
144     * @param epsilon Convergence criterion for cumulative probabilities.
145     * @throws NotStrictlyPositiveException if {@code p <= 0}.
146     * @since 2.1
147     */
148    public PoissonDistribution(double p, double epsilon)
149    throws NotStrictlyPositiveException {
150        this(p, epsilon, DEFAULT_MAX_ITERATIONS);
151    }
152
153    /**
154     * Creates a new Poisson distribution with the specified mean and maximum
155     * number of iterations.
156     *
157     * @param p Poisson mean.
158     * @param maxIterations Maximum number of iterations for cumulative
159     * probabilities.
160     * @since 2.1
161     */
162    public PoissonDistribution(double p, int maxIterations) {
163        this(p, DEFAULT_EPSILON, maxIterations);
164    }
165
166    /**
167     * Get the mean for the distribution.
168     *
169     * @return the mean for the distribution.
170     */
171    public double getMean() {
172        return mean;
173    }
174
175    /** {@inheritDoc} */
176    public double probability(int x) {
177        final double logProbability = logProbability(x);
178        return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability);
179    }
180
181    /** {@inheritDoc} */
182    @Override
183    public double logProbability(int x) {
184        double ret;
185        if (x < 0 || x == Integer.MAX_VALUE) {
186            ret = Double.NEGATIVE_INFINITY;
187        } else if (x == 0) {
188            ret = -mean;
189        } else {
190            ret = -SaddlePointExpansion.getStirlingError(x) -
191                  SaddlePointExpansion.getDeviancePart(x, mean) -
192                  0.5 * FastMath.log(MathUtils.TWO_PI) - 0.5 * FastMath.log(x);
193        }
194        return ret;
195    }
196
197    /** {@inheritDoc} */
198    public double cumulativeProbability(int x) {
199        if (x < 0) {
200            return 0;
201        }
202        if (x == Integer.MAX_VALUE) {
203            return 1;
204        }
205        return Gamma.regularizedGammaQ((double) x + 1, mean, epsilon,
206                                       maxIterations);
207    }
208
209    /**
210     * Calculates the Poisson distribution function using a normal
211     * approximation. The {@code N(mean, sqrt(mean))} distribution is used
212     * to approximate the Poisson distribution. The computation uses
213     * "half-correction" (evaluating the normal distribution function at
214     * {@code x + 0.5}).
215     *
216     * @param x Upper bound, inclusive.
217     * @return the distribution function value calculated using a normal
218     * approximation.
219     */
220    public double normalApproximateProbability(int x)  {
221        // calculate the probability using half-correction
222        return normal.cumulativeProbability(x + 0.5);
223    }
224
225    /**
226     * {@inheritDoc}
227     *
228     * For mean parameter {@code p}, the mean is {@code p}.
229     */
230    public double getNumericalMean() {
231        return getMean();
232    }
233
234    /**
235     * {@inheritDoc}
236     *
237     * For mean parameter {@code p}, the variance is {@code p}.
238     */
239    public double getNumericalVariance() {
240        return getMean();
241    }
242
243    /**
244     * {@inheritDoc}
245     *
246     * The lower bound of the support is always 0 no matter the mean parameter.
247     *
248     * @return lower bound of the support (always 0)
249     */
250    public int getSupportLowerBound() {
251        return 0;
252    }
253
254    /**
255     * {@inheritDoc}
256     *
257     * The upper bound of the support is positive infinity,
258     * regardless of the parameter values. There is no integer infinity,
259     * so this method returns {@code Integer.MAX_VALUE}.
260     *
261     * @return upper bound of the support (always {@code Integer.MAX_VALUE} for
262     * positive infinity)
263     */
264    public int getSupportUpperBound() {
265        return Integer.MAX_VALUE;
266    }
267
268    /**
269     * {@inheritDoc}
270     *
271     * The support of this distribution is connected.
272     *
273     * @return {@code true}
274     */
275    public boolean isSupportConnected() {
276        return true;
277    }
278
279    /**
280     * {@inheritDoc}
281     * <p>
282     * <strong>Algorithm Description</strong>:
283     * <ul>
284     *  <li>For small means, uses simulation of a Poisson process
285     *   using Uniform deviates, as described
286     *   <a href="http://mathaa.epfl.ch/cours/PMMI2001/interactive/rng7.htm"> here</a>.
287     *   The Poisson process (and hence value returned) is bounded by 1000 * mean.
288     *  </li>
289     *  <li>For large means, uses the rejection algorithm described in
290     *   <blockquote>
291     *    Devroye, Luc. (1981).<i>The Computer Generation of Poisson Random Variables</i><br>
292     *    <strong>Computing</strong> vol. 26 pp. 197-207.<br>
293     *   </blockquote>
294     *  </li>
295     * </ul>
296     * </p>
297     *
298     * @return a random value.
299     * @since 2.2
300     */
301    @Override
302    public int sample() {
303        return (int) FastMath.min(nextPoisson(mean), Integer.MAX_VALUE);
304    }
305
306    /**
307     * @param meanPoisson Mean of the Poisson distribution.
308     * @return the next sample.
309     */
310    private long nextPoisson(double meanPoisson) {
311        final double pivot = 40.0d;
312        if (meanPoisson < pivot) {
313            double p = FastMath.exp(-meanPoisson);
314            long n = 0;
315            double r = 1.0d;
316            double rnd = 1.0d;
317
318            while (n < 1000 * meanPoisson) {
319                rnd = random.nextDouble();
320                r *= rnd;
321                if (r >= p) {
322                    n++;
323                } else {
324                    return n;
325                }
326            }
327            return n;
328        } else {
329            final double lambda = FastMath.floor(meanPoisson);
330            final double lambdaFractional = meanPoisson - lambda;
331            final double logLambda = FastMath.log(lambda);
332            final double logLambdaFactorial = CombinatoricsUtils.factorialLog((int) lambda);
333            final long y2 = lambdaFractional < Double.MIN_VALUE ? 0 : nextPoisson(lambdaFractional);
334            final double delta = FastMath.sqrt(lambda * FastMath.log(32 * lambda / FastMath.PI + 1));
335            final double halfDelta = delta / 2;
336            final double twolpd = 2 * lambda + delta;
337            final double a1 = FastMath.sqrt(FastMath.PI * twolpd) * FastMath.exp(1 / (8 * lambda));
338            final double a2 = (twolpd / delta) * FastMath.exp(-delta * (1 + delta) / twolpd);
339            final double aSum = a1 + a2 + 1;
340            final double p1 = a1 / aSum;
341            final double p2 = a2 / aSum;
342            final double c1 = 1 / (8 * lambda);
343
344            double x = 0;
345            double y = 0;
346            double v = 0;
347            int a = 0;
348            double t = 0;
349            double qr = 0;
350            double qa = 0;
351            for (;;) {
352                final double u = random.nextDouble();
353                if (u <= p1) {
354                    final double n = random.nextGaussian();
355                    x = n * FastMath.sqrt(lambda + halfDelta) - 0.5d;
356                    if (x > delta || x < -lambda) {
357                        continue;
358                    }
359                    y = x < 0 ? FastMath.floor(x) : FastMath.ceil(x);
360                    final double e = exponential.sample();
361                    v = -e - (n * n / 2) + c1;
362                } else {
363                    if (u > p1 + p2) {
364                        y = lambda;
365                        break;
366                    } else {
367                        x = delta + (twolpd / delta) * exponential.sample();
368                        y = FastMath.ceil(x);
369                        v = -exponential.sample() - delta * (x + 1) / twolpd;
370                    }
371                }
372                a = x < 0 ? 1 : 0;
373                t = y * (y + 1) / (2 * lambda);
374                if (v < -t && a == 0) {
375                    y = lambda + y;
376                    break;
377                }
378                qr = t * ((2 * y + 1) / (6 * lambda) - 1);
379                qa = qr - (t * t) / (3 * (lambda + a * (y + 1)));
380                if (v < qa) {
381                    y = lambda + y;
382                    break;
383                }
384                if (v > qr) {
385                    continue;
386                }
387                if (v < y * logLambda - CombinatoricsUtils.factorialLog((int) (y + lambda)) + logLambdaFactorial) {
388                    y = lambda + y;
389                    break;
390                }
391            }
392            return y2 + (long) y;
393        }
394    }
395}