PoissonDistribution.java

  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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.distribution.discrete;

  22. import org.hipparchus.distribution.continuous.NormalDistribution;
  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.special.Gamma;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.MathUtils;

  28. /**
  29.  * Implementation of the Poisson distribution.
  30.  *
  31.  * @see <a href="http://en.wikipedia.org/wiki/Poisson_distribution">Poisson distribution (Wikipedia)</a>
  32.  * @see <a href="http://mathworld.wolfram.com/PoissonDistribution.html">Poisson distribution (MathWorld)</a>
  33.  */
  34. public class PoissonDistribution extends AbstractIntegerDistribution {
  35.     /** Default maximum number of iterations for cumulative probability calculations. */
  36.     public static final int DEFAULT_MAX_ITERATIONS = 10000000;
  37.     /** Default convergence criterion. */
  38.     public static final double DEFAULT_EPSILON = 1e-12;
  39.     /** Serializable version identifier. */
  40.     private static final long serialVersionUID = 20160320L;
  41.     /** Distribution used to compute normal approximation. */
  42.     private final NormalDistribution normal;
  43.     /** Mean of the distribution. */
  44.     private final double mean;

  45.     /**
  46.      * Maximum number of iterations for cumulative probability. Cumulative
  47.      * probabilities are estimated using either Lanczos series approximation
  48.      * of {@link Gamma#regularizedGammaP(double, double, double, int)}
  49.      * or continued fraction approximation of
  50.      * {@link Gamma#regularizedGammaQ(double, double, double, int)}.
  51.      */
  52.     private final int maxIterations;

  53.     /** Convergence criterion for cumulative probability. */
  54.     private final double epsilon;

  55.     /**
  56.      * Creates a new Poisson distribution with specified mean.
  57.      *
  58.      * @param p the Poisson mean
  59.      * @throws MathIllegalArgumentException if {@code p <= 0}.
  60.      */
  61.     public PoissonDistribution(double p) throws MathIllegalArgumentException {
  62.         this(p, DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS);
  63.     }

  64.     /**
  65.      * Creates a new Poisson distribution with specified mean, convergence
  66.      * criterion and maximum number of iterations.
  67.      *
  68.      * @param p Poisson mean.
  69.      * @param epsilon Convergence criterion for cumulative probabilities.
  70.      * @param maxIterations the maximum number of iterations for cumulative
  71.      * probabilities.
  72.      * @throws MathIllegalArgumentException if {@code p <= 0}.
  73.      */
  74.     public PoissonDistribution(double p, double epsilon, int maxIterations)
  75.         throws MathIllegalArgumentException {
  76.         if (p <= 0) {
  77.             throw new MathIllegalArgumentException(LocalizedCoreFormats.MEAN, p);
  78.         }
  79.         mean = p;
  80.         this.epsilon = epsilon;
  81.         this.maxIterations = maxIterations;

  82.         // Use the same RNG instance as the parent class.
  83.         normal = new NormalDistribution(p, FastMath.sqrt(p));
  84.     }

  85.     /**
  86.      * Creates a new Poisson distribution with the specified mean and
  87.      * convergence criterion.
  88.      *
  89.      * @param p Poisson mean.
  90.      * @param epsilon Convergence criterion for cumulative probabilities.
  91.      * @throws MathIllegalArgumentException if {@code p <= 0}.
  92.      */
  93.     public PoissonDistribution(double p, double epsilon)
  94.         throws MathIllegalArgumentException {
  95.         this(p, epsilon, DEFAULT_MAX_ITERATIONS);
  96.     }

  97.     /**
  98.      * Creates a new Poisson distribution with the specified mean and maximum
  99.      * number of iterations.
  100.      *
  101.      * @param p Poisson mean.
  102.      * @param maxIterations Maximum number of iterations for cumulative probabilities.
  103.      */
  104.     public PoissonDistribution(double p, int maxIterations) {
  105.         this(p, DEFAULT_EPSILON, maxIterations);
  106.     }

  107.     /**
  108.      * Get the mean for the distribution.
  109.      *
  110.      * @return the mean for the distribution.
  111.      */
  112.     public double getMean() {
  113.         return mean;
  114.     }

  115.     /** {@inheritDoc} */
  116.     @Override
  117.     public double probability(int x) {
  118.         final double logProbability = logProbability(x);
  119.         return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability);
  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public double logProbability(int x) {
  124.         double ret;
  125.         if (x < 0 || x == Integer.MAX_VALUE) {
  126.             ret = Double.NEGATIVE_INFINITY;
  127.         } else if (x == 0) {
  128.             ret = -mean;
  129.         } else {
  130.             ret = -SaddlePointExpansion.getStirlingError(x) -
  131.                   SaddlePointExpansion.getDeviancePart(x, mean) -
  132.                   0.5 * FastMath.log(MathUtils.TWO_PI) - 0.5 * FastMath.log(x);
  133.         }
  134.         return ret;
  135.     }

  136.     /** {@inheritDoc} */
  137.     @Override
  138.     public double cumulativeProbability(int x) {
  139.         if (x < 0) {
  140.             return 0;
  141.         }
  142.         if (x == Integer.MAX_VALUE) {
  143.             return 1;
  144.         }
  145.         return Gamma.regularizedGammaQ((double) x + 1, mean, epsilon,
  146.                                        maxIterations);
  147.     }

  148.     /**
  149.      * Calculates the Poisson distribution function using a normal
  150.      * approximation. The {@code N(mean, sqrt(mean))} distribution is used
  151.      * to approximate the Poisson distribution. The computation uses
  152.      * "half-correction" (evaluating the normal distribution function at
  153.      * {@code x + 0.5}).
  154.      *
  155.      * @param x Upper bound, inclusive.
  156.      * @return the distribution function value calculated using a normal
  157.      * approximation.
  158.      */
  159.     public double normalApproximateProbability(int x)  {
  160.         // calculate the probability using half-correction
  161.         return normal.cumulativeProbability(x + 0.5);
  162.     }

  163.     /**
  164.      * {@inheritDoc}
  165.      *
  166.      * For mean parameter {@code p}, the mean is {@code p}.
  167.      */
  168.     @Override
  169.     public double getNumericalMean() {
  170.         return getMean();
  171.     }

  172.     /**
  173.      * {@inheritDoc}
  174.      *
  175.      * For mean parameter {@code p}, the variance is {@code p}.
  176.      */
  177.     @Override
  178.     public double getNumericalVariance() {
  179.         return getMean();
  180.     }

  181.     /**
  182.      * {@inheritDoc}
  183.      *
  184.      * The lower bound of the support is always 0 no matter the mean parameter.
  185.      *
  186.      * @return lower bound of the support (always 0)
  187.      */
  188.     @Override
  189.     public int getSupportLowerBound() {
  190.         return 0;
  191.     }

  192.     /**
  193.      * {@inheritDoc}
  194.      *
  195.      * The upper bound of the support is positive infinity,
  196.      * regardless of the parameter values. There is no integer infinity,
  197.      * so this method returns {@code Integer.MAX_VALUE}.
  198.      *
  199.      * @return upper bound of the support (always {@code Integer.MAX_VALUE} for
  200.      * positive infinity)
  201.      */
  202.     @Override
  203.     public int getSupportUpperBound() {
  204.         return Integer.MAX_VALUE;
  205.     }

  206.     /**
  207.      * {@inheritDoc}
  208.      *
  209.      * The support of this distribution is connected.
  210.      *
  211.      * @return {@code true}
  212.      */
  213.     @Override
  214.     public boolean isSupportConnected() {
  215.         return true;
  216.     }
  217. }